| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2010 Apple Inc. All rights reserved. | 2 * Copyright (C) 2010 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 15 matching lines...) Expand all Loading... |
| 26 | 26 |
| 27 #include "config.h" | 27 #include "config.h" |
| 28 #include "StringBuilder.h" | 28 #include "StringBuilder.h" |
| 29 | 29 |
| 30 #include "IntegerToStringConversion.h" | 30 #include "IntegerToStringConversion.h" |
| 31 #include "WTFString.h" | 31 #include "WTFString.h" |
| 32 #include "wtf/dtoa.h" | 32 #include "wtf/dtoa.h" |
| 33 | 33 |
| 34 namespace WTF { | 34 namespace WTF { |
| 35 | 35 |
| 36 static unsigned expandedCapacity(unsigned capacity, unsigned requiredLength) | 36 static unsigned expandedCapacity(unsigned capacity, unsigned requiredLength) { |
| 37 { | 37 static const unsigned minimumCapacity = 16; |
| 38 static const unsigned minimumCapacity = 16; | 38 return std::max(requiredLength, std::max(minimumCapacity, capacity * 2)); |
| 39 return std::max(requiredLength, std::max(minimumCapacity, capacity * 2)); | 39 } |
| 40 } | 40 |
| 41 | 41 void StringBuilder::reifyString() { |
| 42 void StringBuilder::reifyString() | 42 if (!m_string.isNull()) { |
| 43 { | 43 ASSERT(m_string.length() == m_length); |
| 44 if (!m_string.isNull()) { | 44 return; |
| 45 ASSERT(m_string.length() == m_length); | 45 } |
| 46 return; | 46 |
| 47 } | 47 if (!m_length) { |
| 48 | 48 m_string = StringImpl::empty(); |
| 49 if (!m_length) { | 49 return; |
| 50 m_string = StringImpl::empty(); | 50 } |
| 51 return; | 51 |
| 52 } | 52 ASSERT(m_buffer && m_length <= m_buffer->length()); |
| 53 | 53 if (m_length == m_buffer->length()) { |
| 54 ASSERT(m_buffer && m_length <= m_buffer->length()); | 54 m_string = m_buffer.release(); |
| 55 if (m_length == m_buffer->length()) { | 55 return; |
| 56 m_string = m_buffer.release(); | 56 } |
| 57 return; | 57 |
| 58 } | 58 m_string = m_buffer->substring(0, m_length); |
| 59 | 59 } |
| 60 m_string = m_buffer->substring(0, m_length); | 60 |
| 61 } | 61 String StringBuilder::reifySubstring(unsigned position, unsigned length) const { |
| 62 | 62 ASSERT(m_string.isNull()); |
| 63 String StringBuilder::reifySubstring(unsigned position, unsigned length) const | 63 ASSERT(m_buffer); |
| 64 { | 64 unsigned substringLength = std::min(length, m_length - position); |
| 65 ASSERT(m_string.isNull()); | 65 return m_buffer->substring(position, substringLength); |
| 66 ASSERT(m_buffer); | 66 } |
| 67 unsigned substringLength = std::min(length, m_length - position); | 67 |
| 68 return m_buffer->substring(position, substringLength); | 68 void StringBuilder::resize(unsigned newSize) { |
| 69 } | 69 // Check newSize < m_length, hence m_length > 0. |
| 70 | 70 ASSERT(newSize <= m_length); |
| 71 void StringBuilder::resize(unsigned newSize) | 71 if (newSize == m_length) |
| 72 { | 72 return; |
| 73 // Check newSize < m_length, hence m_length > 0. | 73 ASSERT(m_length); |
| 74 ASSERT(newSize <= m_length); | 74 |
| 75 if (newSize == m_length) | 75 // If there is a buffer, we only need to duplicate it if it has more than one
ref. |
| 76 return; | 76 if (m_buffer) { |
| 77 ASSERT(m_length); | 77 m_string = String(); // Clear the string to remove the reference to m_buffe
r if any before checking the reference count of m_buffer. |
| 78 | 78 if (!m_buffer->hasOneRef()) { |
| 79 // If there is a buffer, we only need to duplicate it if it has more than on
e ref. | 79 if (m_buffer->is8Bit()) |
| 80 if (m_buffer) { | 80 allocateBuffer(m_buffer->characters8(), m_buffer->length()); |
| 81 m_string = String(); // Clear the string to remove the reference to m_bu
ffer if any before checking the reference count of m_buffer. | 81 else |
| 82 if (!m_buffer->hasOneRef()) { | 82 allocateBuffer(m_buffer->characters16(), m_buffer->length()); |
| 83 if (m_buffer->is8Bit()) | 83 } |
| 84 allocateBuffer(m_buffer->characters8(), m_buffer->length()); | |
| 85 else | |
| 86 allocateBuffer(m_buffer->characters16(), m_buffer->length()); | |
| 87 } | |
| 88 m_length = newSize; | |
| 89 return; | |
| 90 } | |
| 91 | |
| 92 // Since m_length && !m_buffer, the string must be valid in m_string, and m_
string.length() > 0. | |
| 93 ASSERT(!m_string.isEmpty()); | |
| 94 ASSERT(m_length == m_string.length()); | |
| 95 ASSERT(newSize < m_string.length()); | |
| 96 m_length = newSize; | 84 m_length = newSize; |
| 97 RefPtr<StringImpl> string = m_string.releaseImpl(); | 85 return; |
| 98 if (string->hasOneRef()) { | 86 } |
| 99 // If we're the only ones with a reference to the string, we can | 87 |
| 100 // re-purpose the string as m_buffer and continue mutating it. | 88 // Since m_length && !m_buffer, the string must be valid in m_string, and m_st
ring.length() > 0. |
| 101 m_buffer = string; | 89 ASSERT(!m_string.isEmpty()); |
| 102 } else { | 90 ASSERT(m_length == m_string.length()); |
| 103 // Otherwise, we need to make a copy of the string so that we don't | 91 ASSERT(newSize < m_string.length()); |
| 104 // mutate a String that's held elsewhere. | 92 m_length = newSize; |
| 105 m_buffer = string->substring(0, m_length); | 93 RefPtr<StringImpl> string = m_string.releaseImpl(); |
| 106 } | 94 if (string->hasOneRef()) { |
| 95 // If we're the only ones with a reference to the string, we can |
| 96 // re-purpose the string as m_buffer and continue mutating it. |
| 97 m_buffer = string; |
| 98 } else { |
| 99 // Otherwise, we need to make a copy of the string so that we don't |
| 100 // mutate a String that's held elsewhere. |
| 101 m_buffer = string->substring(0, m_length); |
| 102 } |
| 107 } | 103 } |
| 108 | 104 |
| 109 // Allocate a new 8 bit buffer, copying in currentCharacters (these may come fro
m either m_string | 105 // Allocate a new 8 bit buffer, copying in currentCharacters (these may come fro
m either m_string |
| 110 // or m_buffer, neither will be reassigned until the copy has completed). | 106 // or m_buffer, neither will be reassigned until the copy has completed). |
| 111 void StringBuilder::allocateBuffer(const LChar* currentCharacters, unsigned requ
iredLength) | 107 void StringBuilder::allocateBuffer(const LChar* currentCharacters, unsigned requ
iredLength) { |
| 112 { | 108 ASSERT(m_is8Bit); |
| 113 ASSERT(m_is8Bit); | 109 // Copy the existing data into a new buffer, set result to point to the end of
the existing data. |
| 114 // Copy the existing data into a new buffer, set result to point to the end
of the existing data. | 110 RefPtr<StringImpl> buffer = StringImpl::createUninitialized(requiredLength, m_
bufferCharacters8); |
| 115 RefPtr<StringImpl> buffer = StringImpl::createUninitialized(requiredLength,
m_bufferCharacters8); | 111 memcpy(m_bufferCharacters8, currentCharacters, static_cast<size_t>(m_length) *
sizeof(LChar)); // This can't overflow. |
| 116 memcpy(m_bufferCharacters8, currentCharacters, static_cast<size_t>(m_length)
* sizeof(LChar)); // This can't overflow. | 112 |
| 117 | 113 // Update the builder state. |
| 118 // Update the builder state. | 114 m_buffer = buffer.release(); |
| 119 m_buffer = buffer.release(); | 115 m_string = String(); |
| 120 m_string = String(); | |
| 121 } | 116 } |
| 122 | 117 |
| 123 // Allocate a new 16 bit buffer, copying in currentCharacters (these may come fr
om either m_string | 118 // Allocate a new 16 bit buffer, copying in currentCharacters (these may come fr
om either m_string |
| 124 // or m_buffer, neither will be reassigned until the copy has completed). | 119 // or m_buffer, neither will be reassigned until the copy has completed). |
| 125 void StringBuilder::allocateBuffer(const UChar* currentCharacters, unsigned requ
iredLength) | 120 void StringBuilder::allocateBuffer(const UChar* currentCharacters, unsigned requ
iredLength) { |
| 126 { | 121 ASSERT(!m_is8Bit); |
| 127 ASSERT(!m_is8Bit); | 122 // Copy the existing data into a new buffer, set result to point to the end of
the existing data. |
| 128 // Copy the existing data into a new buffer, set result to point to the end
of the existing data. | 123 RefPtr<StringImpl> buffer = StringImpl::createUninitialized(requiredLength, m_
bufferCharacters16); |
| 129 RefPtr<StringImpl> buffer = StringImpl::createUninitialized(requiredLength,
m_bufferCharacters16); | 124 memcpy(m_bufferCharacters16, currentCharacters, static_cast<size_t>(m_length)
* sizeof(UChar)); // This can't overflow. |
| 130 memcpy(m_bufferCharacters16, currentCharacters, static_cast<size_t>(m_length
) * sizeof(UChar)); // This can't overflow. | 125 |
| 131 | 126 // Update the builder state. |
| 132 // Update the builder state. | 127 m_buffer = buffer.release(); |
| 133 m_buffer = buffer.release(); | 128 m_string = String(); |
| 134 m_string = String(); | |
| 135 } | 129 } |
| 136 | 130 |
| 137 // Allocate a new 16 bit buffer, copying in currentCharacters (which is 8 bit an
d may come | 131 // Allocate a new 16 bit buffer, copying in currentCharacters (which is 8 bit an
d may come |
| 138 // from either m_string or m_buffer, neither will be reassigned until the copy h
as completed). | 132 // from either m_string or m_buffer, neither will be reassigned until the copy h
as completed). |
| 139 void StringBuilder::allocateBufferUpConvert(const LChar* currentCharacters, unsi
gned requiredLength) | 133 void StringBuilder::allocateBufferUpConvert(const LChar* currentCharacters, unsi
gned requiredLength) { |
| 140 { | 134 ASSERT(m_is8Bit); |
| 141 ASSERT(m_is8Bit); | 135 // Copy the existing data into a new buffer, set result to point to the end of
the existing data. |
| 142 // Copy the existing data into a new buffer, set result to point to the end
of the existing data. | 136 RefPtr<StringImpl> buffer = StringImpl::createUninitialized(requiredLength, m_
bufferCharacters16); |
| 143 RefPtr<StringImpl> buffer = StringImpl::createUninitialized(requiredLength,
m_bufferCharacters16); | 137 for (unsigned i = 0; i < m_length; ++i) |
| 144 for (unsigned i = 0; i < m_length; ++i) | 138 m_bufferCharacters16[i] = currentCharacters[i]; |
| 145 m_bufferCharacters16[i] = currentCharacters[i]; | 139 |
| 146 | 140 m_is8Bit = false; |
| 147 m_is8Bit = false; | 141 |
| 148 | 142 // Update the builder state. |
| 149 // Update the builder state. | 143 m_buffer = buffer.release(); |
| 150 m_buffer = buffer.release(); | 144 m_string = String(); |
| 151 m_string = String(); | |
| 152 } | 145 } |
| 153 | 146 |
| 154 template <> | 147 template <> |
| 155 void StringBuilder::reallocateBuffer<LChar>(unsigned requiredLength) | 148 void StringBuilder::reallocateBuffer<LChar>(unsigned requiredLength) { |
| 156 { | 149 // If the buffer has only one ref (by this StringBuilder), reallocate it, |
| 157 // If the buffer has only one ref (by this StringBuilder), reallocate it, | 150 // otherwise fall back to "allocate and copy" method. |
| 158 // otherwise fall back to "allocate and copy" method. | 151 m_string = String(); |
| 159 m_string = String(); | 152 |
| 160 | 153 ASSERT(m_is8Bit); |
| 161 ASSERT(m_is8Bit); | 154 ASSERT(m_buffer->is8Bit()); |
| 162 ASSERT(m_buffer->is8Bit()); | 155 |
| 163 | 156 allocateBuffer(m_buffer->characters8(), requiredLength); |
| 164 allocateBuffer(m_buffer->characters8(), requiredLength); | |
| 165 } | 157 } |
| 166 | 158 |
| 167 template <> | 159 template <> |
| 168 void StringBuilder::reallocateBuffer<UChar>(unsigned requiredLength) | 160 void StringBuilder::reallocateBuffer<UChar>(unsigned requiredLength) { |
| 169 { | 161 // If the buffer has only one ref (by this StringBuilder), reallocate it, |
| 170 // If the buffer has only one ref (by this StringBuilder), reallocate it, | 162 // otherwise fall back to "allocate and copy" method. |
| 171 // otherwise fall back to "allocate and copy" method. | 163 m_string = String(); |
| 172 m_string = String(); | 164 |
| 173 | 165 if (m_buffer->is8Bit()) |
| 174 if (m_buffer->is8Bit()) | 166 allocateBufferUpConvert(m_buffer->characters8(), requiredLength); |
| 175 allocateBufferUpConvert(m_buffer->characters8(), requiredLength); | 167 else |
| 176 else | 168 allocateBuffer(m_buffer->characters16(), requiredLength); |
| 177 allocateBuffer(m_buffer->characters16(), requiredLength); | 169 } |
| 178 } | 170 |
| 179 | 171 void StringBuilder::reserveCapacity(unsigned newCapacity) { |
| 180 void StringBuilder::reserveCapacity(unsigned newCapacity) | 172 if (m_buffer) { |
| 181 { | 173 // If there is already a buffer, then grow if necessary. |
| 182 if (m_buffer) { | 174 if (newCapacity > m_buffer->length()) { |
| 183 // If there is already a buffer, then grow if necessary. | 175 if (m_buffer->is8Bit()) |
| 184 if (newCapacity > m_buffer->length()) { | 176 reallocateBuffer<LChar>(newCapacity); |
| 185 if (m_buffer->is8Bit()) | 177 else |
| 186 reallocateBuffer<LChar>(newCapacity); | 178 reallocateBuffer<UChar>(newCapacity); |
| 187 else | 179 } |
| 188 reallocateBuffer<UChar>(newCapacity); | 180 } else { |
| 189 } | 181 // Grow the string, if necessary. |
| 190 } else { | 182 if (newCapacity > m_length) { |
| 191 // Grow the string, if necessary. | 183 if (!m_length) { |
| 192 if (newCapacity > m_length) { | 184 LChar* nullPlaceholder = 0; |
| 193 if (!m_length) { | 185 allocateBuffer(nullPlaceholder, newCapacity); |
| 194 LChar* nullPlaceholder = 0; | 186 } else if (m_string.is8Bit()) { |
| 195 allocateBuffer(nullPlaceholder, newCapacity); | 187 allocateBuffer(m_string.characters8(), newCapacity); |
| 196 } else if (m_string.is8Bit()) { | 188 } else { |
| 197 allocateBuffer(m_string.characters8(), newCapacity); | 189 allocateBuffer(m_string.characters16(), newCapacity); |
| 198 } else { | 190 } |
| 199 allocateBuffer(m_string.characters16(), newCapacity); | 191 } |
| 200 } | 192 } |
| 201 } | |
| 202 } | |
| 203 } | 193 } |
| 204 | 194 |
| 205 // Make 'length' additional capacity be available in m_buffer, update m_string &
m_length, | 195 // Make 'length' additional capacity be available in m_buffer, update m_string &
m_length, |
| 206 // return a pointer to the newly allocated storage. | 196 // return a pointer to the newly allocated storage. |
| 207 template <typename CharType> | 197 template <typename CharType> |
| 208 ALWAYS_INLINE CharType* StringBuilder::appendUninitialized(unsigned length) | 198 ALWAYS_INLINE CharType* StringBuilder::appendUninitialized(unsigned length) { |
| 209 { | 199 ASSERT(length); |
| 210 ASSERT(length); | 200 |
| 201 // Calculate the new size of the builder after appending. |
| 202 unsigned requiredLength = length + m_length; |
| 203 RELEASE_ASSERT(requiredLength >= length); |
| 204 |
| 205 if ((m_buffer) && (requiredLength <= m_buffer->length())) { |
| 206 // If the buffer is valid it must be at least as long as the current builder
contents! |
| 207 ASSERT(m_buffer->length() >= m_length); |
| 208 unsigned currentLength = m_length; |
| 209 m_string = String(); |
| 210 m_length = requiredLength; |
| 211 return getBufferCharacters<CharType>() + currentLength; |
| 212 } |
| 213 |
| 214 return appendUninitializedSlow<CharType>(requiredLength); |
| 215 } |
| 216 |
| 217 // Make 'length' additional capacity be available in m_buffer, update m_string &
m_length, |
| 218 // return a pointer to the newly allocated storage. |
| 219 template <typename CharType> |
| 220 CharType* StringBuilder::appendUninitializedSlow(unsigned requiredLength) { |
| 221 ASSERT(requiredLength); |
| 222 |
| 223 if (m_buffer) { |
| 224 // If the buffer is valid it must be at least as long as the current builder
contents! |
| 225 ASSERT(m_buffer->length() >= m_length); |
| 226 |
| 227 reallocateBuffer<CharType>(expandedCapacity(capacity(), requiredLength)); |
| 228 } else { |
| 229 ASSERT(m_string.length() == m_length); |
| 230 allocateBuffer(m_length ? m_string.getCharacters<CharType>() : 0, expandedCa
pacity(capacity(), requiredLength)); |
| 231 } |
| 232 |
| 233 CharType* result = getBufferCharacters<CharType>() + m_length; |
| 234 m_length = requiredLength; |
| 235 return result; |
| 236 } |
| 237 |
| 238 void StringBuilder::append(const UChar* characters, unsigned length) { |
| 239 if (!length) |
| 240 return; |
| 241 |
| 242 ASSERT(characters); |
| 243 |
| 244 if (m_is8Bit) { |
| 245 if (length == 1 && !(*characters & ~0xff)) { |
| 246 // Append as 8 bit character |
| 247 LChar lChar = static_cast<LChar>(*characters); |
| 248 append(&lChar, 1); |
| 249 return; |
| 250 } |
| 211 | 251 |
| 212 // Calculate the new size of the builder after appending. | 252 // Calculate the new size of the builder after appending. |
| 213 unsigned requiredLength = length + m_length; | 253 unsigned requiredLength = length + m_length; |
| 214 RELEASE_ASSERT(requiredLength >= length); | 254 RELEASE_ASSERT(requiredLength >= length); |
| 215 | 255 |
| 216 if ((m_buffer) && (requiredLength <= m_buffer->length())) { | |
| 217 // If the buffer is valid it must be at least as long as the current bui
lder contents! | |
| 218 ASSERT(m_buffer->length() >= m_length); | |
| 219 unsigned currentLength = m_length; | |
| 220 m_string = String(); | |
| 221 m_length = requiredLength; | |
| 222 return getBufferCharacters<CharType>() + currentLength; | |
| 223 } | |
| 224 | |
| 225 return appendUninitializedSlow<CharType>(requiredLength); | |
| 226 } | |
| 227 | |
| 228 // Make 'length' additional capacity be available in m_buffer, update m_string &
m_length, | |
| 229 // return a pointer to the newly allocated storage. | |
| 230 template <typename CharType> | |
| 231 CharType* StringBuilder::appendUninitializedSlow(unsigned requiredLength) | |
| 232 { | |
| 233 ASSERT(requiredLength); | |
| 234 | |
| 235 if (m_buffer) { | 256 if (m_buffer) { |
| 236 // If the buffer is valid it must be at least as long as the current bui
lder contents! | 257 // If the buffer is valid it must be at least as long as the current build
er contents! |
| 237 ASSERT(m_buffer->length() >= m_length); | 258 ASSERT(m_buffer->length() >= m_length); |
| 238 | 259 |
| 239 reallocateBuffer<CharType>(expandedCapacity(capacity(), requiredLength))
; | 260 allocateBufferUpConvert(m_buffer->characters8(), expandedCapacity(capacity
(), requiredLength)); |
| 240 } else { | 261 } else { |
| 241 ASSERT(m_string.length() == m_length); | 262 ASSERT(m_string.length() == m_length); |
| 242 allocateBuffer(m_length ? m_string.getCharacters<CharType>() : 0, expand
edCapacity(capacity(), requiredLength)); | 263 allocateBufferUpConvert(m_string.isNull() ? 0 : m_string.characters8(), ex
pandedCapacity(capacity(), requiredLength)); |
| 243 } | 264 } |
| 244 | 265 |
| 245 CharType* result = getBufferCharacters<CharType>() + m_length; | 266 memcpy(m_bufferCharacters16 + m_length, characters, static_cast<size_t>(leng
th) * sizeof(UChar)); |
| 246 m_length = requiredLength; | 267 m_length = requiredLength; |
| 247 return result; | 268 } else { |
| 248 } | 269 memcpy(appendUninitialized<UChar>(length), characters, static_cast<size_t>(l
ength) * sizeof(UChar)); |
| 249 | 270 } |
| 250 void StringBuilder::append(const UChar* characters, unsigned length) | 271 } |
| 251 { | 272 |
| 252 if (!length) | 273 void StringBuilder::append(const LChar* characters, unsigned length) { |
| 253 return; | 274 if (!length) |
| 254 | 275 return; |
| 255 ASSERT(characters); | 276 ASSERT(characters); |
| 256 | 277 |
| 257 if (m_is8Bit) { | 278 if (m_is8Bit) { |
| 258 if (length == 1 && !(*characters & ~0xff)) { | 279 LChar* dest = appendUninitialized<LChar>(length); |
| 259 // Append as 8 bit character | 280 if (length > 8) { |
| 260 LChar lChar = static_cast<LChar>(*characters); | 281 memcpy(dest, characters, static_cast<size_t>(length) * sizeof(LChar)); |
| 261 append(&lChar, 1); | |
| 262 return; | |
| 263 } | |
| 264 | |
| 265 // Calculate the new size of the builder after appending. | |
| 266 unsigned requiredLength = length + m_length; | |
| 267 RELEASE_ASSERT(requiredLength >= length); | |
| 268 | |
| 269 if (m_buffer) { | |
| 270 // If the buffer is valid it must be at least as long as the current
builder contents! | |
| 271 ASSERT(m_buffer->length() >= m_length); | |
| 272 | |
| 273 allocateBufferUpConvert(m_buffer->characters8(), expandedCapacity(ca
pacity(), requiredLength)); | |
| 274 } else { | |
| 275 ASSERT(m_string.length() == m_length); | |
| 276 allocateBufferUpConvert(m_string.isNull() ? 0 : m_string.characters8
(), expandedCapacity(capacity(), requiredLength)); | |
| 277 } | |
| 278 | |
| 279 memcpy(m_bufferCharacters16 + m_length, characters, static_cast<size_t>(
length) * sizeof(UChar)); | |
| 280 m_length = requiredLength; | |
| 281 } else { | 282 } else { |
| 282 memcpy(appendUninitialized<UChar>(length), characters, static_cast<size_
t>(length) * sizeof(UChar)); | 283 const LChar* end = characters + length; |
| 283 } | 284 while (characters < end) |
| 284 } | 285 *(dest++) = *(characters++); |
| 285 | 286 } |
| 286 void StringBuilder::append(const LChar* characters, unsigned length) | 287 } else { |
| 287 { | 288 UChar* dest = appendUninitialized<UChar>(length); |
| 288 if (!length) | 289 const LChar* end = characters + length; |
| 289 return; | 290 while (characters < end) |
| 290 ASSERT(characters); | 291 *(dest++) = *(characters++); |
| 291 | 292 } |
| 292 if (m_is8Bit) { | 293 } |
| 293 LChar* dest = appendUninitialized<LChar>(length); | 294 |
| 294 if (length > 8) { | 295 void StringBuilder::appendNumber(int number) { |
| 295 memcpy(dest, characters, static_cast<size_t>(length) * sizeof(LChar)
); | 296 numberToStringSigned<StringBuilder>(number, this); |
| 296 } else { | 297 } |
| 297 const LChar* end = characters + length; | 298 |
| 298 while (characters < end) | 299 void StringBuilder::appendNumber(unsigned number) { |
| 299 *(dest++) = *(characters++); | 300 numberToStringUnsigned<StringBuilder>(number, this); |
| 300 } | 301 } |
| 301 } else { | 302 |
| 302 UChar* dest = appendUninitialized<UChar>(length); | 303 void StringBuilder::appendNumber(long number) { |
| 303 const LChar* end = characters + length; | 304 numberToStringSigned<StringBuilder>(number, this); |
| 304 while (characters < end) | 305 } |
| 305 *(dest++) = *(characters++); | 306 |
| 306 } | 307 void StringBuilder::appendNumber(unsigned long number) { |
| 307 } | 308 numberToStringUnsigned<StringBuilder>(number, this); |
| 308 | 309 } |
| 309 void StringBuilder::appendNumber(int number) | 310 |
| 310 { | 311 void StringBuilder::appendNumber(long long number) { |
| 311 numberToStringSigned<StringBuilder>(number, this); | 312 numberToStringSigned<StringBuilder>(number, this); |
| 312 } | 313 } |
| 313 | 314 |
| 314 void StringBuilder::appendNumber(unsigned number) | 315 void StringBuilder::appendNumber(unsigned long long number) { |
| 315 { | 316 numberToStringUnsigned<StringBuilder>(number, this); |
| 316 numberToStringUnsigned<StringBuilder>(number, this); | 317 } |
| 317 } | 318 |
| 318 | 319 static void expandLCharToUCharInplace(UChar* buffer, size_t length) { |
| 319 void StringBuilder::appendNumber(long number) | 320 const LChar* sourceEnd = reinterpret_cast<LChar*>(buffer) + length; |
| 320 { | 321 UChar* current = buffer + length; |
| 321 numberToStringSigned<StringBuilder>(number, this); | 322 while (current != buffer) |
| 322 } | 323 *--current = *--sourceEnd; |
| 323 | 324 } |
| 324 void StringBuilder::appendNumber(unsigned long number) | 325 |
| 325 { | 326 void StringBuilder::appendNumber(double number, unsigned precision, TrailingZero
sTruncatingPolicy trailingZerosTruncatingPolicy) { |
| 326 numberToStringUnsigned<StringBuilder>(number, this); | 327 bool truncateTrailingZeros = trailingZerosTruncatingPolicy == TruncateTrailing
Zeros; |
| 327 } | 328 size_t numberLength; |
| 328 | 329 if (m_is8Bit) { |
| 329 void StringBuilder::appendNumber(long long number) | 330 LChar* dest = appendUninitialized<LChar>(NumberToStringBufferLength); |
| 330 { | 331 const char* result = numberToFixedPrecisionString(number, precision, reinter
pret_cast<char*>(dest), truncateTrailingZeros); |
| 331 numberToStringSigned<StringBuilder>(number, this); | 332 numberLength = strlen(result); |
| 332 } | 333 } else { |
| 333 | 334 UChar* dest = appendUninitialized<UChar>(NumberToStringBufferLength); |
| 334 void StringBuilder::appendNumber(unsigned long long number) | 335 const char* result = numberToFixedPrecisionString(number, precision, reinter
pret_cast<char*>(dest), truncateTrailingZeros); |
| 335 { | 336 numberLength = strlen(result); |
| 336 numberToStringUnsigned<StringBuilder>(number, this); | 337 expandLCharToUCharInplace(dest, numberLength); |
| 337 } | 338 } |
| 338 | 339 ASSERT(m_length >= NumberToStringBufferLength); |
| 339 static void expandLCharToUCharInplace(UChar* buffer, size_t length) | 340 // Remove what appendUninitialized added. |
| 340 { | 341 m_length -= NumberToStringBufferLength; |
| 341 const LChar* sourceEnd = reinterpret_cast<LChar*>(buffer) + length; | 342 ASSERT(numberLength <= NumberToStringBufferLength); |
| 342 UChar* current = buffer + length; | 343 m_length += numberLength; |
| 343 while (current != buffer) | 344 } |
| 344 *--current = *--sourceEnd; | 345 |
| 345 } | 346 bool StringBuilder::canShrink() const { |
| 346 | 347 // Only shrink the buffer if it's less than 80% full. Need to tune this heuris
tic! |
| 347 void StringBuilder::appendNumber(double number, unsigned precision, TrailingZero
sTruncatingPolicy trailingZerosTruncatingPolicy) | 348 return m_buffer && m_buffer->length() > (m_length + (m_length >> 2)); |
| 348 { | 349 } |
| 349 bool truncateTrailingZeros = trailingZerosTruncatingPolicy == TruncateTraili
ngZeros; | 350 |
| 350 size_t numberLength; | 351 void StringBuilder::shrinkToFit() { |
| 351 if (m_is8Bit) { | 352 if (!canShrink()) |
| 352 LChar* dest = appendUninitialized<LChar>(NumberToStringBufferLength); | 353 return; |
| 353 const char* result = numberToFixedPrecisionString(number, precision, rei
nterpret_cast<char*>(dest), truncateTrailingZeros); | 354 if (m_is8Bit) |
| 354 numberLength = strlen(result); | 355 reallocateBuffer<LChar>(m_length); |
| 355 } else { | 356 else |
| 356 UChar* dest = appendUninitialized<UChar>(NumberToStringBufferLength); | 357 reallocateBuffer<UChar>(m_length); |
| 357 const char* result = numberToFixedPrecisionString(number, precision, rei
nterpret_cast<char*>(dest), truncateTrailingZeros); | 358 m_string = m_buffer.release(); |
| 358 numberLength = strlen(result); | 359 } |
| 359 expandLCharToUCharInplace(dest, numberLength); | 360 |
| 360 } | 361 } // namespace WTF |
| 361 ASSERT(m_length >= NumberToStringBufferLength); | |
| 362 // Remove what appendUninitialized added. | |
| 363 m_length -= NumberToStringBufferLength; | |
| 364 ASSERT(numberLength <= NumberToStringBufferLength); | |
| 365 m_length += numberLength; | |
| 366 } | |
| 367 | |
| 368 bool StringBuilder::canShrink() const | |
| 369 { | |
| 370 // Only shrink the buffer if it's less than 80% full. Need to tune this heur
istic! | |
| 371 return m_buffer && m_buffer->length() > (m_length + (m_length >> 2)); | |
| 372 } | |
| 373 | |
| 374 void StringBuilder::shrinkToFit() | |
| 375 { | |
| 376 if (!canShrink()) | |
| 377 return; | |
| 378 if (m_is8Bit) | |
| 379 reallocateBuffer<LChar>(m_length); | |
| 380 else | |
| 381 reallocateBuffer<UChar>(m_length); | |
| 382 m_string = m_buffer.release(); | |
| 383 } | |
| 384 | |
| 385 } // namespace WTF | |
| OLD | NEW |