1 | /* |
2 | * Copyright (C) 2009-2018 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 | |
53 | public: |
54 | enum class OverflowHandler { |
55 | CrashOnOverflow, |
56 | RecordOverflow |
57 | }; |
58 | |
59 | StringBuilder(OverflowHandler handler = OverflowHandler::CrashOnOverflow) |
60 | : m_bufferCharacters8(nullptr) |
61 | { |
62 | m_length.setShouldCrashOnOverflow(handler == OverflowHandler::CrashOnOverflow); |
63 | } |
64 | StringBuilder(StringBuilder&&) = default; |
65 | StringBuilder& operator=(StringBuilder&&) = default; |
66 | |
67 | ALWAYS_INLINE void didOverflow() { m_length.overflowed(); } |
68 | ALWAYS_INLINE bool hasOverflowed() const { return m_length.hasOverflowed(); } |
69 | ALWAYS_INLINE bool crashesOnOverflow() const { return m_length.shouldCrashOnOverflow(); } |
70 | |
71 | WTF_EXPORT_PRIVATE void append(const UChar*, unsigned); |
72 | WTF_EXPORT_PRIVATE void append(const LChar*, unsigned); |
73 | |
74 | ALWAYS_INLINE void append(const char* characters, unsigned length) { append(reinterpret_cast<const LChar*>(characters), length); } |
75 | |
76 | void append(const AtomString& atomString) |
77 | { |
78 | append(atomString.string()); |
79 | } |
80 | |
81 | void append(const String& string) |
82 | { |
83 | if (hasOverflowed()) |
84 | return; |
85 | |
86 | if (!string.length()) |
87 | return; |
88 | |
89 | // If we're appending to an empty string, and there is not a buffer (reserveCapacity has not been called) |
90 | // then just retain the string. |
91 | if (!m_length && !m_buffer) { |
92 | m_string = string; |
93 | m_length = string.length(); |
94 | m_is8Bit = m_string.is8Bit(); |
95 | return; |
96 | } |
97 | |
98 | if (string.is8Bit()) |
99 | append(string.characters8(), string.length()); |
100 | else |
101 | append(string.characters16(), string.length()); |
102 | } |
103 | |
104 | void append(const StringBuilder& other) |
105 | { |
106 | if (hasOverflowed()) |
107 | return; |
108 | if (other.hasOverflowed()) |
109 | return didOverflow(); |
110 | |
111 | if (!other.m_length) |
112 | return; |
113 | |
114 | // If we're appending to an empty string, and there is not a buffer (reserveCapacity has not been called) |
115 | // then just retain the string. |
116 | if (!m_length && !m_buffer && !other.m_string.isNull()) { |
117 | m_string = other.m_string; |
118 | m_length = other.m_length; |
119 | m_is8Bit = other.m_is8Bit; |
120 | return; |
121 | } |
122 | |
123 | if (other.is8Bit()) |
124 | append(other.characters8(), other.m_length.unsafeGet()); |
125 | else |
126 | append(other.characters16(), other.m_length.unsafeGet()); |
127 | } |
128 | |
129 | void append(StringView stringView) |
130 | { |
131 | if (stringView.is8Bit()) |
132 | append(stringView.characters8(), stringView.length()); |
133 | else |
134 | append(stringView.characters16(), stringView.length()); |
135 | } |
136 | |
137 | #if USE(CF) |
138 | WTF_EXPORT_PRIVATE void append(CFStringRef); |
139 | #endif |
140 | #if USE(CF) && defined(__OBJC__) |
141 | void append(NSString *string) { append((__bridge CFStringRef)string); } |
142 | #endif |
143 | |
144 | void append(const String& string, unsigned offset, unsigned length) |
145 | { |
146 | if (!string.length()) |
147 | return; |
148 | |
149 | if ((offset + length) > string.length()) |
150 | return; |
151 | |
152 | if (string.is8Bit()) |
153 | append(string.characters8() + offset, length); |
154 | else |
155 | append(string.characters16() + offset, length); |
156 | } |
157 | |
158 | void append(const char* characters) |
159 | { |
160 | if (characters) |
161 | append(characters, strlen(characters)); |
162 | } |
163 | |
164 | void append(UChar c) |
165 | { |
166 | if (hasOverflowed()) |
167 | return; |
168 | unsigned length = m_length.unsafeGet<unsigned>(); |
169 | if (m_buffer && length < m_buffer->length() && m_string.isNull()) { |
170 | if (!m_is8Bit) { |
171 | m_bufferCharacters16[length] = c; |
172 | m_length++; |
173 | return; |
174 | } |
175 | |
176 | if (!(c & ~0xff)) { |
177 | m_bufferCharacters8[length] = static_cast<LChar>(c); |
178 | m_length++; |
179 | return; |
180 | } |
181 | } |
182 | append(&c, 1); |
183 | } |
184 | |
185 | void append(LChar c) |
186 | { |
187 | if (hasOverflowed()) |
188 | return; |
189 | unsigned length = m_length.unsafeGet<unsigned>(); |
190 | if (m_buffer && length < m_buffer->length() && m_string.isNull()) { |
191 | if (m_is8Bit) |
192 | m_bufferCharacters8[length] = c; |
193 | else |
194 | m_bufferCharacters16[length] = c; |
195 | m_length++; |
196 | } else |
197 | append(&c, 1); |
198 | } |
199 | |
200 | void append(char c) |
201 | { |
202 | append(static_cast<LChar>(c)); |
203 | } |
204 | |
205 | void append(UChar32 c) |
206 | { |
207 | if (U_IS_BMP(c)) { |
208 | append(static_cast<UChar>(c)); |
209 | return; |
210 | } |
211 | append(U16_LEAD(c)); |
212 | append(U16_TRAIL(c)); |
213 | } |
214 | |
215 | WTF_EXPORT_PRIVATE void appendQuotedJSONString(const String&); |
216 | |
217 | template<unsigned characterCount> |
218 | ALWAYS_INLINE void appendLiteral(const char (&characters)[characterCount]) { append(characters, characterCount - 1); } |
219 | |
220 | WTF_EXPORT_PRIVATE void appendNumber(int); |
221 | WTF_EXPORT_PRIVATE void appendNumber(unsigned); |
222 | WTF_EXPORT_PRIVATE void appendNumber(long); |
223 | WTF_EXPORT_PRIVATE void appendNumber(unsigned long); |
224 | WTF_EXPORT_PRIVATE void appendNumber(long long); |
225 | WTF_EXPORT_PRIVATE void appendNumber(unsigned long long); |
226 | WTF_EXPORT_PRIVATE void appendNumber(float); |
227 | WTF_EXPORT_PRIVATE void appendNumber(double); |
228 | |
229 | WTF_EXPORT_PRIVATE void appendFixedPrecisionNumber(float, unsigned precision = 6, TrailingZerosTruncatingPolicy = TruncateTrailingZeros); |
230 | WTF_EXPORT_PRIVATE void appendFixedPrecisionNumber(double, unsigned precision = 6, TrailingZerosTruncatingPolicy = TruncateTrailingZeros); |
231 | WTF_EXPORT_PRIVATE void appendFixedWidthNumber(float, unsigned decimalPlaces); |
232 | WTF_EXPORT_PRIVATE void appendFixedWidthNumber(double, unsigned decimalPlaces); |
233 | |
234 | String toString() |
235 | { |
236 | if (!m_string.isNull()) { |
237 | ASSERT(!m_buffer || m_isReified); |
238 | ASSERT(!hasOverflowed()); |
239 | return m_string; |
240 | } |
241 | |
242 | RELEASE_ASSERT(!hasOverflowed()); |
243 | shrinkToFit(); |
244 | reifyString(); |
245 | return m_string; |
246 | } |
247 | |
248 | const String& toStringPreserveCapacity() const |
249 | { |
250 | RELEASE_ASSERT(!hasOverflowed()); |
251 | if (m_string.isNull()) |
252 | reifyString(); |
253 | return m_string; |
254 | } |
255 | |
256 | AtomString toAtomString() const |
257 | { |
258 | RELEASE_ASSERT(!hasOverflowed()); |
259 | if (!m_length) |
260 | return emptyAtom(); |
261 | |
262 | // If the buffer is sufficiently over-allocated, make a new AtomString from a copy so its buffer is not so large. |
263 | if (canShrink()) { |
264 | if (is8Bit()) |
265 | return AtomString(characters8(), length()); |
266 | return AtomString(characters16(), length()); |
267 | } |
268 | |
269 | if (!m_string.isNull()) |
270 | return AtomString(m_string); |
271 | |
272 | ASSERT(m_buffer); |
273 | return AtomString(m_buffer.get(), 0, m_length.unsafeGet()); |
274 | } |
275 | |
276 | unsigned length() const |
277 | { |
278 | RELEASE_ASSERT(!hasOverflowed()); |
279 | return m_length.unsafeGet(); |
280 | } |
281 | |
282 | bool isEmpty() const { return !m_length; } |
283 | |
284 | WTF_EXPORT_PRIVATE void reserveCapacity(unsigned newCapacity); |
285 | |
286 | unsigned capacity() const |
287 | { |
288 | RELEASE_ASSERT(!hasOverflowed()); |
289 | return m_buffer ? m_buffer->length() : m_length.unsafeGet(); |
290 | } |
291 | |
292 | WTF_EXPORT_PRIVATE void resize(unsigned newSize); |
293 | |
294 | WTF_EXPORT_PRIVATE bool canShrink() const; |
295 | |
296 | WTF_EXPORT_PRIVATE void shrinkToFit(); |
297 | |
298 | UChar operator[](unsigned i) const |
299 | { |
300 | RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!hasOverflowed() && i < m_length.unsafeGet<unsigned>()); |
301 | if (m_is8Bit) |
302 | return characters8()[i]; |
303 | return characters16()[i]; |
304 | } |
305 | |
306 | const LChar* characters8() const |
307 | { |
308 | ASSERT(m_is8Bit); |
309 | if (!m_length) |
310 | return nullptr; |
311 | if (!m_string.isNull()) |
312 | return m_string.characters8(); |
313 | ASSERT(m_buffer); |
314 | return m_buffer->characters8(); |
315 | } |
316 | |
317 | const UChar* characters16() const |
318 | { |
319 | ASSERT(!m_is8Bit); |
320 | if (!m_length) |
321 | return nullptr; |
322 | if (!m_string.isNull()) |
323 | return m_string.characters16(); |
324 | ASSERT(m_buffer); |
325 | return m_buffer->characters16(); |
326 | } |
327 | |
328 | bool is8Bit() const { return m_is8Bit; } |
329 | |
330 | void clear() |
331 | { |
332 | m_length = 0; |
333 | m_string = String(); |
334 | m_buffer = nullptr; |
335 | m_bufferCharacters8 = nullptr; |
336 | m_is8Bit = true; |
337 | } |
338 | |
339 | void swap(StringBuilder& stringBuilder) |
340 | { |
341 | std::swap(m_length, stringBuilder.m_length); |
342 | m_string.swap(stringBuilder.m_string); |
343 | m_buffer.swap(stringBuilder.m_buffer); |
344 | std::swap(m_is8Bit, stringBuilder.m_is8Bit); |
345 | std::swap(m_bufferCharacters8, stringBuilder.m_bufferCharacters8); |
346 | ASSERT(!m_buffer || hasOverflowed() || m_buffer->length() >= m_length.unsafeGet<unsigned>()); |
347 | } |
348 | |
349 | private: |
350 | void allocateBuffer(const LChar* currentCharacters, unsigned requiredLength); |
351 | void allocateBuffer(const UChar* currentCharacters, unsigned requiredLength); |
352 | void allocateBufferUpConvert(const LChar* currentCharacters, unsigned requiredLength); |
353 | template <typename CharType> |
354 | void reallocateBuffer(unsigned requiredLength); |
355 | template <typename CharType> |
356 | ALWAYS_INLINE CharType* appendUninitialized(unsigned length); |
357 | template <typename CharType> |
358 | CharType* appendUninitializedSlow(unsigned length); |
359 | template <typename CharType> |
360 | ALWAYS_INLINE CharType * getBufferCharacters(); |
361 | WTF_EXPORT_PRIVATE void reifyString() const; |
362 | |
363 | mutable String m_string; |
364 | RefPtr<StringImpl> m_buffer; |
365 | union { |
366 | LChar* m_bufferCharacters8; |
367 | UChar* m_bufferCharacters16; |
368 | }; |
369 | static_assert(String::MaxLength == std::numeric_limits<int32_t>::max(), "" ); |
370 | Checked<int32_t, ConditionalCrashOnOverflow> m_length; |
371 | bool m_is8Bit { true }; |
372 | #if !ASSERT_DISABLED |
373 | mutable bool m_isReified { false }; |
374 | #endif |
375 | }; |
376 | |
377 | template <> |
378 | ALWAYS_INLINE LChar* StringBuilder::getBufferCharacters<LChar>() |
379 | { |
380 | ASSERT(m_is8Bit); |
381 | return m_bufferCharacters8; |
382 | } |
383 | |
384 | template <> |
385 | ALWAYS_INLINE UChar* StringBuilder::getBufferCharacters<UChar>() |
386 | { |
387 | ASSERT(!m_is8Bit); |
388 | return m_bufferCharacters16; |
389 | } |
390 | |
391 | template <typename CharType> |
392 | bool equal(const StringBuilder& s, const CharType* buffer, unsigned length) |
393 | { |
394 | if (s.length() != length) |
395 | return false; |
396 | |
397 | if (s.is8Bit()) |
398 | return equal(s.characters8(), buffer, length); |
399 | |
400 | return equal(s.characters16(), buffer, length); |
401 | } |
402 | |
403 | template <typename StringType> |
404 | bool equal(const StringBuilder& a, const StringType& b) |
405 | { |
406 | if (a.length() != b.length()) |
407 | return false; |
408 | |
409 | if (!a.length()) |
410 | return true; |
411 | |
412 | if (a.is8Bit()) { |
413 | if (b.is8Bit()) |
414 | return equal(a.characters8(), b.characters8(), a.length()); |
415 | return equal(a.characters8(), b.characters16(), a.length()); |
416 | } |
417 | |
418 | if (b.is8Bit()) |
419 | return equal(a.characters16(), b.characters8(), a.length()); |
420 | return equal(a.characters16(), b.characters16(), a.length()); |
421 | } |
422 | |
423 | inline bool operator==(const StringBuilder& a, const StringBuilder& b) { return equal(a, b); } |
424 | inline bool operator!=(const StringBuilder& a, const StringBuilder& b) { return !equal(a, b); } |
425 | inline bool operator==(const StringBuilder& a, const String& b) { return equal(a, b); } |
426 | inline bool operator!=(const StringBuilder& a, const String& b) { return !equal(a, b); } |
427 | inline bool operator==(const String& a, const StringBuilder& b) { return equal(b, a); } |
428 | inline bool operator!=(const String& a, const StringBuilder& b) { return !equal(b, a); } |
429 | |
430 | template<> struct IntegerToStringConversionTrait<StringBuilder> { |
431 | using ReturnType = void; |
432 | using AdditionalArgumentType = StringBuilder; |
433 | static void flush(LChar* characters, unsigned length, StringBuilder* stringBuilder) { stringBuilder->append(characters, length); } |
434 | }; |
435 | |
436 | } // namespace WTF |
437 | |
438 | using WTF::StringBuilder; |
439 | |