[VM/Service] Create JSONBase64String class

TEST=CI

Change-Id: Ia14fcb1788a2685bef4fd61babfdd7089dd85a06
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/287801
Reviewed-by: Ben Konyi <bkonyi@google.com>
Commit-Queue: Derek Xu <derekx@google.com>
This commit is contained in:
Derek Xu 2023-04-17 15:58:28 +00:00 committed by Commit Queue
parent b4aa83b0b7
commit 8df1b88877
5 changed files with 118 additions and 28 deletions

View file

@ -595,6 +595,40 @@ void JSONArray::AddValueF(const char* format, ...) const {
va_end(args);
}
void JSONBase64String::AppendBytes(const uint8_t* bytes, intptr_t length) {
ASSERT(bytes != nullptr);
if (num_queued_bytes_ > 0) {
while (length > 0) {
queued_bytes_[num_queued_bytes_++] = bytes[0];
bytes++;
length--;
if (num_queued_bytes_ == 3) {
break;
}
}
if (num_queued_bytes_ < 3) {
return;
}
stream_->AppendBytesInBase64(queued_bytes_, 3);
num_queued_bytes_ = 0;
}
intptr_t length_mod_3 = length % 3;
intptr_t largest_multiple_of_3_less_than_or_equal_to_length =
length - length_mod_3;
if (largest_multiple_of_3_less_than_or_equal_to_length > 0) {
stream_->AppendBytesInBase64(
bytes, largest_multiple_of_3_less_than_or_equal_to_length);
}
for (intptr_t i = 0; i < length_mod_3; ++i) {
queued_bytes_[i] =
bytes[largest_multiple_of_3_less_than_or_equal_to_length + i];
}
num_queued_bytes_ = length_mod_3;
}
#endif // !PRODUCT
} // namespace dart

View file

@ -217,6 +217,16 @@ class JSONStream : ValueObject {
writer_.CloseArray();
}
// Append the Base64 encoding of |bytes| to the stream.
//
// Beware that padding characters are added when |length| is not a multiple of
// three. Padding is only valid at the end of Base64 strings, so you must be
// careful when trying to populate a single Base64 string with multiple calls
// to this method. |JSONBase64String| should be used for that use-case,
// because it handles padding management.
void AppendBytesInBase64(const uint8_t* bytes, intptr_t length) {
writer_.AppendBytesInBase64(bytes, length);
}
void PrintValueNull() { writer_.PrintValueNull(); }
void PrintValueBool(bool b) { writer_.PrintValueBool(b); }
void PrintValue(intptr_t i) { writer_.PrintValue(i); }
@ -350,6 +360,7 @@ class JSONStream : ValueObject {
intptr_t ignore_object_depth_;
friend class JSONObject;
friend class JSONArray;
friend class JSONBase64String;
friend class TimelineEvent;
};
@ -519,6 +530,28 @@ class JSONArray : public ValueObject {
DISALLOW_COPY_AND_ASSIGN(JSONArray);
};
class JSONBase64String : public ValueObject {
public:
explicit JSONBase64String(JSONStream* stream)
: stream_(stream), queued_bytes_(), num_queued_bytes_(0) {
stream_->AppendBytes(reinterpret_cast<const uint8_t*>("\""), 1);
}
~JSONBase64String() {
stream_->AppendBytesInBase64(queued_bytes_, num_queued_bytes_);
stream_->AppendBytes(reinterpret_cast<const uint8_t*>("\""), 1);
}
void AppendBytes(const uint8_t* bytes, intptr_t length);
private:
JSONStream* stream_;
uint8_t queued_bytes_[3];
intptr_t num_queued_bytes_;
DISALLOW_ALLOCATION();
DISALLOW_COPY_AND_ASSIGN(JSONBase64String);
};
} // namespace dart
#endif // RUNTIME_VM_JSON_STREAM_H_

View file

@ -91,6 +91,17 @@ TEST_CASE(JSON_JSONStream_Array) {
EXPECT_STREQ("[true,false]", js.ToCString());
}
TEST_CASE(JSON_JSONStream_Base64String) {
JSONStream js;
{
JSONBase64String jsonBase64String(&js);
jsonBase64String.AppendBytes(reinterpret_cast<const uint8_t*>("Hello"), 5);
jsonBase64String.AppendBytes(reinterpret_cast<const uint8_t*>(", "), 2);
jsonBase64String.AppendBytes(reinterpret_cast<const uint8_t*>("world!"), 6);
}
EXPECT_STREQ("\"SGVsbG8sIHdvcmxkIQ==\"", js.ToCString());
}
TEST_CASE(JSON_JSONStream_Object) {
JSONStream js;
{

View file

@ -39,6 +39,36 @@ void JSONWriter::AppendBytes(const uint8_t* buffer, intptr_t buffer_length) {
buffer_.AddRaw(buffer, buffer_length);
}
static const char base64_digits[65] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const char base64_pad = '=';
void JSONWriter::AppendBytesInBase64(const uint8_t* bytes, intptr_t length) {
ASSERT(bytes != nullptr);
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);
}
}
void JSONWriter::AppendSerializedObject(const char* serialized_object) {
PrintCommaIfNeeded();
buffer_.AddString(serialized_object);
@ -126,37 +156,10 @@ void JSONWriter::PrintValue(double d) {
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);
}
AppendBytesInBase64(bytes, length);
buffer_.AddChar('"');
}

View file

@ -26,6 +26,15 @@ class JSONWriter : ValueObject {
// Append |buffer| to the stream.
void AppendBytes(const uint8_t* buffer, intptr_t buffer_length);
// Append the Base64 encoding of |bytes| to the stream.
//
// Beware that padding characters are added when |length| is not a multiple of
// three. Padding is only valid at the end of Base64 strings, so you must be
// careful when trying to populate a single Base64 string with multiple calls
// to this method. |JSONBase64String| should be used for that use-case,
// because it handles padding management.
void AppendBytesInBase64(const uint8_t* bytes, intptr_t length);
// Append |serialized_object| to the stream.
void AppendSerializedObject(const char* serialized_object);