1 | /* |
2 | * Copyright (C) 2009-2019 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 | #pragma once |
28 | |
29 | #include <wtf/CheckedArithmetic.h> |
30 | #include <wtf/text/AtomString.h> |
31 | #include <wtf/text/IntegerToStringConversion.h> |
32 | #include <wtf/text/StringConcatenateNumbers.h> |
33 | #include <wtf/text/StringView.h> |
34 | #include <wtf/text/WTFString.h> |
35 | |
36 | namespace WTF { |
37 | |
38 | // StringBuilder currently uses a Checked<int32_t, ConditionalCrashOnOverflow> for m_length. |
39 | // Ideally, we would want to make StringBuilder a template with an OverflowHandler parameter, and |
40 | // m_length can be instantiated based on that OverflowHandler instead. However, currently, we're |
41 | // not able to get clang to export explicitly instantiated template methods (which would be needed |
42 | // if we templatize StringBuilder). As a workaround, we use the ConditionalCrashOnOverflow handler |
43 | // instead to do a runtime check on whether it should crash on overflows or not. |
44 | // |
45 | // When clang is able to export explicitly instantiated template methods, we can templatize |
46 | // StringBuilder and do away with ConditionalCrashOnOverflow. |
47 | // See https://bugs.webkit.org/show_bug.cgi?id=191050. |
48 | |
49 | class StringBuilder { |
50 | // Disallow copying since it's expensive and we don't want code to do it by accident. |
51 | WTF_MAKE_NONCOPYABLE(StringBuilder); |
52 | WTF_MAKE_FAST_ALLOCATED; |
53 | |
54 | public: |
55 | enum class OverflowHandler { |
56 | CrashOnOverflow, |
57 | RecordOverflow |
58 | }; |
59 | |
60 | StringBuilder(OverflowHandler handler = OverflowHandler::CrashOnOverflow) |
61 | : m_bufferCharacters8(nullptr) |
62 | { |
63 | m_length.setShouldCrashOnOverflow(handler == OverflowHandler::CrashOnOverflow); |
64 | } |
65 | StringBuilder(StringBuilder&&) = default; |
66 | StringBuilder& operator=(StringBuilder&&) = default; |
67 | |
68 | ALWAYS_INLINE void didOverflow() { m_length.overflowed(); } |
69 | ALWAYS_INLINE bool hasOverflowed() const { return m_length.hasOverflowed(); } |
70 | ALWAYS_INLINE bool crashesOnOverflow() const { return m_length.shouldCrashOnOverflow(); } |
71 | |
72 | WTF_EXPORT_PRIVATE void appendCharacters(const UChar*, unsigned); |
73 | WTF_EXPORT_PRIVATE void appendCharacters(const LChar*, unsigned); |
74 | |
75 | ALWAYS_INLINE void appendCharacters(const char* characters, unsigned length) { appendCharacters(reinterpret_cast<const LChar*>(characters), length); } |
76 | |
77 | void append(const AtomString& atomString) |
78 | { |
79 | append(atomString.string()); |
80 | } |
81 | |
82 | void append(const String& string) |
83 | { |
84 | if (hasOverflowed()) |
85 | return; |
86 | |
87 | if (!string.length()) |
88 | return; |
89 | |
90 | // If we're appending to an empty string, and there is not a buffer (reserveCapacity has not been called) |
91 | // then just retain the string. |
92 | if (!m_length && !m_buffer) { |
93 | m_string = string; |
94 | m_length = string.length(); |
95 | m_is8Bit = m_string.is8Bit(); |
96 | return; |
97 | } |
98 | |
99 | if (string.is8Bit()) |
100 | appendCharacters(string.characters8(), string.length()); |
101 | else |
102 | appendCharacters(string.characters16(), string.length()); |
103 | } |
104 | |
105 | void append(const StringBuilder& other) |
106 | { |
107 | if (hasOverflowed()) |
108 | return; |
109 | if (other.hasOverflowed()) |
110 | return didOverflow(); |
111 | |
112 | if (!other.m_length) |
113 | return; |
114 | |
115 | // If we're appending to an empty string, and there is not a buffer (reserveCapacity has not been called) |
116 | // then just retain the string. |
117 | if (!m_length && !m_buffer && !other.m_string.isNull()) { |
118 | m_string = other.m_string; |
119 | m_length = other.m_length; |
120 | m_is8Bit = other.m_is8Bit; |
121 | return; |
122 | } |
123 | |
124 | if (other.is8Bit()) |
125 | appendCharacters(other.characters8(), other.m_length.unsafeGet()); |
126 | else |
127 | appendCharacters(other.characters16(), other.m_length.unsafeGet()); |
128 | } |
129 | |
130 | void append(StringView stringView) |
131 | { |
132 | if (stringView.is8Bit()) |
133 | appendCharacters(stringView.characters8(), stringView.length()); |
134 | else |
135 | appendCharacters(stringView.characters16(), stringView.length()); |
136 | } |
137 | |
138 | #if USE(CF) |
139 | WTF_EXPORT_PRIVATE void append(CFStringRef); |
140 | #endif |
141 | #if USE(CF) && defined(__OBJC__) |
142 | void append(NSString *string) { append((__bridge CFStringRef)string); } |
143 | #endif |
144 | |
145 | void appendSubstring(const String& string, unsigned offset, unsigned length) |
146 | { |
147 | if (!string.length()) |
148 | return; |
149 | |
150 | if ((offset + length) > string.length()) |
151 | return; |
152 | |
153 | if (string.is8Bit()) |
154 | appendCharacters(string.characters8() + offset, length); |
155 | else |
156 | appendCharacters(string.characters16() + offset, length); |
157 | } |
158 | |
159 | void append(const char* characters) |
160 | { |
161 | if (characters) |
162 | appendCharacters(characters, strlen(characters)); |
163 | } |
164 | |
165 | void appendCharacter(UChar) = delete; |
166 | void append(UChar c) |
167 | { |
168 | if (hasOverflowed()) |
169 | return; |
170 | unsigned length = m_length.unsafeGet<unsigned>(); |
171 | if (m_buffer && length < m_buffer->length() && m_string.isNull()) { |
172 | if (!m_is8Bit) { |
173 | m_bufferCharacters16[length] = c; |
174 | m_length++; |
175 | return; |
176 | } |
177 | |
178 | if (isLatin1(c)) { |
179 | m_bufferCharacters8[length] = static_cast<LChar>(c); |
180 | m_length++; |
181 | return; |
182 | } |
183 | } |
184 | appendCharacters(&c, 1); |
185 | } |
186 | |
187 | void appendCharacter(LChar) = delete; |
188 | void append(LChar c) |
189 | { |
190 | if (hasOverflowed()) |
191 | return; |
192 | unsigned length = m_length.unsafeGet<unsigned>(); |
193 | if (m_buffer && length < m_buffer->length() && m_string.isNull()) { |
194 | if (m_is8Bit) |
195 | m_bufferCharacters8[length] = c; |
196 | else |
197 | m_bufferCharacters16[length] = c; |
198 | m_length++; |
199 | } else |
200 | appendCharacters(&c, 1); |
201 | } |
202 | |
203 | void appendCharacter(char) = delete; |
204 | void append(char c) |
205 | { |
206 | append(static_cast<LChar>(c)); |
207 | } |
208 | |
209 | void appendCharacter(UChar32 c) |
210 | { |
211 | if (U_IS_BMP(c)) { |
212 | append(static_cast<UChar>(c)); |
213 | return; |
214 | } |
215 | append(U16_LEAD(c)); |
216 | append(U16_TRAIL(c)); |
217 | } |
218 | |
219 | WTF_EXPORT_PRIVATE void appendQuotedJSONString(const String&); |
220 | |
221 | template<unsigned characterCount> |
222 | ALWAYS_INLINE void appendLiteral(const char (&characters)[characterCount]) { appendCharacters(characters, characterCount - 1); } |
223 | |
224 | WTF_EXPORT_PRIVATE void appendNumber(int); |
225 | WTF_EXPORT_PRIVATE void appendNumber(unsigned); |
226 | WTF_EXPORT_PRIVATE void appendNumber(long); |
227 | WTF_EXPORT_PRIVATE void appendNumber(unsigned long); |
228 | WTF_EXPORT_PRIVATE void appendNumber(long long); |
229 | WTF_EXPORT_PRIVATE void appendNumber(unsigned long long); |
230 | WTF_EXPORT_PRIVATE void appendNumber(float); |
231 | WTF_EXPORT_PRIVATE void appendNumber(double); |
232 | |
233 | WTF_EXPORT_PRIVATE void appendFixedPrecisionNumber(float, unsigned precision = 6, TrailingZerosTruncatingPolicy = TruncateTrailingZeros); |
234 | WTF_EXPORT_PRIVATE void appendFixedPrecisionNumber(double, unsigned precision = 6, TrailingZerosTruncatingPolicy = TruncateTrailingZeros); |
235 | WTF_EXPORT_PRIVATE void appendFixedWidthNumber(float, unsigned decimalPlaces); |
236 | WTF_EXPORT_PRIVATE void appendFixedWidthNumber(double, unsigned decimalPlaces); |
237 | |
238 | template<typename... StringTypes> void append(StringTypes...); |
239 | |
240 | String toString() |
241 | { |
242 | if (!m_string.isNull()) { |
243 | ASSERT(!m_buffer || m_isReified); |
244 | ASSERT(!hasOverflowed()); |
245 | return m_string; |
246 | } |
247 | |
248 | RELEASE_ASSERT(!hasOverflowed()); |
249 | shrinkToFit(); |
250 | reifyString(); |
251 | return m_string; |
252 | } |
253 | |
254 | const String& toStringPreserveCapacity() const |
255 | { |
256 | RELEASE_ASSERT(!hasOverflowed()); |
257 | if (m_string.isNull()) |
258 | reifyString(); |
259 | return m_string; |
260 | } |
261 | |
262 | AtomString toAtomString() const |
263 | { |
264 | RELEASE_ASSERT(!hasOverflowed()); |
265 | if (!m_length) |
266 | return emptyAtom(); |
267 | |
268 | // If the buffer is sufficiently over-allocated, make a new AtomString from a copy so its buffer is not so large. |
269 | if (canShrink()) { |
270 | if (is8Bit()) |
271 | return AtomString(characters8(), length()); |
272 | return AtomString(characters16(), length()); |
273 | } |
274 | |
275 | if (!m_string.isNull()) |
276 | return AtomString(m_string); |
277 | |
278 | ASSERT(m_buffer); |
279 | return AtomString(m_buffer.get(), 0, m_length.unsafeGet()); |
280 | } |
281 | |
282 | unsigned length() const |
283 | { |
284 | RELEASE_ASSERT(!hasOverflowed()); |
285 | return m_length.unsafeGet(); |
286 | } |
287 | |
288 | bool isEmpty() const { return !m_length; } |
289 | |
290 | WTF_EXPORT_PRIVATE void reserveCapacity(unsigned newCapacity); |
291 | |
292 | unsigned capacity() const |
293 | { |
294 | RELEASE_ASSERT(!hasOverflowed()); |
295 | return m_buffer ? m_buffer->length() : m_length.unsafeGet(); |
296 | } |
297 | |
298 | WTF_EXPORT_PRIVATE void resize(unsigned newSize); |
299 | |
300 | WTF_EXPORT_PRIVATE bool canShrink() const; |
301 | |
302 | WTF_EXPORT_PRIVATE void shrinkToFit(); |
303 | |
304 | UChar operator[](unsigned i) const |
305 | { |
306 | RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!hasOverflowed() && i < m_length.unsafeGet<unsigned>()); |
307 | if (m_is8Bit) |
308 | return characters8()[i]; |
309 | return characters16()[i]; |
310 | } |
311 | |
312 | const LChar* characters8() const |
313 | { |
314 | ASSERT(m_is8Bit); |
315 | if (!m_length) |
316 | return nullptr; |
317 | if (!m_string.isNull()) |
318 | return m_string.characters8(); |
319 | ASSERT(m_buffer); |
320 | return m_buffer->characters8(); |
321 | } |
322 | |
323 | const UChar* characters16() const |
324 | { |
325 | ASSERT(!m_is8Bit); |
326 | if (!m_length) |
327 | return nullptr; |
328 | if (!m_string.isNull()) |
329 | return m_string.characters16(); |
330 | ASSERT(m_buffer); |
331 | return m_buffer->characters16(); |
332 | } |
333 | |
334 | bool is8Bit() const { return m_is8Bit; } |
335 | |
336 | void clear() |
337 | { |
338 | m_length = 0; |
339 | m_string = String(); |
340 | m_buffer = nullptr; |
341 | m_bufferCharacters8 = nullptr; |
342 | m_is8Bit = true; |
343 | } |
344 | |
345 | void swap(StringBuilder& stringBuilder) |
346 | { |
347 | std::swap(m_length, stringBuilder.m_length); |
348 | m_string.swap(stringBuilder.m_string); |
349 | m_buffer.swap(stringBuilder.m_buffer); |
350 | std::swap(m_is8Bit, stringBuilder.m_is8Bit); |
351 | std::swap(m_bufferCharacters8, stringBuilder.m_bufferCharacters8); |
352 | ASSERT(!m_buffer || hasOverflowed() || m_buffer->length() >= m_length.unsafeGet<unsigned>()); |
353 | } |
354 | |
355 | private: |
356 | void allocateBuffer(const LChar* currentCharacters, unsigned requiredLength); |
357 | void allocateBuffer(const UChar* currentCharacters, unsigned requiredLength); |
358 | void allocateBufferUpConvert(const LChar* currentCharacters, unsigned requiredLength); |
359 | template<typename CharacterType> void reallocateBuffer(unsigned requiredLength); |
360 | template<typename CharacterType> ALWAYS_INLINE CharacterType* extendBufferForAppending(unsigned additionalLength); |
361 | template<typename CharacterType> ALWAYS_INLINE CharacterType* extendBufferForAppendingWithoutOverflowCheck(CheckedInt32 requiredLength); |
362 | template<typename CharacterType> CharacterType* extendBufferForAppendingSlowCase(unsigned requiredLength); |
363 | WTF_EXPORT_PRIVATE LChar* extendBufferForAppending8(CheckedInt32 requiredLength); |
364 | WTF_EXPORT_PRIVATE UChar* extendBufferForAppending16(CheckedInt32 requiredLength); |
365 | |
366 | template<typename CharacterType> ALWAYS_INLINE CharacterType* getBufferCharacters(); |
367 | WTF_EXPORT_PRIVATE void reifyString() const; |
368 | |
369 | template<typename... StringTypeAdapters> void appendFromAdapters(StringTypeAdapters...); |
370 | |
371 | mutable String m_string; |
372 | RefPtr<StringImpl> m_buffer; |
373 | union { |
374 | LChar* m_bufferCharacters8; |
375 | UChar* m_bufferCharacters16; |
376 | }; |
377 | static_assert(String::MaxLength == std::numeric_limits<int32_t>::max(), "" ); |
378 | Checked<int32_t, ConditionalCrashOnOverflow> m_length; |
379 | bool m_is8Bit { true }; |
380 | #if !ASSERT_DISABLED |
381 | mutable bool m_isReified { false }; |
382 | #endif |
383 | }; |
384 | |
385 | template<> |
386 | ALWAYS_INLINE LChar* StringBuilder::getBufferCharacters<LChar>() |
387 | { |
388 | ASSERT(m_is8Bit); |
389 | return m_bufferCharacters8; |
390 | } |
391 | |
392 | template<> |
393 | ALWAYS_INLINE UChar* StringBuilder::getBufferCharacters<UChar>() |
394 | { |
395 | ASSERT(!m_is8Bit); |
396 | return m_bufferCharacters16; |
397 | } |
398 | |
399 | template<typename... StringTypeAdapters> |
400 | void StringBuilder::appendFromAdapters(StringTypeAdapters... adapters) |
401 | { |
402 | auto requiredLength = checkedSum<int32_t>(m_length, adapters.length()...); |
403 | if (m_is8Bit && are8Bit(adapters...)) { |
404 | LChar* destination = extendBufferForAppending8(requiredLength); |
405 | if (!destination) { |
406 | ASSERT(hasOverflowed()); |
407 | return; |
408 | } |
409 | stringTypeAdapterAccumulator(destination, adapters...); |
410 | } else { |
411 | UChar* destination = extendBufferForAppending16(requiredLength); |
412 | if (!destination) { |
413 | ASSERT(hasOverflowed()); |
414 | return; |
415 | } |
416 | stringTypeAdapterAccumulator(destination, adapters...); |
417 | } |
418 | } |
419 | |
420 | template<typename... StringTypes> |
421 | void StringBuilder::append(StringTypes... strings) |
422 | { |
423 | appendFromAdapters(StringTypeAdapter<StringTypes>(strings)...); |
424 | } |
425 | |
426 | template<typename CharacterType> |
427 | bool equal(const StringBuilder& s, const CharacterType* buffer, unsigned length) |
428 | { |
429 | if (s.length() != length) |
430 | return false; |
431 | |
432 | if (s.is8Bit()) |
433 | return equal(s.characters8(), buffer, length); |
434 | |
435 | return equal(s.characters16(), buffer, length); |
436 | } |
437 | |
438 | template<typename StringType> |
439 | bool equal(const StringBuilder& a, const StringType& b) |
440 | { |
441 | if (a.length() != b.length()) |
442 | return false; |
443 | |
444 | if (!a.length()) |
445 | return true; |
446 | |
447 | if (a.is8Bit()) { |
448 | if (b.is8Bit()) |
449 | return equal(a.characters8(), b.characters8(), a.length()); |
450 | return equal(a.characters8(), b.characters16(), a.length()); |
451 | } |
452 | |
453 | if (b.is8Bit()) |
454 | return equal(a.characters16(), b.characters8(), a.length()); |
455 | return equal(a.characters16(), b.characters16(), a.length()); |
456 | } |
457 | |
458 | inline bool operator==(const StringBuilder& a, const StringBuilder& b) { return equal(a, b); } |
459 | inline bool operator!=(const StringBuilder& a, const StringBuilder& b) { return !equal(a, b); } |
460 | inline bool operator==(const StringBuilder& a, const String& b) { return equal(a, b); } |
461 | inline bool operator!=(const StringBuilder& a, const String& b) { return !equal(a, b); } |
462 | inline bool operator==(const String& a, const StringBuilder& b) { return equal(b, a); } |
463 | inline bool operator!=(const String& a, const StringBuilder& b) { return !equal(b, a); } |
464 | |
465 | template<> struct IntegerToStringConversionTrait<StringBuilder> { |
466 | using ReturnType = void; |
467 | using AdditionalArgumentType = StringBuilder; |
468 | static void flush(LChar* characters, unsigned length, StringBuilder* stringBuilder) { stringBuilder->appendCharacters(characters, length); } |
469 | }; |
470 | |
471 | } // namespace WTF |
472 | |
473 | using WTF::StringBuilder; |
474 | |