Chromium Code Reviews| 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 |