summaryrefslogtreecommitdiff
path: root/Source/WTF/wtf/text/StringBuilder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WTF/wtf/text/StringBuilder.cpp')
-rw-r--r--Source/WTF/wtf/text/StringBuilder.cpp153
1 files changed, 134 insertions, 19 deletions
diff --git a/Source/WTF/wtf/text/StringBuilder.cpp b/Source/WTF/wtf/text/StringBuilder.cpp
index c483ba146..436015a43 100644
--- a/Source/WTF/wtf/text/StringBuilder.cpp
+++ b/Source/WTF/wtf/text/StringBuilder.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2010, 2013, 2016 Apple Inc. All rights reserved.
* Copyright (C) 2012 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -28,6 +28,7 @@
#include "StringBuilder.h"
#include "IntegerToStringConversion.h"
+#include "MathExtras.h"
#include "WTFString.h"
#include <wtf/dtoa.h>
@@ -58,12 +59,7 @@ void StringBuilder::reifyString() const
if (m_length == m_buffer->length())
m_string = m_buffer.get();
else
- m_string = StringImpl::create(m_buffer, 0, m_length);
-
- if (m_buffer->has16BitShadow() && m_valid16BitShadowLength < m_length)
- m_buffer->upconvertCharacters(m_valid16BitShadowLength, m_length);
-
- m_valid16BitShadowLength = m_length;
+ m_string = StringImpl::createSubstringSharingImpl(*m_buffer, 0, m_length);
}
void StringBuilder::resize(unsigned newSize)
@@ -84,6 +80,7 @@ void StringBuilder::resize(unsigned newSize)
allocateBuffer(m_buffer->characters16(), m_buffer->length());
}
m_length = newSize;
+ ASSERT(m_buffer->length() >= m_length);
return;
}
@@ -92,7 +89,7 @@ void StringBuilder::resize(unsigned newSize)
ASSERT(m_length == m_string.length());
ASSERT(newSize < m_string.length());
m_length = newSize;
- m_string = StringImpl::create(m_string.impl(), 0, newSize);
+ m_string = StringImpl::createSubstringSharingImpl(*m_string.impl(), 0, newSize);
}
// Allocate a new 8 bit buffer, copying in currentCharacters (these may come from either m_string
@@ -101,12 +98,13 @@ void StringBuilder::allocateBuffer(const LChar* currentCharacters, unsigned requ
{
ASSERT(m_is8Bit);
// Copy the existing data into a new buffer, set result to point to the end of the existing data.
- RefPtr<StringImpl> buffer = StringImpl::createUninitialized(requiredLength, m_bufferCharacters8);
+ auto buffer = StringImpl::createUninitialized(requiredLength, m_bufferCharacters8);
memcpy(m_bufferCharacters8, currentCharacters, static_cast<size_t>(m_length) * sizeof(LChar)); // This can't overflow.
// Update the builder state.
- m_buffer = buffer.release();
+ m_buffer = WTFMove(buffer);
m_string = String();
+ ASSERT(m_buffer->length() == requiredLength);
}
// Allocate a new 16 bit buffer, copying in currentCharacters (these may come from either m_string
@@ -115,12 +113,13 @@ void StringBuilder::allocateBuffer(const UChar* currentCharacters, unsigned requ
{
ASSERT(!m_is8Bit);
// Copy the existing data into a new buffer, set result to point to the end of the existing data.
- RefPtr<StringImpl> buffer = StringImpl::createUninitialized(requiredLength, m_bufferCharacters16);
+ auto buffer = StringImpl::createUninitialized(requiredLength, m_bufferCharacters16);
memcpy(m_bufferCharacters16, currentCharacters, static_cast<size_t>(m_length) * sizeof(UChar)); // This can't overflow.
// Update the builder state.
- m_buffer = buffer.release();
+ m_buffer = WTFMove(buffer);
m_string = String();
+ ASSERT(m_buffer->length() == requiredLength);
}
// Allocate a new 16 bit buffer, copying in currentCharacters (which is 8 bit and may come
@@ -128,16 +127,18 @@ void StringBuilder::allocateBuffer(const UChar* currentCharacters, unsigned requ
void StringBuilder::allocateBufferUpConvert(const LChar* currentCharacters, unsigned requiredLength)
{
ASSERT(m_is8Bit);
+ ASSERT(requiredLength >= m_length);
// Copy the existing data into a new buffer, set result to point to the end of the existing data.
- RefPtr<StringImpl> buffer = StringImpl::createUninitialized(requiredLength, m_bufferCharacters16);
+ auto buffer = StringImpl::createUninitialized(requiredLength, m_bufferCharacters16);
for (unsigned i = 0; i < m_length; ++i)
m_bufferCharacters16[i] = currentCharacters[i];
m_is8Bit = false;
// Update the builder state.
- m_buffer = buffer.release();
+ m_buffer = WTFMove(buffer);
m_string = String();
+ ASSERT(m_buffer->length() == requiredLength);
}
template <>
@@ -151,9 +152,10 @@ void StringBuilder::reallocateBuffer<LChar>(unsigned requiredLength)
ASSERT(m_buffer->is8Bit());
if (m_buffer->hasOneRef())
- m_buffer = StringImpl::reallocate(m_buffer.release(), requiredLength, m_bufferCharacters8);
+ m_buffer = StringImpl::reallocate(m_buffer.releaseNonNull(), requiredLength, m_bufferCharacters8);
else
allocateBuffer(m_buffer->characters8(), requiredLength);
+ ASSERT(m_buffer->length() == requiredLength);
}
template <>
@@ -166,9 +168,10 @@ void StringBuilder::reallocateBuffer<UChar>(unsigned requiredLength)
if (m_buffer->is8Bit())
allocateBufferUpConvert(m_buffer->characters8(), requiredLength);
else if (m_buffer->hasOneRef())
- m_buffer = StringImpl::reallocate(m_buffer.release(), requiredLength, m_bufferCharacters16);
+ m_buffer = StringImpl::reallocate(m_buffer.releaseNonNull(), requiredLength, m_bufferCharacters16);
else
allocateBuffer(m_buffer->characters16(), requiredLength);
+ ASSERT(m_buffer->length() == requiredLength);
}
void StringBuilder::reserveCapacity(unsigned newCapacity)
@@ -193,6 +196,7 @@ void StringBuilder::reserveCapacity(unsigned newCapacity)
allocateBuffer(m_string.characters16(), newCapacity);
}
}
+ ASSERT(!newCapacity || m_buffer->length() >= newCapacity);
}
// Make 'length' additional capacity be available in m_buffer, update m_string & m_length,
@@ -233,11 +237,12 @@ CharType* StringBuilder::appendUninitializedSlow(unsigned requiredLength)
reallocateBuffer<CharType>(expandedCapacity(capacity(), requiredLength));
} else {
ASSERT(m_string.length() == m_length);
- allocateBuffer(m_length ? m_string.getCharacters<CharType>() : 0, expandedCapacity(capacity(), requiredLength));
+ allocateBuffer(m_length ? m_string.characters<CharType>() : 0, expandedCapacity(capacity(), requiredLength));
}
CharType* result = getBufferCharacters<CharType>() + m_length;
m_length = requiredLength;
+ ASSERT(m_buffer->length() >= m_length);
return result;
}
@@ -271,10 +276,11 @@ void StringBuilder::append(const UChar* characters, unsigned length)
allocateBufferUpConvert(m_string.isNull() ? 0 : m_string.characters8(), expandedCapacity(capacity(), requiredLength));
}
- memcpy(m_bufferCharacters16 + m_length, characters, static_cast<size_t>(length) * sizeof(UChar));
+ memcpy(m_bufferCharacters16 + m_length, characters, static_cast<size_t>(length) * sizeof(UChar));
m_length = requiredLength;
} else
memcpy(appendUninitialized<UChar>(length), characters, static_cast<size_t>(length) * sizeof(UChar));
+ ASSERT(m_buffer->length() >= m_length);
}
void StringBuilder::append(const LChar* characters, unsigned length)
@@ -300,6 +306,20 @@ void StringBuilder::append(const LChar* characters, unsigned length)
}
}
+#if USE(CF)
+
+void StringBuilder::append(CFStringRef string)
+{
+ // Fast path: avoid constructing a temporary String when possible.
+ if (auto* characters = CFStringGetCStringPtr(string, kCFStringEncodingISOLatin1)) {
+ append(reinterpret_cast<const LChar*>(characters), CFStringGetLength(string));
+ return;
+ }
+ append(String(string));
+}
+
+#endif
+
void StringBuilder::appendNumber(int number)
{
numberToStringSigned<StringBuilder>(number, this);
@@ -361,8 +381,103 @@ void StringBuilder::shrinkToFit()
reallocateBuffer<LChar>(m_length);
else
reallocateBuffer<UChar>(m_length);
- m_string = m_buffer.release();
+ m_string = WTFMove(m_buffer);
+ }
+}
+
+template <typename OutputCharacterType, typename InputCharacterType>
+static void appendQuotedJSONStringInternalSlow(OutputCharacterType*& output, const InputCharacterType character)
+{
+ switch (character) {
+ case '\t':
+ *output++ = '\\';
+ *output++ = 't';
+ break;
+ case '\r':
+ *output++ = '\\';
+ *output++ = 'r';
+ break;
+ case '\n':
+ *output++ = '\\';
+ *output++ = 'n';
+ break;
+ case '\f':
+ *output++ = '\\';
+ *output++ = 'f';
+ break;
+ case '\b':
+ *output++ = '\\';
+ *output++ = 'b';
+ break;
+ default:
+ ASSERT(!(character & 0xFF00));
+ *output++ = '\\';
+ *output++ = 'u';
+ *output++ = '0';
+ *output++ = '0';
+ *output++ = upperNibbleToLowercaseASCIIHexDigit(character);
+ *output++ = lowerNibbleToLowercaseASCIIHexDigit(character);
+ break;
+ }
+}
+
+template <typename OutputCharacterType, typename InputCharacterType>
+static void appendQuotedJSONStringInternal(OutputCharacterType*& output, const InputCharacterType* input, unsigned length)
+{
+ for (const InputCharacterType* end = input + length; input != end; ++input) {
+ const InputCharacterType character = *input;
+ if (LIKELY(character != '"' && character != '\\' && character > 0x1F)) {
+ *output++ = character;
+ continue;
+ }
+
+ if (character == '"' || character == '\\') {
+ *output++ = '\\';
+ *output++ = character;
+ continue;
+ }
+
+ appendQuotedJSONStringInternalSlow(output, character);
+ }
+}
+
+void StringBuilder::appendQuotedJSONString(const String& string)
+{
+ // Make sure we have enough buffer space to append this string without having
+ // to worry about reallocating in the middle.
+ // The 2 is for the '"' quotes on each end.
+ // The 6 is for characters that need to be \uNNNN encoded.
+ Checked<unsigned> stringLength = string.length();
+ Checked<unsigned> maximumCapacityRequired = length();
+ maximumCapacityRequired += 2 + stringLength * 6;
+ unsigned allocationSize = maximumCapacityRequired.unsafeGet();
+ // This max() is here to allow us to allocate sizes between the range [2^31, 2^32 - 2] because roundUpToPowerOfTwo(1<<31 + some int smaller than 1<<31) == 0.
+ allocationSize = std::max(allocationSize, roundUpToPowerOfTwo(allocationSize));
+
+ if (is8Bit() && !string.is8Bit())
+ allocateBufferUpConvert(m_bufferCharacters8, allocationSize);
+ else
+ reserveCapacity(allocationSize);
+ ASSERT(m_buffer->length() >= allocationSize);
+
+ if (is8Bit()) {
+ ASSERT(string.is8Bit());
+ LChar* output = m_bufferCharacters8 + m_length;
+ *output++ = '"';
+ appendQuotedJSONStringInternal(output, string.characters8(), string.length());
+ *output++ = '"';
+ m_length = output - m_bufferCharacters8;
+ } else {
+ UChar* output = m_bufferCharacters16 + m_length;
+ *output++ = '"';
+ if (string.is8Bit())
+ appendQuotedJSONStringInternal(output, string.characters8(), string.length());
+ else
+ appendQuotedJSONStringInternal(output, string.characters16(), string.length());
+ *output++ = '"';
+ m_length = output - m_bufferCharacters16;
}
+ ASSERT(m_buffer->length() >= m_length);
}
} // namespace WTF