blob: f9ae5913e8d70fa4c6c0bd8dac79aac2e753e83f [file] [log] [blame]
// Copyright 2017 The Chromium 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 "third_party/blink/public/common/messaging/string_message_codec.h"
#include <vector>
#include "base/containers/buffer_iterator.h"
#include "base/logging.h"
namespace blink {
namespace {
const uint32_t kVarIntShift = 7;
const uint32_t kVarIntMask = (1 << kVarIntShift) - 1;
const uint8_t kVersionTag = 0xFF;
const uint8_t kPaddingTag = '\0';
const uint8_t kOneByteStringTag = '"';
const uint8_t kTwoByteStringTag = 'c';
const uint32_t kVersion = 10;
static size_t BytesNeededForUint32(uint32_t value) {
size_t result = 0;
do {
result++;
value >>= kVarIntShift;
} while (value);
return result;
}
void WriteUint8(uint8_t value, std::vector<uint8_t>* buffer) {
buffer->push_back(value);
}
void WriteUint32(uint32_t value, std::vector<uint8_t>* buffer) {
for (;;) {
uint8_t b = (value & kVarIntMask);
value >>= kVarIntShift;
if (!value) {
WriteUint8(b, buffer);
break;
}
WriteUint8(b | (1 << kVarIntShift), buffer);
}
}
void WriteBytes(const char* bytes,
size_t num_bytes,
std::vector<uint8_t>* buffer) {
buffer->insert(buffer->end(), bytes, bytes + num_bytes);
}
bool ReadUint8(base::BufferIterator<const uint8_t>& iter, uint8_t* value) {
if (const uint8_t* ptr = iter.Object<uint8_t>()) {
*value = *ptr;
return true;
}
return false;
}
bool ReadUint32(base::BufferIterator<const uint8_t>& iter, uint32_t* value) {
*value = 0;
uint8_t current_byte;
int shift = 0;
do {
if (!ReadUint8(iter, &current_byte))
return false;
*value |= (static_cast<uint32_t>(current_byte & kVarIntMask) << shift);
shift += kVarIntShift;
} while (current_byte & (1 << kVarIntShift));
return true;
}
bool ContainsOnlyLatin1(const base::string16& data) {
base::char16 x = 0;
for (base::char16 c : data)
x |= c;
return !(x & 0xFF00);
}
} // namespace
std::vector<uint8_t> EncodeStringMessage(const base::string16& data) {
std::vector<uint8_t> buffer;
WriteUint8(kVersionTag, &buffer);
WriteUint32(kVersion, &buffer);
if (ContainsOnlyLatin1(data)) {
std::string data_latin1(data.begin(), data.end());
WriteUint8(kOneByteStringTag, &buffer);
WriteUint32(data_latin1.size(), &buffer);
WriteBytes(data_latin1.c_str(), data_latin1.size(), &buffer);
} else {
size_t num_bytes = data.size() * sizeof(base::char16);
if ((buffer.size() + 1 + BytesNeededForUint32(num_bytes)) & 1)
WriteUint8(kPaddingTag, &buffer);
WriteUint8(kTwoByteStringTag, &buffer);
WriteUint32(num_bytes, &buffer);
WriteBytes(reinterpret_cast<const char*>(data.data()), num_bytes, &buffer);
}
return buffer;
}
bool DecodeStringMessage(base::span<const uint8_t> encoded_data,
base::string16* result) {
base::BufferIterator<const uint8_t> iter(encoded_data);
uint8_t tag;
// Discard any leading version and padding tags.
// There may be more than one version, due to Blink and V8 having separate
// version tags.
do {
if (!ReadUint8(iter, &tag))
return false;
uint32_t version;
if (tag == kVersionTag && !ReadUint32(iter, &version))
return false;
} while (tag == kVersionTag || tag == kPaddingTag);
switch (tag) {
case kOneByteStringTag: {
uint32_t num_bytes;
if (!ReadUint32(iter, &num_bytes))
return false;
auto span = iter.Span<char>(num_bytes / sizeof(char));
result->assign(span.begin(), span.end());
return span.size_bytes() == num_bytes;
}
case kTwoByteStringTag: {
uint32_t num_bytes;
if (!ReadUint32(iter, &num_bytes))
return false;
auto span = iter.Span<char16_t>(num_bytes / sizeof(char16_t));
result->assign(span.begin(), span.end());
return span.size_bytes() == num_bytes;
}
}
DLOG(WARNING) << "Unexpected tag: " << tag;
return false;
}
} // namespace blink