blob: 38ced6452156948fdaa5340c0f91cb66962b27f9 [file] [log] [blame]
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/inspector/string-util.h"
#include <cinttypes>
#include <cmath>
#include <cstddef>
#include "src/base/platform/platform.h"
#include "src/inspector/protocol/Protocol.h"
#include "src/numbers/conversions.h"
namespace v8_inspector {
namespace protocol {
namespace {
std::pair<uint8_t, uint8_t> SplitByte(uint8_t byte, uint8_t split) {
return {byte >> split, (byte & ((1 << split) - 1)) << (6 - split)};
}
v8::Maybe<uint8_t> DecodeByte(char byte) {
if ('A' <= byte && byte <= 'Z') return v8::Just<uint8_t>(byte - 'A');
if ('a' <= byte && byte <= 'z') return v8::Just<uint8_t>(byte - 'a' + 26);
if ('0' <= byte && byte <= '9')
return v8::Just<uint8_t>(byte - '0' + 26 + 26);
if (byte == '+') return v8::Just<uint8_t>(62);
if (byte == '/') return v8::Just<uint8_t>(63);
return v8::Nothing<uint8_t>();
}
} // namespace
String Binary::toBase64() const {
const char* table =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
if (size() == 0) return {};
std::basic_string<UChar> result;
result.reserve(4 * ((size() + 2) / 3));
uint8_t last = 0;
for (size_t n = 0; n < size();) {
auto split = SplitByte((*bytes_)[n], 2 + 2 * (n % 3));
result.push_back(table[split.first | last]);
++n;
if (n < size() && n % 3 == 0) {
result.push_back(table[split.second]);
last = 0;
} else {
last = split.second;
}
}
result.push_back(table[last]);
while (result.size() % 4 > 0) result.push_back('=');
return String16(std::move(result));
}
/* static */
Binary Binary::fromBase64(const String& base64, bool* success) {
if (base64.isEmpty()) {
*success = true;
return Binary::fromSpan(nullptr, 0);
}
*success = false;
// Fail if the length is invalid or decoding would overflow.
if (base64.length() % 4 != 0 || base64.length() + 4 < base64.length()) {
return Binary::fromSpan(nullptr, 0);
}
std::vector<uint8_t> result;
result.reserve(3 * base64.length() / 4);
char pad = '=';
// Iterate groups of four
for (size_t i = 0; i < base64.length(); i += 4) {
uint8_t a = 0, b = 0, c = 0, d = 0;
if (!DecodeByte(base64[i + 0]).To(&a)) return Binary::fromSpan(nullptr, 0);
if (!DecodeByte(base64[i + 1]).To(&b)) return Binary::fromSpan(nullptr, 0);
if (!DecodeByte(base64[i + 2]).To(&c)) {
// Padding is allowed only in the group on the last two positions
if (i + 4 < base64.length() || base64[i + 2] != pad ||
base64[i + 3] != pad) {
return Binary::fromSpan(nullptr, 0);
}
}
if (!DecodeByte(base64[i + 3]).To(&d)) {
// Padding is allowed only in the group on the last two positions
if (i + 4 < base64.length() || base64[i + 3] != pad) {
return Binary::fromSpan(nullptr, 0);
}
}
result.push_back((a << 2) | (b >> 4));
if (base64[i + 2] != '=') result.push_back((0xFF & (b << 4)) | (c >> 2));
if (base64[i + 3] != '=') result.push_back((0xFF & (c << 6)) | d);
}
*success = true;
return Binary(std::make_shared<std::vector<uint8_t>>(std::move(result)));
}
} // namespace protocol
v8::Local<v8::String> toV8String(v8::Isolate* isolate, const String16& string) {
if (string.isEmpty()) return v8::String::Empty(isolate);
DCHECK_GT(v8::String::kMaxLength, string.length());
return v8::String::NewFromTwoByte(
isolate, reinterpret_cast<const uint16_t*>(string.characters16()),
v8::NewStringType::kNormal, static_cast<int>(string.length()))
.ToLocalChecked();
}
v8::Local<v8::String> toV8StringInternalized(v8::Isolate* isolate,
const String16& string) {
if (string.isEmpty()) return v8::String::Empty(isolate);
DCHECK_GT(v8::String::kMaxLength, string.length());
return v8::String::NewFromTwoByte(
isolate, reinterpret_cast<const uint16_t*>(string.characters16()),
v8::NewStringType::kInternalized,
static_cast<int>(string.length()))
.ToLocalChecked();
}
v8::Local<v8::String> toV8StringInternalized(v8::Isolate* isolate,
const char* str) {
return v8::String::NewFromUtf8(isolate, str, v8::NewStringType::kInternalized)
.ToLocalChecked();
}
v8::Local<v8::String> toV8String(v8::Isolate* isolate,
const StringView& string) {
if (!string.length()) return v8::String::Empty(isolate);
DCHECK_GT(v8::String::kMaxLength, string.length());
if (string.is8Bit())
return v8::String::NewFromOneByte(
isolate, reinterpret_cast<const uint8_t*>(string.characters8()),
v8::NewStringType::kNormal, static_cast<int>(string.length()))
.ToLocalChecked();
return v8::String::NewFromTwoByte(
isolate, reinterpret_cast<const uint16_t*>(string.characters16()),
v8::NewStringType::kNormal, static_cast<int>(string.length()))
.ToLocalChecked();
}
String16 toProtocolString(v8::Isolate* isolate, v8::Local<v8::String> value) {
if (value.IsEmpty() || value->IsNullOrUndefined()) return String16();
std::unique_ptr<UChar[]> buffer(new UChar[value->Length()]);
value->Write(isolate, reinterpret_cast<uint16_t*>(buffer.get()), 0,
value->Length());
return String16(buffer.get(), value->Length());
}
String16 toProtocolStringWithTypeCheck(v8::Isolate* isolate,
v8::Local<v8::Value> value) {
if (value.IsEmpty() || !value->IsString()) return String16();
return toProtocolString(isolate, value.As<v8::String>());
}
String16 toString16(const StringView& string) {
if (!string.length()) return String16();
if (string.is8Bit())
return String16(reinterpret_cast<const char*>(string.characters8()),
string.length());
return String16(string.characters16(), string.length());
}
StringView toStringView(const String16& string) {
if (string.isEmpty()) return StringView();
return StringView(string.characters16(), string.length());
}
bool stringViewStartsWith(const StringView& string, const char* prefix) {
if (!string.length()) return !(*prefix);
if (string.is8Bit()) {
for (size_t i = 0, j = 0; prefix[j] && i < string.length(); ++i, ++j) {
if (string.characters8()[i] != prefix[j]) return false;
}
} else {
for (size_t i = 0, j = 0; prefix[j] && i < string.length(); ++i, ++j) {
if (string.characters16()[i] != prefix[j]) return false;
}
}
return true;
}
namespace {
// An empty string buffer doesn't own any string data; its ::string() returns a
// default-constructed StringView instance.
class EmptyStringBuffer : public StringBuffer {
public:
StringView string() const override { return StringView(); }
};
// Contains LATIN1 text data or CBOR encoded binary data in a vector.
class StringBuffer8 : public StringBuffer {
public:
explicit StringBuffer8(std::vector<uint8_t> data) : data_(std::move(data)) {}
StringView string() const override {
return StringView(data_.data(), data_.size());
}
private:
std::vector<uint8_t> data_;
};
// Contains a 16 bit string (String16).
class StringBuffer16 : public StringBuffer {
public:
explicit StringBuffer16(String16 data) : data_(std::move(data)) {}
StringView string() const override {
return StringView(data_.characters16(), data_.length());
}
private:
String16 data_;
};
} // namespace
// static
std::unique_ptr<StringBuffer> StringBuffer::create(StringView string) {
if (string.length() == 0) return std::make_unique<EmptyStringBuffer>();
if (string.is8Bit()) {
return std::make_unique<StringBuffer8>(std::vector<uint8_t>(
string.characters8(), string.characters8() + string.length()));
}
return std::make_unique<StringBuffer16>(
String16(string.characters16(), string.length()));
}
std::unique_ptr<StringBuffer> StringBufferFrom(String16 str) {
if (str.isEmpty()) return std::make_unique<EmptyStringBuffer>();
return std::make_unique<StringBuffer16>(std::move(str));
}
std::unique_ptr<StringBuffer> StringBufferFrom(std::vector<uint8_t> str) {
if (str.empty()) return std::make_unique<EmptyStringBuffer>();
return std::make_unique<StringBuffer8>(std::move(str));
}
String16 stackTraceIdToString(uintptr_t id) {
String16Builder builder;
builder.appendNumber(static_cast<size_t>(id));
return builder.toString();
}
} // namespace v8_inspector
namespace v8_crdtp {
using v8_inspector::String16;
using v8_inspector::protocol::Binary;
using v8_inspector::protocol::StringUtil;
// static
bool ProtocolTypeTraits<String16>::Deserialize(DeserializerState* state,
String16* value) {
auto* tokenizer = state->tokenizer();
if (tokenizer->TokenTag() == cbor::CBORTokenTag::STRING8) {
const auto str = tokenizer->GetString8();
*value = StringUtil::fromUTF8(str.data(), str.size());
return true;
}
if (tokenizer->TokenTag() == cbor::CBORTokenTag::STRING16) {
const auto str = tokenizer->GetString16WireRep();
*value = StringUtil::fromUTF16LE(
reinterpret_cast<const uint16_t*>(str.data()), str.size() / 2);
return true;
}
state->RegisterError(Error::BINDINGS_STRING_VALUE_EXPECTED);
return false;
}
// static
void ProtocolTypeTraits<String16>::Serialize(const String16& value,
std::vector<uint8_t>* bytes) {
cbor::EncodeFromUTF16(
span<uint16_t>(reinterpret_cast<const uint16_t*>(value.characters16()),
value.length()),
bytes);
}
// static
bool ProtocolTypeTraits<Binary>::Deserialize(DeserializerState* state,
Binary* value) {
auto* tokenizer = state->tokenizer();
if (tokenizer->TokenTag() == cbor::CBORTokenTag::BINARY) {
const span<uint8_t> bin = tokenizer->GetBinary();
*value = Binary::fromSpan(bin.data(), bin.size());
return true;
}
if (tokenizer->TokenTag() == cbor::CBORTokenTag::STRING8) {
const auto str_span = tokenizer->GetString8();
auto str = StringUtil::fromUTF8(str_span.data(), str_span.size());
bool success = false;
*value = Binary::fromBase64(str, &success);
return success;
}
state->RegisterError(Error::BINDINGS_BINARY_VALUE_EXPECTED);
return false;
}
// static
void ProtocolTypeTraits<Binary>::Serialize(const Binary& value,
std::vector<uint8_t>* bytes) {
cbor::EncodeBinary(span<uint8_t>(value.data(), value.size()), bytes);
}
void SerializerTraits<Binary>::Serialize(
const v8_inspector::protocol::Binary& binary, std::vector<uint8_t>* out) {
cbor::EncodeBinary(span<uint8_t>(binary.data(), binary.size()), out);
}
} // namespace v8_crdtp