OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright (C) 2009, 2010, 2012, 2013 Apple Inc. All rights reserved. | 2 * Copyright (C) 2009, 2010, 2012, 2013 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 17 matching lines...) Expand all Loading... | |
28 #define StringBuilder_h | 28 #define StringBuilder_h |
29 | 29 |
30 #include "wtf/WTFExport.h" | 30 #include "wtf/WTFExport.h" |
31 #include "wtf/text/AtomicString.h" | 31 #include "wtf/text/AtomicString.h" |
32 #include "wtf/text/StringView.h" | 32 #include "wtf/text/StringView.h" |
33 #include "wtf/text/WTFString.h" | 33 #include "wtf/text/WTFString.h" |
34 | 34 |
35 namespace WTF { | 35 namespace WTF { |
36 | 36 |
37 class WTF_EXPORT StringBuilder { | 37 class WTF_EXPORT StringBuilder { |
38 // Disallow copying since it's expensive and we don't want code to do it by accident. | |
39 WTF_MAKE_NONCOPYABLE(StringBuilder); | 38 WTF_MAKE_NONCOPYABLE(StringBuilder); |
40 | |
41 public: | 39 public: |
42 StringBuilder() | 40 StringBuilder() |
43 : m_bufferCharacters8(0) | 41 : m_buffer(nullptr) |
44 , m_length(0) | 42 , m_length(0) |
45 , m_is8Bit(true) | 43 , m_is8Bit(true) {} |
46 { | |
47 } | |
48 | 44 |
49 void append(const UChar*, unsigned); | 45 ~StringBuilder() { clear(); } |
50 void append(const LChar*, unsigned); | 46 |
47 void append(const UChar*, unsigned length); | |
48 void append(const LChar*, unsigned length); | |
51 | 49 |
52 ALWAYS_INLINE void append(const char* characters, unsigned length) { append( reinterpret_cast<const LChar*>(characters), length); } | 50 ALWAYS_INLINE void append(const char* characters, unsigned length) { append( reinterpret_cast<const LChar*>(characters), length); } |
53 | 51 |
54 void append(const StringBuilder& other) | 52 void append(const StringBuilder& other) |
55 { | 53 { |
56 if (!other.m_length) | 54 if (!other.m_length) |
57 return; | 55 return; |
58 | 56 |
59 // If we're appending to an empty string, and there is not a buffer (res erveCapacity has not been called) | 57 if (!m_length && !hasBuffer() && !other.m_string.isNull()) { |
60 // then just retain the string. | |
61 if (!m_length && !m_buffer && !other.m_string.isNull()) { | |
62 m_string = other.m_string; | 58 m_string = other.m_string; |
63 m_length = other.m_length; | 59 m_length = other.m_string.length(); |
60 m_is8Bit = other.m_string.is8Bit(); | |
64 return; | 61 return; |
65 } | 62 } |
66 | 63 |
67 if (other.is8Bit()) | 64 if (other.is8Bit()) |
68 append(other.characters8(), other.m_length); | 65 append(other.characters8(), other.m_length); |
69 else | 66 else |
70 append(other.characters16(), other.m_length); | 67 append(other.characters16(), other.m_length); |
71 } | 68 } |
72 | 69 |
73 // NOTE: The semantics of this are different than StringView(..., offset, le ngth) | 70 // NOTE: The semantics of this are different than StringView(..., offset, le ngth) |
(...skipping 18 matching lines...) Expand all Loading... | |
92 | 89 |
93 // If we're appending to an empty builder, and there is not a buffer | 90 // If we're appending to an empty builder, and there is not a buffer |
94 // (reserveCapacity has not been called), then share the impl if | 91 // (reserveCapacity has not been called), then share the impl if |
95 // possible. | 92 // possible. |
96 // | 93 // |
97 // This is important to avoid string copies inside dom operations like | 94 // This is important to avoid string copies inside dom operations like |
98 // Node::textContent when there's only a single Text node child, or | 95 // Node::textContent when there's only a single Text node child, or |
99 // inside the parser in the common case when flushing buffered text to | 96 // inside the parser in the common case when flushing buffered text to |
100 // a Text node. | 97 // a Text node. |
101 StringImpl* impl = string.sharedImpl(); | 98 StringImpl* impl = string.sharedImpl(); |
102 if (!m_length && !m_buffer && impl) { | 99 if (!m_length && !hasBuffer() && impl) { |
103 m_string = impl; | 100 m_string = impl; |
104 m_length = impl->length(); | 101 m_length = impl->length(); |
105 m_is8Bit = impl->is8Bit(); | 102 m_is8Bit = impl->is8Bit(); |
106 return; | 103 return; |
107 } | 104 } |
108 | 105 |
109 if (string.is8Bit()) | 106 if (string.is8Bit()) |
110 append(string.characters8(), string.length()); | 107 append(string.characters8(), string.length()); |
111 else | 108 else |
112 append(string.characters16(), string.length()); | 109 append(string.characters16(), string.length()); |
113 } | 110 } |
114 | 111 |
115 void append(UChar c) | 112 void append(UChar c) |
116 { | 113 { |
117 if (m_buffer && m_length < m_buffer->length() && m_string.isNull()) { | 114 if (m_is8Bit && c <= 0xFF) { |
118 if (!m_is8Bit) { | 115 append(static_cast<LChar>(c)); |
119 m_bufferCharacters16[m_length++] = c; | 116 return; |
120 return; | |
121 } | |
122 | |
123 if (!(c & ~0xff)) { | |
124 m_bufferCharacters8[m_length++] = static_cast<LChar>(c); | |
125 return; | |
126 } | |
127 } | 117 } |
128 append(&c, 1); | 118 ensureBuffer16(); |
119 m_string = String(); | |
120 m_buffer16->append(c); | |
121 ++m_length; | |
129 } | 122 } |
130 | 123 |
131 void append(LChar c) | 124 void append(LChar c) |
132 { | 125 { |
133 if (m_buffer && m_length < m_buffer->length() && m_string.isNull()) { | 126 if (!m_is8Bit) { |
134 if (m_is8Bit) | 127 append(static_cast<UChar>(c)); |
135 m_bufferCharacters8[m_length++] = c; | 128 return; |
136 else | |
137 m_bufferCharacters16[m_length++] = c; | |
138 } else { | |
139 append(&c, 1); | |
140 } | 129 } |
130 ensureBuffer8(); | |
131 m_string = String(); | |
132 m_buffer8->append(c); | |
133 ++m_length; | |
141 } | 134 } |
142 | 135 |
143 void append(char c) | 136 void append(char c) |
144 { | 137 { |
145 append(static_cast<LChar>(c)); | 138 append(static_cast<LChar>(c)); |
146 } | 139 } |
147 | 140 |
148 void append(UChar32 c) | 141 void append(UChar32 c) |
149 { | 142 { |
150 if (U_IS_BMP(c)) { | 143 if (U_IS_BMP(c)) { |
151 append(static_cast<UChar>(c)); | 144 append(static_cast<UChar>(c)); |
152 return; | 145 return; |
153 } | 146 } |
154 append(U16_LEAD(c)); | 147 append(U16_LEAD(c)); |
155 append(U16_TRAIL(c)); | 148 append(U16_TRAIL(c)); |
156 } | 149 } |
157 | 150 |
158 void appendNumber(int); | 151 void appendNumber(int); |
159 void appendNumber(unsigned); | 152 void appendNumber(unsigned); |
160 void appendNumber(long); | 153 void appendNumber(long); |
161 void appendNumber(unsigned long); | 154 void appendNumber(unsigned long); |
162 void appendNumber(long long); | 155 void appendNumber(long long); |
163 void appendNumber(unsigned long long); | 156 void appendNumber(unsigned long long); |
164 void appendNumber(double, unsigned precision = 6, TrailingZerosTruncatingPol icy = TruncateTrailingZeros); | 157 void appendNumber(double, unsigned precision = 6, TrailingZerosTruncatingPol icy = TruncateTrailingZeros); |
165 | 158 |
166 String toString() | 159 String toString(); |
167 { | 160 AtomicString toAtomicString(); |
168 shrinkToFit(); | 161 String substring(unsigned start, unsigned length) const; |
169 if (m_string.isNull()) | |
170 reifyString(); | |
171 return m_string; | |
172 } | |
173 | 162 |
174 String substring(unsigned position, unsigned length) const | 163 unsigned length() const { return m_length; } |
175 { | |
176 if (!m_length) | |
177 return emptyString(); | |
178 if (!m_string.isNull()) | |
179 return m_string.substring(position, length); | |
180 return reifySubstring(position, length); | |
181 } | |
182 | |
183 AtomicString toAtomicString() const | |
184 { | |
185 if (!m_length) | |
186 return emptyAtom; | |
187 | |
188 // If the buffer is sufficiently over-allocated, make a new AtomicString from a copy so its buffer is not so large. | |
189 if (canShrink()) { | |
190 if (is8Bit()) | |
191 return AtomicString(characters8(), length()); | |
192 return AtomicString(characters16(), length()); | |
193 } | |
194 | |
195 if (!m_string.isNull()) | |
196 return AtomicString(m_string); | |
197 | |
198 DCHECK(m_buffer); | |
199 return AtomicString(m_buffer.get(), 0, m_length); | |
200 } | |
201 | |
202 unsigned length() const | |
203 { | |
204 return m_length; | |
205 } | |
206 | |
207 bool isEmpty() const { return !m_length; } | 164 bool isEmpty() const { return !m_length; } |
208 | 165 |
166 unsigned capacity() const; | |
209 void reserveCapacity(unsigned newCapacity); | 167 void reserveCapacity(unsigned newCapacity); |
210 | 168 |
211 unsigned capacity() const | 169 // TODO(esprehn): Rename to shrink(). |
212 { | |
213 return m_buffer ? m_buffer->length() : m_length; | |
214 } | |
215 | |
216 void resize(unsigned newSize); | 170 void resize(unsigned newSize); |
217 | 171 |
218 bool canShrink() const; | |
219 | |
220 void shrinkToFit(); | |
221 | |
222 UChar operator[](unsigned i) const | 172 UChar operator[](unsigned i) const |
223 { | 173 { |
224 ASSERT_WITH_SECURITY_IMPLICATION(i < m_length); | 174 ASSERT_WITH_SECURITY_IMPLICATION(i < m_length); |
225 if (m_is8Bit) | 175 if (m_is8Bit) |
226 return characters8()[i]; | 176 return characters8()[i]; |
227 return characters16()[i]; | 177 return characters16()[i]; |
228 } | 178 } |
229 | 179 |
230 const LChar* characters8() const | 180 const LChar* characters8() const |
231 { | 181 { |
232 DCHECK(m_is8Bit); | 182 DCHECK(m_is8Bit); |
233 if (!m_length) | 183 if (!length()) |
234 return 0; | 184 return nullptr; |
235 if (!m_string.isNull()) | 185 if (!m_string.isNull()) |
236 return m_string.characters8(); | 186 return m_string.characters8(); |
237 DCHECK(m_buffer); | 187 DCHECK(m_buffer8); |
238 return m_buffer->characters8(); | 188 return m_buffer8->data(); |
239 } | 189 } |
240 | 190 |
241 const UChar* characters16() const | 191 const UChar* characters16() const |
242 { | 192 { |
243 DCHECK(!m_is8Bit); | 193 DCHECK(!m_is8Bit); |
244 if (!m_length) | 194 if (!length()) |
245 return 0; | 195 return nullptr; |
246 if (!m_string.isNull()) | 196 if (!m_string.isNull()) |
247 return m_string.characters16(); | 197 return m_string.characters16(); |
248 DCHECK(m_buffer); | 198 DCHECK(m_buffer16); |
249 return m_buffer->characters16(); | 199 return m_buffer16->data(); |
250 } | 200 } |
251 | 201 |
252 bool is8Bit() const { return m_is8Bit; } | 202 bool is8Bit() const { return m_is8Bit; } |
253 | 203 |
254 void clear() | 204 void clear(); |
205 void swap(StringBuilder&); | |
206 | |
207 private: | |
208 typedef Vector<LChar, 16> Buffer8; | |
209 typedef Vector<UChar, 16> Buffer16; | |
haraken
2016/06/27 06:36:01
I don't mind specifying a larger inline capacity f
esprehn
2016/06/27 23:36:40
Yeah we can do that, there's a bunch of StringBuil
| |
210 | |
211 void ensureBuffer8() | |
255 { | 212 { |
256 m_length = 0; | 213 DCHECK(m_is8Bit); |
257 m_string = String(); | 214 if (!hasBuffer()) |
258 m_buffer = nullptr; | 215 createBuffer8(); |
259 m_bufferCharacters8 = 0; | |
260 m_is8Bit = true; | |
261 } | 216 } |
262 | 217 |
263 void swap(StringBuilder& stringBuilder) | 218 void ensureBuffer16() |
264 { | 219 { |
265 std::swap(m_length, stringBuilder.m_length); | 220 if (m_is8Bit || !hasBuffer()) |
haraken
2016/06/27 06:36:01
Shouldn't this be:
DCHECK(!m_is8Bit);
if (!ha
esprehn
2016/06/27 23:36:40
No, this is called whenever we need a 16 bit buffe
| |
266 m_string.swap(stringBuilder.m_string); | 221 createBuffer16(); |
267 m_buffer.swap(stringBuilder.m_buffer); | |
268 std::swap(m_is8Bit, stringBuilder.m_is8Bit); | |
269 std::swap(m_bufferCharacters8, stringBuilder.m_bufferCharacters8); | |
270 } | 222 } |
271 | 223 |
272 private: | 224 void createBuffer8(); |
273 void allocateBuffer(const LChar* currentCharacters, unsigned requiredLength) ; | 225 void createBuffer16(); |
274 void allocateBuffer(const UChar* currentCharacters, unsigned requiredLength) ; | |
275 void allocateBufferUpConvert(const LChar* currentCharacters, unsigned requir edLength); | |
276 template <typename CharType> | |
277 void reallocateBuffer(unsigned requiredLength); | |
278 template <typename CharType> | |
279 ALWAYS_INLINE CharType* appendUninitialized(unsigned length); | |
280 template <typename CharType> | |
281 CharType* appendUninitializedSlow(unsigned length); | |
282 template <typename CharType> | |
283 ALWAYS_INLINE CharType * getBufferCharacters(); | |
284 void reifyString(); | |
285 String reifySubstring(unsigned position, unsigned length) const; | |
286 | 226 |
287 String m_string; // Pointers first: crbug.com/232031 | 227 bool hasBuffer() const { return m_buffer; } |
288 RefPtr<StringImpl> m_buffer; | 228 |
229 String m_string; | |
289 union { | 230 union { |
290 LChar* m_bufferCharacters8; | 231 Buffer8* m_buffer8; |
291 UChar* m_bufferCharacters16; | 232 Buffer16* m_buffer16; |
233 void* m_buffer; | |
hajimehoshi
2016/06/27 06:13:20
Where is |m_buffer| used now?
esprehn
2016/06/27 06:15:15
hasBuffer() uses it since it doesn't care about th
| |
292 }; | 234 }; |
293 unsigned m_length; | 235 unsigned m_length; |
294 bool m_is8Bit; | 236 bool m_is8Bit; |
295 }; | 237 }; |
296 | 238 |
297 template <> | |
298 ALWAYS_INLINE LChar* StringBuilder::getBufferCharacters<LChar>() | |
299 { | |
300 DCHECK(m_is8Bit); | |
301 return m_bufferCharacters8; | |
302 } | |
303 | |
304 template <> | |
305 ALWAYS_INLINE UChar* StringBuilder::getBufferCharacters<UChar>() | |
306 { | |
307 DCHECK(!m_is8Bit); | |
308 return m_bufferCharacters16; | |
309 } | |
310 | |
311 template <typename CharType> | 239 template <typename CharType> |
312 bool equal(const StringBuilder& s, const CharType* buffer, unsigned length) | 240 bool equal(const StringBuilder& s, const CharType* buffer, unsigned length) |
313 { | 241 { |
314 if (s.length() != length) | 242 if (s.length() != length) |
315 return false; | 243 return false; |
316 | 244 |
317 if (s.is8Bit()) | 245 if (s.is8Bit()) |
318 return equal(s.characters8(), buffer, length); | 246 return equal(s.characters8(), buffer, length); |
319 | 247 |
320 return equal(s.characters16(), buffer, length); | 248 return equal(s.characters16(), buffer, length); |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
382 inline bool operator==(const StringBuilder& a, const String& b) { return equal(a , b); } | 310 inline bool operator==(const StringBuilder& a, const String& b) { return equal(a , b); } |
383 inline bool operator!=(const StringBuilder& a, const String& b) { return !equal( a, b); } | 311 inline bool operator!=(const StringBuilder& a, const String& b) { return !equal( a, b); } |
384 inline bool operator==(const String& a, const StringBuilder& b) { return equal(b , a); } | 312 inline bool operator==(const String& a, const StringBuilder& b) { return equal(b , a); } |
385 inline bool operator!=(const String& a, const StringBuilder& b) { return !equal( b, a); } | 313 inline bool operator!=(const String& a, const StringBuilder& b) { return !equal( b, a); } |
386 | 314 |
387 } // namespace WTF | 315 } // namespace WTF |
388 | 316 |
389 using WTF::StringBuilder; | 317 using WTF::StringBuilder; |
390 | 318 |
391 #endif // StringBuilder_h | 319 #endif // StringBuilder_h |
OLD | NEW |