blob: aff44f55d3bffd5062a6ae1b78d519480334d543 [file] [log] [blame]
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "platform/text_buffer.h"
#include "platform/assert.h"
#include "platform/globals.h"
#include "platform/unicode.h"
#include "platform/utils.h"
namespace dart {
intptr_t BaseTextBuffer::Printf(const char* format, ...) {
va_list args;
va_start(args, format);
intptr_t len = VPrintf(format, args);
va_end(args);
return len;
}
intptr_t BaseTextBuffer::VPrintf(const char* format, va_list args) {
va_list args1;
va_copy(args1, args);
intptr_t remaining = capacity_ - length_;
ASSERT(remaining >= 0);
intptr_t len = Utils::VSNPrint(buffer_ + length_, remaining, format, args1);
va_end(args1);
if (len >= remaining) {
if (!EnsureCapacity(len)) {
length_ = capacity_ - 1;
buffer_[length_] = '\0';
return remaining - 1;
}
remaining = capacity_ - length_;
ASSERT(remaining > len);
va_list args2;
va_copy(args2, args);
intptr_t len2 =
Utils::VSNPrint(buffer_ + length_, remaining, format, args2);
va_end(args2);
ASSERT(len == len2);
}
length_ += len;
buffer_[length_] = '\0';
return len;
}
void BaseTextBuffer::AddChar(char ch) {
if (!EnsureCapacity(sizeof(ch))) return;
buffer_[length_] = ch;
length_++;
buffer_[length_] = '\0';
}
void BaseTextBuffer::AddRaw(const uint8_t* buffer, intptr_t buffer_length) {
ASSERT(buffer != nullptr);
if (!EnsureCapacity(buffer_length)) {
buffer_length = capacity_ - length_ - 1; // Copy what fits.
}
memmove(&buffer_[length_], buffer, buffer_length);
length_ += buffer_length;
buffer_[length_] = '\0';
}
void BaseTextBuffer::AddEscapedUTF8(const char* const s, intptr_t len) {
const uint8_t* cursor = reinterpret_cast<const uint8_t*>(s);
const uint8_t* end = cursor + len;
intptr_t needed = 0;
while (cursor < end) {
uint8_t codeunit = *cursor++;
if (codeunit >= 0x80) {
needed += 1;
} else {
needed += EscapedCodeUnitLength(codeunit);
}
}
if (!EnsureCapacity(needed)) return;
cursor = reinterpret_cast<const uint8_t*>(s);
while (cursor < end) {
uint8_t codeunit = *cursor++;
if (codeunit >= 0x80) {
buffer_[length_++] = codeunit;
} else {
EscapeAndAddCodeUnit(codeunit);
}
}
buffer_[length_] = '\0';
}
void BaseTextBuffer::AddEscapedLatin1(const uint8_t* const s, intptr_t len) {
const uint8_t* cursor = s;
const uint8_t* end = cursor + len;
intptr_t needed = 0;
while (cursor < end) {
needed += EscapedCodeUnitLength(*cursor++);
}
if (!EnsureCapacity(needed)) return;
cursor = s;
while (cursor < end) {
EscapeAndAddCodeUnit(*cursor++);
}
buffer_[length_] = '\0';
}
void BaseTextBuffer::AddEscapedUTF16(const uint16_t* s, intptr_t len) {
for (const uint16_t* end = s + len; s < end; s++) {
if (!EnsureCapacity(6)) return;
uint16_t code_unit = *s;
if (Utf16::IsTrailSurrogate(code_unit)) {
EscapeAndAddUTF16CodeUnit(code_unit);
} else if (Utf16::IsLeadSurrogate(code_unit)) {
if (s + 1 == end) {
EscapeAndAddUTF16CodeUnit(code_unit);
} else {
uint16_t next_code_unit = *(s + 1);
if (Utf16::IsTrailSurrogate(next_code_unit)) {
uint32_t decoded = Utf16::Decode(code_unit, next_code_unit);
EscapeAndAddCodeUnit(decoded);
s++;
} else {
EscapeAndAddUTF16CodeUnit(code_unit);
}
}
} else {
EscapeAndAddCodeUnit(code_unit);
}
}
buffer_[length_] = '\0';
}
DART_FORCE_INLINE
intptr_t BaseTextBuffer::EscapedCodeUnitLength(uint32_t codeunit) {
switch (codeunit) {
case '"':
case '\\':
case '/':
case '\b':
case '\f':
case '\n':
case '\r':
case '\t':
return 2;
default:
if (codeunit < 0x20) {
return 6;
} else if (codeunit <= Utf8::kMaxOneByteChar) {
return 1;
} else if (codeunit <= Utf8::kMaxTwoByteChar) {
return 2;
} else if (codeunit <= Utf8::kMaxThreeByteChar) {
return 3;
} else {
ASSERT(codeunit <= Utf8::kMaxFourByteChar);
return 4;
}
}
}
static uint8_t Hex(uint8_t value) {
return value < 10 ? '0' + value : 'A' + value - 10;
}
// Write a UTF-32 code unit so it can be read by a JSON parser in a string
// literal. Use official encoding from JSON specification. http://json.org/
DART_FORCE_INLINE
void BaseTextBuffer::EscapeAndAddCodeUnit(uint32_t codeunit) {
intptr_t remaining = capacity_ - length_;
switch (codeunit) {
case '"':
ASSERT(remaining > 2);
buffer_[length_++] = '\\';
buffer_[length_++] = '\"';
break;
case '\\':
ASSERT(remaining > 2);
buffer_[length_++] = '\\';
buffer_[length_++] = '\\';
break;
case '/':
ASSERT(remaining > 2);
buffer_[length_++] = '\\';
buffer_[length_++] = '/';
break;
case '\b':
ASSERT(remaining > 2);
buffer_[length_++] = '\\';
buffer_[length_++] = 'b';
break;
case '\f':
ASSERT(remaining > 2);
buffer_[length_++] = '\\';
buffer_[length_++] = 'f';
break;
case '\n':
ASSERT(remaining > 2);
buffer_[length_++] = '\\';
buffer_[length_++] = 'n';
break;
case '\r':
ASSERT(remaining > 2);
buffer_[length_++] = '\\';
buffer_[length_++] = 'r';
break;
case '\t':
ASSERT(remaining > 2);
buffer_[length_++] = '\\';
buffer_[length_++] = 't';
break;
default:
constexpr int kMask = ~(1 << 6);
if (codeunit < 0x20) {
ASSERT(remaining > 6);
buffer_[length_++] = '\\';
buffer_[length_++] = 'u';
buffer_[length_++] = Hex((codeunit >> 12) & 0xF);
buffer_[length_++] = Hex((codeunit >> 8) & 0xF);
buffer_[length_++] = Hex((codeunit >> 4) & 0xF);
buffer_[length_++] = Hex((codeunit >> 0) & 0xF);
} else if (codeunit <= Utf8::kMaxOneByteChar) {
ASSERT(remaining > 1);
buffer_[length_++] = codeunit;
} else if (codeunit <= Utf8::kMaxTwoByteChar) {
ASSERT(remaining > 2);
buffer_[length_++] = 0xC0 | (codeunit >> 6);
buffer_[length_++] = 0x80 | (codeunit & kMask);
} else if (codeunit <= Utf8::kMaxThreeByteChar) {
ASSERT(remaining > 3);
buffer_[length_++] = 0xE0 | (codeunit >> 12);
buffer_[length_++] = 0x80 | ((codeunit >> 6) & kMask);
buffer_[length_++] = 0x80 | (codeunit & kMask);
} else {
ASSERT(codeunit <= Utf8::kMaxFourByteChar);
ASSERT(remaining > 4);
buffer_[length_++] = 0xF0 | (codeunit >> 18);
buffer_[length_++] = 0x80 | ((codeunit >> 12) & kMask);
buffer_[length_++] = 0x80 | ((codeunit >> 6) & kMask);
buffer_[length_++] = 0x80 | (codeunit & kMask);
}
}
}
// Write an incomplete UTF-16 code unit so it can be read by a JSON parser in a
// string literal.
void BaseTextBuffer::EscapeAndAddUTF16CodeUnit(uint16_t codeunit) {
intptr_t remaining = capacity_ - length_;
ASSERT(remaining > 6);
buffer_[length_++] = '\\';
buffer_[length_++] = 'u';
buffer_[length_++] = Hex((codeunit >> 12) & 0xF);
buffer_[length_++] = Hex((codeunit >> 8) & 0xF);
buffer_[length_++] = Hex((codeunit >> 4) & 0xF);
buffer_[length_++] = Hex((codeunit >> 0) & 0xF);
}
void BaseTextBuffer::AddString(const char* s) {
AddRaw(reinterpret_cast<const uint8_t*>(s), strlen(s));
}
void BaseTextBuffer::AddEscapedString(const char* s) {
AddEscapedUTF8(s, strlen(s));
}
TextBuffer::TextBuffer(intptr_t buf_size) {
ASSERT(buf_size > 0);
buffer_ = reinterpret_cast<char*>(malloc(buf_size));
capacity_ = buf_size;
Clear();
}
TextBuffer::~TextBuffer() {
free(buffer_);
buffer_ = nullptr;
}
char* TextBuffer::Steal() {
char* r = buffer_;
buffer_ = nullptr;
capacity_ = 0;
length_ = 0;
return r;
}
bool TextBuffer::EnsureCapacity(intptr_t len) {
intptr_t remaining = capacity_ - length_;
if (remaining <= len) {
intptr_t new_size = capacity_ + Utils::Maximum(capacity_, len + 1);
new_size = Utils::Maximum(new_size, static_cast<intptr_t>(256));
char* new_buf = reinterpret_cast<char*>(realloc(buffer_, new_size));
buffer_ = new_buf;
capacity_ = new_size;
}
return true;
}
} // namespace dart