• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 Apple Inc. All rights reserved.
3  * Copyright (C) 2012 Google Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include "config.h"
28 #include "StringBuilder.h"
29 
30 #include "IntegerToStringConversion.h"
31 #include "WTFString.h"
32 
33 namespace WTF {
34 
expandedCapacity(unsigned capacity,unsigned requiredLength)35 static unsigned expandedCapacity(unsigned capacity, unsigned requiredLength)
36 {
37     static const unsigned minimumCapacity = 16;
38     return std::max(requiredLength, std::max(minimumCapacity, capacity * 2));
39 }
40 
reifyString()41 void StringBuilder::reifyString()
42 {
43     if (!m_string.isNull()) {
44         ASSERT(m_string.length() == m_length);
45         return;
46     }
47 
48     if (!m_length) {
49         m_string = StringImpl::empty();
50         return;
51     }
52 
53     ASSERT(m_buffer && m_length <= m_buffer->length());
54     if (m_length == m_buffer->length()) {
55         m_string = m_buffer.release();
56         return;
57     }
58 
59     if (m_buffer->hasOneRef()) {
60         m_buffer->truncateAssumingIsolated(m_length);
61         m_string = m_buffer.release();
62         return;
63     }
64 
65     m_string = m_buffer->substring(0, m_length);
66 }
67 
reifySubstring(unsigned position,unsigned length) const68 String StringBuilder::reifySubstring(unsigned position, unsigned length) const
69 {
70     ASSERT(m_string.isNull());
71     ASSERT(m_buffer);
72     unsigned substringLength = std::min(length, m_length - position);
73     return m_buffer->substring(position, substringLength);
74 }
75 
resize(unsigned newSize)76 void StringBuilder::resize(unsigned newSize)
77 {
78     // Check newSize < m_length, hence m_length > 0.
79     ASSERT(newSize <= m_length);
80     if (newSize == m_length)
81         return;
82     ASSERT(m_length);
83 
84     // If there is a buffer, we only need to duplicate it if it has more than one ref.
85     if (m_buffer) {
86         m_string = String(); // Clear the string to remove the reference to m_buffer if any before checking the reference count of m_buffer.
87         if (!m_buffer->hasOneRef()) {
88             if (m_buffer->is8Bit())
89                 allocateBuffer(m_buffer->characters8(), m_buffer->length());
90             else
91                 allocateBuffer(m_buffer->characters16(), m_buffer->length());
92         }
93         m_length = newSize;
94         return;
95     }
96 
97     // Since m_length && !m_buffer, the string must be valid in m_string, and m_string.length() > 0.
98     ASSERT(!m_string.isEmpty());
99     ASSERT(m_length == m_string.length());
100     ASSERT(newSize < m_string.length());
101     m_length = newSize;
102     RefPtr<StringImpl> string = m_string.releaseImpl();
103     if (string->hasOneRef()) {
104         // If we're the only ones with a reference to the string, we can
105         // re-purpose the string as m_buffer and continue mutating it.
106         m_buffer = string;
107     } else {
108         // Otherwise, we need to make a copy of the string so that we don't
109         // mutate a String that's held elsewhere.
110         m_buffer = string->substring(0, m_length);
111     }
112 }
113 
114 // Allocate a new 8 bit buffer, copying in currentCharacters (these may come from either m_string
115 // or m_buffer, neither will be reassigned until the copy has completed).
allocateBuffer(const LChar * currentCharacters,unsigned requiredLength)116 void StringBuilder::allocateBuffer(const LChar* currentCharacters, unsigned requiredLength)
117 {
118     ASSERT(m_is8Bit);
119     // Copy the existing data into a new buffer, set result to point to the end of the existing data.
120     RefPtr<StringImpl> buffer = StringImpl::createUninitialized(requiredLength, m_bufferCharacters8);
121     memcpy(m_bufferCharacters8, currentCharacters, static_cast<size_t>(m_length) * sizeof(LChar)); // This can't overflow.
122 
123     // Update the builder state.
124     m_buffer = buffer.release();
125     m_string = String();
126 }
127 
128 // Allocate a new 16 bit buffer, copying in currentCharacters (these may come from either m_string
129 // or m_buffer,  neither will be reassigned until the copy has completed).
allocateBuffer(const UChar * currentCharacters,unsigned requiredLength)130 void StringBuilder::allocateBuffer(const UChar* currentCharacters, unsigned requiredLength)
131 {
132     ASSERT(!m_is8Bit);
133     // Copy the existing data into a new buffer, set result to point to the end of the existing data.
134     RefPtr<StringImpl> buffer = StringImpl::createUninitialized(requiredLength, m_bufferCharacters16);
135     memcpy(m_bufferCharacters16, currentCharacters, static_cast<size_t>(m_length) * sizeof(UChar)); // This can't overflow.
136 
137     // Update the builder state.
138     m_buffer = buffer.release();
139     m_string = String();
140 }
141 
142 // Allocate a new 16 bit buffer, copying in currentCharacters (which is 8 bit and may come
143 // from either m_string or m_buffer, neither will be reassigned until the copy has completed).
allocateBufferUpConvert(const LChar * currentCharacters,unsigned requiredLength)144 void StringBuilder::allocateBufferUpConvert(const LChar* currentCharacters, unsigned requiredLength)
145 {
146     ASSERT(m_is8Bit);
147     // Copy the existing data into a new buffer, set result to point to the end of the existing data.
148     RefPtr<StringImpl> buffer = StringImpl::createUninitialized(requiredLength, m_bufferCharacters16);
149     for (unsigned i = 0; i < m_length; ++i)
150         m_bufferCharacters16[i] = currentCharacters[i];
151 
152     m_is8Bit = false;
153 
154     // Update the builder state.
155     m_buffer = buffer.release();
156     m_string = String();
157 }
158 
159 template <>
reallocateBuffer(unsigned requiredLength)160 void StringBuilder::reallocateBuffer<LChar>(unsigned requiredLength)
161 {
162     // If the buffer has only one ref (by this StringBuilder), reallocate it,
163     // otherwise fall back to "allocate and copy" method.
164     m_string = String();
165 
166     ASSERT(m_is8Bit);
167     ASSERT(m_buffer->is8Bit());
168 
169     if (m_buffer->hasOneRef())
170         m_buffer = StringImpl::reallocate(m_buffer.release(), requiredLength, m_bufferCharacters8);
171     else
172         allocateBuffer(m_buffer->characters8(), requiredLength);
173 }
174 
175 template <>
reallocateBuffer(unsigned requiredLength)176 void StringBuilder::reallocateBuffer<UChar>(unsigned requiredLength)
177 {
178     // If the buffer has only one ref (by this StringBuilder), reallocate it,
179     // otherwise fall back to "allocate and copy" method.
180     m_string = String();
181 
182     if (m_buffer->is8Bit())
183         allocateBufferUpConvert(m_buffer->characters8(), requiredLength);
184     else if (m_buffer->hasOneRef())
185         m_buffer = StringImpl::reallocate(m_buffer.release(), requiredLength, m_bufferCharacters16);
186     else
187         allocateBuffer(m_buffer->characters16(), requiredLength);
188 }
189 
reserveCapacity(unsigned newCapacity)190 void StringBuilder::reserveCapacity(unsigned newCapacity)
191 {
192     if (m_buffer) {
193         // If there is already a buffer, then grow if necessary.
194         if (newCapacity > m_buffer->length()) {
195             if (m_buffer->is8Bit())
196                 reallocateBuffer<LChar>(newCapacity);
197             else
198                 reallocateBuffer<UChar>(newCapacity);
199         }
200     } else {
201         // Grow the string, if necessary.
202         if (newCapacity > m_length) {
203             if (!m_length) {
204                 LChar* nullPlaceholder = 0;
205                 allocateBuffer(nullPlaceholder, newCapacity);
206             } else if (m_string.is8Bit())
207                 allocateBuffer(m_string.characters8(), newCapacity);
208             else
209                 allocateBuffer(m_string.characters16(), newCapacity);
210         }
211     }
212 }
213 
214 // Make 'length' additional capacity be available in m_buffer, update m_string & m_length,
215 // return a pointer to the newly allocated storage.
216 template <typename CharType>
appendUninitialized(unsigned length)217 ALWAYS_INLINE CharType* StringBuilder::appendUninitialized(unsigned length)
218 {
219     ASSERT(length);
220 
221     // Calculate the new size of the builder after appending.
222     unsigned requiredLength = length + m_length;
223     RELEASE_ASSERT(requiredLength >= length);
224 
225     if ((m_buffer) && (requiredLength <= m_buffer->length())) {
226         // If the buffer is valid it must be at least as long as the current builder contents!
227         ASSERT(m_buffer->length() >= m_length);
228         unsigned currentLength = m_length;
229         m_string = String();
230         m_length = requiredLength;
231         return getBufferCharacters<CharType>() + currentLength;
232     }
233 
234     return appendUninitializedSlow<CharType>(requiredLength);
235 }
236 
237 // Make 'length' additional capacity be available in m_buffer, update m_string & m_length,
238 // return a pointer to the newly allocated storage.
239 template <typename CharType>
appendUninitializedSlow(unsigned requiredLength)240 CharType* StringBuilder::appendUninitializedSlow(unsigned requiredLength)
241 {
242     ASSERT(requiredLength);
243 
244     if (m_buffer) {
245         // If the buffer is valid it must be at least as long as the current builder contents!
246         ASSERT(m_buffer->length() >= m_length);
247 
248         reallocateBuffer<CharType>(expandedCapacity(capacity(), requiredLength));
249     } else {
250         ASSERT(m_string.length() == m_length);
251         allocateBuffer(m_length ? m_string.getCharacters<CharType>() : 0, expandedCapacity(capacity(), requiredLength));
252     }
253 
254     CharType* result = getBufferCharacters<CharType>() + m_length;
255     m_length = requiredLength;
256     return result;
257 }
258 
append(const UChar * characters,unsigned length)259 void StringBuilder::append(const UChar* characters, unsigned length)
260 {
261     if (!length)
262         return;
263 
264     ASSERT(characters);
265 
266     if (m_is8Bit) {
267         if (length == 1 && !(*characters & ~0xff)) {
268             // Append as 8 bit character
269             LChar lChar = static_cast<LChar>(*characters);
270             append(&lChar, 1);
271             return;
272         }
273 
274         // Calculate the new size of the builder after appending.
275         unsigned requiredLength = length + m_length;
276         RELEASE_ASSERT(requiredLength >= length);
277 
278         if (m_buffer) {
279             // If the buffer is valid it must be at least as long as the current builder contents!
280             ASSERT(m_buffer->length() >= m_length);
281 
282             allocateBufferUpConvert(m_buffer->characters8(), expandedCapacity(capacity(), requiredLength));
283         } else {
284             ASSERT(m_string.length() == m_length);
285             allocateBufferUpConvert(m_string.isNull() ? 0 : m_string.characters8(), expandedCapacity(capacity(), requiredLength));
286         }
287 
288         memcpy(m_bufferCharacters16 + m_length, characters, static_cast<size_t>(length) * sizeof(UChar));
289         m_length = requiredLength;
290     } else
291         memcpy(appendUninitialized<UChar>(length), characters, static_cast<size_t>(length) * sizeof(UChar));
292 }
293 
append(const LChar * characters,unsigned length)294 void StringBuilder::append(const LChar* characters, unsigned length)
295 {
296     if (!length)
297         return;
298     ASSERT(characters);
299 
300     if (m_is8Bit) {
301         LChar* dest = appendUninitialized<LChar>(length);
302         if (length > 8)
303             memcpy(dest, characters, static_cast<size_t>(length) * sizeof(LChar));
304         else {
305             const LChar* end = characters + length;
306             while (characters < end)
307                 *(dest++) = *(characters++);
308         }
309     } else {
310         UChar* dest = appendUninitialized<UChar>(length);
311         const LChar* end = characters + length;
312         while (characters < end)
313             *(dest++) = *(characters++);
314     }
315 }
316 
appendNumber(int number)317 void StringBuilder::appendNumber(int number)
318 {
319     numberToStringSigned<StringBuilder>(number, this);
320 }
321 
appendNumber(unsigned int number)322 void StringBuilder::appendNumber(unsigned int number)
323 {
324     numberToStringUnsigned<StringBuilder>(number, this);
325 }
326 
appendNumber(long number)327 void StringBuilder::appendNumber(long number)
328 {
329     numberToStringSigned<StringBuilder>(number, this);
330 }
331 
appendNumber(unsigned long number)332 void StringBuilder::appendNumber(unsigned long number)
333 {
334     numberToStringUnsigned<StringBuilder>(number, this);
335 }
336 
appendNumber(long long number)337 void StringBuilder::appendNumber(long long number)
338 {
339     numberToStringSigned<StringBuilder>(number, this);
340 }
341 
appendNumber(unsigned long long number)342 void StringBuilder::appendNumber(unsigned long long number)
343 {
344     numberToStringUnsigned<StringBuilder>(number, this);
345 }
346 
canShrink() const347 bool StringBuilder::canShrink() const
348 {
349     // Only shrink the buffer if it's less than 80% full. Need to tune this heuristic!
350     return m_buffer && m_buffer->length() > (m_length + (m_length >> 2));
351 }
352 
shrinkToFit()353 void StringBuilder::shrinkToFit()
354 {
355     if (!canShrink())
356         return;
357     if (m_is8Bit)
358         reallocateBuffer<LChar>(m_length);
359     else
360         reallocateBuffer<UChar>(m_length);
361     m_string = m_buffer.release();
362 }
363 
364 } // namespace WTF
365