OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright (C) 2010 Apple Inc. All rights reserved. | 2 * Copyright (C) 2010 Apple Inc. All rights reserved. |
3 * Copyright (C) 2012 Google Inc. All rights reserved. | 3 * Copyright (C) 2012 Google Inc. All rights reserved. |
4 * | 4 * |
5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
6 * modification, are permitted provided that the following conditions | 6 * modification, are permitted provided that the following conditions |
7 * are met: | 7 * are met: |
8 * 1. Redistributions of source code must retain the above copyright | 8 * 1. Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * 2. Redistributions in binary form must reproduce the above copyright | 10 * 2. Redistributions in binary form must reproduce the above copyright |
(...skipping 15 matching lines...) Expand all Loading... | |
26 | 26 |
27 #include "wtf/text/StringBuilder.h" | 27 #include "wtf/text/StringBuilder.h" |
28 | 28 |
29 #include "wtf/dtoa.h" | 29 #include "wtf/dtoa.h" |
30 #include "wtf/text/IntegerToStringConversion.h" | 30 #include "wtf/text/IntegerToStringConversion.h" |
31 #include "wtf/text/WTFString.h" | 31 #include "wtf/text/WTFString.h" |
32 #include <algorithm> | 32 #include <algorithm> |
33 | 33 |
34 namespace WTF { | 34 namespace WTF { |
35 | 35 |
36 static unsigned expandedCapacity(unsigned capacity, unsigned requiredLength) | 36 String StringBuilder::toString() |
37 { | 37 { |
38 static const unsigned minimumCapacity = 16; | 38 if (!m_length) |
39 return std::max(requiredLength, std::max(minimumCapacity, capacity * 2)); | 39 return emptyString(); |
40 if (m_string.isNull()) { | |
41 if (m_is8Bit) | |
42 m_string = String(characters8(), m_length); | |
43 else | |
44 m_string = String(characters16(), m_length); | |
45 } | |
46 return m_string; | |
40 } | 47 } |
41 | 48 |
42 void StringBuilder::reifyString() | 49 AtomicString StringBuilder::toAtomicString() |
43 { | 50 { |
44 if (!m_string.isNull()) { | 51 if (!m_length) |
45 DCHECK_EQ(m_string.length(), m_length); | 52 return emptyAtom; |
46 return; | 53 if (m_string.isNull()) { |
54 if (m_is8Bit) | |
55 m_string = AtomicString(characters8(), m_length); | |
56 else | |
57 m_string = AtomicString(characters16(), m_length); | |
47 } | 58 } |
48 | 59 return AtomicString(m_string); |
49 if (!m_length) { | |
50 m_string = StringImpl::empty(); | |
51 return; | |
52 } | |
53 | |
54 DCHECK(m_buffer); | |
55 DCHECK_LE(m_length, m_buffer->length()); | |
56 if (m_length == m_buffer->length()) { | |
57 m_string = m_buffer.release(); | |
58 return; | |
59 } | |
60 | |
61 m_string = m_buffer->substring(0, m_length); | |
62 } | 60 } |
63 | 61 |
64 String StringBuilder::reifySubstring(unsigned position, unsigned length) const | 62 String StringBuilder::substring(unsigned start, unsigned length) const |
65 { | 63 { |
66 DCHECK(m_string.isNull()); | 64 if (start >= m_length) |
67 DCHECK(m_buffer); | 65 return emptyString(); |
68 unsigned substringLength = std::min(length, m_length - position); | 66 if (!m_string.isNull()) |
69 return m_buffer->substring(position, substringLength); | 67 return m_string.substring(start, length); |
68 length = std::min(length, m_length - start); | |
69 if (m_is8Bit) | |
70 return String(characters8() + start, length); | |
71 return String(characters16() + start, length); | |
72 } | |
73 | |
74 void StringBuilder::swap(StringBuilder& builder) | |
75 { | |
76 std::swap(m_string, builder.m_string); | |
77 std::swap(m_buffer, builder.m_buffer); | |
78 std::swap(m_length, builder.m_length); | |
79 std::swap(m_is8Bit, builder.m_is8Bit); | |
80 } | |
81 | |
82 void StringBuilder::clear() | |
83 { | |
84 m_string = String(); | |
85 if (m_is8Bit) | |
86 delete m_buffer8; | |
haraken
2016/06/27 06:36:01
Can we use OwnPtr and avoid using manual delete?
hajimehoshi
2016/06/27 06:38:02
unique_ptr I think :-)
esprehn
2016/06/27 23:36:40
No, you can't store a unique_ptr in a union. So we
hajimehoshi
2016/06/28 05:01:35
Out of curiosity, isn't it possible to use two uni
| |
87 else | |
88 delete m_buffer16; | |
89 m_buffer = nullptr; | |
90 m_length = 0; | |
91 m_is8Bit = true; | |
92 } | |
93 | |
94 unsigned StringBuilder::capacity() const | |
95 { | |
96 if (!hasBuffer()) | |
97 return 0; | |
98 if (m_is8Bit) | |
99 return m_buffer8->capacity(); | |
100 return m_buffer16->capacity(); | |
101 } | |
102 | |
103 void StringBuilder::reserveCapacity(unsigned newCapacity) | |
104 { | |
105 if (m_is8Bit) { | |
106 ensureBuffer8(); | |
107 m_buffer8->reserveCapacity(newCapacity); | |
108 } else { | |
109 ensureBuffer16(); | |
110 m_buffer16->reserveCapacity(newCapacity); | |
111 } | |
70 } | 112 } |
71 | 113 |
72 void StringBuilder::resize(unsigned newSize) | 114 void StringBuilder::resize(unsigned newSize) |
73 { | 115 { |
74 // Check newSize < m_length, hence m_length > 0. | |
75 DCHECK_LE(newSize, m_length); | 116 DCHECK_LE(newSize, m_length); |
76 if (newSize == m_length) | 117 m_length = newSize; |
118 m_string = String(); | |
119 if (!hasBuffer()) | |
77 return; | 120 return; |
78 DCHECK(m_length); | 121 if (m_is8Bit) |
122 m_buffer8->resize(newSize); | |
123 else | |
124 m_buffer16->resize(newSize); | |
125 } | |
79 | 126 |
80 // If there is a buffer, we only need to duplicate it if it has more than on e ref. | 127 void StringBuilder::createBuffer8() |
81 if (m_buffer) { | 128 { |
82 m_string = String(); // Clear the string to remove the reference to m_bu ffer if any before checking the reference count of m_buffer. | 129 DCHECK(!hasBuffer()); |
83 if (!m_buffer->hasOneRef()) { | 130 DCHECK(m_is8Bit); |
84 if (m_buffer->is8Bit()) | 131 m_buffer8 = new Buffer8; |
85 allocateBuffer(m_buffer->characters8(), m_buffer->length()); | 132 m_length = 0; |
86 else | 133 // Must keep a ref to the string since append will clear it. |
87 allocateBuffer(m_buffer->characters16(), m_buffer->length()); | 134 String string(m_string); |
88 } | 135 append(string); |
89 m_length = newSize; | 136 } |
137 | |
138 void StringBuilder::createBuffer16() | |
139 { | |
140 DCHECK(m_is8Bit || !hasBuffer()); | |
haraken
2016/06/27 06:36:01
Slightly better:
DCHECK((m_is8Bit && m_buffer8)
esprehn
2016/06/27 23:36:40
I'm not sure what that does, if m_buffer8 is null
| |
141 Buffer8 buffer8; | |
142 unsigned length = m_length; | |
143 if (m_buffer8) { | |
144 m_buffer8->swap(buffer8); | |
145 delete m_buffer8; | |
146 } | |
147 m_buffer16 = new Buffer16; | |
148 m_is8Bit = false; | |
149 m_length = 0; | |
150 if (!buffer8.isEmpty()) { | |
151 append(buffer8.data(), length); | |
90 return; | 152 return; |
91 } | 153 } |
92 | 154 // Must keep a ref to the string since append will clear it. |
93 // Since m_length && !m_buffer, the string must be valid in m_string, and m_ string.length() > 0. | 155 String string(m_string); |
94 DCHECK(!m_string.isEmpty()); | 156 append(string); |
95 DCHECK_EQ(m_length, m_string.length()); | |
96 DCHECK_LT(newSize, m_string.length()); | |
97 m_length = newSize; | |
98 RefPtr<StringImpl> string = m_string.releaseImpl(); | |
99 if (string->hasOneRef()) { | |
100 // If we're the only ones with a reference to the string, we can | |
101 // re-purpose the string as m_buffer and continue mutating it. | |
102 m_buffer = string; | |
103 } else { | |
104 // Otherwise, we need to make a copy of the string so that we don't | |
105 // mutate a String that's held elsewhere. | |
106 m_buffer = string->substring(0, m_length); | |
107 } | |
108 } | |
109 | |
110 // Allocate a new 8 bit buffer, copying in currentCharacters (these may come fro m either m_string | |
111 // or m_buffer, neither will be reassigned until the copy has completed). | |
112 void StringBuilder::allocateBuffer(const LChar* currentCharacters, unsigned requ iredLength) | |
113 { | |
114 DCHECK(m_is8Bit); | |
115 // Copy the existing data into a new buffer, set result to point to the end of the existing data. | |
116 RefPtr<StringImpl> buffer = StringImpl::createUninitialized(requiredLength, m_bufferCharacters8); | |
117 memcpy(m_bufferCharacters8, currentCharacters, static_cast<size_t>(m_length) * sizeof(LChar)); // This can't overflow. | |
118 | |
119 // Update the builder state. | |
120 m_buffer = buffer.release(); | |
121 m_string = String(); | |
122 } | |
123 | |
124 // Allocate a new 16 bit buffer, copying in currentCharacters (these may come fr om either m_string | |
125 // or m_buffer, neither will be reassigned until the copy has completed). | |
126 void StringBuilder::allocateBuffer(const UChar* currentCharacters, unsigned requ iredLength) | |
127 { | |
128 DCHECK(!m_is8Bit); | |
129 // Copy the existing data into a new buffer, set result to point to the end of the existing data. | |
130 RefPtr<StringImpl> buffer = StringImpl::createUninitialized(requiredLength, m_bufferCharacters16); | |
131 memcpy(m_bufferCharacters16, currentCharacters, static_cast<size_t>(m_length ) * sizeof(UChar)); // This can't overflow. | |
132 | |
133 // Update the builder state. | |
134 m_buffer = buffer.release(); | |
135 m_string = String(); | |
136 } | |
137 | |
138 // Allocate a new 16 bit buffer, copying in currentCharacters (which is 8 bit an d may come | |
139 // from either m_string or m_buffer, neither will be reassigned until the copy h as completed). | |
140 void StringBuilder::allocateBufferUpConvert(const LChar* currentCharacters, unsi gned requiredLength) | |
141 { | |
142 DCHECK(m_is8Bit); | |
143 // Copy the existing data into a new buffer, set result to point to the end of the existing data. | |
144 RefPtr<StringImpl> buffer = StringImpl::createUninitialized(requiredLength, m_bufferCharacters16); | |
145 for (unsigned i = 0; i < m_length; ++i) | |
146 m_bufferCharacters16[i] = currentCharacters[i]; | |
147 | |
148 m_is8Bit = false; | |
149 | |
150 // Update the builder state. | |
151 m_buffer = buffer.release(); | |
152 m_string = String(); | |
153 } | |
154 | |
155 template <> | |
156 void StringBuilder::reallocateBuffer<LChar>(unsigned requiredLength) | |
157 { | |
158 // If the buffer has only one ref (by this StringBuilder), reallocate it, | |
159 // otherwise fall back to "allocate and copy" method. | |
160 m_string = String(); | |
161 | |
162 DCHECK(m_is8Bit); | |
163 DCHECK(m_buffer->is8Bit()); | |
164 | |
165 allocateBuffer(m_buffer->characters8(), requiredLength); | |
166 } | |
167 | |
168 template <> | |
169 void StringBuilder::reallocateBuffer<UChar>(unsigned requiredLength) | |
170 { | |
171 // If the buffer has only one ref (by this StringBuilder), reallocate it, | |
172 // otherwise fall back to "allocate and copy" method. | |
173 m_string = String(); | |
174 | |
175 if (m_buffer->is8Bit()) | |
176 allocateBufferUpConvert(m_buffer->characters8(), requiredLength); | |
177 else | |
178 allocateBuffer(m_buffer->characters16(), requiredLength); | |
179 } | |
180 | |
181 void StringBuilder::reserveCapacity(unsigned newCapacity) | |
182 { | |
183 if (m_buffer) { | |
184 // If there is already a buffer, then grow if necessary. | |
185 if (newCapacity > m_buffer->length()) { | |
186 if (m_buffer->is8Bit()) | |
187 reallocateBuffer<LChar>(newCapacity); | |
188 else | |
189 reallocateBuffer<UChar>(newCapacity); | |
190 } | |
191 } else { | |
192 // Grow the string, if necessary. | |
193 if (newCapacity > m_length) { | |
194 if (!m_length) { | |
195 LChar* nullPlaceholder = 0; | |
196 allocateBuffer(nullPlaceholder, newCapacity); | |
197 } else if (m_string.is8Bit()) { | |
198 allocateBuffer(m_string.characters8(), newCapacity); | |
199 } else { | |
200 allocateBuffer(m_string.characters16(), newCapacity); | |
201 } | |
202 } | |
203 } | |
204 } | |
205 | |
206 // Make 'length' additional capacity be available in m_buffer, update m_string & m_length, | |
207 // return a pointer to the newly allocated storage. | |
208 template <typename CharType> | |
209 ALWAYS_INLINE CharType* StringBuilder::appendUninitialized(unsigned length) | |
210 { | |
211 DCHECK(length); | |
212 | |
213 // Calculate the new size of the builder after appending. | |
214 unsigned requiredLength = length + m_length; | |
215 CHECK_GE(requiredLength, length); | |
216 | |
217 if ((m_buffer) && (requiredLength <= m_buffer->length())) { | |
218 // If the buffer is valid it must be at least as long as the current bui lder contents! | |
219 DCHECK_GE(m_buffer->length(), m_length); | |
220 unsigned currentLength = m_length; | |
221 m_string = String(); | |
222 m_length = requiredLength; | |
223 return getBufferCharacters<CharType>() + currentLength; | |
224 } | |
225 | |
226 return appendUninitializedSlow<CharType>(requiredLength); | |
227 } | |
228 | |
229 // Make 'length' additional capacity be available in m_buffer, update m_string & m_length, | |
230 // return a pointer to the newly allocated storage. | |
231 template <typename CharType> | |
232 CharType* StringBuilder::appendUninitializedSlow(unsigned requiredLength) | |
233 { | |
234 DCHECK(requiredLength); | |
235 | |
236 if (m_buffer) { | |
237 // If the buffer is valid it must be at least as long as the current bui lder contents! | |
238 DCHECK_GE(m_buffer->length(), m_length); | |
239 | |
240 reallocateBuffer<CharType>(expandedCapacity(capacity(), requiredLength)) ; | |
241 } else { | |
242 DCHECK_EQ(m_string.length(), m_length); | |
243 allocateBuffer(m_length ? m_string.getCharacters<CharType>() : 0, expand edCapacity(capacity(), requiredLength)); | |
244 } | |
245 | |
246 CharType* result = getBufferCharacters<CharType>() + m_length; | |
247 m_length = requiredLength; | |
248 return result; | |
249 } | 157 } |
250 | 158 |
251 void StringBuilder::append(const UChar* characters, unsigned length) | 159 void StringBuilder::append(const UChar* characters, unsigned length) |
252 { | 160 { |
253 if (!length) | 161 if (!length) |
254 return; | 162 return; |
255 | |
256 DCHECK(characters); | 163 DCHECK(characters); |
257 | 164 |
258 if (m_is8Bit) { | 165 // If there's only one char we use append(UChar) instead since it will |
259 if (length == 1 && !(*characters & ~0xff)) { | 166 // check for latin1 and avoid converting to 16bit if possible. |
260 // Append as 8 bit character | 167 if (length == 1) { |
261 LChar lChar = static_cast<LChar>(*characters); | 168 append(*characters); |
262 append(&lChar, 1); | 169 return; |
263 return; | 170 } |
264 } | |
265 | 171 |
266 // Calculate the new size of the builder after appending. | 172 ensureBuffer16(); |
267 unsigned requiredLength = length + m_length; | 173 m_string = String(); |
268 CHECK_GE(requiredLength, length); | 174 m_buffer16->append(characters, length); |
269 | 175 m_length += length; |
270 if (m_buffer) { | |
271 // If the buffer is valid it must be at least as long as the current builder contents! | |
272 DCHECK_GE(m_buffer->length(), m_length); | |
273 | |
274 allocateBufferUpConvert(m_buffer->characters8(), expandedCapacity(ca pacity(), requiredLength)); | |
275 } else { | |
276 DCHECK_EQ(m_string.length(), m_length); | |
277 allocateBufferUpConvert(m_string.isNull() ? 0 : m_string.characters8 (), expandedCapacity(capacity(), requiredLength)); | |
278 } | |
279 | |
280 memcpy(m_bufferCharacters16 + m_length, characters, static_cast<size_t>( length) * sizeof(UChar)); | |
281 m_length = requiredLength; | |
282 } else { | |
283 memcpy(appendUninitialized<UChar>(length), characters, static_cast<size_ t>(length) * sizeof(UChar)); | |
284 } | |
285 } | 176 } |
286 | 177 |
287 void StringBuilder::append(const LChar* characters, unsigned length) | 178 void StringBuilder::append(const LChar* characters, unsigned length) |
288 { | 179 { |
289 if (!length) | 180 if (!length) |
290 return; | 181 return; |
291 DCHECK(characters); | 182 DCHECK(characters); |
292 | 183 |
293 if (m_is8Bit) { | 184 if (m_is8Bit) { |
294 LChar* dest = appendUninitialized<LChar>(length); | 185 ensureBuffer8(); |
295 if (length > 8) { | 186 m_string = String(); |
296 memcpy(dest, characters, static_cast<size_t>(length) * sizeof(LChar) ); | 187 m_buffer8->append(characters, length); |
297 } else { | 188 m_length += length; |
298 const LChar* end = characters + length; | 189 return; |
299 while (characters < end) | |
300 *(dest++) = *(characters++); | |
301 } | |
302 } else { | |
303 UChar* dest = appendUninitialized<UChar>(length); | |
304 const LChar* end = characters + length; | |
305 while (characters < end) | |
306 *(dest++) = *(characters++); | |
307 } | 190 } |
191 | |
192 ensureBuffer16(); | |
193 m_string = String(); | |
194 m_buffer16->reserveCapacity(m_buffer16->size() + length); | |
195 for (size_t i = 0; i < length; ++i) | |
196 m_buffer16->uncheckedAppend(characters[i]); | |
197 m_length += length; | |
308 } | 198 } |
309 | 199 |
310 template<typename IntegerType> | 200 template<typename IntegerType> |
311 static void appendIntegerInternal(StringBuilder& builder, IntegerType input) | 201 static void appendIntegerInternal(StringBuilder& builder, IntegerType input) |
312 { | 202 { |
313 IntegerToStringConverter<IntegerType> converter(input); | 203 IntegerToStringConverter<IntegerType> converter(input); |
314 builder.append(converter.characters8(), converter.length()); | 204 builder.append(converter.characters8(), converter.length()); |
315 } | 205 } |
316 | 206 |
317 void StringBuilder::appendNumber(int number) | 207 void StringBuilder::appendNumber(int number) |
(...skipping 19 matching lines...) Expand all Loading... | |
337 void StringBuilder::appendNumber(long long number) | 227 void StringBuilder::appendNumber(long long number) |
338 { | 228 { |
339 appendIntegerInternal(*this, number); | 229 appendIntegerInternal(*this, number); |
340 } | 230 } |
341 | 231 |
342 void StringBuilder::appendNumber(unsigned long long number) | 232 void StringBuilder::appendNumber(unsigned long long number) |
343 { | 233 { |
344 appendIntegerInternal(*this, number); | 234 appendIntegerInternal(*this, number); |
345 } | 235 } |
346 | 236 |
347 static void expandLCharToUCharInplace(UChar* buffer, size_t length) | |
348 { | |
349 const LChar* sourceEnd = reinterpret_cast<LChar*>(buffer) + length; | |
350 UChar* current = buffer + length; | |
351 while (current != buffer) | |
352 *--current = *--sourceEnd; | |
353 } | |
354 | |
355 void StringBuilder::appendNumber(double number, unsigned precision, TrailingZero sTruncatingPolicy trailingZerosTruncatingPolicy) | 237 void StringBuilder::appendNumber(double number, unsigned precision, TrailingZero sTruncatingPolicy trailingZerosTruncatingPolicy) |
356 { | 238 { |
357 bool truncateTrailingZeros = trailingZerosTruncatingPolicy == TruncateTraili ngZeros; | 239 NumberToStringBuffer buffer; |
358 size_t numberLength; | 240 append(numberToFixedPrecisionString(number, precision, buffer, trailingZeros TruncatingPolicy == TruncateTrailingZeros)); |
359 if (m_is8Bit) { | |
360 LChar* dest = appendUninitialized<LChar>(NumberToStringBufferLength); | |
361 const char* result = numberToFixedPrecisionString(number, precision, rei nterpret_cast<char*>(dest), truncateTrailingZeros); | |
362 numberLength = strlen(result); | |
363 } else { | |
364 UChar* dest = appendUninitialized<UChar>(NumberToStringBufferLength); | |
365 const char* result = numberToFixedPrecisionString(number, precision, rei nterpret_cast<char*>(dest), truncateTrailingZeros); | |
366 numberLength = strlen(result); | |
367 expandLCharToUCharInplace(dest, numberLength); | |
368 } | |
369 DCHECK_GE(m_length, NumberToStringBufferLength); | |
370 // Remove what appendUninitialized added. | |
371 m_length -= NumberToStringBufferLength; | |
372 DCHECK_LE(numberLength, NumberToStringBufferLength); | |
373 m_length += numberLength; | |
374 } | |
375 | |
376 bool StringBuilder::canShrink() const | |
377 { | |
378 // Only shrink the buffer if it's less than 80% full. Need to tune this heur istic! | |
379 return m_buffer && m_buffer->length() > (m_length + (m_length >> 2)); | |
380 } | |
381 | |
382 void StringBuilder::shrinkToFit() | |
383 { | |
384 if (!canShrink()) | |
385 return; | |
386 if (m_is8Bit) | |
387 reallocateBuffer<LChar>(m_length); | |
388 else | |
389 reallocateBuffer<UChar>(m_length); | |
390 m_string = m_buffer.release(); | |
391 } | 241 } |
392 | 242 |
393 } // namespace WTF | 243 } // namespace WTF |
OLD | NEW |