6703647b93
... to revision c40253f87c475880d1bdad4a90cf21c38dadf4ac Also, preseve binary protocol when restoring session. Bug: chromium:929862 Change-Id: Icb1cb04b42ca7238b46e2978337b36e32398665f Reviewed-on: https://chromium-review.googlesource.com/c/1474556 Commit-Queue: Andrey Kosyakov <caseq@chromium.org> Commit-Queue: Pavel Feldman <pfeldman@chromium.org> Reviewed-by: Pavel Feldman <pfeldman@chromium.org> Cr-Commit-Position: refs/heads/master@{#59614}
417 lines
15 KiB
Plaintext
417 lines
15 KiB
Plaintext
{# This template is generated by gen_cbor_templates.py. #}
|
|
// Generated by lib/CBOR_h.template.
|
|
|
|
// Copyright 2019 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.
|
|
|
|
#ifndef {{"_".join(config.protocol.namespace)}}_CBOR_h
|
|
#define {{"_".join(config.protocol.namespace)}}_CBOR_h
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <memory>
|
|
#include <vector>
|
|
|
|
{% for namespace in config.protocol.namespace %}
|
|
namespace {{namespace}} {
|
|
{% endfor %}
|
|
|
|
// ===== encoding/status.h =====
|
|
|
|
// Error codes.
|
|
enum class Error {
|
|
OK = 0,
|
|
// JSON parsing errors - json_parser.{h,cc}.
|
|
JSON_PARSER_UNPROCESSED_INPUT_REMAINS = 0x01,
|
|
JSON_PARSER_STACK_LIMIT_EXCEEDED = 0x02,
|
|
JSON_PARSER_NO_INPUT = 0x03,
|
|
JSON_PARSER_INVALID_TOKEN = 0x04,
|
|
JSON_PARSER_INVALID_NUMBER = 0x05,
|
|
JSON_PARSER_INVALID_STRING = 0x06,
|
|
JSON_PARSER_UNEXPECTED_ARRAY_END = 0x07,
|
|
JSON_PARSER_COMMA_OR_ARRAY_END_EXPECTED = 0x08,
|
|
JSON_PARSER_STRING_LITERAL_EXPECTED = 0x09,
|
|
JSON_PARSER_COLON_EXPECTED = 0x0a,
|
|
JSON_PARSER_UNEXPECTED_OBJECT_END = 0x0b,
|
|
JSON_PARSER_COMMA_OR_OBJECT_END_EXPECTED = 0x0c,
|
|
JSON_PARSER_VALUE_EXPECTED = 0x0d,
|
|
|
|
CBOR_INVALID_INT32 = 0x0e,
|
|
CBOR_INVALID_DOUBLE = 0x0f,
|
|
CBOR_INVALID_ENVELOPE = 0x10,
|
|
CBOR_INVALID_STRING8 = 0x11,
|
|
CBOR_INVALID_STRING16 = 0x12,
|
|
CBOR_INVALID_BINARY = 0x13,
|
|
CBOR_UNSUPPORTED_VALUE = 0x14,
|
|
CBOR_NO_INPUT = 0x15,
|
|
CBOR_INVALID_START_BYTE = 0x16,
|
|
CBOR_UNEXPECTED_EOF_EXPECTED_VALUE = 0x17,
|
|
CBOR_UNEXPECTED_EOF_IN_ARRAY = 0x18,
|
|
CBOR_UNEXPECTED_EOF_IN_MAP = 0x19,
|
|
CBOR_INVALID_MAP_KEY = 0x1a,
|
|
CBOR_STACK_LIMIT_EXCEEDED = 0x1b,
|
|
CBOR_STRING8_MUST_BE_7BIT = 0x1c,
|
|
CBOR_TRAILING_JUNK = 0x1d,
|
|
CBOR_MAP_START_EXPECTED = 0x1e,
|
|
};
|
|
|
|
// A status value with position that can be copied. The default status
|
|
// is OK. Usually, error status values should come with a valid position.
|
|
struct Status {
|
|
static constexpr std::ptrdiff_t npos() { return -1; }
|
|
|
|
bool ok() const { return error == Error::OK; }
|
|
|
|
Error error = Error::OK;
|
|
std::ptrdiff_t pos = npos();
|
|
Status(Error error, std::ptrdiff_t pos) : error(error), pos(pos) {}
|
|
Status() = default;
|
|
};
|
|
|
|
// ===== encoding/span.h =====
|
|
|
|
// This template is similar to std::span, which will be included in C++20. Like
|
|
// std::span it uses ptrdiff_t, which is signed (and thus a bit annoying
|
|
// sometimes when comparing with size_t), but other than this it's much simpler.
|
|
template <typename T>
|
|
class span {
|
|
public:
|
|
using index_type = std::ptrdiff_t;
|
|
|
|
span() : data_(nullptr), size_(0) {}
|
|
span(const T* data, index_type size) : data_(data), size_(size) {}
|
|
|
|
const T* data() const { return data_; }
|
|
|
|
const T* begin() const { return data_; }
|
|
const T* end() const { return data_ + size_; }
|
|
|
|
const T& operator[](index_type idx) const { return data_[idx]; }
|
|
|
|
span<T> subspan(index_type offset, index_type count) const {
|
|
return span(data_ + offset, count);
|
|
}
|
|
|
|
span<T> subspan(index_type offset) const {
|
|
return span(data_ + offset, size_ - offset);
|
|
}
|
|
|
|
bool empty() const { return size_ == 0; }
|
|
|
|
index_type size() const { return size_; }
|
|
index_type size_bytes() const { return size_ * sizeof(T); }
|
|
|
|
private:
|
|
const T* data_;
|
|
index_type size_;
|
|
};
|
|
|
|
// ===== encoding/json_parser_handler.h =====
|
|
|
|
// Handler interface for JSON parser events. See also json_parser.h.
|
|
class JSONParserHandler {
|
|
public:
|
|
virtual ~JSONParserHandler() = default;
|
|
virtual void HandleObjectBegin() = 0;
|
|
virtual void HandleObjectEnd() = 0;
|
|
virtual void HandleArrayBegin() = 0;
|
|
virtual void HandleArrayEnd() = 0;
|
|
// TODO(johannes): Support utf8 (requires utf16->utf8 conversion
|
|
// internally, including handling mismatched surrogate pairs).
|
|
virtual void HandleString16(std::vector<uint16_t> chars) = 0;
|
|
virtual void HandleBinary(std::vector<uint8_t> bytes) = 0;
|
|
virtual void HandleDouble(double value) = 0;
|
|
virtual void HandleInt32(int32_t value) = 0;
|
|
virtual void HandleBool(bool value) = 0;
|
|
virtual void HandleNull() = 0;
|
|
|
|
// The parser may send one error even after other events have already
|
|
// been received. Client code is reponsible to then discard the
|
|
// already processed events.
|
|
// |error| must be an eror, as in, |error.is_ok()| can't be true.
|
|
virtual void HandleError(Status error) = 0;
|
|
};
|
|
|
|
// ===== encoding/cbor_internals.h =====
|
|
|
|
namespace cbor {
|
|
enum class MajorType;
|
|
}
|
|
|
|
namespace cbor_internals {
|
|
|
|
// Reads the start of a token with definitive size from |bytes|.
|
|
// |type| is the major type as specified in RFC 7049 Section 2.1.
|
|
// |value| is the payload (e.g. for MajorType::UNSIGNED) or is the size
|
|
// (e.g. for BYTE_STRING).
|
|
// If successful, returns the number of bytes read. Otherwise returns -1.
|
|
int8_t ReadTokenStart(span<uint8_t> bytes, cbor::MajorType* type,
|
|
uint64_t* value);
|
|
|
|
// Writes the start of a token with |type|. The |value| may indicate the size,
|
|
// or it may be the payload if the value is an unsigned integer.
|
|
void WriteTokenStart(cbor::MajorType type, uint64_t value,
|
|
std::vector<uint8_t>* encoded);
|
|
} // namespace cbor_internals
|
|
|
|
// ===== encoding/cbor.h =====
|
|
|
|
|
|
namespace cbor {
|
|
|
|
// The major types from RFC 7049 Section 2.1.
|
|
enum class MajorType {
|
|
UNSIGNED = 0,
|
|
NEGATIVE = 1,
|
|
BYTE_STRING = 2,
|
|
STRING = 3,
|
|
ARRAY = 4,
|
|
MAP = 5,
|
|
TAG = 6,
|
|
SIMPLE_VALUE = 7
|
|
};
|
|
|
|
// Indicates the number of bits the "initial byte" needs to be shifted to the
|
|
// right after applying |kMajorTypeMask| to produce the major type in the
|
|
// lowermost bits.
|
|
static constexpr uint8_t kMajorTypeBitShift = 5u;
|
|
// Mask selecting the low-order 5 bits of the "initial byte", which is where
|
|
// the additional information is encoded.
|
|
static constexpr uint8_t kAdditionalInformationMask = 0x1f;
|
|
// Mask selecting the high-order 3 bits of the "initial byte", which indicates
|
|
// the major type of the encoded value.
|
|
static constexpr uint8_t kMajorTypeMask = 0xe0;
|
|
// Indicates the integer is in the following byte.
|
|
static constexpr uint8_t kAdditionalInformation1Byte = 24u;
|
|
// Indicates the integer is in the next 2 bytes.
|
|
static constexpr uint8_t kAdditionalInformation2Bytes = 25u;
|
|
// Indicates the integer is in the next 4 bytes.
|
|
static constexpr uint8_t kAdditionalInformation4Bytes = 26u;
|
|
// Indicates the integer is in the next 8 bytes.
|
|
static constexpr uint8_t kAdditionalInformation8Bytes = 27u;
|
|
|
|
// Encodes the initial byte, consisting of the |type| in the first 3 bits
|
|
// followed by 5 bits of |additional_info|.
|
|
constexpr uint8_t EncodeInitialByte(MajorType type, uint8_t additional_info) {
|
|
return (static_cast<uint8_t>(type) << kMajorTypeBitShift) |
|
|
(additional_info & kAdditionalInformationMask);
|
|
}
|
|
|
|
// TAG 24 indicates that what follows is a byte string which is
|
|
// encoded in CBOR format. We use this as a wrapper for
|
|
// maps and arrays, allowing us to skip them, because the
|
|
// byte string carries its size (byte length).
|
|
// https://tools.ietf.org/html/rfc7049#section-2.4.4.1
|
|
static constexpr uint8_t kInitialByteForEnvelope =
|
|
EncodeInitialByte(MajorType::TAG, 24);
|
|
// The initial byte for a byte string with at most 2^32 bytes
|
|
// of payload. This is used for envelope encoding, even if
|
|
// the byte string is shorter.
|
|
static constexpr uint8_t kInitialByteFor32BitLengthByteString =
|
|
EncodeInitialByte(MajorType::BYTE_STRING, 26);
|
|
|
|
// See RFC 7049 Section 2.2.1, indefinite length arrays / maps have additional
|
|
// info = 31.
|
|
static constexpr uint8_t kInitialByteIndefiniteLengthArray =
|
|
EncodeInitialByte(MajorType::ARRAY, 31);
|
|
static constexpr uint8_t kInitialByteIndefiniteLengthMap =
|
|
EncodeInitialByte(MajorType::MAP, 31);
|
|
// See RFC 7049 Section 2.3, Table 1; this is used for finishing indefinite
|
|
// length maps / arrays.
|
|
static constexpr uint8_t kStopByte =
|
|
EncodeInitialByte(MajorType::SIMPLE_VALUE, 31);
|
|
|
|
} // namespace cbor
|
|
|
|
// The binary encoding for the inspector protocol follows the CBOR specification
|
|
// (RFC 7049). Additional constraints:
|
|
// - Only indefinite length maps and arrays are supported.
|
|
// - Maps and arrays are wrapped with an envelope, that is, a
|
|
// CBOR tag with value 24 followed by a byte string specifying
|
|
// the byte length of the enclosed map / array. The byte string
|
|
// must use a 32 bit wide length.
|
|
// - At the top level, a message must be an indefinite length map
|
|
// wrapped by an envelope.
|
|
// - Maximal size for messages is 2^32 (4 GB).
|
|
// - For scalars, we support only the int32_t range, encoded as
|
|
// UNSIGNED/NEGATIVE (major types 0 / 1).
|
|
// - UTF16 strings, including with unbalanced surrogate pairs, are encoded
|
|
// as CBOR BYTE_STRING (major type 2). For such strings, the number of
|
|
// bytes encoded must be even.
|
|
// - UTF8 strings (major type 3) may only have ASCII characters
|
|
// (7 bit US-ASCII).
|
|
// - Arbitrary byte arrays, in the inspector protocol called 'binary',
|
|
// are encoded as BYTE_STRING (major type 2), prefixed with a byte
|
|
// indicating base64 when rendered as JSON.
|
|
|
|
// Encodes |value| as |UNSIGNED| (major type 0) iff >= 0, or |NEGATIVE|
|
|
// (major type 1) iff < 0.
|
|
void EncodeInt32(int32_t value, std::vector<uint8_t>* out);
|
|
|
|
// Encodes a UTF16 string as a BYTE_STRING (major type 2). Each utf16
|
|
// character in |in| is emitted with most significant byte first,
|
|
// appending to |out|.
|
|
void EncodeString16(span<uint16_t> in, std::vector<uint8_t>* out);
|
|
|
|
// Encodes a UTF8 string |in| as STRING (major type 3).
|
|
void EncodeString8(span<uint8_t> in, std::vector<uint8_t>* out);
|
|
|
|
// Encodes arbitrary binary data in |in| as a BYTE_STRING (major type 2) with
|
|
// definitive length, prefixed with tag 22 indicating expected conversion to
|
|
// base64 (see RFC 7049, Table 3 and Section 2.4.4.2).
|
|
void EncodeBinary(span<uint8_t> in, std::vector<uint8_t>* out);
|
|
|
|
// Encodes / decodes a double as Major type 7 (SIMPLE_VALUE),
|
|
// with additional info = 27, followed by 8 bytes in big endian.
|
|
void EncodeDouble(double value, std::vector<uint8_t>* out);
|
|
|
|
// Some constants for CBOR tokens that only take a single byte on the wire.
|
|
uint8_t EncodeTrue();
|
|
uint8_t EncodeFalse();
|
|
uint8_t EncodeNull();
|
|
uint8_t EncodeIndefiniteLengthArrayStart();
|
|
uint8_t EncodeIndefiniteLengthMapStart();
|
|
uint8_t EncodeStop();
|
|
|
|
// An envelope indicates the byte length of a wrapped item.
|
|
// We use this for maps and array, which allows the decoder
|
|
// to skip such (nested) values whole sale.
|
|
// It's implemented as a CBOR tag (major type 6) with additional
|
|
// info = 24, followed by a byte string with a 32 bit length value;
|
|
// so the maximal structure that we can wrap is 2^32 bits long.
|
|
// See also: https://tools.ietf.org/html/rfc7049#section-2.4.4.1
|
|
class EnvelopeEncoder {
|
|
public:
|
|
// Emits the envelope start bytes and records the position for the
|
|
// byte size in |byte_size_pos_|. Also emits empty bytes for the
|
|
// byte sisze so that encoding can continue.
|
|
void EncodeStart(std::vector<uint8_t>* out);
|
|
// This records the current size in |out| at position byte_size_pos_.
|
|
// Returns true iff successful.
|
|
bool EncodeStop(std::vector<uint8_t>* out);
|
|
|
|
private:
|
|
std::size_t byte_size_pos_ = 0;
|
|
};
|
|
|
|
// This can be used to convert from JSON to CBOR, by passing the
|
|
// return value to the routines in json_parser.h. The handler will encode into
|
|
// |out|, and iff an error occurs it will set |status| to an error and clear
|
|
// |out|. Otherwise, |status.ok()| will be |true|.
|
|
std::unique_ptr<JSONParserHandler> NewJSONToCBOREncoder(
|
|
std::vector<uint8_t>* out, Status* status);
|
|
|
|
// Parses a CBOR encoded message from |bytes|, sending JSON events to
|
|
// |json_out|. If an error occurs, sends |out->HandleError|, and parsing stops.
|
|
// The client is responsible for discarding the already received information in
|
|
// that case.
|
|
void ParseCBOR(span<uint8_t> bytes, JSONParserHandler* json_out);
|
|
|
|
// Tags for the tokens within a CBOR message that CBORStream understands.
|
|
// Note that this is not the same terminology as the CBOR spec (RFC 7049),
|
|
// but rather, our adaptation. For instance, we lump unsigned and signed
|
|
// major type into INT32 here (and disallow values outside the int32_t range).
|
|
enum class CBORTokenTag {
|
|
// Encountered an error in the structure of the message. Consult
|
|
// status() for details.
|
|
ERROR_VALUE,
|
|
// Booleans and NULL.
|
|
TRUE_VALUE,
|
|
FALSE_VALUE,
|
|
NULL_VALUE,
|
|
// An int32_t (signed 32 bit integer).
|
|
INT32,
|
|
// A double (64 bit floating point).
|
|
DOUBLE,
|
|
// A UTF8 string.
|
|
STRING8,
|
|
// A UTF16 string.
|
|
STRING16,
|
|
// A binary string.
|
|
BINARY,
|
|
// Starts an indefinite length map; after the map start we expect
|
|
// alternating keys and values, followed by STOP.
|
|
MAP_START,
|
|
// Starts an indefinite length array; after the array start we
|
|
// expect values, followed by STOP.
|
|
ARRAY_START,
|
|
// Ends a map or an array.
|
|
STOP,
|
|
// An envelope indicator, wrapping a map or array.
|
|
// Internally this carries the byte length of the wrapped
|
|
// map or array. While CBORTokenizer::Next() will read / skip the entire
|
|
// envelope, CBORTokenizer::EnterEnvelope() reads the tokens
|
|
// inside of it.
|
|
ENVELOPE,
|
|
// We've reached the end there is nothing else to read.
|
|
DONE,
|
|
};
|
|
|
|
// CBORTokenizer segments a CBOR message, presenting the tokens therein as
|
|
// numbers, strings, etc. This is not a complete CBOR parser, but makes it much
|
|
// easier to implement one (e.g. ParseCBOR, above). It can also be used to parse
|
|
// messages partially.
|
|
class CBORTokenizer {
|
|
public:
|
|
explicit CBORTokenizer(span<uint8_t> bytes);
|
|
~CBORTokenizer();
|
|
|
|
// Identifies the current token that we're looking at,
|
|
// or ERROR_VALUE (in which ase ::Status() has details)
|
|
// or DONE (if we're past the last token).
|
|
CBORTokenTag TokenTag() const;
|
|
|
|
// Advances to the next token.
|
|
void Next();
|
|
// Can only be called if TokenTag() == CBORTokenTag::ENVELOPE.
|
|
// While Next() would skip past the entire envelope / what it's
|
|
// wrapping, EnterEnvelope positions the cursor inside of the envelope,
|
|
// letting the client explore the nested structure.
|
|
void EnterEnvelope();
|
|
|
|
// If TokenTag() is CBORTokenTag::ERROR_VALUE, then Status().error describes
|
|
// the error more precisely; otherwise it'll be set to Error::OK.
|
|
// In either case, Status().pos is the current position.
|
|
struct Status Status() const;
|
|
|
|
// The following methods retrieve the token values. They can only
|
|
// be called if TokenTag() matches.
|
|
|
|
// To be called only if ::TokenTag() == CBORTokenTag::INT32.
|
|
int32_t GetInt32() const;
|
|
|
|
// To be called only if ::TokenTag() == CBORTokenTag::DOUBLE.
|
|
double GetDouble() const;
|
|
|
|
// To be called only if ::TokenTag() == CBORTokenTag::STRING8.
|
|
span<uint8_t> GetString8() const;
|
|
|
|
// Wire representation for STRING16 is low byte first (little endian).
|
|
// To be called only if ::TokenTag() == CBORTokenTag::STRING16.
|
|
span<uint8_t> GetString16WireRep() const;
|
|
|
|
// To be called only if ::TokenTag() == CBORTokenTag::BINARY.
|
|
span<uint8_t> GetBinary() const;
|
|
|
|
private:
|
|
void ReadNextToken(bool enter_envelope);
|
|
void SetToken(CBORTokenTag token, std::ptrdiff_t token_byte_length);
|
|
void SetError(Error error);
|
|
|
|
span<uint8_t> bytes_;
|
|
CBORTokenTag token_tag_;
|
|
struct Status status_;
|
|
std::ptrdiff_t token_byte_length_;
|
|
cbor::MajorType token_start_type_;
|
|
uint64_t token_start_internal_value_;
|
|
};
|
|
|
|
void DumpCBOR(span<uint8_t> cbor);
|
|
|
|
|
|
{% for namespace in config.protocol.namespace %}
|
|
} // namespace {{namespace}}
|
|
{% endfor %}
|
|
#endif // !defined({{"_".join(config.protocol.namespace)}}_CBOR_h)
|