blob: a051787103b60af6e36e5275cf747befbb76df8a [file] [log] [blame]
/*
* Copyright (C) 2010 Apple Inc. All rights reserved.
* Copyright (C) 2012 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
#include <algorithm>
#include "base/strings/string_util.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/renderer/platform/wtf/dtoa.h"
#include "third_party/blink/renderer/platform/wtf/text/integer_to_string_conversion.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
namespace WTF {
String StringBuilder::ToString() {
if (!length_)
return g_empty_string;
if (string_.IsNull()) {
if (is_8bit_)
string_ = String(Characters8(), length_);
else
string_ = String(Characters16(), length_);
ClearBuffer();
}
return string_;
}
AtomicString StringBuilder::ToAtomicString() {
if (!length_)
return g_empty_atom;
if (string_.IsNull()) {
if (is_8bit_)
string_ = AtomicString(Characters8(), length_);
else
string_ = AtomicString(Characters16(), length_);
ClearBuffer();
}
return AtomicString(string_);
}
String StringBuilder::Substring(unsigned start, unsigned length) const {
if (start >= length_)
return g_empty_string;
if (!string_.IsNull())
return string_.Substring(start, length);
length = std::min(length, length_ - start);
if (is_8bit_)
return String(Characters8() + start, length);
return String(Characters16() + start, length);
}
void StringBuilder::Swap(StringBuilder& builder) {
absl::optional<Buffer8> buffer8;
absl::optional<Buffer16> buffer16;
if (has_buffer_) {
if (is_8bit_) {
buffer8 = std::move(buffer8_);
buffer8_.~Buffer8();
} else {
buffer16 = std::move(buffer16_);
buffer16_.~Buffer16();
}
}
if (builder.has_buffer_) {
if (builder.is_8bit_) {
new (&buffer8_) Buffer8(std::move(builder.buffer8_));
builder.buffer8_.~Buffer8();
} else {
new (&buffer16_) Buffer16(std::move(builder.buffer16_));
builder.buffer16_.~Buffer16();
}
}
if (buffer8)
new (&builder.buffer8_) Buffer8(std::move(*buffer8));
else if (buffer16)
new (&builder.buffer16_) Buffer16(std::move(*buffer16));
std::swap(string_, builder.string_);
std::swap(length_, builder.length_);
std::swap(is_8bit_, builder.is_8bit_);
std::swap(has_buffer_, builder.has_buffer_);
}
void StringBuilder::ClearBuffer() {
if (!has_buffer_)
return;
if (is_8bit_)
buffer8_.~Buffer8();
else
buffer16_.~Buffer16();
has_buffer_ = false;
}
void StringBuilder::Ensure16Bit() {
EnsureBuffer16(0);
}
void StringBuilder::Clear() {
ClearBuffer();
string_ = String();
length_ = 0;
is_8bit_ = true;
}
unsigned StringBuilder::Capacity() const {
if (!HasBuffer())
return 0;
if (is_8bit_)
return buffer8_.capacity();
return buffer16_.capacity();
}
void StringBuilder::ReserveCapacity(unsigned new_capacity) {
if (!HasBuffer()) {
if (is_8bit_)
CreateBuffer8(new_capacity);
else
CreateBuffer16(new_capacity);
return;
}
if (is_8bit_)
buffer8_.ReserveCapacity(new_capacity);
else
buffer16_.ReserveCapacity(new_capacity);
}
void StringBuilder::Reserve16BitCapacity(unsigned new_capacity) {
if (is_8bit_ || !HasBuffer())
CreateBuffer16(new_capacity);
else
buffer16_.ReserveCapacity(new_capacity);
}
void StringBuilder::Resize(unsigned new_size) {
DCHECK_LE(new_size, length_);
string_ = string_.Left(new_size);
length_ = new_size;
if (HasBuffer()) {
if (is_8bit_)
buffer8_.resize(new_size);
else
buffer16_.resize(new_size);
}
}
void StringBuilder::CreateBuffer8(unsigned added_size) {
DCHECK(!HasBuffer());
DCHECK(is_8bit_);
new (&buffer8_) Buffer8;
has_buffer_ = true;
// createBuffer is called right before appending addedSize more bytes. We
// want to ensure we have enough space to fit m_string plus the added
// size.
//
// We also ensure that we have at least the initialBufferSize of extra space
// for appending new bytes to avoid future mallocs for appending short
// strings or single characters. This is a no-op if m_length == 0 since
// initialBufferSize() is the same as the inline capacity of the vector.
// This allows doing append(string); append('\0') without extra mallocs.
buffer8_.ReserveInitialCapacity(length_ +
std::max(added_size, InitialBufferSize()));
length_ = 0;
Append(string_);
string_ = String();
}
void StringBuilder::CreateBuffer16(unsigned added_size) {
DCHECK(is_8bit_ || !HasBuffer());
Buffer8 buffer8;
unsigned length = length_;
wtf_size_t capacity = 0;
if (has_buffer_) {
buffer8 = std::move(buffer8_);
buffer8_.~Buffer8();
capacity = buffer8.capacity();
}
new (&buffer16_) Buffer16;
has_buffer_ = true;
capacity = std::max<wtf_size_t>(
capacity, length_ + std::max<unsigned>(
added_size, InitialBufferSize() / sizeof(UChar)));
// See CreateBuffer8's call to ReserveInitialCapacity for why we do this.
buffer16_.ReserveInitialCapacity(capacity);
is_8bit_ = false;
length_ = 0;
if (!buffer8.IsEmpty()) {
Append(buffer8.data(), length);
return;
}
Append(string_);
string_ = String();
}
void StringBuilder::Append(const UChar* characters, unsigned length) {
if (!length)
return;
DCHECK(characters);
// If there's only one char we use append(UChar) instead since it will
// check for latin1 and avoid converting to 16bit if possible.
if (length == 1) {
Append(*characters);
return;
}
EnsureBuffer16(length);
buffer16_.Append(characters, length);
length_ += length;
}
void StringBuilder::Append(const LChar* characters, unsigned length) {
if (!length)
return;
DCHECK(characters);
if (is_8bit_) {
EnsureBuffer8(length);
buffer8_.Append(characters, length);
length_ += length;
return;
}
EnsureBuffer16(length);
buffer16_.Append(characters, length);
length_ += length;
}
void StringBuilder::AppendNumber(bool number) {
AppendNumber(static_cast<uint8_t>(number));
}
void StringBuilder::AppendNumber(float number) {
AppendNumber(static_cast<double>(number));
}
void StringBuilder::AppendNumber(double number, unsigned precision) {
NumberToStringBuffer buffer;
Append(NumberToFixedPrecisionString(number, precision, buffer));
}
void StringBuilder::AppendFormat(const char* format, ...) {
va_list args;
static constexpr unsigned kDefaultSize = 256;
Vector<char, kDefaultSize> buffer(kDefaultSize);
va_start(args, format);
int length = base::vsnprintf(buffer.data(), kDefaultSize, format, args);
va_end(args);
DCHECK_GE(length, 0);
if (length >= static_cast<int>(kDefaultSize)) {
buffer.Grow(length + 1);
va_start(args, format);
length = base::vsnprintf(buffer.data(), buffer.size(), format, args);
va_end(args);
}
DCHECK_LT(static_cast<wtf_size_t>(length), buffer.size());
Append(reinterpret_cast<const LChar*>(buffer.data()), length);
}
void StringBuilder::erase(unsigned index) {
if (index >= length_)
return;
if (is_8bit_) {
EnsureBuffer8(0);
buffer8_.EraseAt(index);
} else {
EnsureBuffer16(0);
buffer16_.EraseAt(index);
}
--length_;
}
} // namespace WTF