// 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(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(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(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(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(256)); char* new_buf = reinterpret_cast(realloc(buffer_, new_size)); buffer_ = new_buf; capacity_ = new_size; } return true; } } // namespace dart