|  | // 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 |