mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
![Tess Strickland](/assets/img/avatar_default.png)
Generally, methods that take a *TextBuffer pointer do not care how the internal buffer is allocated, and so they could be used for either if both were subclasses of a base class that contained the printing methods. This CL makes that base class, and now TextBuffer and ZoneTextBuffer now share the exact same set of methods for printing to the internal buffer. Since the base class is in platform, this does mean dropping the overload of AddString for Dart String objects that was part of ZoneTextBuffer. Instead, this CL just adds an intermediate call to ToCString() for the small number of callers that used the overload, keeping the printing interface the same for both. In addition, one use of TextBuffer that then re-allocated the buffer contents into the zone manually has been replaced with a ZoneTextBuffer instead. Change-Id: I438a085e7e20d55d93987fd7f36afd636f95955f Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/157741 Reviewed-by: Ryan Macnak <rmacnak@google.com> Commit-Queue: Tess Strickland <sstrickl@google.com>
403 lines
10 KiB
C++
403 lines
10 KiB
C++
// Copyright (c) 2017, 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/assert.h"
|
|
|
|
#include "platform/unicode.h"
|
|
#include "vm/double_conversion.h"
|
|
#include "vm/json_writer.h"
|
|
#include "vm/object.h"
|
|
|
|
namespace dart {
|
|
|
|
class MaybeOnStackBuffer {
|
|
public:
|
|
explicit MaybeOnStackBuffer(intptr_t size) {
|
|
if (size > kOnStackBufferCapacity) {
|
|
p_ = reinterpret_cast<char*>(malloc(size));
|
|
} else {
|
|
p_ = &buffer_[0];
|
|
}
|
|
}
|
|
~MaybeOnStackBuffer() {
|
|
if (p_ != &buffer_[0]) free(p_);
|
|
}
|
|
|
|
char* p() { return p_; }
|
|
|
|
private:
|
|
static const intptr_t kOnStackBufferCapacity = 4096;
|
|
char* p_;
|
|
char buffer_[kOnStackBufferCapacity];
|
|
};
|
|
|
|
JSONWriter::JSONWriter(intptr_t buf_size)
|
|
: open_objects_(0), buffer_(buf_size) {}
|
|
|
|
void JSONWriter::AppendSerializedObject(const char* serialized_object) {
|
|
PrintCommaIfNeeded();
|
|
buffer_.AddString(serialized_object);
|
|
}
|
|
|
|
void JSONWriter::AppendSerializedObject(const uint8_t* buffer,
|
|
intptr_t buffer_length) {
|
|
buffer_.AddRaw(buffer, buffer_length);
|
|
}
|
|
|
|
void JSONWriter::AppendSerializedObject(const char* property_name,
|
|
const char* serialized_object) {
|
|
PrintCommaIfNeeded();
|
|
PrintPropertyName(property_name);
|
|
buffer_.AddString(serialized_object);
|
|
}
|
|
|
|
void JSONWriter::Clear() {
|
|
buffer_.Clear();
|
|
open_objects_ = 0;
|
|
}
|
|
|
|
void JSONWriter::OpenObject(const char* property_name) {
|
|
PrintCommaIfNeeded();
|
|
open_objects_++;
|
|
if (property_name != NULL) {
|
|
PrintPropertyName(property_name);
|
|
}
|
|
buffer_.AddChar('{');
|
|
}
|
|
|
|
void JSONWriter::UncloseObject() {
|
|
intptr_t len = buffer_.length();
|
|
ASSERT(len > 0);
|
|
ASSERT(buffer_.buffer()[len - 1] == '}');
|
|
open_objects_++;
|
|
buffer_.set_length(len - 1);
|
|
}
|
|
|
|
void JSONWriter::CloseObject() {
|
|
ASSERT(open_objects_ > 0);
|
|
open_objects_--;
|
|
buffer_.AddChar('}');
|
|
}
|
|
|
|
void JSONWriter::OpenArray(const char* property_name) {
|
|
PrintCommaIfNeeded();
|
|
if (property_name != NULL) {
|
|
PrintPropertyName(property_name);
|
|
}
|
|
open_objects_++;
|
|
buffer_.AddChar('[');
|
|
}
|
|
|
|
void JSONWriter::CloseArray() {
|
|
ASSERT(open_objects_ > 0);
|
|
open_objects_--;
|
|
buffer_.AddChar(']');
|
|
}
|
|
|
|
void JSONWriter::PrintValueNull() {
|
|
PrintCommaIfNeeded();
|
|
buffer_.Printf("null");
|
|
}
|
|
|
|
void JSONWriter::PrintValueBool(bool b) {
|
|
PrintCommaIfNeeded();
|
|
buffer_.Printf("%s", b ? "true" : "false");
|
|
}
|
|
|
|
void JSONWriter::PrintValue(intptr_t i) {
|
|
EnsureIntegerIsRepresentableInJavaScript(static_cast<int64_t>(i));
|
|
PrintCommaIfNeeded();
|
|
buffer_.Printf("%" Pd "", i);
|
|
}
|
|
|
|
void JSONWriter::PrintValue64(int64_t i) {
|
|
EnsureIntegerIsRepresentableInJavaScript(i);
|
|
PrintCommaIfNeeded();
|
|
buffer_.Printf("%" Pd64 "", i);
|
|
}
|
|
|
|
void JSONWriter::PrintValue(double d) {
|
|
// Max length of a double in characters (including \0).
|
|
// See double_conversion.cc.
|
|
const size_t kBufferLen = 25;
|
|
char buffer[kBufferLen];
|
|
DoubleToCString(d, buffer, kBufferLen);
|
|
PrintCommaIfNeeded();
|
|
buffer_.Printf("%s", buffer);
|
|
}
|
|
|
|
static const char base64_digits[65] =
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
static const char base64_pad = '=';
|
|
|
|
void JSONWriter::PrintValueBase64(const uint8_t* bytes, intptr_t length) {
|
|
PrintCommaIfNeeded();
|
|
buffer_.AddChar('"');
|
|
|
|
intptr_t odd_bits = length % 3;
|
|
intptr_t even_bits = length - odd_bits;
|
|
for (intptr_t i = 0; i < even_bits; i += 3) {
|
|
intptr_t triplet = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
|
|
buffer_.AddChar(base64_digits[triplet >> 18]);
|
|
buffer_.AddChar(base64_digits[(triplet >> 12) & 63]);
|
|
buffer_.AddChar(base64_digits[(triplet >> 6) & 63]);
|
|
buffer_.AddChar(base64_digits[triplet & 63]);
|
|
}
|
|
if (odd_bits == 1) {
|
|
intptr_t triplet = bytes[even_bits] << 16;
|
|
buffer_.AddChar(base64_digits[triplet >> 18]);
|
|
buffer_.AddChar(base64_digits[(triplet >> 12) & 63]);
|
|
buffer_.AddChar(base64_pad);
|
|
buffer_.AddChar(base64_pad);
|
|
} else if (odd_bits == 2) {
|
|
intptr_t triplet = (bytes[even_bits] << 16) | (bytes[even_bits + 1] << 8);
|
|
buffer_.AddChar(base64_digits[triplet >> 18]);
|
|
buffer_.AddChar(base64_digits[(triplet >> 12) & 63]);
|
|
buffer_.AddChar(base64_digits[(triplet >> 6) & 63]);
|
|
buffer_.AddChar(base64_pad);
|
|
}
|
|
|
|
buffer_.AddChar('"');
|
|
}
|
|
|
|
void JSONWriter::PrintValue(const char* s) {
|
|
PrintCommaIfNeeded();
|
|
buffer_.AddChar('"');
|
|
AddEscapedUTF8String(s);
|
|
buffer_.AddChar('"');
|
|
}
|
|
|
|
bool JSONWriter::PrintValueStr(const String& s,
|
|
intptr_t offset,
|
|
intptr_t count) {
|
|
PrintCommaIfNeeded();
|
|
buffer_.AddChar('"');
|
|
bool did_truncate = AddDartString(s, offset, count);
|
|
buffer_.AddChar('"');
|
|
return did_truncate;
|
|
}
|
|
|
|
void JSONWriter::PrintValueNoEscape(const char* s) {
|
|
PrintCommaIfNeeded();
|
|
buffer_.Printf("%s", s);
|
|
}
|
|
|
|
void JSONWriter::PrintfValue(const char* format, ...) {
|
|
va_list args;
|
|
va_start(args, format);
|
|
VPrintfValue(format, args);
|
|
va_end(args);
|
|
}
|
|
|
|
void JSONWriter::VPrintfValue(const char* format, va_list args) {
|
|
PrintCommaIfNeeded();
|
|
|
|
va_list measure_args;
|
|
va_copy(measure_args, args);
|
|
intptr_t len = Utils::VSNPrint(NULL, 0, format, measure_args);
|
|
va_end(measure_args);
|
|
|
|
MaybeOnStackBuffer mosb(len + 1);
|
|
char* p = mosb.p();
|
|
|
|
va_list print_args;
|
|
va_copy(print_args, args);
|
|
intptr_t len2 = Utils::VSNPrint(p, len + 1, format, print_args);
|
|
va_end(print_args);
|
|
ASSERT(len == len2);
|
|
|
|
buffer_.AddChar('"');
|
|
AddEscapedUTF8String(p, len);
|
|
buffer_.AddChar('"');
|
|
}
|
|
|
|
void JSONWriter::PrintPropertyBool(const char* name, bool b) {
|
|
PrintPropertyName(name);
|
|
PrintValueBool(b);
|
|
}
|
|
|
|
void JSONWriter::PrintProperty(const char* name, intptr_t i) {
|
|
PrintPropertyName(name);
|
|
PrintValue(i);
|
|
}
|
|
|
|
void JSONWriter::PrintProperty64(const char* name, int64_t i) {
|
|
PrintPropertyName(name);
|
|
PrintValue64(i);
|
|
}
|
|
|
|
void JSONWriter::PrintProperty(const char* name, double d) {
|
|
PrintPropertyName(name);
|
|
PrintValue(d);
|
|
}
|
|
|
|
void JSONWriter::PrintProperty(const char* name, const char* s) {
|
|
PrintPropertyName(name);
|
|
PrintValue(s);
|
|
}
|
|
|
|
void JSONWriter::PrintPropertyBase64(const char* name,
|
|
const uint8_t* b,
|
|
intptr_t len) {
|
|
PrintPropertyName(name);
|
|
PrintValueBase64(b, len);
|
|
}
|
|
|
|
bool JSONWriter::PrintPropertyStr(const char* name,
|
|
const String& s,
|
|
intptr_t offset,
|
|
intptr_t count) {
|
|
PrintPropertyName(name);
|
|
return PrintValueStr(s, offset, count);
|
|
}
|
|
|
|
void JSONWriter::PrintPropertyNoEscape(const char* name, const char* s) {
|
|
PrintPropertyName(name);
|
|
PrintValueNoEscape(s);
|
|
}
|
|
|
|
void JSONWriter::PrintfProperty(const char* name, const char* format, ...) {
|
|
va_list args;
|
|
va_start(args, format);
|
|
VPrintfProperty(name, format, args);
|
|
va_end(args);
|
|
}
|
|
|
|
void JSONWriter::VPrintfProperty(const char* name,
|
|
const char* format,
|
|
va_list args) {
|
|
PrintPropertyName(name);
|
|
|
|
va_list measure_args;
|
|
va_copy(measure_args, args);
|
|
intptr_t len = Utils::VSNPrint(NULL, 0, format, measure_args);
|
|
va_end(measure_args);
|
|
|
|
MaybeOnStackBuffer mosb(len + 1);
|
|
char* p = mosb.p();
|
|
|
|
va_list print_args;
|
|
va_copy(print_args, args);
|
|
intptr_t len2 = Utils::VSNPrint(p, len + 1, format, print_args);
|
|
va_end(print_args);
|
|
ASSERT(len == len2);
|
|
|
|
buffer_.AddChar('"');
|
|
AddEscapedUTF8String(p, len);
|
|
buffer_.AddChar('"');
|
|
}
|
|
|
|
void JSONWriter::Steal(char** buffer, intptr_t* buffer_length) {
|
|
ASSERT(buffer != NULL);
|
|
ASSERT(buffer_length != NULL);
|
|
*buffer_length = buffer_.length();
|
|
*buffer = buffer_.Steal();
|
|
}
|
|
|
|
void JSONWriter::PrintPropertyName(const char* name) {
|
|
ASSERT(name != NULL);
|
|
PrintCommaIfNeeded();
|
|
buffer_.AddChar('"');
|
|
AddEscapedUTF8String(name);
|
|
buffer_.AddChar('"');
|
|
buffer_.AddChar(':');
|
|
}
|
|
|
|
void JSONWriter::PrintNewline() {
|
|
buffer_.AddChar('\n');
|
|
}
|
|
|
|
void JSONWriter::PrintCommaIfNeeded() {
|
|
if (NeedComma()) {
|
|
buffer_.AddChar(',');
|
|
}
|
|
}
|
|
|
|
bool JSONWriter::NeedComma() {
|
|
const char* buffer = buffer_.buffer();
|
|
intptr_t length = buffer_.length();
|
|
if (length == 0) {
|
|
return false;
|
|
}
|
|
char ch = buffer[length - 1];
|
|
return (ch != '[') && (ch != '{') && (ch != ':') && (ch != ',');
|
|
}
|
|
|
|
void JSONWriter::EnsureIntegerIsRepresentableInJavaScript(int64_t i) {
|
|
#ifdef DEBUG
|
|
if (!Utils::IsJavascriptInt(i)) {
|
|
OS::PrintErr(
|
|
"JSONWriter::EnsureIntegerIsRepresentableInJavaScript failed on "
|
|
"%" Pd64 "\n",
|
|
i);
|
|
UNREACHABLE();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void JSONWriter::AddEscapedUTF8String(const char* s) {
|
|
if (s == NULL) {
|
|
return;
|
|
}
|
|
intptr_t len = strlen(s);
|
|
AddEscapedUTF8String(s, len);
|
|
}
|
|
|
|
void JSONWriter::AddEscapedUTF8String(const char* s, intptr_t len) {
|
|
if (s == NULL) {
|
|
return;
|
|
}
|
|
const uint8_t* s8 = reinterpret_cast<const uint8_t*>(s);
|
|
intptr_t i = 0;
|
|
for (; i < len;) {
|
|
// Extract next UTF8 character.
|
|
int32_t ch = 0;
|
|
int32_t ch_len = Utf8::Decode(&s8[i], len - i, &ch);
|
|
ASSERT(ch_len != 0);
|
|
buffer_.EscapeAndAddCodeUnit(ch);
|
|
// Move i forward.
|
|
i += ch_len;
|
|
}
|
|
ASSERT(i == len);
|
|
}
|
|
|
|
bool JSONWriter::AddDartString(const String& s,
|
|
intptr_t offset,
|
|
intptr_t count) {
|
|
intptr_t length = s.Length();
|
|
ASSERT(offset >= 0);
|
|
if (offset > length) {
|
|
offset = length;
|
|
}
|
|
if (!Utils::RangeCheck(offset, count, length)) {
|
|
count = length - offset;
|
|
}
|
|
intptr_t limit = offset + count;
|
|
for (intptr_t i = offset; i < limit; i++) {
|
|
uint16_t code_unit = s.CharAt(i);
|
|
if (Utf16::IsTrailSurrogate(code_unit)) {
|
|
buffer_.EscapeAndAddUTF16CodeUnit(code_unit);
|
|
} else if (Utf16::IsLeadSurrogate(code_unit)) {
|
|
if (i + 1 == limit) {
|
|
buffer_.EscapeAndAddUTF16CodeUnit(code_unit);
|
|
} else {
|
|
uint16_t next_code_unit = s.CharAt(i + 1);
|
|
if (Utf16::IsTrailSurrogate(next_code_unit)) {
|
|
uint32_t decoded = Utf16::Decode(code_unit, next_code_unit);
|
|
buffer_.EscapeAndAddCodeUnit(decoded);
|
|
i++;
|
|
} else {
|
|
buffer_.EscapeAndAddUTF16CodeUnit(code_unit);
|
|
}
|
|
}
|
|
} else {
|
|
buffer_.EscapeAndAddCodeUnit(code_unit);
|
|
}
|
|
}
|
|
// Return value indicates whether the string is truncated.
|
|
return (offset > 0) || (limit < length);
|
|
}
|
|
|
|
} // namespace dart
|