Streamline and speed up tokenizer and segmented string classes
[WebKit-https.git] / Source / WTF / wtf / text / StringBuilder.h
1 /*
2  * Copyright (C) 2009-2016 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/text/StringView.h>
30
31 namespace WTF {
32
33 class StringBuilder {
34     // Disallow copying since it's expensive and we don't want anyone to do it by accident.
35     WTF_MAKE_NONCOPYABLE(StringBuilder);
36
37 public:
38     StringBuilder() = default;
39
40     WTF_EXPORT_PRIVATE void append(const UChar*, unsigned);
41     WTF_EXPORT_PRIVATE void append(const LChar*, unsigned);
42
43     ALWAYS_INLINE void append(const char* characters, unsigned length) { append(reinterpret_cast<const LChar*>(characters), length); }
44
45     void append(const AtomicString& atomicString) { append(atomicString.string()); }
46
47     void append(const String& string)
48     {
49         unsigned length = string.length();
50         if (!length)
51             return;
52
53         // If we're appending to an empty string, and there is not a buffer
54         // (reserveCapacity has not been called) then just retain the string.
55         if (!m_length && !m_buffer) {
56             m_string = string;
57             m_length = length;
58             m_is8Bit = string.is8Bit();
59             return;
60         }
61
62         if (string.is8Bit())
63             append(string.characters8(), length);
64         else
65             append(string.characters16(), length);
66     }
67
68     void append(const StringBuilder& other)
69     {
70         if (!other.m_length)
71             return;
72
73         // If we're appending to an empty string, and there is not a buffer
74         // (reserveCapacity has not been called) then just retain the string.
75         if (!m_length && !m_buffer && !other.m_string.isNull()) {
76             m_string = other.m_string;
77             m_length = other.m_length;
78             m_is8Bit = other.m_is8Bit;
79             return;
80         }
81
82         if (other.is8Bit())
83             append(other.characters8(), other.m_length);
84         else
85             append(other.characters16(), other.m_length);
86     }
87
88     void append(StringView stringView)
89     {
90         if (stringView.is8Bit())
91             append(stringView.characters8(), stringView.length());
92         else
93             append(stringView.characters16(), stringView.length());
94     }
95
96 #if USE(CF)
97     WTF_EXPORT_PRIVATE void append(CFStringRef);
98 #endif
99
100 #if USE(CF) && defined(__OBJC__)
101     void append(NSString *string) { append((__bridge CFStringRef)string); }
102 #endif
103     
104     void append(const String& string, unsigned offset, unsigned length)
105     {
106         ASSERT(offset <= string.length());
107         ASSERT(offset + length <= string.length());
108
109         if (!length)
110             return;
111
112         // If we're appending to an empty string, and there is not a buffer
113         // (reserveCapacity has not been called) then just retain the string.
114         if (!offset && !m_length && !m_buffer && length == string.length()) {
115             m_string = string;
116             m_length = length;
117             m_is8Bit = string.is8Bit();
118             return;
119         }
120
121         if (string.is8Bit())
122             append(string.characters8() + offset, length);
123         else
124             append(string.characters16() + offset, length);
125     }
126
127     void append(const char* characters)
128     {
129         if (characters)
130             append(characters, strlen(characters));
131     }
132
133     void append(UChar character)
134     {
135         if (m_buffer && m_length < m_buffer->length() && m_string.isNull()) {
136             if (!m_is8Bit) {
137                 m_bufferCharacters16[m_length++] = character;
138                 return;
139             }
140             if (!(character & ~0xFF)) {
141                 m_bufferCharacters8[m_length++] = static_cast<LChar>(character);
142                 return;
143             }
144         }
145         append(&character, 1);
146     }
147
148     void append(LChar character)
149     {
150         if (m_buffer && m_length < m_buffer->length() && m_string.isNull()) {
151             if (m_is8Bit)
152                 m_bufferCharacters8[m_length++] = character;
153             else
154                 m_bufferCharacters16[m_length++] = character;
155         } else
156             append(&character, 1);
157     }
158
159     void append(char character) { append(static_cast<LChar>(character)); }
160
161     void append(UChar32 c)
162     {
163         if (U_IS_BMP(c)) {
164             append(static_cast<UChar>(c));
165             return;
166         }
167         append(U16_LEAD(c));
168         append(U16_TRAIL(c));
169     }
170
171     WTF_EXPORT_PRIVATE void appendQuotedJSONString(StringView);
172
173     template<unsigned charactersCount> ALWAYS_INLINE void appendLiteral(const char (&characters)[charactersCount]) { append(characters, charactersCount - 1); }
174
175     WTF_EXPORT_PRIVATE void appendNumber(int);
176     WTF_EXPORT_PRIVATE void appendNumber(unsigned int);
177     WTF_EXPORT_PRIVATE void appendNumber(long);
178     WTF_EXPORT_PRIVATE void appendNumber(unsigned long);
179     WTF_EXPORT_PRIVATE void appendNumber(long long);
180     WTF_EXPORT_PRIVATE void appendNumber(unsigned long long);
181     WTF_EXPORT_PRIVATE void appendNumber(double, unsigned precision = 6, TrailingZerosTruncatingPolicy = TruncateTrailingZeros);
182     WTF_EXPORT_PRIVATE void appendECMAScriptNumber(double);
183     WTF_EXPORT_PRIVATE void appendFixedWidthNumber(double, unsigned decimalPlaces);
184
185     String toString()
186     {
187         shrinkToFit();
188         if (m_string.isNull())
189             reifyString();
190         return m_string;
191     }
192
193     const String& toStringPreserveCapacity() const
194     {
195         if (m_string.isNull())
196             reifyString();
197         return m_string;
198     }
199
200     AtomicString toAtomicString() const
201     {
202         if (!m_length)
203             return emptyAtom;
204
205         // If the buffer is sufficiently over-allocated, make a new AtomicString from a copy so its buffer is not so large.
206         if (canShrink()) {
207             if (is8Bit())
208                 return AtomicString(characters8(), length());
209             return AtomicString(characters16(), length());            
210         }
211
212         if (!m_string.isNull())
213             return AtomicString(m_string);
214
215         ASSERT(m_buffer);
216         return AtomicString(m_buffer.get(), 0, m_length);
217     }
218
219     unsigned length() const { return m_length; }
220     bool isEmpty() const { return !m_length; }
221
222     WTF_EXPORT_PRIVATE void reserveCapacity(unsigned newCapacity);
223
224     unsigned capacity() const { return m_buffer ? m_buffer->length() : m_length; }
225
226     WTF_EXPORT_PRIVATE void resize(unsigned newSize);
227     WTF_EXPORT_PRIVATE bool canShrink() const;
228     WTF_EXPORT_PRIVATE void shrinkToFit();
229
230     UChar operator[](unsigned i) const
231     {
232         ASSERT_WITH_SECURITY_IMPLICATION(i < m_length);
233         if (m_is8Bit)
234             return characters8()[i];
235         return characters16()[i];
236     }
237
238     const LChar* characters8() const
239     {
240         ASSERT(m_is8Bit);
241         if (!m_length)
242             return 0;
243         if (!m_string.isNull())
244             return m_string.characters8();
245         ASSERT(m_buffer);
246         return m_buffer->characters8();
247     }
248
249     const UChar* characters16() const
250     {
251         ASSERT(!m_is8Bit);
252         if (!m_length)
253             return 0;
254         if (!m_string.isNull())
255             return m_string.characters16();
256         ASSERT(m_buffer);
257         return m_buffer->characters16();
258     }
259     
260     bool is8Bit() const { return m_is8Bit; }
261
262     void clear()
263     {
264         m_length = 0;
265         m_string = String();
266         m_buffer = nullptr;
267         m_bufferCharacters8 = 0;
268         m_is8Bit = true;
269     }
270
271     void swap(StringBuilder& stringBuilder)
272     {
273         std::swap(m_length, stringBuilder.m_length);
274         m_string.swap(stringBuilder.m_string);
275         m_buffer.swap(stringBuilder.m_buffer);
276         std::swap(m_is8Bit, stringBuilder.m_is8Bit);
277         std::swap(m_bufferCharacters8, stringBuilder.m_bufferCharacters8);
278         ASSERT(!m_buffer || m_buffer->length() >= m_length);
279     }
280
281 private:
282     void allocateBuffer(const LChar* currentCharacters, unsigned requiredLength);
283     void allocateBuffer(const UChar* currentCharacters, unsigned requiredLength);
284     void allocateBufferUpconvert(const LChar* currentCharacters, unsigned requiredLength);
285     template<typename CharacterType> void reallocateBuffer(unsigned requiredLength);
286     UChar* appendUninitializedUpconvert(unsigned length);
287     template<typename CharacterType> CharacterType* appendUninitialized(unsigned length);
288     template<typename CharacterType> CharacterType* appendUninitializedSlow(unsigned length);
289     template<typename CharacterType> CharacterType* bufferCharacters();
290     WTF_EXPORT_PRIVATE void reifyString() const;
291
292     unsigned m_length { 0 };
293     mutable String m_string;
294     RefPtr<StringImpl> m_buffer;
295     bool m_is8Bit { true };
296     union {
297         LChar* m_bufferCharacters8 { nullptr };
298         UChar* m_bufferCharacters16;
299     };
300 };
301
302 template<typename StringType> bool equal(const StringBuilder&, const StringType&);
303 bool equal(const StringBuilder&, const String&); // Only needed because is8Bit dereferences nullptr when the string is null.
304 template<typename CharacterType> bool equal(const StringBuilder&, const CharacterType*, unsigned length);
305
306 bool operator==(const StringBuilder&, const StringBuilder&);
307 bool operator!=(const StringBuilder&, const StringBuilder&);
308 bool operator==(const StringBuilder&, const String&);
309 bool operator!=(const StringBuilder&, const String&);
310 bool operator==(const String&, const StringBuilder&);
311 bool operator!=(const String&, const StringBuilder&);
312
313 template<typename CharacterType> inline bool equal(const StringBuilder& s, const CharacterType* buffer, unsigned length)
314 {
315     if (s.length() != length)
316         return false;
317
318     if (s.is8Bit())
319         return equal(s.characters8(), buffer, length);
320
321     return equal(s.characters16(), buffer, length);
322 }
323
324 template<typename StringType> inline bool equal(const StringBuilder& a, const StringType& b)
325 {
326     return equalCommon(a, b);
327 }
328
329 inline bool equal(const StringBuilder& a, const String& b)
330 {
331     // FIXME: This preserves historic behavior where an empty StringBuilder compares as equal
332     // to a null String. This does not seem like desirable behavior since a null String and
333     // an empty String do not compare as equal, but we have regression tests expecting it,
334     // and there is a slim chance we also have code depending on it.
335     return b.isNull() ? a.isEmpty() : equalCommon(a, b);
336 }
337
338 inline bool operator==(const StringBuilder& a, const StringBuilder& b) { return equal(a, b); }
339 inline bool operator!=(const StringBuilder& a, const StringBuilder& b) { return !equal(a, b); }
340 inline bool operator==(const StringBuilder& a, const String& b) { return equal(a, b); }
341 inline bool operator!=(const StringBuilder& a, const String& b) { return !equal(a, b); }
342 inline bool operator==(const String& a, const StringBuilder& b) { return equal(b, a); }
343 inline bool operator!=(const String& a, const StringBuilder& b) { return !equal(b, a); }
344
345 } // namespace WTF
346
347 using WTF::StringBuilder;