| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (C) 2010 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 #include "config.h" | |
| 28 #include "StringBuilder.h" | |
| 29 | |
| 30 #include "IntegerToStringConversion.h" | |
| 31 #include "WTFString.h" | |
| 32 | |
| 33 namespace WTF { | |
| 34 | |
| 35 static const unsigned minimumCapacity = 16; | |
| 36 | |
| 37 void StringBuilder::reifyString() const | |
| 38 { | |
| 39 // Check if the string already exists. | |
| 40 if (!m_string.isNull()) { | |
| 41 ASSERT(m_string.length() == m_length); | |
| 42 return; | |
| 43 } | |
| 44 | |
| 45 // Check for empty. | |
| 46 if (!m_length) { | |
| 47 m_string = StringImpl::empty(); | |
| 48 return; | |
| 49 } | |
| 50 | |
| 51 // Must be valid in the buffer, take a substring (unless string fills the bu
ffer). | |
| 52 ASSERT(m_buffer && m_length <= m_buffer->length()); | |
| 53 m_string = (m_length == m_buffer->length()) | |
| 54 ? m_buffer.get() | |
| 55 : StringImpl::create(m_buffer, 0, m_length); | |
| 56 | |
| 57 if (m_buffer->has16BitShadow() && m_valid16BitShadowLength < m_length) | |
| 58 m_buffer->upconvertCharacters(m_valid16BitShadowLength, m_length); | |
| 59 | |
| 60 m_valid16BitShadowLength = m_length; | |
| 61 } | |
| 62 | |
| 63 void StringBuilder::resize(unsigned newSize) | |
| 64 { | |
| 65 // Check newSize < m_length, hence m_length > 0. | |
| 66 ASSERT(newSize <= m_length); | |
| 67 if (newSize == m_length) | |
| 68 return; | |
| 69 ASSERT(m_length); | |
| 70 | |
| 71 // If there is a buffer, we only need to duplicate it if it has more than on
e ref. | |
| 72 if (m_buffer) { | |
| 73 m_string = String(); // Clear the string to remove the reference to m_bu
ffer if any before checking the reference count of m_buffer. | |
| 74 if (!m_buffer->hasOneRef()) { | |
| 75 if (m_buffer->is8Bit()) | |
| 76 allocateBuffer(m_buffer->characters8(), m_buffer->length()); | |
| 77 else | |
| 78 allocateBuffer(m_buffer->characters16(), m_buffer->length()); | |
| 79 } | |
| 80 m_length = newSize; | |
| 81 return; | |
| 82 } | |
| 83 | |
| 84 // Since m_length && !m_buffer, the string must be valid in m_string, and m_
string.length() > 0. | |
| 85 ASSERT(!m_string.isEmpty()); | |
| 86 ASSERT(m_length == m_string.length()); | |
| 87 ASSERT(newSize < m_string.length()); | |
| 88 m_length = newSize; | |
| 89 m_string = StringImpl::create(m_string.impl(), 0, newSize); | |
| 90 } | |
| 91 | |
| 92 // Allocate a new 8 bit buffer, copying in currentCharacters (these may come fro
m either m_string | |
| 93 // or m_buffer, neither will be reassigned until the copy has completed). | |
| 94 void StringBuilder::allocateBuffer(const LChar* currentCharacters, unsigned requ
iredLength) | |
| 95 { | |
| 96 ASSERT(m_is8Bit); | |
| 97 // Copy the existing data into a new buffer, set result to point to the end
of the existing data. | |
| 98 RefPtr<StringImpl> buffer = StringImpl::createUninitialized(requiredLength,
m_bufferCharacters8); | |
| 99 memcpy(m_bufferCharacters8, currentCharacters, static_cast<size_t>(m_length)
* sizeof(LChar)); // This can't overflow. | |
| 100 | |
| 101 // Update the builder state. | |
| 102 m_buffer = buffer.release(); | |
| 103 m_string = String(); | |
| 104 } | |
| 105 | |
| 106 // Allocate a new 16 bit buffer, copying in currentCharacters (these may come fr
om either m_string | |
| 107 // or m_buffer, neither will be reassigned until the copy has completed). | |
| 108 void StringBuilder::allocateBuffer(const UChar* currentCharacters, unsigned requ
iredLength) | |
| 109 { | |
| 110 ASSERT(!m_is8Bit); | |
| 111 // Copy the existing data into a new buffer, set result to point to the end
of the existing data. | |
| 112 RefPtr<StringImpl> buffer = StringImpl::createUninitialized(requiredLength,
m_bufferCharacters16); | |
| 113 memcpy(m_bufferCharacters16, currentCharacters, static_cast<size_t>(m_length
) * sizeof(UChar)); // This can't overflow. | |
| 114 | |
| 115 // Update the builder state. | |
| 116 m_buffer = buffer.release(); | |
| 117 m_string = String(); | |
| 118 } | |
| 119 | |
| 120 // Allocate a new 16 bit buffer, copying in currentCharacters (which is 8 bit an
d may come | |
| 121 // from either m_string or m_buffer, neither will be reassigned until the copy h
as completed). | |
| 122 void StringBuilder::allocateBufferUpConvert(const LChar* currentCharacters, unsi
gned requiredLength) | |
| 123 { | |
| 124 ASSERT(m_is8Bit); | |
| 125 // Copy the existing data into a new buffer, set result to point to the end
of the existing data. | |
| 126 RefPtr<StringImpl> buffer = StringImpl::createUninitialized(requiredLength,
m_bufferCharacters16); | |
| 127 for (unsigned i = 0; i < m_length; ++i) | |
| 128 m_bufferCharacters16[i] = currentCharacters[i]; | |
| 129 | |
| 130 m_is8Bit = false; | |
| 131 | |
| 132 // Update the builder state. | |
| 133 m_buffer = buffer.release(); | |
| 134 m_string = String(); | |
| 135 } | |
| 136 | |
| 137 template <> | |
| 138 void StringBuilder::reallocateBuffer<LChar>(unsigned requiredLength) | |
| 139 { | |
| 140 // If the buffer has only one ref (by this StringBuilder), reallocate it, | |
| 141 // otherwise fall back to "allocate and copy" method. | |
| 142 m_string = String(); | |
| 143 | |
| 144 ASSERT(m_is8Bit); | |
| 145 ASSERT(m_buffer->is8Bit()); | |
| 146 | |
| 147 if (m_buffer->hasOneRef()) | |
| 148 m_buffer = StringImpl::reallocate(m_buffer.release(), requiredLength, m_
bufferCharacters8); | |
| 149 else | |
| 150 allocateBuffer(m_buffer->characters8(), requiredLength); | |
| 151 } | |
| 152 | |
| 153 template <> | |
| 154 void StringBuilder::reallocateBuffer<UChar>(unsigned requiredLength) | |
| 155 { | |
| 156 // If the buffer has only one ref (by this StringBuilder), reallocate it, | |
| 157 // otherwise fall back to "allocate and copy" method. | |
| 158 m_string = String(); | |
| 159 | |
| 160 if (m_buffer->is8Bit()) | |
| 161 allocateBufferUpConvert(m_buffer->characters8(), requiredLength); | |
| 162 else if (m_buffer->hasOneRef()) | |
| 163 m_buffer = StringImpl::reallocate(m_buffer.release(), requiredLength, m_
bufferCharacters16); | |
| 164 else | |
| 165 allocateBuffer(m_buffer->characters16(), requiredLength); | |
| 166 } | |
| 167 | |
| 168 void StringBuilder::reserveCapacity(unsigned newCapacity) | |
| 169 { | |
| 170 if (m_buffer) { | |
| 171 // If there is already a buffer, then grow if necessary. | |
| 172 if (newCapacity > m_buffer->length()) { | |
| 173 if (m_buffer->is8Bit()) | |
| 174 reallocateBuffer<LChar>(newCapacity); | |
| 175 else | |
| 176 reallocateBuffer<UChar>(newCapacity); | |
| 177 } | |
| 178 } else { | |
| 179 // Grow the string, if necessary. | |
| 180 if (newCapacity > m_length) { | |
| 181 if (!m_length) { | |
| 182 LChar* nullPlaceholder = 0; | |
| 183 allocateBuffer(nullPlaceholder, newCapacity); | |
| 184 } else if (m_string.is8Bit()) | |
| 185 allocateBuffer(m_string.characters8(), newCapacity); | |
| 186 else | |
| 187 allocateBuffer(m_string.characters16(), newCapacity); | |
| 188 } | |
| 189 } | |
| 190 } | |
| 191 | |
| 192 // Make 'length' additional capacity be available in m_buffer, update m_string &
m_length, | |
| 193 // return a pointer to the newly allocated storage. | |
| 194 template <typename CharType> | |
| 195 ALWAYS_INLINE CharType* StringBuilder::appendUninitialized(unsigned length) | |
| 196 { | |
| 197 ASSERT(length); | |
| 198 | |
| 199 // Calculate the new size of the builder after appending. | |
| 200 unsigned requiredLength = length + m_length; | |
| 201 RELEASE_ASSERT(requiredLength >= length); | |
| 202 | |
| 203 if ((m_buffer) && (requiredLength <= m_buffer->length())) { | |
| 204 // If the buffer is valid it must be at least as long as the current bui
lder contents! | |
| 205 ASSERT(m_buffer->length() >= m_length); | |
| 206 unsigned currentLength = m_length; | |
| 207 m_string = String(); | |
| 208 m_length = requiredLength; | |
| 209 return getBufferCharacters<CharType>() + currentLength; | |
| 210 } | |
| 211 | |
| 212 return appendUninitializedSlow<CharType>(requiredLength); | |
| 213 } | |
| 214 | |
| 215 // Make 'length' additional capacity be available in m_buffer, update m_string &
m_length, | |
| 216 // return a pointer to the newly allocated storage. | |
| 217 template <typename CharType> | |
| 218 CharType* StringBuilder::appendUninitializedSlow(unsigned requiredLength) | |
| 219 { | |
| 220 ASSERT(requiredLength); | |
| 221 | |
| 222 if (m_buffer) { | |
| 223 // If the buffer is valid it must be at least as long as the current bui
lder contents! | |
| 224 ASSERT(m_buffer->length() >= m_length); | |
| 225 | |
| 226 reallocateBuffer<CharType>(std::max(requiredLength, std::max(minimumCapa
city, m_buffer->length() * 2))); | |
| 227 } else { | |
| 228 ASSERT(m_string.length() == m_length); | |
| 229 allocateBuffer(m_length ? m_string.getCharacters<CharType>() : 0, std::m
ax(requiredLength, std::max(minimumCapacity, m_length * 2))); | |
| 230 } | |
| 231 | |
| 232 CharType* result = getBufferCharacters<CharType>() + m_length; | |
| 233 m_length = requiredLength; | |
| 234 return result; | |
| 235 } | |
| 236 | |
| 237 void StringBuilder::append(const UChar* characters, unsigned length) | |
| 238 { | |
| 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 } | |
| 251 | |
| 252 // Calculate the new size of the builder after appending. | |
| 253 unsigned requiredLength = length + m_length; | |
| 254 RELEASE_ASSERT(requiredLength >= length); | |
| 255 | |
| 256 if (m_buffer) { | |
| 257 // If the buffer is valid it must be at least as long as the current
builder contents! | |
| 258 ASSERT(m_buffer->length() >= m_length); | |
| 259 | |
| 260 allocateBufferUpConvert(m_buffer->characters8(), requiredLength); | |
| 261 } else { | |
| 262 ASSERT(m_string.length() == m_length); | |
| 263 allocateBufferUpConvert(m_string.isNull() ? 0 : m_string.characters8
(), std::max(requiredLength, std::max(minimumCapacity, m_length * 2))); | |
| 264 } | |
| 265 | |
| 266 memcpy(m_bufferCharacters16 + m_length, characters, static_cast<size_t>(
length) * sizeof(UChar)); | |
| 267 m_length = requiredLength; | |
| 268 } else | |
| 269 memcpy(appendUninitialized<UChar>(length), characters, static_cast<size_
t>(length) * sizeof(UChar)); | |
| 270 } | |
| 271 | |
| 272 void StringBuilder::append(const LChar* characters, unsigned length) | |
| 273 { | |
| 274 if (!length) | |
| 275 return; | |
| 276 ASSERT(characters); | |
| 277 | |
| 278 if (m_is8Bit) { | |
| 279 LChar* dest = appendUninitialized<LChar>(length); | |
| 280 if (length > 8) | |
| 281 memcpy(dest, characters, static_cast<size_t>(length) * sizeof(LChar)
); | |
| 282 else { | |
| 283 const LChar* end = characters + length; | |
| 284 while (characters < end) | |
| 285 *(dest++) = *(characters++); | |
| 286 } | |
| 287 } else { | |
| 288 UChar* dest = appendUninitialized<UChar>(length); | |
| 289 const LChar* end = characters + length; | |
| 290 while (characters < end) | |
| 291 *(dest++) = *(characters++); | |
| 292 } | |
| 293 } | |
| 294 | |
| 295 void StringBuilder::appendNumber(int number) | |
| 296 { | |
| 297 numberToStringSigned<StringBuilder>(number, this); | |
| 298 } | |
| 299 | |
| 300 void StringBuilder::appendNumber(unsigned int number) | |
| 301 { | |
| 302 numberToStringUnsigned<StringBuilder>(number, this); | |
| 303 } | |
| 304 | |
| 305 void StringBuilder::appendNumber(long number) | |
| 306 { | |
| 307 numberToStringSigned<StringBuilder>(number, this); | |
| 308 } | |
| 309 | |
| 310 void StringBuilder::appendNumber(unsigned long number) | |
| 311 { | |
| 312 numberToStringUnsigned<StringBuilder>(number, this); | |
| 313 } | |
| 314 | |
| 315 void StringBuilder::appendNumber(long long number) | |
| 316 { | |
| 317 numberToStringSigned<StringBuilder>(number, this); | |
| 318 } | |
| 319 | |
| 320 void StringBuilder::appendNumber(unsigned long long number) | |
| 321 { | |
| 322 numberToStringUnsigned<StringBuilder>(number, this); | |
| 323 } | |
| 324 | |
| 325 bool StringBuilder::canShrink() const | |
| 326 { | |
| 327 // Only shrink the buffer if it's less than 80% full. Need to tune this heur
istic! | |
| 328 return m_buffer && m_buffer->length() > (m_length + (m_length >> 2)); | |
| 329 } | |
| 330 | |
| 331 void StringBuilder::shrinkToFit() | |
| 332 { | |
| 333 if (canShrink()) { | |
| 334 if (m_is8Bit) | |
| 335 reallocateBuffer<LChar>(m_length); | |
| 336 else | |
| 337 reallocateBuffer<UChar>(m_length); | |
| 338 m_string = m_buffer; | |
| 339 m_buffer = 0; | |
| 340 } | |
| 341 } | |
| 342 | |
| 343 } // namespace WTF | |
| OLD | NEW |