tools: update inspector_protocol to 0aafd2
Co-authored-by: Ben Noordhuis <info@bnoordhuis.nl> PR-URL: https://github.com/nodejs/node/pull/27770 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Eugene Ostroukhov <eostroukhov@google.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
This commit is contained in:
parent
aa8b820aaa
commit
5aaa7fee2e
@ -15,9 +15,12 @@
|
|||||||
'node_protocol_files': [
|
'node_protocol_files': [
|
||||||
'<(protocol_tool_path)/lib/Allocator_h.template',
|
'<(protocol_tool_path)/lib/Allocator_h.template',
|
||||||
'<(protocol_tool_path)/lib/Array_h.template',
|
'<(protocol_tool_path)/lib/Array_h.template',
|
||||||
'<(protocol_tool_path)/lib/Collections_h.template',
|
'<(protocol_tool_path)/lib/base_string_adapter_cc.template',
|
||||||
|
'<(protocol_tool_path)/lib/base_string_adapter_h.template',
|
||||||
'<(protocol_tool_path)/lib/DispatcherBase_cpp.template',
|
'<(protocol_tool_path)/lib/DispatcherBase_cpp.template',
|
||||||
'<(protocol_tool_path)/lib/DispatcherBase_h.template',
|
'<(protocol_tool_path)/lib/DispatcherBase_h.template',
|
||||||
|
'<(protocol_tool_path)/lib/encoding_cpp.template',
|
||||||
|
'<(protocol_tool_path)/lib/encoding_h.template',
|
||||||
'<(protocol_tool_path)/lib/ErrorSupport_cpp.template',
|
'<(protocol_tool_path)/lib/ErrorSupport_cpp.template',
|
||||||
'<(protocol_tool_path)/lib/ErrorSupport_h.template',
|
'<(protocol_tool_path)/lib/ErrorSupport_h.template',
|
||||||
'<(protocol_tool_path)/lib/Forward_h.template',
|
'<(protocol_tool_path)/lib/Forward_h.template',
|
||||||
|
@ -107,6 +107,22 @@ String fromUTF8(const uint8_t* data, size_t length) {
|
|||||||
return std::string(reinterpret_cast<const char*>(data), length);
|
return std::string(reinterpret_cast<const char*>(data), length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String fromUTF16(const uint16_t* data, size_t length) {
|
||||||
|
icu::UnicodeString utf16(reinterpret_cast<const char16_t*>(data), length);
|
||||||
|
std::string result;
|
||||||
|
return utf16.toUTF8String(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t* CharactersUTF8(const String& s) {
|
||||||
|
return reinterpret_cast<const uint8_t*>(s.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t CharacterCount(const String& s) {
|
||||||
|
icu::UnicodeString utf16 =
|
||||||
|
icu::UnicodeString::fromUTF8(icu::StringPiece(s.data(), s.length()));
|
||||||
|
return utf16.countChar32();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace StringUtil
|
} // namespace StringUtil
|
||||||
} // namespace protocol
|
} // namespace protocol
|
||||||
} // namespace inspector
|
} // namespace inspector
|
||||||
|
@ -20,16 +20,6 @@ using String = std::string;
|
|||||||
using StringBuilder = std::ostringstream;
|
using StringBuilder = std::ostringstream;
|
||||||
using ProtocolMessage = std::string;
|
using ProtocolMessage = std::string;
|
||||||
|
|
||||||
class StringUTF8Adapter {
|
|
||||||
public:
|
|
||||||
explicit StringUTF8Adapter(const std::string& string) : string_(string) { }
|
|
||||||
const char* Data() const { return string_.data(); }
|
|
||||||
size_t length() const { return string_.length(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
const std::string& string_;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace StringUtil {
|
namespace StringUtil {
|
||||||
// NOLINTNEXTLINE(runtime/references) This is V8 API...
|
// NOLINTNEXTLINE(runtime/references) This is V8 API...
|
||||||
inline void builderAppend(StringBuilder& builder, char c) {
|
inline void builderAppend(StringBuilder& builder, char c) {
|
||||||
@ -82,6 +72,13 @@ std::unique_ptr<Value> parseMessage(const std::string& message, bool binary);
|
|||||||
ProtocolMessage jsonToMessage(String message);
|
ProtocolMessage jsonToMessage(String message);
|
||||||
ProtocolMessage binaryToMessage(std::vector<uint8_t> message);
|
ProtocolMessage binaryToMessage(std::vector<uint8_t> message);
|
||||||
String fromUTF8(const uint8_t* data, size_t length);
|
String fromUTF8(const uint8_t* data, size_t length);
|
||||||
|
String fromUTF16(const uint16_t* data, size_t length);
|
||||||
|
const uint8_t* CharactersUTF8(const String& s);
|
||||||
|
size_t CharacterCount(const String& s);
|
||||||
|
|
||||||
|
// Unimplemented. The generated code will fall back to CharactersUTF8().
|
||||||
|
inline uint8_t* CharactersLatin1(const String& s) { return nullptr; }
|
||||||
|
inline const uint16_t* CharactersUTF16(const String& s) { return nullptr; }
|
||||||
|
|
||||||
extern size_t kNotFound;
|
extern size_t kNotFound;
|
||||||
} // namespace StringUtil
|
} // namespace StringUtil
|
||||||
|
@ -70,7 +70,7 @@ class SendMessageRequest : public Request {
|
|||||||
if (frontend_wrapper == nullptr) return;
|
if (frontend_wrapper == nullptr) return;
|
||||||
auto frontend = frontend_wrapper->get();
|
auto frontend = frontend_wrapper->get();
|
||||||
if (frontend != nullptr) {
|
if (frontend != nullptr) {
|
||||||
frontend->sendRawNotification(message_);
|
frontend->sendRawJSONNotification(message_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
36
tools/inspector_protocol/.clang-format
Normal file
36
tools/inspector_protocol/.clang-format
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# Defines the Chromium style for automatic reformatting.
|
||||||
|
# http://clang.llvm.org/docs/ClangFormatStyleOptions.html
|
||||||
|
BasedOnStyle: Chromium
|
||||||
|
# This defaults to 'Auto'. Explicitly set it for a while, so that
|
||||||
|
# 'vector<vector<int> >' in existing files gets formatted to
|
||||||
|
# 'vector<vector<int>>'. ('Auto' means that clang-format will only use
|
||||||
|
# 'int>>' if the file already contains at least one such instance.)
|
||||||
|
Standard: Cpp11
|
||||||
|
|
||||||
|
# Make sure code like:
|
||||||
|
# IPC_BEGIN_MESSAGE_MAP()
|
||||||
|
# IPC_MESSAGE_HANDLER(WidgetHostViewHost_Update, OnUpdate)
|
||||||
|
# IPC_END_MESSAGE_MAP()
|
||||||
|
# gets correctly indented.
|
||||||
|
MacroBlockBegin: "^\
|
||||||
|
BEGIN_MSG_MAP|\
|
||||||
|
BEGIN_MSG_MAP_EX|\
|
||||||
|
BEGIN_SAFE_MSG_MAP_EX|\
|
||||||
|
CR_BEGIN_MSG_MAP_EX|\
|
||||||
|
IPC_BEGIN_MESSAGE_MAP|\
|
||||||
|
IPC_BEGIN_MESSAGE_MAP_WITH_PARAM|\
|
||||||
|
IPC_PROTOBUF_MESSAGE_TRAITS_BEGIN|\
|
||||||
|
IPC_STRUCT_BEGIN|\
|
||||||
|
IPC_STRUCT_BEGIN_WITH_PARENT|\
|
||||||
|
IPC_STRUCT_TRAITS_BEGIN|\
|
||||||
|
POLPARAMS_BEGIN|\
|
||||||
|
PPAPI_BEGIN_MESSAGE_MAP$"
|
||||||
|
MacroBlockEnd: "^\
|
||||||
|
CR_END_MSG_MAP|\
|
||||||
|
END_MSG_MAP|\
|
||||||
|
IPC_END_MESSAGE_MAP|\
|
||||||
|
IPC_PROTOBUF_MESSAGE_TRAITS_END|\
|
||||||
|
IPC_STRUCT_END|\
|
||||||
|
IPC_STRUCT_TRAITS_END|\
|
||||||
|
POLPARAMS_END|\
|
||||||
|
PPAPI_END_MESSAGE_MAP$"
|
34
tools/inspector_protocol/BUILD.gn
Normal file
34
tools/inspector_protocol/BUILD.gn
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# Copyright 2019 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.
|
||||||
|
|
||||||
|
static_library("encoding") {
|
||||||
|
sources = [
|
||||||
|
"encoding/encoding.cc",
|
||||||
|
"encoding/encoding.h",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
# encoding_test is part of the unittests, defined in
|
||||||
|
# test/unittests/BUILD.gn.
|
||||||
|
|
||||||
|
import("../../gni/v8.gni")
|
||||||
|
|
||||||
|
v8_source_set("encoding_test") {
|
||||||
|
sources = [
|
||||||
|
"encoding/encoding_test.cc",
|
||||||
|
"encoding/encoding_test_helper.h",
|
||||||
|
]
|
||||||
|
configs = [
|
||||||
|
"../..:external_config",
|
||||||
|
"../..:internal_config_base",
|
||||||
|
]
|
||||||
|
deps = [
|
||||||
|
":encoding",
|
||||||
|
"../..:v8_libbase",
|
||||||
|
"../../src/inspector:inspector_string_conversions",
|
||||||
|
"//testing/gmock",
|
||||||
|
"//testing/gtest",
|
||||||
|
]
|
||||||
|
testonly = true
|
||||||
|
}
|
33
tools/inspector_protocol/README.md
Normal file
33
tools/inspector_protocol/README.md
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# Chromium inspector (devtools) protocol
|
||||||
|
|
||||||
|
This package contains code generators and templates for the Chromium
|
||||||
|
inspector protocol.
|
||||||
|
|
||||||
|
The canonical location of this package is at
|
||||||
|
https://chromium.googlesource.com/deps/inspector_protocol/
|
||||||
|
|
||||||
|
In the Chromium tree, it's rolled into
|
||||||
|
https://cs.chromium.org/chromium/src/third_party/inspector_protocol/
|
||||||
|
|
||||||
|
In the V8 tree, it's rolled into
|
||||||
|
https://cs.chromium.org/chromium/src/v8/third_party/inspector_protocol/
|
||||||
|
|
||||||
|
See also [Contributing to Chrome Devtools Protocol](https://docs.google.com/document/d/1c-COD2kaK__5iMM5SEx-PzNA7HFmgttcYfOHHX0HaOM/edit).
|
||||||
|
|
||||||
|
We're working on enabling standalone builds for parts of this package for
|
||||||
|
testing and development, please feel free to ignore this for now.
|
||||||
|
But, if you're familiar with
|
||||||
|
[Chromium's development process](https://www.chromium.org/developers/contributing-code)
|
||||||
|
and have the depot_tools installed, you may use these commands
|
||||||
|
to fetch the package (and dependencies) and build and run the tests:
|
||||||
|
|
||||||
|
fetch inspector_protocol
|
||||||
|
cd src
|
||||||
|
gn gen out/Release
|
||||||
|
ninja -C out/Release json_parser_test
|
||||||
|
out/Release/json_parser_test
|
||||||
|
|
||||||
|
You'll probably also need to install g++, since Clang uses this to find the
|
||||||
|
standard C++ headers. E.g.,
|
||||||
|
|
||||||
|
sudo apt-get install g++-8
|
@ -2,7 +2,7 @@ Name: inspector protocol
|
|||||||
Short Name: inspector_protocol
|
Short Name: inspector_protocol
|
||||||
URL: https://chromium.googlesource.com/deps/inspector_protocol/
|
URL: https://chromium.googlesource.com/deps/inspector_protocol/
|
||||||
Version: 0
|
Version: 0
|
||||||
Revision: f67ec5180f476830e839226b5ca948e43070fdab
|
Revision: 0aafd2876f7485db7b07c513c0457b7cbbbe3304
|
||||||
License: BSD
|
License: BSD
|
||||||
License File: LICENSE
|
License File: LICENSE
|
||||||
Security Critical: no
|
Security Critical: no
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import os.path
|
import os.path
|
||||||
import sys
|
import sys
|
||||||
import optparse
|
import argparse
|
||||||
import collections
|
import collections
|
||||||
import functools
|
import functools
|
||||||
import re
|
import re
|
||||||
@ -17,6 +17,13 @@ except ImportError:
|
|||||||
|
|
||||||
import pdl
|
import pdl
|
||||||
|
|
||||||
|
try:
|
||||||
|
unicode
|
||||||
|
except NameError:
|
||||||
|
# Define unicode for Py3
|
||||||
|
def unicode(s, *_):
|
||||||
|
return s
|
||||||
|
|
||||||
# Path handling for libraries and templates
|
# Path handling for libraries and templates
|
||||||
# Paths have to be normalized because Jinja uses the exact template path to
|
# Paths have to be normalized because Jinja uses the exact template path to
|
||||||
# determine the hash used in the cache filename, and we need a pre-caching step
|
# determine the hash used in the cache filename, and we need a pre-caching step
|
||||||
@ -53,27 +60,16 @@ def read_config():
|
|||||||
return collections.namedtuple('X', keys)(*values)
|
return collections.namedtuple('X', keys)(*values)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cmdline_parser = optparse.OptionParser()
|
cmdline_parser = argparse.ArgumentParser()
|
||||||
cmdline_parser.add_option("--output_base")
|
cmdline_parser.add_argument("--output_base", type=unicode, required=True)
|
||||||
cmdline_parser.add_option("--jinja_dir")
|
cmdline_parser.add_argument("--jinja_dir", type=unicode, required=True)
|
||||||
cmdline_parser.add_option("--config")
|
cmdline_parser.add_argument("--config", type=unicode, required=True)
|
||||||
cmdline_parser.add_option("--config_value", action="append", type="string")
|
cmdline_parser.add_argument("--config_value", default=[], action="append")
|
||||||
arg_options, _ = cmdline_parser.parse_args()
|
arg_options = cmdline_parser.parse_args()
|
||||||
jinja_dir = arg_options.jinja_dir
|
jinja_dir = arg_options.jinja_dir
|
||||||
if not jinja_dir:
|
|
||||||
raise Exception("jinja directory must be specified")
|
|
||||||
jinja_dir = jinja_dir.decode('utf8')
|
|
||||||
output_base = arg_options.output_base
|
output_base = arg_options.output_base
|
||||||
if not output_base:
|
|
||||||
raise Exception("Base output directory must be specified")
|
|
||||||
output_base = output_base.decode('utf8')
|
|
||||||
config_file = arg_options.config
|
config_file = arg_options.config
|
||||||
if not config_file:
|
|
||||||
raise Exception("Config file name must be specified")
|
|
||||||
config_file = config_file.decode('utf8')
|
|
||||||
config_values = arg_options.config_value
|
config_values = arg_options.config_value
|
||||||
if not config_values:
|
|
||||||
config_values = []
|
|
||||||
except Exception:
|
except Exception:
|
||||||
# Work with python 2 and 3 http://docs.python.org/py3k/howto/pyporting.html
|
# Work with python 2 and 3 http://docs.python.org/py3k/howto/pyporting.html
|
||||||
exc = sys.exc_info()[1]
|
exc = sys.exc_info()[1]
|
||||||
@ -631,7 +627,7 @@ def main():
|
|||||||
"Array_h.template",
|
"Array_h.template",
|
||||||
"DispatcherBase_h.template",
|
"DispatcherBase_h.template",
|
||||||
"Parser_h.template",
|
"Parser_h.template",
|
||||||
"CBOR_h.template",
|
"encoding_h.template",
|
||||||
]
|
]
|
||||||
|
|
||||||
protocol_cpp_templates = [
|
protocol_cpp_templates = [
|
||||||
@ -641,7 +637,7 @@ def main():
|
|||||||
"Object_cpp.template",
|
"Object_cpp.template",
|
||||||
"DispatcherBase_cpp.template",
|
"DispatcherBase_cpp.template",
|
||||||
"Parser_cpp.template",
|
"Parser_cpp.template",
|
||||||
"CBOR_cpp.template",
|
"encoding_cpp.template",
|
||||||
]
|
]
|
||||||
|
|
||||||
forward_h_templates = [
|
forward_h_templates = [
|
||||||
|
6
tools/inspector_protocol/codereview.settings
Normal file
6
tools/inspector_protocol/codereview.settings
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# This file is used by git-cl to get repository specific information.
|
||||||
|
CC_LIST: chromium-reviews@chromium.org
|
||||||
|
CODE_REVIEW_SERVER: codereview.chromium.org
|
||||||
|
GERRIT_HOST: True
|
||||||
|
PROJECT: inspector_protocol
|
||||||
|
VIEW_VC: https://chromium.googlesource.com/deps/inspector_protocol/+/
|
@ -4,10 +4,8 @@
|
|||||||
# found in the LICENSE file.
|
# found in the LICENSE file.
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import collections
|
|
||||||
import json
|
import json
|
||||||
import os.path
|
import os.path
|
||||||
import re
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import pdl
|
import pdl
|
||||||
|
2189
tools/inspector_protocol/encoding/encoding.cc
Normal file
2189
tools/inspector_protocol/encoding/encoding.cc
Normal file
File diff suppressed because it is too large
Load Diff
510
tools/inspector_protocol/encoding/encoding.h
Normal file
510
tools/inspector_protocol/encoding/encoding.h
Normal file
@ -0,0 +1,510 @@
|
|||||||
|
// 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 V8_INSPECTOR_PROTOCOL_ENCODING_ENCODING_H_
|
||||||
|
#define V8_INSPECTOR_PROTOCOL_ENCODING_ENCODING_H_
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
#include <limits>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace v8_inspector_protocol_encoding {
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// span - sequence of bytes
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
// This template is similar to std::span, which will be included in C++20.
|
||||||
|
template <typename T>
|
||||||
|
class span {
|
||||||
|
public:
|
||||||
|
using index_type = size_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_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
span<T> SpanFrom(const std::vector<T>& v) {
|
||||||
|
return span<T>(v.data(), v.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t N>
|
||||||
|
span<uint8_t> SpanFrom(const char (&str)[N]) {
|
||||||
|
return span<uint8_t>(reinterpret_cast<const uint8_t*>(str), N - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline span<uint8_t> SpanFrom(const char* str) {
|
||||||
|
return str ? span<uint8_t>(reinterpret_cast<const uint8_t*>(str), strlen(str))
|
||||||
|
: span<uint8_t>();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline span<uint8_t> SpanFrom(const std::string& v) {
|
||||||
|
return span<uint8_t>(reinterpret_cast<const uint8_t*>(v.data()), v.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Status and 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_MAP_END = 0x0b,
|
||||||
|
JSON_PARSER_COMMA_OR_MAP_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_TRAILING_JUNK = 0x1c,
|
||||||
|
CBOR_MAP_START_EXPECTED = 0x1d,
|
||||||
|
CBOR_MAP_STOP_EXPECTED = 0x1e,
|
||||||
|
CBOR_ENVELOPE_SIZE_LIMIT_EXCEEDED = 0x1f,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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 size_t npos() { return std::numeric_limits<size_t>::max(); }
|
||||||
|
|
||||||
|
bool ok() const { return error == Error::OK; }
|
||||||
|
|
||||||
|
Error error = Error::OK;
|
||||||
|
size_t pos = npos();
|
||||||
|
Status(Error error, size_t pos) : error(error), pos(pos) {}
|
||||||
|
Status() = default;
|
||||||
|
|
||||||
|
// Returns a 7 bit US-ASCII string, either "OK" or an error message
|
||||||
|
// that includes the position.
|
||||||
|
std::string ToASCIIString() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string ToASCIIString(const char* msg) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handler interface for parser events emitted by a streaming parser.
|
||||||
|
// See cbor::NewCBOREncoder, cbor::ParseCBOR, json::NewJSONEncoder,
|
||||||
|
// json::ParseJSON.
|
||||||
|
class StreamingParserHandler {
|
||||||
|
public:
|
||||||
|
virtual ~StreamingParserHandler() = default;
|
||||||
|
virtual void HandleMapBegin() = 0;
|
||||||
|
virtual void HandleMapEnd() = 0;
|
||||||
|
virtual void HandleArrayBegin() = 0;
|
||||||
|
virtual void HandleArrayEnd() = 0;
|
||||||
|
virtual void HandleString8(span<uint8_t> chars) = 0;
|
||||||
|
virtual void HandleString16(span<uint16_t> chars) = 0;
|
||||||
|
virtual void HandleBinary(span<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;
|
||||||
|
};
|
||||||
|
|
||||||
|
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) are supported.
|
||||||
|
// - 7 bit US-ASCII strings must always be encoded as UTF8 strings, never
|
||||||
|
// as UTF16 strings.
|
||||||
|
// - 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.
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Detecting CBOR content
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
// The first byte for an envelope, which we use for wrapping dictionaries
|
||||||
|
// and arrays; and the byte that indicates a byte string with 32 bit length.
|
||||||
|
// These two bytes start an envelope, and thereby also any CBOR message
|
||||||
|
// produced or consumed by this protocol. See also |EnvelopeEncoder| below.
|
||||||
|
uint8_t InitialByteForEnvelope();
|
||||||
|
uint8_t InitialByteFor32BitLengthByteString();
|
||||||
|
|
||||||
|
// Checks whether |msg| is a cbor message.
|
||||||
|
bool IsCBORMessage(span<uint8_t> msg);
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Encoding individual CBOR items
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
void EncodeInt32(int32_t value, std::string* 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);
|
||||||
|
void EncodeString16(span<uint16_t> in, std::string* out);
|
||||||
|
|
||||||
|
// Encodes a UTF8 string |in| as STRING (major type 3).
|
||||||
|
void EncodeString8(span<uint8_t> in, std::vector<uint8_t>* out);
|
||||||
|
void EncodeString8(span<uint8_t> in, std::string* out);
|
||||||
|
|
||||||
|
// Encodes the given |latin1| string as STRING8.
|
||||||
|
// If any non-ASCII character is present, it will be represented
|
||||||
|
// as a 2 byte UTF8 sequence.
|
||||||
|
void EncodeFromLatin1(span<uint8_t> latin1, std::vector<uint8_t>* out);
|
||||||
|
void EncodeFromLatin1(span<uint8_t> latin1, std::string* out);
|
||||||
|
|
||||||
|
// Encodes the given |utf16| string as STRING8 if it's entirely US-ASCII.
|
||||||
|
// Otherwise, encodes as STRING16.
|
||||||
|
void EncodeFromUTF16(span<uint16_t> utf16, std::vector<uint8_t>* out);
|
||||||
|
void EncodeFromUTF16(span<uint16_t> utf16, std::string* 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);
|
||||||
|
void EncodeBinary(span<uint8_t> in, std::string* 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);
|
||||||
|
void EncodeDouble(double value, std::string* out);
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// cbor::EnvelopeEncoder - for wrapping submessages
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
void EncodeStart(std::string* out);
|
||||||
|
// This records the current size in |out| at position byte_size_pos_.
|
||||||
|
// Returns true iff successful.
|
||||||
|
bool EncodeStop(std::vector<uint8_t>* out);
|
||||||
|
bool EncodeStop(std::string* out);
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t byte_size_pos_ = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// cbor::NewCBOREncoder - for encoding from a streaming parser
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
// This can be used to convert to CBOR, by passing the return value to a parser
|
||||||
|
// that drives it. 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<StreamingParserHandler> NewCBOREncoder(
|
||||||
|
std::vector<uint8_t>* out,
|
||||||
|
Status* status);
|
||||||
|
std::unique_ptr<StreamingParserHandler> NewCBOREncoder(std::string* out,
|
||||||
|
Status* status);
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// cbor::CBORTokenizer - for parsing individual CBOR items
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
// Tags for the tokens within a CBOR message that CBORTokenizer 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,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// To be called only if ::TokenTag() == CBORTokenTag::ENVELOPE.
|
||||||
|
span<uint8_t> GetEnvelopeContents() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void ReadNextToken(bool enter_envelope);
|
||||||
|
void SetToken(CBORTokenTag token, size_t token_byte_length);
|
||||||
|
void SetError(Error error);
|
||||||
|
|
||||||
|
span<uint8_t> bytes_;
|
||||||
|
CBORTokenTag token_tag_;
|
||||||
|
struct Status status_;
|
||||||
|
size_t token_byte_length_;
|
||||||
|
MajorType token_start_type_;
|
||||||
|
uint64_t token_start_internal_value_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// cbor::ParseCBOR - for receiving streaming parser events for CBOR messages
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
// Parses a CBOR encoded message from |bytes|, sending events to
|
||||||
|
// |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, StreamingParserHandler* out);
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// cbor::AppendString8EntryToMap - for limited in-place editing of messages
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
// Modifies the |cbor| message by appending a new key/value entry at the end
|
||||||
|
// of the map. Patches up the envelope size; Status.ok() iff successful.
|
||||||
|
// If not successful, |cbor| may be corrupted after this call.
|
||||||
|
Status AppendString8EntryToCBORMap(span<uint8_t> string8_key,
|
||||||
|
span<uint8_t> string8_value,
|
||||||
|
std::vector<uint8_t>* cbor);
|
||||||
|
Status AppendString8EntryToCBORMap(span<uint8_t> string8_key,
|
||||||
|
span<uint8_t> string8_value,
|
||||||
|
std::string* cbor);
|
||||||
|
|
||||||
|
namespace internals { // Exposed only for writing tests.
|
||||||
|
int8_t ReadTokenStart(span<uint8_t> bytes,
|
||||||
|
cbor::MajorType* type,
|
||||||
|
uint64_t* value);
|
||||||
|
|
||||||
|
void WriteTokenStart(cbor::MajorType type,
|
||||||
|
uint64_t value,
|
||||||
|
std::vector<uint8_t>* encoded);
|
||||||
|
void WriteTokenStart(cbor::MajorType type,
|
||||||
|
uint64_t value,
|
||||||
|
std::string* encoded);
|
||||||
|
} // namespace internals
|
||||||
|
} // namespace cbor
|
||||||
|
|
||||||
|
namespace json {
|
||||||
|
// Client code must provide an instance. Implementation should delegate
|
||||||
|
// to whatever is appropriate.
|
||||||
|
class Platform {
|
||||||
|
public:
|
||||||
|
virtual ~Platform() = default;
|
||||||
|
// Parses |str| into |result|. Returns false iff there are
|
||||||
|
// leftover characters or parsing errors.
|
||||||
|
virtual bool StrToD(const char* str, double* result) const = 0;
|
||||||
|
|
||||||
|
// Prints |value| in a format suitable for JSON.
|
||||||
|
virtual std::unique_ptr<char[]> DToStr(double value) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// json::NewJSONEncoder - for encoding streaming parser events as JSON
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
// Returns a handler object which will write ascii characters to |out|.
|
||||||
|
// |status->ok()| will be false iff the handler routine HandleError() is called.
|
||||||
|
// In that case, we'll stop emitting output.
|
||||||
|
// Except for calling the HandleError routine at any time, the client
|
||||||
|
// code must call the Handle* methods in an order in which they'd occur
|
||||||
|
// in valid JSON; otherwise we may crash (the code uses assert).
|
||||||
|
std::unique_ptr<StreamingParserHandler> NewJSONEncoder(
|
||||||
|
const Platform* platform,
|
||||||
|
std::vector<uint8_t>* out,
|
||||||
|
Status* status);
|
||||||
|
std::unique_ptr<StreamingParserHandler> NewJSONEncoder(const Platform* platform,
|
||||||
|
std::string* out,
|
||||||
|
Status* status);
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// json::ParseJSON - for receiving streaming parser events for JSON
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
void ParseJSON(const Platform& platform,
|
||||||
|
span<uint8_t> chars,
|
||||||
|
StreamingParserHandler* handler);
|
||||||
|
void ParseJSON(const Platform& platform,
|
||||||
|
span<uint16_t> chars,
|
||||||
|
StreamingParserHandler* handler);
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// json::ConvertCBORToJSON, json::ConvertJSONToCBOR - for transcoding
|
||||||
|
// =============================================================================
|
||||||
|
Status ConvertCBORToJSON(const Platform& platform,
|
||||||
|
span<uint8_t> cbor,
|
||||||
|
std::string* json);
|
||||||
|
Status ConvertCBORToJSON(const Platform& platform,
|
||||||
|
span<uint8_t> cbor,
|
||||||
|
std::vector<uint8_t>* json);
|
||||||
|
Status ConvertJSONToCBOR(const Platform& platform,
|
||||||
|
span<uint8_t> json,
|
||||||
|
std::vector<uint8_t>* cbor);
|
||||||
|
Status ConvertJSONToCBOR(const Platform& platform,
|
||||||
|
span<uint16_t> json,
|
||||||
|
std::vector<uint8_t>* cbor);
|
||||||
|
Status ConvertJSONToCBOR(const Platform& platform,
|
||||||
|
span<uint8_t> json,
|
||||||
|
std::string* cbor);
|
||||||
|
Status ConvertJSONToCBOR(const Platform& platform,
|
||||||
|
span<uint16_t> json,
|
||||||
|
std::string* cbor);
|
||||||
|
} // namespace json
|
||||||
|
} // namespace v8_inspector_protocol_encoding
|
||||||
|
|
||||||
|
#endif // V8_INSPECTOR_PROTOCOL_ENCODING_ENCODING_H_
|
1838
tools/inspector_protocol/encoding/encoding_test.cc
Normal file
1838
tools/inspector_protocol/encoding/encoding_test.cc
Normal file
File diff suppressed because it is too large
Load Diff
33
tools/inspector_protocol/encoding/encoding_test_helper.h
Normal file
33
tools/inspector_protocol/encoding/encoding_test_helper.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// Copyright 2019 The V8 Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file is V8 specific, to make encoding_test.cc work.
|
||||||
|
// It is not rolled from the upstream project.
|
||||||
|
|
||||||
|
#ifndef V8_INSPECTOR_PROTOCOL_ENCODING_ENCODING_TEST_HELPER_H_
|
||||||
|
#define V8_INSPECTOR_PROTOCOL_ENCODING_ENCODING_TEST_HELPER_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "src/base/logging.h"
|
||||||
|
#include "src/inspector/v8-string-conversions.h"
|
||||||
|
#include "testing/gmock/include/gmock/gmock.h"
|
||||||
|
#include "testing/gtest/include/gtest/gtest.h"
|
||||||
|
|
||||||
|
namespace v8_inspector_protocol_encoding {
|
||||||
|
|
||||||
|
std::string UTF16ToUTF8(span<uint16_t> in) {
|
||||||
|
return v8_inspector::UTF16ToUTF8(in.data(), in.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint16_t> UTF8ToUTF16(span<uint8_t> in) {
|
||||||
|
std::basic_string<uint16_t> utf16 = v8_inspector::UTF8ToUTF16(
|
||||||
|
reinterpret_cast<const char*>(in.data()), in.size());
|
||||||
|
return std::vector<uint16_t>(utf16.begin(), utf16.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace v8_inspector_protocol_encoding
|
||||||
|
|
||||||
|
#endif // V8_INSPECTOR_PROTOCOL_ENCODING_ENCODING_TEST_HELPER_H_
|
@ -27,13 +27,16 @@ template("inspector_protocol_generate") {
|
|||||||
inspector_protocol_dir = invoker.inspector_protocol_dir
|
inspector_protocol_dir = invoker.inspector_protocol_dir
|
||||||
|
|
||||||
action(target_name) {
|
action(target_name) {
|
||||||
script = "$inspector_protocol_dir/CodeGenerator.py"
|
script = "$inspector_protocol_dir/code_generator.py"
|
||||||
|
|
||||||
inputs = [
|
inputs = [
|
||||||
invoker.config_file,
|
invoker.config_file,
|
||||||
|
"$inspector_protocol_dir/lib/base_string_adapter_cc.template",
|
||||||
|
"$inspector_protocol_dir/lib/base_string_adapter_h.template",
|
||||||
|
"$inspector_protocol_dir/lib/encoding_h.template",
|
||||||
|
"$inspector_protocol_dir/lib/encoding_cpp.template",
|
||||||
"$inspector_protocol_dir/lib/Allocator_h.template",
|
"$inspector_protocol_dir/lib/Allocator_h.template",
|
||||||
"$inspector_protocol_dir/lib/Array_h.template",
|
"$inspector_protocol_dir/lib/Array_h.template",
|
||||||
"$inspector_protocol_dir/lib/Collections_h.template",
|
|
||||||
"$inspector_protocol_dir/lib/DispatcherBase_cpp.template",
|
"$inspector_protocol_dir/lib/DispatcherBase_cpp.template",
|
||||||
"$inspector_protocol_dir/lib/DispatcherBase_h.template",
|
"$inspector_protocol_dir/lib/DispatcherBase_h.template",
|
||||||
"$inspector_protocol_dir/lib/ErrorSupport_cpp.template",
|
"$inspector_protocol_dir/lib/ErrorSupport_cpp.template",
|
||||||
|
@ -5,9 +5,10 @@
|
|||||||
{
|
{
|
||||||
'variables': {
|
'variables': {
|
||||||
'inspector_protocol_files': [
|
'inspector_protocol_files': [
|
||||||
|
'lib/encoding_h.template',
|
||||||
|
'lib/encoding_cpp.template',
|
||||||
'lib/Allocator_h.template',
|
'lib/Allocator_h.template',
|
||||||
'lib/Array_h.template',
|
'lib/Array_h.template',
|
||||||
'lib/Collections_h.template',
|
|
||||||
'lib/DispatcherBase_cpp.template',
|
'lib/DispatcherBase_cpp.template',
|
||||||
'lib/DispatcherBase_h.template',
|
'lib/DispatcherBase_h.template',
|
||||||
'lib/ErrorSupport_cpp.template',
|
'lib/ErrorSupport_cpp.template',
|
||||||
@ -27,7 +28,7 @@
|
|||||||
'templates/Imported_h.template',
|
'templates/Imported_h.template',
|
||||||
'templates/TypeBuilder_cpp.template',
|
'templates/TypeBuilder_cpp.template',
|
||||||
'templates/TypeBuilder_h.template',
|
'templates/TypeBuilder_h.template',
|
||||||
'CodeGenerator.py',
|
'code_generator.py',
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,803 +0,0 @@
|
|||||||
{# This template is generated by gen_cbor_templates.py. #}
|
|
||||||
// Generated by lib/CBOR_cpp.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.
|
|
||||||
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <limits>
|
|
||||||
|
|
||||||
{% for namespace in config.protocol.namespace %}
|
|
||||||
namespace {{namespace}} {
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
// ===== encoding/cbor.cc =====
|
|
||||||
|
|
||||||
using namespace cbor;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
// See RFC 7049 Section 2.3, Table 2.
|
|
||||||
static constexpr uint8_t kEncodedTrue =
|
|
||||||
EncodeInitialByte(MajorType::SIMPLE_VALUE, 21);
|
|
||||||
static constexpr uint8_t kEncodedFalse =
|
|
||||||
EncodeInitialByte(MajorType::SIMPLE_VALUE, 20);
|
|
||||||
static constexpr uint8_t kEncodedNull =
|
|
||||||
EncodeInitialByte(MajorType::SIMPLE_VALUE, 22);
|
|
||||||
static constexpr uint8_t kInitialByteForDouble =
|
|
||||||
EncodeInitialByte(MajorType::SIMPLE_VALUE, 27);
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
uint8_t EncodeTrue() { return kEncodedTrue; }
|
|
||||||
uint8_t EncodeFalse() { return kEncodedFalse; }
|
|
||||||
uint8_t EncodeNull() { return kEncodedNull; }
|
|
||||||
|
|
||||||
uint8_t EncodeIndefiniteLengthArrayStart() {
|
|
||||||
return kInitialByteIndefiniteLengthArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t EncodeIndefiniteLengthMapStart() {
|
|
||||||
return kInitialByteIndefiniteLengthMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t EncodeStop() { return kStopByte; }
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
// See RFC 7049 Table 3 and Section 2.4.4.2. This is used as a prefix for
|
|
||||||
// arbitrary binary data encoded as BYTE_STRING.
|
|
||||||
static constexpr uint8_t kExpectedConversionToBase64Tag =
|
|
||||||
EncodeInitialByte(MajorType::TAG, 22);
|
|
||||||
|
|
||||||
// When parsing CBOR, we limit recursion depth for objects and arrays
|
|
||||||
// to this constant.
|
|
||||||
static constexpr int kStackLimit = 1000;
|
|
||||||
|
|
||||||
// Writes the bytes for |v| to |out|, starting with the most significant byte.
|
|
||||||
// See also: https://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html
|
|
||||||
template <typename T>
|
|
||||||
void WriteBytesMostSignificantByteFirst(T v, std::vector<uint8_t>* out) {
|
|
||||||
for (int shift_bytes = sizeof(T) - 1; shift_bytes >= 0; --shift_bytes)
|
|
||||||
out->push_back(0xff & (v >> (shift_bytes * 8)));
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
namespace cbor_internals {
|
|
||||||
// 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(MajorType type, uint64_t value,
|
|
||||||
std::vector<uint8_t>* encoded) {
|
|
||||||
if (value < 24) {
|
|
||||||
// Values 0-23 are encoded directly into the additional info of the
|
|
||||||
// initial byte.
|
|
||||||
encoded->push_back(EncodeInitialByte(type, /*additional_info=*/value));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (value <= std::numeric_limits<uint8_t>::max()) {
|
|
||||||
// Values 24-255 are encoded with one initial byte, followed by the value.
|
|
||||||
encoded->push_back(EncodeInitialByte(type, kAdditionalInformation1Byte));
|
|
||||||
encoded->push_back(value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (value <= std::numeric_limits<uint16_t>::max()) {
|
|
||||||
// Values 256-65535: 1 initial byte + 2 bytes payload.
|
|
||||||
encoded->push_back(EncodeInitialByte(type, kAdditionalInformation2Bytes));
|
|
||||||
WriteBytesMostSignificantByteFirst<uint16_t>(value, encoded);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (value <= std::numeric_limits<uint32_t>::max()) {
|
|
||||||
// 32 bit uint: 1 initial byte + 4 bytes payload.
|
|
||||||
encoded->push_back(EncodeInitialByte(type, kAdditionalInformation4Bytes));
|
|
||||||
WriteBytesMostSignificantByteFirst<uint32_t>(static_cast<uint32_t>(value),
|
|
||||||
encoded);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 64 bit uint: 1 initial byte + 8 bytes payload.
|
|
||||||
encoded->push_back(EncodeInitialByte(type, kAdditionalInformation8Bytes));
|
|
||||||
WriteBytesMostSignificantByteFirst<uint64_t>(value, encoded);
|
|
||||||
}
|
|
||||||
} // namespace cbor_internals
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
// Extracts sizeof(T) bytes from |in| to extract a value of type T
|
|
||||||
// (e.g. uint64_t, uint32_t, ...), most significant byte first.
|
|
||||||
// See also: https://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html
|
|
||||||
template <typename T>
|
|
||||||
T ReadBytesMostSignificantByteFirst(span<uint8_t> in) {
|
|
||||||
assert(static_cast<std::size_t>(in.size()) >= sizeof(T));
|
|
||||||
T result = 0;
|
|
||||||
for (std::size_t shift_bytes = 0; shift_bytes < sizeof(T); ++shift_bytes)
|
|
||||||
result |= T(in[sizeof(T) - 1 - shift_bytes]) << (shift_bytes * 8);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
namespace cbor_internals {
|
|
||||||
int8_t ReadTokenStart(span<uint8_t> bytes, MajorType* type, uint64_t* value) {
|
|
||||||
if (bytes.empty()) return -1;
|
|
||||||
uint8_t initial_byte = bytes[0];
|
|
||||||
*type = MajorType((initial_byte & kMajorTypeMask) >> kMajorTypeBitShift);
|
|
||||||
|
|
||||||
uint8_t additional_information = initial_byte & kAdditionalInformationMask;
|
|
||||||
if (additional_information < 24) {
|
|
||||||
// Values 0-23 are encoded directly into the additional info of the
|
|
||||||
// initial byte.
|
|
||||||
*value = additional_information;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (additional_information == kAdditionalInformation1Byte) {
|
|
||||||
// Values 24-255 are encoded with one initial byte, followed by the value.
|
|
||||||
if (bytes.size() < 2) return -1;
|
|
||||||
*value = ReadBytesMostSignificantByteFirst<uint8_t>(bytes.subspan(1));
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
if (additional_information == kAdditionalInformation2Bytes) {
|
|
||||||
// Values 256-65535: 1 initial byte + 2 bytes payload.
|
|
||||||
if (static_cast<std::size_t>(bytes.size()) < 1 + sizeof(uint16_t))
|
|
||||||
return -1;
|
|
||||||
*value = ReadBytesMostSignificantByteFirst<uint16_t>(bytes.subspan(1));
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
if (additional_information == kAdditionalInformation4Bytes) {
|
|
||||||
// 32 bit uint: 1 initial byte + 4 bytes payload.
|
|
||||||
if (static_cast<std::size_t>(bytes.size()) < 1 + sizeof(uint32_t))
|
|
||||||
return -1;
|
|
||||||
*value = ReadBytesMostSignificantByteFirst<uint32_t>(bytes.subspan(1));
|
|
||||||
return 5;
|
|
||||||
}
|
|
||||||
if (additional_information == kAdditionalInformation8Bytes) {
|
|
||||||
// 64 bit uint: 1 initial byte + 8 bytes payload.
|
|
||||||
if (static_cast<std::size_t>(bytes.size()) < 1 + sizeof(uint64_t))
|
|
||||||
return -1;
|
|
||||||
*value = ReadBytesMostSignificantByteFirst<uint64_t>(bytes.subspan(1));
|
|
||||||
return 9;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
} // namespace cbor_internals
|
|
||||||
|
|
||||||
using cbor_internals::WriteTokenStart;
|
|
||||||
using cbor_internals::ReadTokenStart;
|
|
||||||
|
|
||||||
void EncodeInt32(int32_t value, std::vector<uint8_t>* out) {
|
|
||||||
if (value >= 0) {
|
|
||||||
WriteTokenStart(MajorType::UNSIGNED, value, out);
|
|
||||||
} else {
|
|
||||||
uint64_t representation = static_cast<uint64_t>(-(value + 1));
|
|
||||||
WriteTokenStart(MajorType::NEGATIVE, representation, out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EncodeString16(span<uint16_t> in, std::vector<uint8_t>* out) {
|
|
||||||
uint64_t byte_length = static_cast<uint64_t>(in.size_bytes());
|
|
||||||
WriteTokenStart(MajorType::BYTE_STRING, byte_length, out);
|
|
||||||
// When emitting UTF16 characters, we always write the least significant byte
|
|
||||||
// first; this is because it's the native representation for X86.
|
|
||||||
// TODO(johannes): Implement a more efficient thing here later, e.g.
|
|
||||||
// casting *iff* the machine has this byte order.
|
|
||||||
// The wire format for UTF16 chars will probably remain the same
|
|
||||||
// (least significant byte first) since this way we can have
|
|
||||||
// golden files, unittests, etc. that port easily and universally.
|
|
||||||
// See also:
|
|
||||||
// https://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html
|
|
||||||
for (const uint16_t two_bytes : in) {
|
|
||||||
out->push_back(two_bytes);
|
|
||||||
out->push_back(two_bytes >> 8);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EncodeString8(span<uint8_t> in, std::vector<uint8_t>* out) {
|
|
||||||
WriteTokenStart(MajorType::STRING, static_cast<uint64_t>(in.size_bytes()),
|
|
||||||
out);
|
|
||||||
out->insert(out->end(), in.begin(), in.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
void EncodeBinary(span<uint8_t> in, std::vector<uint8_t>* out) {
|
|
||||||
out->push_back(kExpectedConversionToBase64Tag);
|
|
||||||
uint64_t byte_length = static_cast<uint64_t>(in.size_bytes());
|
|
||||||
WriteTokenStart(MajorType::BYTE_STRING, byte_length, out);
|
|
||||||
out->insert(out->end(), in.begin(), in.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
// A double is encoded with a specific initial byte
|
|
||||||
// (kInitialByteForDouble) plus the 64 bits of payload for its value.
|
|
||||||
constexpr std::ptrdiff_t kEncodedDoubleSize = 1 + sizeof(uint64_t);
|
|
||||||
|
|
||||||
// An envelope is encoded with a specific initial byte
|
|
||||||
// (kInitialByteForEnvelope), plus the start byte for a BYTE_STRING with a 32
|
|
||||||
// bit wide length, plus a 32 bit length for that string.
|
|
||||||
constexpr std::ptrdiff_t kEncodedEnvelopeHeaderSize = 1 + 1 + sizeof(uint32_t);
|
|
||||||
|
|
||||||
void EncodeDouble(double value, std::vector<uint8_t>* out) {
|
|
||||||
// The additional_info=27 indicates 64 bits for the double follow.
|
|
||||||
// See RFC 7049 Section 2.3, Table 1.
|
|
||||||
out->push_back(kInitialByteForDouble);
|
|
||||||
union {
|
|
||||||
double from_double;
|
|
||||||
uint64_t to_uint64;
|
|
||||||
} reinterpret;
|
|
||||||
reinterpret.from_double = value;
|
|
||||||
WriteBytesMostSignificantByteFirst<uint64_t>(reinterpret.to_uint64, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EnvelopeEncoder::EncodeStart(std::vector<uint8_t>* out) {
|
|
||||||
assert(byte_size_pos_ == 0);
|
|
||||||
out->push_back(kInitialByteForEnvelope);
|
|
||||||
out->push_back(kInitialByteFor32BitLengthByteString);
|
|
||||||
byte_size_pos_ = out->size();
|
|
||||||
out->resize(out->size() + sizeof(uint32_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool EnvelopeEncoder::EncodeStop(std::vector<uint8_t>* out) {
|
|
||||||
assert(byte_size_pos_ != 0);
|
|
||||||
// The byte size is the size of the payload, that is, all the
|
|
||||||
// bytes that were written past the byte size position itself.
|
|
||||||
uint64_t byte_size = out->size() - (byte_size_pos_ + sizeof(uint32_t));
|
|
||||||
// We store exactly 4 bytes, so at most INT32MAX, with most significant
|
|
||||||
// byte first.
|
|
||||||
if (byte_size > std::numeric_limits<uint32_t>::max()) return false;
|
|
||||||
for (int shift_bytes = sizeof(uint32_t) - 1; shift_bytes >= 0;
|
|
||||||
--shift_bytes) {
|
|
||||||
(*out)[byte_size_pos_++] = 0xff & (byte_size >> (shift_bytes * 8));
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
class JSONToCBOREncoder : public JSONParserHandler {
|
|
||||||
public:
|
|
||||||
JSONToCBOREncoder(std::vector<uint8_t>* out, Status* status)
|
|
||||||
: out_(out), status_(status) {
|
|
||||||
*status_ = Status();
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleObjectBegin() override {
|
|
||||||
envelopes_.emplace_back();
|
|
||||||
envelopes_.back().EncodeStart(out_);
|
|
||||||
out_->push_back(kInitialByteIndefiniteLengthMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleObjectEnd() override {
|
|
||||||
out_->push_back(kStopByte);
|
|
||||||
assert(!envelopes_.empty());
|
|
||||||
envelopes_.back().EncodeStop(out_);
|
|
||||||
envelopes_.pop_back();
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleArrayBegin() override {
|
|
||||||
envelopes_.emplace_back();
|
|
||||||
envelopes_.back().EncodeStart(out_);
|
|
||||||
out_->push_back(kInitialByteIndefiniteLengthArray);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleArrayEnd() override {
|
|
||||||
out_->push_back(kStopByte);
|
|
||||||
assert(!envelopes_.empty());
|
|
||||||
envelopes_.back().EncodeStop(out_);
|
|
||||||
envelopes_.pop_back();
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleString16(std::vector<uint16_t> chars) override {
|
|
||||||
for (uint16_t ch : chars) {
|
|
||||||
if (ch >= 0x7f) {
|
|
||||||
// If there's at least one non-7bit character, we encode as UTF16.
|
|
||||||
EncodeString16(span<uint16_t>(chars.data(), chars.size()), out_);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::vector<uint8_t> sevenbit_chars(chars.begin(), chars.end());
|
|
||||||
EncodeString8(span<uint8_t>(sevenbit_chars.data(), sevenbit_chars.size()),
|
|
||||||
out_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleBinary(std::vector<uint8_t> bytes) override {
|
|
||||||
EncodeBinary(span<uint8_t>(bytes.data(), bytes.size()), out_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleDouble(double value) override { EncodeDouble(value, out_); }
|
|
||||||
|
|
||||||
void HandleInt32(int32_t value) override { EncodeInt32(value, out_); }
|
|
||||||
|
|
||||||
void HandleBool(bool value) override {
|
|
||||||
// See RFC 7049 Section 2.3, Table 2.
|
|
||||||
out_->push_back(value ? kEncodedTrue : kEncodedFalse);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleNull() override {
|
|
||||||
// See RFC 7049 Section 2.3, Table 2.
|
|
||||||
out_->push_back(kEncodedNull);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleError(Status error) override {
|
|
||||||
assert(!error.ok());
|
|
||||||
*status_ = error;
|
|
||||||
out_->clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<uint8_t>* out_;
|
|
||||||
std::vector<EnvelopeEncoder> envelopes_;
|
|
||||||
Status* status_;
|
|
||||||
};
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
std::unique_ptr<JSONParserHandler> NewJSONToCBOREncoder(
|
|
||||||
std::vector<uint8_t>* out, Status* status) {
|
|
||||||
return std::unique_ptr<JSONParserHandler>(new JSONToCBOREncoder(out, status));
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
// Below are three parsing routines for CBOR, which cover enough
|
|
||||||
// to roundtrip JSON messages.
|
|
||||||
bool ParseMap(int32_t stack_depth, CBORTokenizer* tokenizer,
|
|
||||||
JSONParserHandler* out);
|
|
||||||
bool ParseArray(int32_t stack_depth, CBORTokenizer* tokenizer,
|
|
||||||
JSONParserHandler* out);
|
|
||||||
bool ParseValue(int32_t stack_depth, CBORTokenizer* tokenizer,
|
|
||||||
JSONParserHandler* out);
|
|
||||||
|
|
||||||
void ParseUTF16String(CBORTokenizer* tokenizer, JSONParserHandler* out) {
|
|
||||||
std::vector<uint16_t> value;
|
|
||||||
span<uint8_t> rep = tokenizer->GetString16WireRep();
|
|
||||||
for (std::ptrdiff_t ii = 0; ii < rep.size(); ii += 2)
|
|
||||||
value.push_back((rep[ii + 1] << 8) | rep[ii]);
|
|
||||||
out->HandleString16(std::move(value));
|
|
||||||
tokenizer->Next();
|
|
||||||
}
|
|
||||||
|
|
||||||
// For now this method only covers US-ASCII. Later, we may allow UTF8.
|
|
||||||
bool ParseASCIIString(CBORTokenizer* tokenizer, JSONParserHandler* out) {
|
|
||||||
assert(tokenizer->TokenTag() == CBORTokenTag::STRING8);
|
|
||||||
std::vector<uint16_t> value16;
|
|
||||||
for (uint8_t ch : tokenizer->GetString8()) {
|
|
||||||
// We only accept us-ascii (7 bit) strings here. Other strings must
|
|
||||||
// be encoded with 16 bit (the BYTE_STRING case).
|
|
||||||
if (ch >= 0x7f) {
|
|
||||||
out->HandleError(
|
|
||||||
Status{Error::CBOR_STRING8_MUST_BE_7BIT, tokenizer->Status().pos});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
value16.push_back(ch);
|
|
||||||
}
|
|
||||||
out->HandleString16(std::move(value16));
|
|
||||||
tokenizer->Next();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ParseValue(int32_t stack_depth, CBORTokenizer* tokenizer,
|
|
||||||
JSONParserHandler* out) {
|
|
||||||
if (stack_depth > kStackLimit) {
|
|
||||||
out->HandleError(
|
|
||||||
Status{Error::CBOR_STACK_LIMIT_EXCEEDED, tokenizer->Status().pos});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Skip past the envelope to get to what's inside.
|
|
||||||
if (tokenizer->TokenTag() == CBORTokenTag::ENVELOPE)
|
|
||||||
tokenizer->EnterEnvelope();
|
|
||||||
switch (tokenizer->TokenTag()) {
|
|
||||||
case CBORTokenTag::ERROR_VALUE:
|
|
||||||
out->HandleError(tokenizer->Status());
|
|
||||||
return false;
|
|
||||||
case CBORTokenTag::DONE:
|
|
||||||
out->HandleError(Status{Error::CBOR_UNEXPECTED_EOF_EXPECTED_VALUE,
|
|
||||||
tokenizer->Status().pos});
|
|
||||||
return false;
|
|
||||||
case CBORTokenTag::TRUE_VALUE:
|
|
||||||
out->HandleBool(true);
|
|
||||||
tokenizer->Next();
|
|
||||||
return true;
|
|
||||||
case CBORTokenTag::FALSE_VALUE:
|
|
||||||
out->HandleBool(false);
|
|
||||||
tokenizer->Next();
|
|
||||||
return true;
|
|
||||||
case CBORTokenTag::NULL_VALUE:
|
|
||||||
out->HandleNull();
|
|
||||||
tokenizer->Next();
|
|
||||||
return true;
|
|
||||||
case CBORTokenTag::INT32:
|
|
||||||
out->HandleInt32(tokenizer->GetInt32());
|
|
||||||
tokenizer->Next();
|
|
||||||
return true;
|
|
||||||
case CBORTokenTag::DOUBLE:
|
|
||||||
out->HandleDouble(tokenizer->GetDouble());
|
|
||||||
tokenizer->Next();
|
|
||||||
return true;
|
|
||||||
case CBORTokenTag::STRING8:
|
|
||||||
return ParseASCIIString(tokenizer, out);
|
|
||||||
case CBORTokenTag::STRING16:
|
|
||||||
ParseUTF16String(tokenizer, out);
|
|
||||||
return true;
|
|
||||||
case CBORTokenTag::BINARY: {
|
|
||||||
span<uint8_t> binary = tokenizer->GetBinary();
|
|
||||||
out->HandleBinary(std::vector<uint8_t>(binary.begin(), binary.end()));
|
|
||||||
tokenizer->Next();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case CBORTokenTag::MAP_START:
|
|
||||||
return ParseMap(stack_depth + 1, tokenizer, out);
|
|
||||||
case CBORTokenTag::ARRAY_START:
|
|
||||||
return ParseArray(stack_depth + 1, tokenizer, out);
|
|
||||||
default:
|
|
||||||
out->HandleError(
|
|
||||||
Status{Error::CBOR_UNSUPPORTED_VALUE, tokenizer->Status().pos});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// |bytes| must start with the indefinite length array byte, so basically,
|
|
||||||
// ParseArray may only be called after an indefinite length array has been
|
|
||||||
// detected.
|
|
||||||
bool ParseArray(int32_t stack_depth, CBORTokenizer* tokenizer,
|
|
||||||
JSONParserHandler* out) {
|
|
||||||
assert(tokenizer->TokenTag() == CBORTokenTag::ARRAY_START);
|
|
||||||
tokenizer->Next();
|
|
||||||
out->HandleArrayBegin();
|
|
||||||
while (tokenizer->TokenTag() != CBORTokenTag::STOP) {
|
|
||||||
if (tokenizer->TokenTag() == CBORTokenTag::DONE) {
|
|
||||||
out->HandleError(
|
|
||||||
Status{Error::CBOR_UNEXPECTED_EOF_IN_ARRAY, tokenizer->Status().pos});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (tokenizer->TokenTag() == CBORTokenTag::ERROR_VALUE) {
|
|
||||||
out->HandleError(tokenizer->Status());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Parse value.
|
|
||||||
if (!ParseValue(stack_depth, tokenizer, out)) return false;
|
|
||||||
}
|
|
||||||
out->HandleArrayEnd();
|
|
||||||
tokenizer->Next();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// |bytes| must start with the indefinite length array byte, so basically,
|
|
||||||
// ParseArray may only be called after an indefinite length array has been
|
|
||||||
// detected.
|
|
||||||
bool ParseMap(int32_t stack_depth, CBORTokenizer* tokenizer,
|
|
||||||
JSONParserHandler* out) {
|
|
||||||
assert(tokenizer->TokenTag() == CBORTokenTag::MAP_START);
|
|
||||||
out->HandleObjectBegin();
|
|
||||||
tokenizer->Next();
|
|
||||||
while (tokenizer->TokenTag() != CBORTokenTag::STOP) {
|
|
||||||
if (tokenizer->TokenTag() == CBORTokenTag::DONE) {
|
|
||||||
out->HandleError(
|
|
||||||
Status{Error::CBOR_UNEXPECTED_EOF_IN_MAP, tokenizer->Status().pos});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (tokenizer->TokenTag() == CBORTokenTag::ERROR_VALUE) {
|
|
||||||
out->HandleError(tokenizer->Status());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Parse key.
|
|
||||||
if (tokenizer->TokenTag() == CBORTokenTag::STRING8) {
|
|
||||||
if (!ParseASCIIString(tokenizer, out)) return false;
|
|
||||||
} else if (tokenizer->TokenTag() == CBORTokenTag::STRING16) {
|
|
||||||
ParseUTF16String(tokenizer, out);
|
|
||||||
} else {
|
|
||||||
out->HandleError(
|
|
||||||
Status{Error::CBOR_INVALID_MAP_KEY, tokenizer->Status().pos});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Parse value.
|
|
||||||
if (!ParseValue(stack_depth, tokenizer, out)) return false;
|
|
||||||
}
|
|
||||||
out->HandleObjectEnd();
|
|
||||||
tokenizer->Next();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
void ParseCBOR(span<uint8_t> bytes, JSONParserHandler* json_out) {
|
|
||||||
if (bytes.empty()) {
|
|
||||||
json_out->HandleError(Status{Error::CBOR_NO_INPUT, 0});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (bytes[0] != kInitialByteForEnvelope) {
|
|
||||||
json_out->HandleError(Status{Error::CBOR_INVALID_START_BYTE, 0});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
CBORTokenizer tokenizer(bytes);
|
|
||||||
if (tokenizer.TokenTag() == CBORTokenTag::ERROR_VALUE) {
|
|
||||||
json_out->HandleError(tokenizer.Status());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// We checked for the envelope start byte above, so the tokenizer
|
|
||||||
// must agree here, since it's not an error.
|
|
||||||
assert(tokenizer.TokenTag() == CBORTokenTag::ENVELOPE);
|
|
||||||
tokenizer.EnterEnvelope();
|
|
||||||
if (tokenizer.TokenTag() != CBORTokenTag::MAP_START) {
|
|
||||||
json_out->HandleError(
|
|
||||||
Status{Error::CBOR_MAP_START_EXPECTED, tokenizer.Status().pos});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!ParseMap(/*stack_depth=*/1, &tokenizer, json_out)) return;
|
|
||||||
if (tokenizer.TokenTag() == CBORTokenTag::DONE) return;
|
|
||||||
if (tokenizer.TokenTag() == CBORTokenTag::ERROR_VALUE) {
|
|
||||||
json_out->HandleError(tokenizer.Status());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
json_out->HandleError(
|
|
||||||
Status{Error::CBOR_TRAILING_JUNK, tokenizer.Status().pos});
|
|
||||||
}
|
|
||||||
|
|
||||||
CBORTokenizer::CBORTokenizer(span<uint8_t> bytes) : bytes_(bytes) {
|
|
||||||
ReadNextToken(/*enter_envelope=*/false);
|
|
||||||
}
|
|
||||||
CBORTokenizer::~CBORTokenizer() {}
|
|
||||||
|
|
||||||
CBORTokenTag CBORTokenizer::TokenTag() const { return token_tag_; }
|
|
||||||
|
|
||||||
void CBORTokenizer::Next() {
|
|
||||||
if (token_tag_ == CBORTokenTag::ERROR_VALUE || token_tag_ == CBORTokenTag::DONE)
|
|
||||||
return;
|
|
||||||
ReadNextToken(/*enter_envelope=*/false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBORTokenizer::EnterEnvelope() {
|
|
||||||
assert(token_tag_ == CBORTokenTag::ENVELOPE);
|
|
||||||
ReadNextToken(/*enter_envelope=*/true);
|
|
||||||
}
|
|
||||||
|
|
||||||
Status CBORTokenizer::Status() const { return status_; }
|
|
||||||
|
|
||||||
int32_t CBORTokenizer::GetInt32() const {
|
|
||||||
assert(token_tag_ == CBORTokenTag::INT32);
|
|
||||||
// The range checks happen in ::ReadNextToken().
|
|
||||||
return static_cast<uint32_t>(
|
|
||||||
token_start_type_ == MajorType::UNSIGNED
|
|
||||||
? token_start_internal_value_
|
|
||||||
: -static_cast<int64_t>(token_start_internal_value_) - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
double CBORTokenizer::GetDouble() const {
|
|
||||||
assert(token_tag_ == CBORTokenTag::DOUBLE);
|
|
||||||
union {
|
|
||||||
uint64_t from_uint64;
|
|
||||||
double to_double;
|
|
||||||
} reinterpret;
|
|
||||||
reinterpret.from_uint64 = ReadBytesMostSignificantByteFirst<uint64_t>(
|
|
||||||
bytes_.subspan(status_.pos + 1));
|
|
||||||
return reinterpret.to_double;
|
|
||||||
}
|
|
||||||
|
|
||||||
span<uint8_t> CBORTokenizer::GetString8() const {
|
|
||||||
assert(token_tag_ == CBORTokenTag::STRING8);
|
|
||||||
auto length = static_cast<std::ptrdiff_t>(token_start_internal_value_);
|
|
||||||
return bytes_.subspan(status_.pos + (token_byte_length_ - length), length);
|
|
||||||
}
|
|
||||||
|
|
||||||
span<uint8_t> CBORTokenizer::GetString16WireRep() const {
|
|
||||||
assert(token_tag_ == CBORTokenTag::STRING16);
|
|
||||||
auto length = static_cast<std::ptrdiff_t>(token_start_internal_value_);
|
|
||||||
return bytes_.subspan(status_.pos + (token_byte_length_ - length), length);
|
|
||||||
}
|
|
||||||
|
|
||||||
span<uint8_t> CBORTokenizer::GetBinary() const {
|
|
||||||
assert(token_tag_ == CBORTokenTag::BINARY);
|
|
||||||
auto length = static_cast<std::ptrdiff_t>(token_start_internal_value_);
|
|
||||||
return bytes_.subspan(status_.pos + (token_byte_length_ - length), length);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBORTokenizer::ReadNextToken(bool enter_envelope) {
|
|
||||||
if (enter_envelope) {
|
|
||||||
status_.pos += kEncodedEnvelopeHeaderSize;
|
|
||||||
} else {
|
|
||||||
status_.pos =
|
|
||||||
status_.pos == Status::npos() ? 0 : status_.pos + token_byte_length_;
|
|
||||||
}
|
|
||||||
status_.error = Error::OK;
|
|
||||||
if (status_.pos >= bytes_.size()) {
|
|
||||||
token_tag_ = CBORTokenTag::DONE;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
switch (bytes_[status_.pos]) {
|
|
||||||
case kStopByte:
|
|
||||||
SetToken(CBORTokenTag::STOP, 1);
|
|
||||||
return;
|
|
||||||
case kInitialByteIndefiniteLengthMap:
|
|
||||||
SetToken(CBORTokenTag::MAP_START, 1);
|
|
||||||
return;
|
|
||||||
case kInitialByteIndefiniteLengthArray:
|
|
||||||
SetToken(CBORTokenTag::ARRAY_START, 1);
|
|
||||||
return;
|
|
||||||
case kEncodedTrue:
|
|
||||||
SetToken(CBORTokenTag::TRUE_VALUE, 1);
|
|
||||||
return;
|
|
||||||
case kEncodedFalse:
|
|
||||||
SetToken(CBORTokenTag::FALSE_VALUE, 1);
|
|
||||||
return;
|
|
||||||
case kEncodedNull:
|
|
||||||
SetToken(CBORTokenTag::NULL_VALUE, 1);
|
|
||||||
return;
|
|
||||||
case kExpectedConversionToBase64Tag: { // BINARY
|
|
||||||
int8_t bytes_read =
|
|
||||||
ReadTokenStart(bytes_.subspan(status_.pos + 1), &token_start_type_,
|
|
||||||
&token_start_internal_value_);
|
|
||||||
int64_t token_byte_length = 1 + bytes_read + token_start_internal_value_;
|
|
||||||
if (-1 == bytes_read || token_start_type_ != MajorType::BYTE_STRING ||
|
|
||||||
status_.pos + token_byte_length > bytes_.size()) {
|
|
||||||
SetError(Error::CBOR_INVALID_BINARY);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SetToken(CBORTokenTag::BINARY,
|
|
||||||
static_cast<std::ptrdiff_t>(token_byte_length));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case kInitialByteForDouble: { // DOUBLE
|
|
||||||
if (status_.pos + kEncodedDoubleSize > bytes_.size()) {
|
|
||||||
SetError(Error::CBOR_INVALID_DOUBLE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SetToken(CBORTokenTag::DOUBLE, kEncodedDoubleSize);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case kInitialByteForEnvelope: { // ENVELOPE
|
|
||||||
if (status_.pos + kEncodedEnvelopeHeaderSize > bytes_.size()) {
|
|
||||||
SetError(Error::CBOR_INVALID_ENVELOPE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// The envelope must be a byte string with 32 bit length.
|
|
||||||
if (bytes_[status_.pos + 1] != kInitialByteFor32BitLengthByteString) {
|
|
||||||
SetError(Error::CBOR_INVALID_ENVELOPE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Read the length of the byte string.
|
|
||||||
token_start_internal_value_ = ReadBytesMostSignificantByteFirst<uint32_t>(
|
|
||||||
bytes_.subspan(status_.pos + 2));
|
|
||||||
// Make sure the payload is contained within the message.
|
|
||||||
if (token_start_internal_value_ + kEncodedEnvelopeHeaderSize +
|
|
||||||
status_.pos >
|
|
||||||
static_cast<std::size_t>(bytes_.size())) {
|
|
||||||
SetError(Error::CBOR_INVALID_ENVELOPE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto length = static_cast<std::ptrdiff_t>(token_start_internal_value_);
|
|
||||||
SetToken(CBORTokenTag::ENVELOPE,
|
|
||||||
kEncodedEnvelopeHeaderSize + length);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
span<uint8_t> remainder =
|
|
||||||
bytes_.subspan(status_.pos, bytes_.size() - status_.pos);
|
|
||||||
assert(!remainder.empty());
|
|
||||||
int8_t token_start_length = ReadTokenStart(remainder, &token_start_type_,
|
|
||||||
&token_start_internal_value_);
|
|
||||||
bool success = token_start_length != -1;
|
|
||||||
switch (token_start_type_) {
|
|
||||||
case MajorType::UNSIGNED: // INT32.
|
|
||||||
if (!success || std::numeric_limits<int32_t>::max() <
|
|
||||||
token_start_internal_value_) {
|
|
||||||
SetError(Error::CBOR_INVALID_INT32);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SetToken(CBORTokenTag::INT32, token_start_length);
|
|
||||||
return;
|
|
||||||
case MajorType::NEGATIVE: // INT32.
|
|
||||||
if (!success ||
|
|
||||||
std::numeric_limits<int32_t>::min() >
|
|
||||||
-static_cast<int64_t>(token_start_internal_value_) - 1) {
|
|
||||||
SetError(Error::CBOR_INVALID_INT32);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SetToken(CBORTokenTag::INT32, token_start_length);
|
|
||||||
return;
|
|
||||||
case MajorType::STRING: { // STRING8.
|
|
||||||
if (!success || remainder.size() < static_cast<int64_t>(
|
|
||||||
token_start_internal_value_)) {
|
|
||||||
SetError(Error::CBOR_INVALID_STRING8);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto length = static_cast<std::ptrdiff_t>(token_start_internal_value_);
|
|
||||||
SetToken(CBORTokenTag::STRING8, token_start_length + length);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case MajorType::BYTE_STRING: { // STRING16.
|
|
||||||
if (!success ||
|
|
||||||
remainder.size() <
|
|
||||||
static_cast<int64_t>(token_start_internal_value_) ||
|
|
||||||
// Must be divisible by 2 since UTF16 is 2 bytes per character.
|
|
||||||
token_start_internal_value_ & 1) {
|
|
||||||
SetError(Error::CBOR_INVALID_STRING16);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto length = static_cast<std::ptrdiff_t>(token_start_internal_value_);
|
|
||||||
SetToken(CBORTokenTag::STRING16, token_start_length + length);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case MajorType::ARRAY:
|
|
||||||
case MajorType::MAP:
|
|
||||||
case MajorType::TAG:
|
|
||||||
case MajorType::SIMPLE_VALUE:
|
|
||||||
SetError(Error::CBOR_UNSUPPORTED_VALUE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBORTokenizer::SetToken(CBORTokenTag token_tag,
|
|
||||||
std::ptrdiff_t token_byte_length) {
|
|
||||||
token_tag_ = token_tag;
|
|
||||||
token_byte_length_ = token_byte_length;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CBORTokenizer::SetError(Error error) {
|
|
||||||
token_tag_ = CBORTokenTag::ERROR_VALUE;
|
|
||||||
status_.error = error;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
void DumpCBOR(span<uint8_t> cbor) {
|
|
||||||
std::string indent;
|
|
||||||
CBORTokenizer tokenizer(cbor);
|
|
||||||
while (true) {
|
|
||||||
fprintf(stderr, "%s", indent.c_str());
|
|
||||||
switch (tokenizer.TokenTag()) {
|
|
||||||
case CBORTokenTag::ERROR_VALUE:
|
|
||||||
fprintf(stderr, "ERROR {status.error=%d, status.pos=%ld}\n",
|
|
||||||
tokenizer.Status().error, tokenizer.Status().pos);
|
|
||||||
return;
|
|
||||||
case CBORTokenTag::DONE:
|
|
||||||
fprintf(stderr, "DONE\n");
|
|
||||||
return;
|
|
||||||
case CBORTokenTag::TRUE_VALUE:
|
|
||||||
fprintf(stderr, "TRUE_VALUE\n");
|
|
||||||
break;
|
|
||||||
case CBORTokenTag::FALSE_VALUE:
|
|
||||||
fprintf(stderr, "FALSE_VALUE\n");
|
|
||||||
break;
|
|
||||||
case CBORTokenTag::NULL_VALUE:
|
|
||||||
fprintf(stderr, "NULL_VALUE\n");
|
|
||||||
break;
|
|
||||||
case CBORTokenTag::INT32:
|
|
||||||
fprintf(stderr, "INT32 [%d]\n", tokenizer.GetInt32());
|
|
||||||
break;
|
|
||||||
case CBORTokenTag::DOUBLE:
|
|
||||||
fprintf(stderr, "DOUBLE [%lf]\n", tokenizer.GetDouble());
|
|
||||||
break;
|
|
||||||
case CBORTokenTag::STRING8: {
|
|
||||||
span<uint8_t> v = tokenizer.GetString8();
|
|
||||||
std::string t(v.begin(), v.end());
|
|
||||||
fprintf(stderr, "STRING8 [%s]\n", t.c_str());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CBORTokenTag::STRING16: {
|
|
||||||
span<uint8_t> v = tokenizer.GetString16WireRep();
|
|
||||||
std::string t(v.begin(), v.end());
|
|
||||||
fprintf(stderr, "STRING16 [%s]\n", t.c_str());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CBORTokenTag::BINARY: {
|
|
||||||
span<uint8_t> v = tokenizer.GetBinary();
|
|
||||||
std::string t(v.begin(), v.end());
|
|
||||||
fprintf(stderr, "BINARY [%s]\n", t.c_str());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CBORTokenTag::MAP_START:
|
|
||||||
fprintf(stderr, "MAP_START\n");
|
|
||||||
indent += " ";
|
|
||||||
break;
|
|
||||||
case CBORTokenTag::ARRAY_START:
|
|
||||||
fprintf(stderr, "ARRAY_START\n");
|
|
||||||
indent += " ";
|
|
||||||
break;
|
|
||||||
case CBORTokenTag::STOP:
|
|
||||||
fprintf(stderr, "STOP\n");
|
|
||||||
indent.erase(0, 2);
|
|
||||||
break;
|
|
||||||
case CBORTokenTag::ENVELOPE:
|
|
||||||
fprintf(stderr, "ENVELOPE\n");
|
|
||||||
tokenizer.EnterEnvelope();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
tokenizer.Next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
{% for namespace in config.protocol.namespace %}
|
|
||||||
} // namespace {{namespace}}
|
|
||||||
{% endfor %}
|
|
@ -1,416 +0,0 @@
|
|||||||
{# 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)
|
|
@ -1,43 +0,0 @@
|
|||||||
// Copyright 2016 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)}}_Collections_h
|
|
||||||
#define {{"_".join(config.protocol.namespace)}}_Collections_h
|
|
||||||
|
|
||||||
#include {{format_include(config.protocol.package, "Forward")}}
|
|
||||||
#include <cstddef>
|
|
||||||
|
|
||||||
#if defined(__APPLE__) && !defined(_LIBCPP_VERSION)
|
|
||||||
#include <map>
|
|
||||||
#include <set>
|
|
||||||
|
|
||||||
{% for namespace in config.protocol.namespace %}
|
|
||||||
namespace {{namespace}} {
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
template <class Key, class T> using HashMap = std::map<Key, T>;
|
|
||||||
template <class Key> using HashSet = std::set<Key>;
|
|
||||||
|
|
||||||
{% for namespace in config.protocol.namespace %}
|
|
||||||
} // namespace {{namespace}}
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
#else
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <unordered_set>
|
|
||||||
|
|
||||||
{% for namespace in config.protocol.namespace %}
|
|
||||||
namespace {{namespace}} {
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
template <class Key, class T> using HashMap = std::unordered_map<Key, T>;
|
|
||||||
template <class Key> using HashSet = std::unordered_set<Key>;
|
|
||||||
|
|
||||||
{% for namespace in config.protocol.namespace %}
|
|
||||||
} // namespace {{namespace}}
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
#endif // defined(__APPLE__) && !defined(_LIBCPP_VERSION)
|
|
||||||
|
|
||||||
#endif // !defined({{"_".join(config.protocol.namespace)}}_Collections_h)
|
|
@ -66,21 +66,21 @@ static constexpr int kStackLimitValues = 1000;
|
|||||||
|
|
||||||
// Below are three parsing routines for CBOR, which cover enough
|
// Below are three parsing routines for CBOR, which cover enough
|
||||||
// to roundtrip JSON messages.
|
// to roundtrip JSON messages.
|
||||||
std::unique_ptr<DictionaryValue> parseMap(int32_t stack_depth, CBORTokenizer* tokenizer);
|
std::unique_ptr<DictionaryValue> parseMap(int32_t stack_depth, cbor::CBORTokenizer* tokenizer);
|
||||||
std::unique_ptr<ListValue> parseArray(int32_t stack_depth, CBORTokenizer* tokenizer);
|
std::unique_ptr<ListValue> parseArray(int32_t stack_depth, cbor::CBORTokenizer* tokenizer);
|
||||||
std::unique_ptr<Value> parseValue(int32_t stack_depth, CBORTokenizer* tokenizer);
|
std::unique_ptr<Value> parseValue(int32_t stack_depth, cbor::CBORTokenizer* tokenizer);
|
||||||
|
|
||||||
// |bytes| must start with the indefinite length array byte, so basically,
|
// |bytes| must start with the indefinite length array byte, so basically,
|
||||||
// ParseArray may only be called after an indefinite length array has been
|
// ParseArray may only be called after an indefinite length array has been
|
||||||
// detected.
|
// detected.
|
||||||
std::unique_ptr<ListValue> parseArray(int32_t stack_depth, CBORTokenizer* tokenizer) {
|
std::unique_ptr<ListValue> parseArray(int32_t stack_depth, cbor::CBORTokenizer* tokenizer) {
|
||||||
DCHECK(tokenizer->TokenTag() == CBORTokenTag::ARRAY_START);
|
DCHECK(tokenizer->TokenTag() == cbor::CBORTokenTag::ARRAY_START);
|
||||||
tokenizer->Next();
|
tokenizer->Next();
|
||||||
auto list = ListValue::create();
|
auto list = ListValue::create();
|
||||||
while (tokenizer->TokenTag() != CBORTokenTag::STOP) {
|
while (tokenizer->TokenTag() != cbor::CBORTokenTag::STOP) {
|
||||||
// Error::CBOR_UNEXPECTED_EOF_IN_ARRAY
|
// Error::CBOR_UNEXPECTED_EOF_IN_ARRAY
|
||||||
if (tokenizer->TokenTag() == CBORTokenTag::DONE) return nullptr;
|
if (tokenizer->TokenTag() == cbor::CBORTokenTag::DONE) return nullptr;
|
||||||
if (tokenizer->TokenTag() == CBORTokenTag::ERROR_VALUE) return nullptr;
|
if (tokenizer->TokenTag() == cbor::CBORTokenTag::ERROR_VALUE) return nullptr;
|
||||||
// Parse value.
|
// Parse value.
|
||||||
auto value = parseValue(stack_depth, tokenizer);
|
auto value = parseValue(stack_depth, tokenizer);
|
||||||
if (!value) return nullptr;
|
if (!value) return nullptr;
|
||||||
@ -91,60 +91,66 @@ std::unique_ptr<ListValue> parseArray(int32_t stack_depth, CBORTokenizer* tokeni
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Value> parseValue(
|
std::unique_ptr<Value> parseValue(
|
||||||
int32_t stack_depth, CBORTokenizer* tokenizer) {
|
int32_t stack_depth, cbor::CBORTokenizer* tokenizer) {
|
||||||
// Error::CBOR_STACK_LIMIT_EXCEEDED
|
// Error::CBOR_STACK_LIMIT_EXCEEDED
|
||||||
if (stack_depth > kStackLimitValues) return nullptr;
|
if (stack_depth > kStackLimitValues) return nullptr;
|
||||||
// Skip past the envelope to get to what's inside.
|
// Skip past the envelope to get to what's inside.
|
||||||
if (tokenizer->TokenTag() == CBORTokenTag::ENVELOPE)
|
if (tokenizer->TokenTag() == cbor::CBORTokenTag::ENVELOPE)
|
||||||
tokenizer->EnterEnvelope();
|
tokenizer->EnterEnvelope();
|
||||||
switch (tokenizer->TokenTag()) {
|
switch (tokenizer->TokenTag()) {
|
||||||
case CBORTokenTag::ERROR_VALUE:
|
case cbor::CBORTokenTag::ERROR_VALUE:
|
||||||
return nullptr;
|
return nullptr;
|
||||||
case CBORTokenTag::DONE:
|
case cbor::CBORTokenTag::DONE:
|
||||||
// Error::CBOR_UNEXPECTED_EOF_EXPECTED_VALUE
|
// Error::CBOR_UNEXPECTED_EOF_EXPECTED_VALUE
|
||||||
return nullptr;
|
return nullptr;
|
||||||
case CBORTokenTag::TRUE_VALUE: {
|
case cbor::CBORTokenTag::TRUE_VALUE: {
|
||||||
std::unique_ptr<Value> value = FundamentalValue::create(true);
|
std::unique_ptr<Value> value = FundamentalValue::create(true);
|
||||||
tokenizer->Next();
|
tokenizer->Next();
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
case CBORTokenTag::FALSE_VALUE: {
|
case cbor::CBORTokenTag::FALSE_VALUE: {
|
||||||
std::unique_ptr<Value> value = FundamentalValue::create(false);
|
std::unique_ptr<Value> value = FundamentalValue::create(false);
|
||||||
tokenizer->Next();
|
tokenizer->Next();
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
case CBORTokenTag::NULL_VALUE: {
|
case cbor::CBORTokenTag::NULL_VALUE: {
|
||||||
std::unique_ptr<Value> value = FundamentalValue::null();
|
std::unique_ptr<Value> value = FundamentalValue::null();
|
||||||
tokenizer->Next();
|
tokenizer->Next();
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
case CBORTokenTag::INT32: {
|
case cbor::CBORTokenTag::INT32: {
|
||||||
std::unique_ptr<Value> value = FundamentalValue::create(tokenizer->GetInt32());
|
std::unique_ptr<Value> value = FundamentalValue::create(tokenizer->GetInt32());
|
||||||
tokenizer->Next();
|
tokenizer->Next();
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
case CBORTokenTag::DOUBLE: {
|
case cbor::CBORTokenTag::DOUBLE: {
|
||||||
std::unique_ptr<Value> value = FundamentalValue::create(tokenizer->GetDouble());
|
std::unique_ptr<Value> value = FundamentalValue::create(tokenizer->GetDouble());
|
||||||
tokenizer->Next();
|
tokenizer->Next();
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
case CBORTokenTag::STRING8: {
|
case cbor::CBORTokenTag::STRING8: {
|
||||||
span<uint8_t> str = tokenizer->GetString8();
|
span<uint8_t> str = tokenizer->GetString8();
|
||||||
std::unique_ptr<Value> value = StringValue::create(StringUtil::fromUTF8(str.data(), str.size()));
|
std::unique_ptr<Value> value =
|
||||||
|
StringValue::create(StringUtil::fromUTF8(str.data(), str.size()));
|
||||||
tokenizer->Next();
|
tokenizer->Next();
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
case CBORTokenTag::STRING16:
|
case cbor::CBORTokenTag::STRING16: {
|
||||||
// NOT SUPPORTED YET.
|
span<uint8_t> wire = tokenizer->GetString16WireRep();
|
||||||
return nullptr;
|
DCHECK_EQ(wire.size() & 1, 0u);
|
||||||
case CBORTokenTag::BINARY: {
|
std::unique_ptr<Value> value = StringValue::create(StringUtil::fromUTF16(
|
||||||
|
reinterpret_cast<const uint16_t*>(wire.data()), wire.size() / 2));
|
||||||
|
tokenizer->Next();
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
case cbor::CBORTokenTag::BINARY: {
|
||||||
span<uint8_t> payload = tokenizer->GetBinary();
|
span<uint8_t> payload = tokenizer->GetBinary();
|
||||||
tokenizer->Next();
|
tokenizer->Next();
|
||||||
return BinaryValue::create(Binary::fromSpan(payload.data(), payload.size()));
|
return BinaryValue::create(Binary::fromSpan(payload.data(), payload.size()));
|
||||||
}
|
}
|
||||||
case CBORTokenTag::MAP_START:
|
case cbor::CBORTokenTag::MAP_START:
|
||||||
return parseMap(stack_depth + 1, tokenizer);
|
return parseMap(stack_depth + 1, tokenizer);
|
||||||
case CBORTokenTag::ARRAY_START:
|
case cbor::CBORTokenTag::ARRAY_START:
|
||||||
return parseArray(stack_depth + 1, tokenizer);
|
return parseArray(stack_depth + 1, tokenizer);
|
||||||
default:
|
default:
|
||||||
// Error::CBOR_UNSUPPORTED_VALUE
|
// Error::CBOR_UNSUPPORTED_VALUE
|
||||||
@ -156,22 +162,22 @@ std::unique_ptr<Value> parseValue(
|
|||||||
// ParseArray may only be called after an indefinite length array has been
|
// ParseArray may only be called after an indefinite length array has been
|
||||||
// detected.
|
// detected.
|
||||||
std::unique_ptr<DictionaryValue> parseMap(
|
std::unique_ptr<DictionaryValue> parseMap(
|
||||||
int32_t stack_depth, CBORTokenizer* tokenizer) {
|
int32_t stack_depth, cbor::CBORTokenizer* tokenizer) {
|
||||||
auto dict = DictionaryValue::create();
|
auto dict = DictionaryValue::create();
|
||||||
tokenizer->Next();
|
tokenizer->Next();
|
||||||
while (tokenizer->TokenTag() != CBORTokenTag::STOP) {
|
while (tokenizer->TokenTag() != cbor::CBORTokenTag::STOP) {
|
||||||
if (tokenizer->TokenTag() == CBORTokenTag::DONE) {
|
if (tokenizer->TokenTag() == cbor::CBORTokenTag::DONE) {
|
||||||
// Error::CBOR_UNEXPECTED_EOF_IN_MAP
|
// Error::CBOR_UNEXPECTED_EOF_IN_MAP
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (tokenizer->TokenTag() == CBORTokenTag::ERROR_VALUE) return nullptr;
|
if (tokenizer->TokenTag() == cbor::CBORTokenTag::ERROR_VALUE) return nullptr;
|
||||||
// Parse key.
|
// Parse key.
|
||||||
String key;
|
String key;
|
||||||
if (tokenizer->TokenTag() == CBORTokenTag::STRING8) {
|
if (tokenizer->TokenTag() == cbor::CBORTokenTag::STRING8) {
|
||||||
span<uint8_t> key_span = tokenizer->GetString8();
|
span<uint8_t> key_span = tokenizer->GetString8();
|
||||||
key = StringUtil::fromUTF8(key_span.data(), key_span.size());
|
key = StringUtil::fromUTF8(key_span.data(), key_span.size());
|
||||||
tokenizer->Next();
|
tokenizer->Next();
|
||||||
} else if (tokenizer->TokenTag() == CBORTokenTag::STRING16) {
|
} else if (tokenizer->TokenTag() == cbor::CBORTokenTag::STRING16) {
|
||||||
return nullptr; // STRING16 not supported yet.
|
return nullptr; // STRING16 not supported yet.
|
||||||
} else {
|
} else {
|
||||||
// Error::CBOR_INVALID_MAP_KEY
|
// Error::CBOR_INVALID_MAP_KEY
|
||||||
@ -196,22 +202,21 @@ std::unique_ptr<Value> Value::parseBinary(const uint8_t* data, size_t size) {
|
|||||||
if (bytes.empty()) return nullptr;
|
if (bytes.empty()) return nullptr;
|
||||||
|
|
||||||
// Error::CBOR_INVALID_START_BYTE
|
// Error::CBOR_INVALID_START_BYTE
|
||||||
// TODO(johannes): EncodeInitialByteForEnvelope() method.
|
if (bytes[0] != cbor::InitialByteForEnvelope()) return nullptr;
|
||||||
if (bytes[0] != 0xd8) return nullptr;
|
|
||||||
|
|
||||||
CBORTokenizer tokenizer(bytes);
|
cbor::CBORTokenizer tokenizer(bytes);
|
||||||
if (tokenizer.TokenTag() == CBORTokenTag::ERROR_VALUE) return nullptr;
|
if (tokenizer.TokenTag() == cbor::CBORTokenTag::ERROR_VALUE) return nullptr;
|
||||||
|
|
||||||
// We checked for the envelope start byte above, so the tokenizer
|
// We checked for the envelope start byte above, so the tokenizer
|
||||||
// must agree here, since it's not an error.
|
// must agree here, since it's not an error.
|
||||||
DCHECK(tokenizer.TokenTag() == CBORTokenTag::ENVELOPE);
|
DCHECK(tokenizer.TokenTag() == cbor::CBORTokenTag::ENVELOPE);
|
||||||
tokenizer.EnterEnvelope();
|
tokenizer.EnterEnvelope();
|
||||||
// Error::MAP_START_EXPECTED
|
// Error::MAP_START_EXPECTED
|
||||||
if (tokenizer.TokenTag() != CBORTokenTag::MAP_START) return nullptr;
|
if (tokenizer.TokenTag() != cbor::CBORTokenTag::MAP_START) return nullptr;
|
||||||
std::unique_ptr<Value> result = parseMap(/*stack_depth=*/1, &tokenizer);
|
std::unique_ptr<Value> result = parseMap(/*stack_depth=*/1, &tokenizer);
|
||||||
if (!result) return nullptr;
|
if (!result) return nullptr;
|
||||||
if (tokenizer.TokenTag() == CBORTokenTag::DONE) return result;
|
if (tokenizer.TokenTag() == cbor::CBORTokenTag::DONE) return result;
|
||||||
if (tokenizer.TokenTag() == CBORTokenTag::ERROR_VALUE) return nullptr;
|
if (tokenizer.TokenTag() == cbor::CBORTokenTag::ERROR_VALUE) return nullptr;
|
||||||
// Error::CBOR_TRAILING_JUNK
|
// Error::CBOR_TRAILING_JUNK
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@ -249,7 +254,7 @@ void Value::writeJSON(StringBuilder* output) const
|
|||||||
|
|
||||||
void Value::writeBinary(std::vector<uint8_t>* bytes) const {
|
void Value::writeBinary(std::vector<uint8_t>* bytes) const {
|
||||||
DCHECK(m_type == TypeNull);
|
DCHECK(m_type == TypeNull);
|
||||||
bytes->push_back(EncodeNull());
|
bytes->push_back(cbor::EncodeNull());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Value> Value::clone() const
|
std::unique_ptr<Value> Value::clone() const
|
||||||
@ -326,13 +331,13 @@ void FundamentalValue::writeJSON(StringBuilder* output) const
|
|||||||
void FundamentalValue::writeBinary(std::vector<uint8_t>* bytes) const {
|
void FundamentalValue::writeBinary(std::vector<uint8_t>* bytes) const {
|
||||||
switch (type()) {
|
switch (type()) {
|
||||||
case TypeDouble:
|
case TypeDouble:
|
||||||
EncodeDouble(m_doubleValue, bytes);
|
cbor::EncodeDouble(m_doubleValue, bytes);
|
||||||
return;
|
return;
|
||||||
case TypeInteger:
|
case TypeInteger:
|
||||||
EncodeInt32(m_integerValue, bytes);
|
cbor::EncodeInt32(m_integerValue, bytes);
|
||||||
return;
|
return;
|
||||||
case TypeBoolean:
|
case TypeBoolean:
|
||||||
bytes->push_back(m_boolValue ? EncodeTrue() : EncodeFalse());
|
bytes->push_back(m_boolValue ? cbor::EncodeTrue() : cbor::EncodeFalse());
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
DCHECK(false);
|
DCHECK(false);
|
||||||
@ -363,10 +368,37 @@ void StringValue::writeJSON(StringBuilder* output) const
|
|||||||
StringUtil::builderAppendQuotedString(*output, m_stringValue);
|
StringUtil::builderAppendQuotedString(*output, m_stringValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// This routine distinguishes between the current encoding for a given
|
||||||
|
// string |s|, and calls encoding routines that will
|
||||||
|
// - Ensure that all ASCII strings end up being encoded as UTF8 in
|
||||||
|
// the wire format - e.g., EncodeFromUTF16 will detect ASCII and
|
||||||
|
// do the (trivial) transcode to STRING8 on the wire, but if it's
|
||||||
|
// not ASCII it'll do STRING16.
|
||||||
|
// - Select a format that's cheap to convert to. E.g., we don't
|
||||||
|
// have LATIN1 on the wire, so we call EncodeFromLatin1 which
|
||||||
|
// transcodes to UTF8 if needed.
|
||||||
|
void EncodeString(const String& s, std::vector<uint8_t>* out) {
|
||||||
|
if (StringUtil::CharacterCount(s) == 0) {
|
||||||
|
cbor::EncodeString8(span<uint8_t>(nullptr, 0), out); // Empty string.
|
||||||
|
} else if (StringUtil::CharactersLatin1(s)) {
|
||||||
|
cbor::EncodeFromLatin1(span<uint8_t>(StringUtil::CharactersLatin1(s),
|
||||||
|
StringUtil::CharacterCount(s)),
|
||||||
|
out);
|
||||||
|
} else if (StringUtil::CharactersUTF16(s)) {
|
||||||
|
cbor::EncodeFromUTF16(span<uint16_t>(StringUtil::CharactersUTF16(s),
|
||||||
|
StringUtil::CharacterCount(s)),
|
||||||
|
out);
|
||||||
|
} else if (StringUtil::CharactersUTF8(s)) {
|
||||||
|
cbor::EncodeString8(span<uint8_t>(StringUtil::CharactersUTF8(s),
|
||||||
|
StringUtil::CharacterCount(s)),
|
||||||
|
out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
void StringValue::writeBinary(std::vector<uint8_t>* bytes) const {
|
void StringValue::writeBinary(std::vector<uint8_t>* bytes) const {
|
||||||
StringUTF8Adapter utf8(m_stringValue);
|
EncodeString(m_stringValue, bytes);
|
||||||
EncodeString8(span<uint8_t>(reinterpret_cast<const uint8_t*>(utf8.Data()),
|
|
||||||
utf8.length()), bytes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Value> StringValue::clone() const
|
std::unique_ptr<Value> StringValue::clone() const
|
||||||
@ -387,7 +419,8 @@ void BinaryValue::writeJSON(StringBuilder* output) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BinaryValue::writeBinary(std::vector<uint8_t>* bytes) const {
|
void BinaryValue::writeBinary(std::vector<uint8_t>* bytes) const {
|
||||||
EncodeBinary(span<uint8_t>(m_binaryValue.data(), m_binaryValue.size()), bytes);
|
cbor::EncodeBinary(span<uint8_t>(m_binaryValue.data(),
|
||||||
|
m_binaryValue.size()), bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Value> BinaryValue::clone() const
|
std::unique_ptr<Value> BinaryValue::clone() const
|
||||||
@ -550,19 +583,17 @@ void DictionaryValue::writeJSON(StringBuilder* output) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DictionaryValue::writeBinary(std::vector<uint8_t>* bytes) const {
|
void DictionaryValue::writeBinary(std::vector<uint8_t>* bytes) const {
|
||||||
EnvelopeEncoder encoder;
|
cbor::EnvelopeEncoder encoder;
|
||||||
encoder.EncodeStart(bytes);
|
encoder.EncodeStart(bytes);
|
||||||
bytes->push_back(EncodeIndefiniteLengthMapStart());
|
bytes->push_back(cbor::EncodeIndefiniteLengthMapStart());
|
||||||
for (size_t i = 0; i < m_order.size(); ++i) {
|
for (size_t i = 0; i < m_order.size(); ++i) {
|
||||||
const String& key = m_order[i];
|
const String& key = m_order[i];
|
||||||
Dictionary::const_iterator value = m_data.find(key);
|
Dictionary::const_iterator value = m_data.find(key);
|
||||||
DCHECK(value != m_data.cend() && value->second);
|
DCHECK(value != m_data.cend() && value->second);
|
||||||
StringUTF8Adapter utf8(key);
|
EncodeString(key, bytes);
|
||||||
EncodeString8(span<uint8_t>(reinterpret_cast<const uint8_t*>(utf8.Data()),
|
|
||||||
utf8.length()), bytes);
|
|
||||||
value->second->writeBinary(bytes);
|
value->second->writeBinary(bytes);
|
||||||
}
|
}
|
||||||
bytes->push_back(EncodeStop());
|
bytes->push_back(cbor::EncodeStop());
|
||||||
encoder.EncodeStop(bytes);
|
encoder.EncodeStop(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -601,13 +632,13 @@ void ListValue::writeJSON(StringBuilder* output) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ListValue::writeBinary(std::vector<uint8_t>* bytes) const {
|
void ListValue::writeBinary(std::vector<uint8_t>* bytes) const {
|
||||||
EnvelopeEncoder encoder;
|
cbor::EnvelopeEncoder encoder;
|
||||||
encoder.EncodeStart(bytes);
|
encoder.EncodeStart(bytes);
|
||||||
bytes->push_back(EncodeIndefiniteLengthArrayStart());
|
bytes->push_back(cbor::EncodeIndefiniteLengthArrayStart());
|
||||||
for (size_t i = 0; i < m_data.size(); ++i) {
|
for (size_t i = 0; i < m_data.size(); ++i) {
|
||||||
m_data[i]->writeBinary(bytes);
|
m_data[i]->writeBinary(bytes);
|
||||||
}
|
}
|
||||||
bytes->push_back(EncodeStop());
|
bytes->push_back(cbor::EncodeStop());
|
||||||
encoder.EncodeStop(bytes);
|
encoder.EncodeStop(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,7 +136,7 @@ std::unique_ptr<Value> StringUtil::parseMessage(
|
|||||||
reinterpret_cast<const uint8_t*>(message.data()),
|
reinterpret_cast<const uint8_t*>(message.data()),
|
||||||
message.length());
|
message.length());
|
||||||
}
|
}
|
||||||
std::unique_ptr<base::Value> value = base::JSONReader::Read(message);
|
std::unique_ptr<base::Value> value = base::JSONReader::ReadDeprecated(message);
|
||||||
return toProtocolValue(value.get(), 1000);
|
return toProtocolValue(value.get(), 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,6 +185,13 @@ void StringBuilder::reserveCapacity(size_t capacity) {
|
|||||||
string_.reserve(capacity);
|
string_.reserve(capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
String StringUtil::fromUTF16(const uint16_t* data, size_t length) {
|
||||||
|
std::string utf8;
|
||||||
|
base::UTF16ToUTF8(reinterpret_cast<const base::char16*>(data), length, &utf8);
|
||||||
|
return utf8;
|
||||||
|
}
|
||||||
|
|
||||||
Binary::Binary() : bytes_(new base::RefCountedBytes) {}
|
Binary::Binary() : bytes_(new base::RefCountedBytes) {}
|
||||||
Binary::Binary(const Binary& binary) : bytes_(binary.bytes_) {}
|
Binary::Binary(const Binary& binary) : bytes_(binary.bytes_) {}
|
||||||
Binary::Binary(scoped_refptr<base::RefCountedMemory> bytes) : bytes_(bytes) {}
|
Binary::Binary(scoped_refptr<base::RefCountedMemory> bytes) : bytes_(bytes) {}
|
||||||
@ -230,75 +237,6 @@ Binary Binary::fromSpan(const uint8_t* data, size_t size) {
|
|||||||
new base::RefCountedBytes(data, size)));
|
new base::RefCountedBytes(data, size)));
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
|
||||||
int32_t ReadEnvelopeSize(const uint8_t* in) {
|
|
||||||
return (in[0] << 24) + (in[1] << 16) + (in[2] << 8) + in[3];
|
|
||||||
}
|
|
||||||
|
|
||||||
void WriteEnvelopeSize(uint32_t value, uint8_t* out) {
|
|
||||||
*(out++) = (value >> 24) & 0xFF;
|
|
||||||
*(out++) = (value >> 16) & 0xFF;
|
|
||||||
*(out++) = (value >> 8) & 0xFF;
|
|
||||||
*(out++) = (value) & 0xFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AppendStringValueToMapBinary(base::StringPiece in,
|
|
||||||
base::StringPiece key, base::StringPiece value, std::string* out) {
|
|
||||||
if (in.size() < 1 + 1 + 4 + 1 + 1)
|
|
||||||
return false;
|
|
||||||
const uint8_t* envelope = reinterpret_cast<const uint8_t*>(in.data());
|
|
||||||
if (cbor::kInitialByteForEnvelope != envelope[0])
|
|
||||||
return false;
|
|
||||||
if (cbor::kInitialByteFor32BitLengthByteString != envelope[1])
|
|
||||||
return false;
|
|
||||||
if (cbor::kInitialByteIndefiniteLengthMap != envelope[6])
|
|
||||||
return false;
|
|
||||||
|
|
||||||
uint32_t envelope_size = ReadEnvelopeSize(envelope + 2);
|
|
||||||
if (envelope_size + 2 + 4 != in.size())
|
|
||||||
return false;
|
|
||||||
if (cbor::kStopByte != static_cast<uint8_t>(*in.rbegin()))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
std::vector<uint8_t> encoded_entry;
|
|
||||||
encoded_entry.reserve(1 + 4 + key.size() + 1 + 4 + value.size());
|
|
||||||
span<uint8_t> key_span(
|
|
||||||
reinterpret_cast<const uint8_t*>(key.data()), key.size());
|
|
||||||
EncodeString8(key_span, &encoded_entry);
|
|
||||||
span<uint8_t> value_span(
|
|
||||||
reinterpret_cast<const uint8_t*>(value.data()), value.size());
|
|
||||||
EncodeString8(value_span, &encoded_entry);
|
|
||||||
|
|
||||||
out->clear();
|
|
||||||
out->reserve(in.size() + encoded_entry.size());
|
|
||||||
out->append(in.begin(), in.end() - 1);
|
|
||||||
out->append(reinterpret_cast<const char*>(encoded_entry.data()),
|
|
||||||
encoded_entry.size());
|
|
||||||
out->append(1, static_cast<char>(cbor::kStopByte));
|
|
||||||
std::size_t new_size = envelope_size + out->size() - in.size();
|
|
||||||
if (new_size > static_cast<std::size_t>(
|
|
||||||
std::numeric_limits<uint32_t>::max())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
WriteEnvelopeSize(new_size, reinterpret_cast<uint8_t*>(&*out->begin() + 2));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AppendStringValueToMapJSON(base::StringPiece in,
|
|
||||||
base::StringPiece key, base::StringPiece value, std::string* out) {
|
|
||||||
if (!in.length() || *in.rbegin() != '}')
|
|
||||||
return false;
|
|
||||||
std::string suffix =
|
|
||||||
base::StringPrintf(", \"%s\": \"%s\"}", key.begin(), value.begin());
|
|
||||||
out->clear();
|
|
||||||
out->reserve(in.length() + suffix.length() - 1);
|
|
||||||
out->append(in.data(), in.length() - 1);
|
|
||||||
out->append(suffix);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
{% for namespace in config.protocol.namespace %}
|
{% for namespace in config.protocol.namespace %}
|
||||||
} // namespace {{namespace}}
|
} // namespace {{namespace}}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -32,16 +32,6 @@ class Value;
|
|||||||
using String = std::string;
|
using String = std::string;
|
||||||
using ProtocolMessage = std::string;
|
using ProtocolMessage = std::string;
|
||||||
|
|
||||||
class {{config.lib.export_macro}} StringUTF8Adapter {
|
|
||||||
public:
|
|
||||||
StringUTF8Adapter(const std::string& string) : string_(string) { }
|
|
||||||
const char* Data() const { return string_.data(); }
|
|
||||||
size_t length() const { return string_.length(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
const std::string& string_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class {{config.lib.export_macro}} StringBuilder {
|
class {{config.lib.export_macro}} StringBuilder {
|
||||||
public:
|
public:
|
||||||
StringBuilder();
|
StringBuilder();
|
||||||
@ -109,6 +99,15 @@ class {{config.lib.export_macro}} StringUtil {
|
|||||||
static String fromUTF8(const uint8_t* data, size_t length) {
|
static String fromUTF8(const uint8_t* data, size_t length) {
|
||||||
return std::string(reinterpret_cast<const char*>(data), length);
|
return std::string(reinterpret_cast<const char*>(data), length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static String fromUTF16(const uint16_t* data, size_t length);
|
||||||
|
|
||||||
|
static const uint8_t* CharactersLatin1(const String& s) { return nullptr; }
|
||||||
|
static const uint8_t* CharactersUTF8(const String& s) {
|
||||||
|
return reinterpret_cast<const uint8_t*>(s.data());
|
||||||
|
}
|
||||||
|
static const uint16_t* CharactersUTF16(const String& s) { return nullptr; }
|
||||||
|
static size_t CharacterCount(const String& s) { return s.size(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
// A read-only sequence of uninterpreted bytes with reference-counted storage.
|
// A read-only sequence of uninterpreted bytes with reference-counted storage.
|
||||||
@ -137,12 +136,6 @@ class {{config.lib.export_macro}} Binary {
|
|||||||
|
|
||||||
std::unique_ptr<Value> toProtocolValue(const base::Value* value, int depth);
|
std::unique_ptr<Value> toProtocolValue(const base::Value* value, int depth);
|
||||||
std::unique_ptr<base::Value> toBaseValue(Value* value, int depth);
|
std::unique_ptr<base::Value> toBaseValue(Value* value, int depth);
|
||||||
|
|
||||||
bool AppendStringValueToMapBinary(base::StringPiece in,
|
|
||||||
base::StringPiece key, base::StringPiece value, std::string* out);
|
|
||||||
bool AppendStringValueToMapJSON(base::StringPiece in,
|
|
||||||
base::StringPiece key, base::StringPiece value, std::string* out);
|
|
||||||
|
|
||||||
{% for namespace in config.protocol.namespace %}
|
{% for namespace in config.protocol.namespace %}
|
||||||
} // namespace {{namespace}}
|
} // namespace {{namespace}}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
2199
tools/inspector_protocol/lib/encoding_cpp.template
Normal file
2199
tools/inspector_protocol/lib/encoding_cpp.template
Normal file
File diff suppressed because it is too large
Load Diff
520
tools/inspector_protocol/lib/encoding_h.template
Normal file
520
tools/inspector_protocol/lib/encoding_h.template
Normal file
@ -0,0 +1,520 @@
|
|||||||
|
{# This template is generated by gen_cbor_templates.py. #}
|
||||||
|
// Generated by lib/encoding_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)}}_encoding_h
|
||||||
|
#define {{"_".join(config.protocol.namespace)}}_encoding_h
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
#include <limits>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
{% for namespace in config.protocol.namespace %}
|
||||||
|
namespace {{namespace}} {
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
// ===== encoding/encoding.h =====
|
||||||
|
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// span - sequence of bytes
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
// This template is similar to std::span, which will be included in C++20.
|
||||||
|
template <typename T>
|
||||||
|
class span {
|
||||||
|
public:
|
||||||
|
using index_type = size_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_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
span<T> SpanFrom(const std::vector<T>& v) {
|
||||||
|
return span<T>(v.data(), v.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t N>
|
||||||
|
span<uint8_t> SpanFrom(const char (&str)[N]) {
|
||||||
|
return span<uint8_t>(reinterpret_cast<const uint8_t*>(str), N - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline span<uint8_t> SpanFrom(const char* str) {
|
||||||
|
return str ? span<uint8_t>(reinterpret_cast<const uint8_t*>(str), strlen(str))
|
||||||
|
: span<uint8_t>();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline span<uint8_t> SpanFrom(const std::string& v) {
|
||||||
|
return span<uint8_t>(reinterpret_cast<const uint8_t*>(v.data()), v.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Status and 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_MAP_END = 0x0b,
|
||||||
|
JSON_PARSER_COMMA_OR_MAP_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_TRAILING_JUNK = 0x1c,
|
||||||
|
CBOR_MAP_START_EXPECTED = 0x1d,
|
||||||
|
CBOR_MAP_STOP_EXPECTED = 0x1e,
|
||||||
|
CBOR_ENVELOPE_SIZE_LIMIT_EXCEEDED = 0x1f,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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 size_t npos() { return std::numeric_limits<size_t>::max(); }
|
||||||
|
|
||||||
|
bool ok() const { return error == Error::OK; }
|
||||||
|
|
||||||
|
Error error = Error::OK;
|
||||||
|
size_t pos = npos();
|
||||||
|
Status(Error error, size_t pos) : error(error), pos(pos) {}
|
||||||
|
Status() = default;
|
||||||
|
|
||||||
|
// Returns a 7 bit US-ASCII string, either "OK" or an error message
|
||||||
|
// that includes the position.
|
||||||
|
std::string ToASCIIString() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string ToASCIIString(const char* msg) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handler interface for parser events emitted by a streaming parser.
|
||||||
|
// See cbor::NewCBOREncoder, cbor::ParseCBOR, json::NewJSONEncoder,
|
||||||
|
// json::ParseJSON.
|
||||||
|
class StreamingParserHandler {
|
||||||
|
public:
|
||||||
|
virtual ~StreamingParserHandler() = default;
|
||||||
|
virtual void HandleMapBegin() = 0;
|
||||||
|
virtual void HandleMapEnd() = 0;
|
||||||
|
virtual void HandleArrayBegin() = 0;
|
||||||
|
virtual void HandleArrayEnd() = 0;
|
||||||
|
virtual void HandleString8(span<uint8_t> chars) = 0;
|
||||||
|
virtual void HandleString16(span<uint16_t> chars) = 0;
|
||||||
|
virtual void HandleBinary(span<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;
|
||||||
|
};
|
||||||
|
|
||||||
|
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) are supported.
|
||||||
|
// - 7 bit US-ASCII strings must always be encoded as UTF8 strings, never
|
||||||
|
// as UTF16 strings.
|
||||||
|
// - 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.
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Detecting CBOR content
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
// The first byte for an envelope, which we use for wrapping dictionaries
|
||||||
|
// and arrays; and the byte that indicates a byte string with 32 bit length.
|
||||||
|
// These two bytes start an envelope, and thereby also any CBOR message
|
||||||
|
// produced or consumed by this protocol. See also |EnvelopeEncoder| below.
|
||||||
|
uint8_t InitialByteForEnvelope();
|
||||||
|
uint8_t InitialByteFor32BitLengthByteString();
|
||||||
|
|
||||||
|
// Checks whether |msg| is a cbor message.
|
||||||
|
bool IsCBORMessage(span<uint8_t> msg);
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Encoding individual CBOR items
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
void EncodeInt32(int32_t value, std::string* 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);
|
||||||
|
void EncodeString16(span<uint16_t> in, std::string* out);
|
||||||
|
|
||||||
|
// Encodes a UTF8 string |in| as STRING (major type 3).
|
||||||
|
void EncodeString8(span<uint8_t> in, std::vector<uint8_t>* out);
|
||||||
|
void EncodeString8(span<uint8_t> in, std::string* out);
|
||||||
|
|
||||||
|
// Encodes the given |latin1| string as STRING8.
|
||||||
|
// If any non-ASCII character is present, it will be represented
|
||||||
|
// as a 2 byte UTF8 sequence.
|
||||||
|
void EncodeFromLatin1(span<uint8_t> latin1, std::vector<uint8_t>* out);
|
||||||
|
void EncodeFromLatin1(span<uint8_t> latin1, std::string* out);
|
||||||
|
|
||||||
|
// Encodes the given |utf16| string as STRING8 if it's entirely US-ASCII.
|
||||||
|
// Otherwise, encodes as STRING16.
|
||||||
|
void EncodeFromUTF16(span<uint16_t> utf16, std::vector<uint8_t>* out);
|
||||||
|
void EncodeFromUTF16(span<uint16_t> utf16, std::string* 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);
|
||||||
|
void EncodeBinary(span<uint8_t> in, std::string* 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);
|
||||||
|
void EncodeDouble(double value, std::string* out);
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// cbor::EnvelopeEncoder - for wrapping submessages
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
void EncodeStart(std::string* out);
|
||||||
|
// This records the current size in |out| at position byte_size_pos_.
|
||||||
|
// Returns true iff successful.
|
||||||
|
bool EncodeStop(std::vector<uint8_t>* out);
|
||||||
|
bool EncodeStop(std::string* out);
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t byte_size_pos_ = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// cbor::NewCBOREncoder - for encoding from a streaming parser
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
// This can be used to convert to CBOR, by passing the return value to a parser
|
||||||
|
// that drives it. 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<StreamingParserHandler> NewCBOREncoder(
|
||||||
|
std::vector<uint8_t>* out,
|
||||||
|
Status* status);
|
||||||
|
std::unique_ptr<StreamingParserHandler> NewCBOREncoder(std::string* out,
|
||||||
|
Status* status);
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// cbor::CBORTokenizer - for parsing individual CBOR items
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
// Tags for the tokens within a CBOR message that CBORTokenizer 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,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// To be called only if ::TokenTag() == CBORTokenTag::ENVELOPE.
|
||||||
|
span<uint8_t> GetEnvelopeContents() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void ReadNextToken(bool enter_envelope);
|
||||||
|
void SetToken(CBORTokenTag token, size_t token_byte_length);
|
||||||
|
void SetError(Error error);
|
||||||
|
|
||||||
|
span<uint8_t> bytes_;
|
||||||
|
CBORTokenTag token_tag_;
|
||||||
|
struct Status status_;
|
||||||
|
size_t token_byte_length_;
|
||||||
|
MajorType token_start_type_;
|
||||||
|
uint64_t token_start_internal_value_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// cbor::ParseCBOR - for receiving streaming parser events for CBOR messages
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
// Parses a CBOR encoded message from |bytes|, sending events to
|
||||||
|
// |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, StreamingParserHandler* out);
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// cbor::AppendString8EntryToMap - for limited in-place editing of messages
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
// Modifies the |cbor| message by appending a new key/value entry at the end
|
||||||
|
// of the map. Patches up the envelope size; Status.ok() iff successful.
|
||||||
|
// If not successful, |cbor| may be corrupted after this call.
|
||||||
|
Status AppendString8EntryToCBORMap(span<uint8_t> string8_key,
|
||||||
|
span<uint8_t> string8_value,
|
||||||
|
std::vector<uint8_t>* cbor);
|
||||||
|
Status AppendString8EntryToCBORMap(span<uint8_t> string8_key,
|
||||||
|
span<uint8_t> string8_value,
|
||||||
|
std::string* cbor);
|
||||||
|
|
||||||
|
namespace internals { // Exposed only for writing tests.
|
||||||
|
int8_t ReadTokenStart(span<uint8_t> bytes,
|
||||||
|
cbor::MajorType* type,
|
||||||
|
uint64_t* value);
|
||||||
|
|
||||||
|
void WriteTokenStart(cbor::MajorType type,
|
||||||
|
uint64_t value,
|
||||||
|
std::vector<uint8_t>* encoded);
|
||||||
|
void WriteTokenStart(cbor::MajorType type,
|
||||||
|
uint64_t value,
|
||||||
|
std::string* encoded);
|
||||||
|
} // namespace internals
|
||||||
|
} // namespace cbor
|
||||||
|
|
||||||
|
namespace json {
|
||||||
|
// Client code must provide an instance. Implementation should delegate
|
||||||
|
// to whatever is appropriate.
|
||||||
|
class Platform {
|
||||||
|
public:
|
||||||
|
virtual ~Platform() = default;
|
||||||
|
// Parses |str| into |result|. Returns false iff there are
|
||||||
|
// leftover characters or parsing errors.
|
||||||
|
virtual bool StrToD(const char* str, double* result) const = 0;
|
||||||
|
|
||||||
|
// Prints |value| in a format suitable for JSON.
|
||||||
|
virtual std::unique_ptr<char[]> DToStr(double value) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// json::NewJSONEncoder - for encoding streaming parser events as JSON
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
// Returns a handler object which will write ascii characters to |out|.
|
||||||
|
// |status->ok()| will be false iff the handler routine HandleError() is called.
|
||||||
|
// In that case, we'll stop emitting output.
|
||||||
|
// Except for calling the HandleError routine at any time, the client
|
||||||
|
// code must call the Handle* methods in an order in which they'd occur
|
||||||
|
// in valid JSON; otherwise we may crash (the code uses assert).
|
||||||
|
std::unique_ptr<StreamingParserHandler> NewJSONEncoder(
|
||||||
|
const Platform* platform,
|
||||||
|
std::vector<uint8_t>* out,
|
||||||
|
Status* status);
|
||||||
|
std::unique_ptr<StreamingParserHandler> NewJSONEncoder(const Platform* platform,
|
||||||
|
std::string* out,
|
||||||
|
Status* status);
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// json::ParseJSON - for receiving streaming parser events for JSON
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
void ParseJSON(const Platform& platform,
|
||||||
|
span<uint8_t> chars,
|
||||||
|
StreamingParserHandler* handler);
|
||||||
|
void ParseJSON(const Platform& platform,
|
||||||
|
span<uint16_t> chars,
|
||||||
|
StreamingParserHandler* handler);
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// json::ConvertCBORToJSON, json::ConvertJSONToCBOR - for transcoding
|
||||||
|
// =============================================================================
|
||||||
|
Status ConvertCBORToJSON(const Platform& platform,
|
||||||
|
span<uint8_t> cbor,
|
||||||
|
std::string* json);
|
||||||
|
Status ConvertCBORToJSON(const Platform& platform,
|
||||||
|
span<uint8_t> cbor,
|
||||||
|
std::vector<uint8_t>* json);
|
||||||
|
Status ConvertJSONToCBOR(const Platform& platform,
|
||||||
|
span<uint8_t> json,
|
||||||
|
std::vector<uint8_t>* cbor);
|
||||||
|
Status ConvertJSONToCBOR(const Platform& platform,
|
||||||
|
span<uint16_t> json,
|
||||||
|
std::vector<uint8_t>* cbor);
|
||||||
|
Status ConvertJSONToCBOR(const Platform& platform,
|
||||||
|
span<uint8_t> json,
|
||||||
|
std::string* cbor);
|
||||||
|
Status ConvertJSONToCBOR(const Platform& platform,
|
||||||
|
span<uint16_t> json,
|
||||||
|
std::string* cbor);
|
||||||
|
} // namespace json
|
||||||
|
|
||||||
|
{% for namespace in config.protocol.namespace %}
|
||||||
|
} // namespace {{namespace}}
|
||||||
|
{% endfor %}
|
||||||
|
#endif // !defined({{"_".join(config.protocol.namespace)}}_encoding_h)
|
@ -74,20 +74,20 @@ def parse(data, file_name, map_binary_to_string=False):
|
|||||||
if len(trimLine) == 0:
|
if len(trimLine) == 0:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
match = re.compile('^(experimental )?(deprecated )?domain (.*)').match(line)
|
match = re.compile(r'^(experimental )?(deprecated )?domain (.*)').match(line)
|
||||||
if match:
|
if match:
|
||||||
domain = createItem({'domain' : match.group(3)}, match.group(1), match.group(2))
|
domain = createItem({'domain' : match.group(3)}, match.group(1), match.group(2))
|
||||||
protocol['domains'].append(domain)
|
protocol['domains'].append(domain)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
match = re.compile('^ depends on ([^\s]+)').match(line)
|
match = re.compile(r'^ depends on ([^\s]+)').match(line)
|
||||||
if match:
|
if match:
|
||||||
if 'dependencies' not in domain:
|
if 'dependencies' not in domain:
|
||||||
domain['dependencies'] = []
|
domain['dependencies'] = []
|
||||||
domain['dependencies'].append(match.group(1))
|
domain['dependencies'].append(match.group(1))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
match = re.compile('^ (experimental )?(deprecated )?type (.*) extends (array of )?([^\s]+)').match(line)
|
match = re.compile(r'^ (experimental )?(deprecated )?type (.*) extends (array of )?([^\s]+)').match(line)
|
||||||
if match:
|
if match:
|
||||||
if 'types' not in domain:
|
if 'types' not in domain:
|
||||||
domain['types'] = []
|
domain['types'] = []
|
||||||
@ -96,7 +96,7 @@ def parse(data, file_name, map_binary_to_string=False):
|
|||||||
domain['types'].append(item)
|
domain['types'].append(item)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
match = re.compile('^ (experimental )?(deprecated )?(command|event) (.*)').match(line)
|
match = re.compile(r'^ (experimental )?(deprecated )?(command|event) (.*)').match(line)
|
||||||
if match:
|
if match:
|
||||||
list = []
|
list = []
|
||||||
if match.group(3) == 'command':
|
if match.group(3) == 'command':
|
||||||
@ -114,7 +114,7 @@ def parse(data, file_name, map_binary_to_string=False):
|
|||||||
list.append(item)
|
list.append(item)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
match = re.compile('^ (experimental )?(deprecated )?(optional )?(array of )?([^\s]+) ([^\s]+)').match(line)
|
match = re.compile(r'^ (experimental )?(deprecated )?(optional )?(array of )?([^\s]+) ([^\s]+)').match(line)
|
||||||
if match:
|
if match:
|
||||||
param = createItem({}, match.group(1), match.group(2), match.group(6))
|
param = createItem({}, match.group(1), match.group(2), match.group(6))
|
||||||
if match.group(3):
|
if match.group(3):
|
||||||
@ -125,36 +125,36 @@ def parse(data, file_name, map_binary_to_string=False):
|
|||||||
subitems.append(param)
|
subitems.append(param)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
match = re.compile('^ (parameters|returns|properties)').match(line)
|
match = re.compile(r'^ (parameters|returns|properties)').match(line)
|
||||||
if match:
|
if match:
|
||||||
subitems = item[match.group(1)] = []
|
subitems = item[match.group(1)] = []
|
||||||
continue
|
continue
|
||||||
|
|
||||||
match = re.compile('^ enum').match(line)
|
match = re.compile(r'^ enum').match(line)
|
||||||
if match:
|
if match:
|
||||||
enumliterals = item['enum'] = []
|
enumliterals = item['enum'] = []
|
||||||
continue
|
continue
|
||||||
|
|
||||||
match = re.compile('^version').match(line)
|
match = re.compile(r'^version').match(line)
|
||||||
if match:
|
if match:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
match = re.compile('^ major (\d+)').match(line)
|
match = re.compile(r'^ major (\d+)').match(line)
|
||||||
if match:
|
if match:
|
||||||
protocol['version']['major'] = match.group(1)
|
protocol['version']['major'] = match.group(1)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
match = re.compile('^ minor (\d+)').match(line)
|
match = re.compile(r'^ minor (\d+)').match(line)
|
||||||
if match:
|
if match:
|
||||||
protocol['version']['minor'] = match.group(1)
|
protocol['version']['minor'] = match.group(1)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
match = re.compile('^ redirect ([^\s]+)').match(line)
|
match = re.compile(r'^ redirect ([^\s]+)').match(line)
|
||||||
if match:
|
if match:
|
||||||
item['redirect'] = match.group(1)
|
item['redirect'] = match.group(1)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
match = re.compile('^ ( )?[^\s]+$').match(line)
|
match = re.compile(r'^ ( )?[^\s]+$').match(line)
|
||||||
if match:
|
if match:
|
||||||
# enum literal
|
# enum literal
|
||||||
enumliterals.append(trimLine)
|
enumliterals.append(trimLine)
|
||||||
|
162
tools/inspector_protocol/roll.py
Normal file
162
tools/inspector_protocol/roll.py
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
import argparse
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import glob
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
|
||||||
|
FILES_TO_SYNC = [
|
||||||
|
'README.md',
|
||||||
|
'check_protocol_compatibility.py',
|
||||||
|
'code_generator.py',
|
||||||
|
'concatenate_protocols.py',
|
||||||
|
'convert_protocol_to_json.py',
|
||||||
|
'encoding/encoding.h',
|
||||||
|
'encoding/encoding.cc',
|
||||||
|
'encoding/encoding_test.cc',
|
||||||
|
'inspector_protocol.gni',
|
||||||
|
'inspector_protocol.gypi',
|
||||||
|
'lib/*',
|
||||||
|
'pdl.py',
|
||||||
|
'templates/*',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def RunCmd(cmd):
|
||||||
|
p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
|
||||||
|
(stdoutdata, stderrdata) = p.communicate()
|
||||||
|
if p.returncode != 0:
|
||||||
|
raise Exception('%s: exit status %d', str(cmd), p.returncode)
|
||||||
|
return stdoutdata
|
||||||
|
|
||||||
|
|
||||||
|
def CheckRepoIsClean(path, suffix):
|
||||||
|
os.chdir(path) # As a side effect this also checks for existence of the dir.
|
||||||
|
# If path isn't a git repo, this will throw and exception.
|
||||||
|
# And if it is a git repo and 'git status' has anything interesting to say,
|
||||||
|
# then it's not clean (uncommitted files etc.)
|
||||||
|
if len(RunCmd(['git', 'status', '--porcelain'])) != 0:
|
||||||
|
raise Exception('%s is not a clean git repo (run git status)' % path)
|
||||||
|
if not path.endswith(suffix):
|
||||||
|
raise Exception('%s does not end with /%s' % (path, suffix))
|
||||||
|
|
||||||
|
|
||||||
|
def CheckRepoIsNotAtMasterBranch(path):
|
||||||
|
os.chdir(path)
|
||||||
|
stdout = RunCmd(['git', 'rev-parse', '--abbrev-ref', 'HEAD']).strip()
|
||||||
|
if stdout == 'master':
|
||||||
|
raise Exception('%s is at master branch - refusing to copy there.' % path)
|
||||||
|
|
||||||
|
|
||||||
|
def CheckRepoIsV8Checkout(path):
|
||||||
|
os.chdir(path)
|
||||||
|
if (RunCmd(['git', 'config', '--get', 'remote.origin.url']).strip() !=
|
||||||
|
'https://chromium.googlesource.com/v8/v8.git'):
|
||||||
|
raise Exception('%s is not a proper V8 checkout.' % path)
|
||||||
|
|
||||||
|
|
||||||
|
def CheckRepoIsInspectorProtocolCheckout(path):
|
||||||
|
os.chdir(path)
|
||||||
|
if (RunCmd(['git', 'config', '--get', 'remote.origin.url']).strip() !=
|
||||||
|
'https://chromium.googlesource.com/deps/inspector_protocol.git'):
|
||||||
|
raise Exception('%s is not a proper inspector_protocol checkout.' % path)
|
||||||
|
|
||||||
|
|
||||||
|
def FindFilesToSyncIn(path):
|
||||||
|
files = []
|
||||||
|
for f in FILES_TO_SYNC:
|
||||||
|
files += glob.glob(os.path.join(path, f))
|
||||||
|
files = [os.path.relpath(f, path) for f in files]
|
||||||
|
return files
|
||||||
|
|
||||||
|
|
||||||
|
def FilesAreEqual(path1, path2):
|
||||||
|
# We check for permissions (useful for executable scripts) and contents.
|
||||||
|
return (os.stat(path1).st_mode == os.stat(path2).st_mode and
|
||||||
|
open(path1).read() == open(path2).read())
|
||||||
|
|
||||||
|
|
||||||
|
def GetHeadRevision(path):
|
||||||
|
os.chdir(path)
|
||||||
|
return RunCmd(['git', 'rev-parse', 'HEAD'])
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv):
|
||||||
|
parser = argparse.ArgumentParser(description=(
|
||||||
|
"Rolls the inspector_protocol project (upstream) into V8's "
|
||||||
|
"third_party (downstream)."))
|
||||||
|
parser.add_argument("--ip_src_upstream",
|
||||||
|
help="The inspector_protocol (upstream) tree.",
|
||||||
|
default="~/ip/src")
|
||||||
|
parser.add_argument("--v8_src_downstream",
|
||||||
|
help="The V8 src tree.",
|
||||||
|
default="~/v8/v8")
|
||||||
|
parser.add_argument('--force', dest='force', action='store_true',
|
||||||
|
help=("Whether to carry out the modifications "
|
||||||
|
"in the destination tree."))
|
||||||
|
parser.set_defaults(force=False)
|
||||||
|
|
||||||
|
args = parser.parse_args(argv)
|
||||||
|
upstream = os.path.normpath(os.path.expanduser(args.ip_src_upstream))
|
||||||
|
downstream = os.path.normpath(os.path.expanduser(
|
||||||
|
args.v8_src_downstream))
|
||||||
|
CheckRepoIsClean(upstream, '/src')
|
||||||
|
CheckRepoIsClean(downstream, '/v8')
|
||||||
|
CheckRepoIsInspectorProtocolCheckout(upstream)
|
||||||
|
CheckRepoIsV8Checkout(downstream)
|
||||||
|
# Check that the destination Git repo isn't at the master branch - it's
|
||||||
|
# generally a bad idea to check into the master branch, so we catch this
|
||||||
|
# common pilot error here early.
|
||||||
|
CheckRepoIsNotAtMasterBranch(downstream)
|
||||||
|
src_dir = upstream
|
||||||
|
dest_dir = os.path.join(downstream, 'third_party/inspector_protocol')
|
||||||
|
print('Rolling %s into %s ...' % (src_dir, dest_dir))
|
||||||
|
src_files = set(FindFilesToSyncIn(src_dir))
|
||||||
|
dest_files = set(FindFilesToSyncIn(dest_dir))
|
||||||
|
to_add = [f for f in src_files if f not in dest_files]
|
||||||
|
to_delete = [f for f in dest_files if f not in src_files]
|
||||||
|
to_copy = [f for f in src_files
|
||||||
|
if (f in dest_files and not FilesAreEqual(
|
||||||
|
os.path.join(src_dir, f), os.path.join(dest_dir, f)))]
|
||||||
|
print('To add: %s' % to_add)
|
||||||
|
print('To delete: %s' % to_delete)
|
||||||
|
print('To copy: %s' % to_copy)
|
||||||
|
if not to_add and not to_delete and not to_copy:
|
||||||
|
print('Nothing to do. You\'re good.')
|
||||||
|
sys.exit(0)
|
||||||
|
if not args.force:
|
||||||
|
print('Rerun with --force if you wish the modifications to be done.')
|
||||||
|
sys.exit(1)
|
||||||
|
print('You said --force ... as you wish, modifying the destination.')
|
||||||
|
for f in to_add + to_copy:
|
||||||
|
contents = open(os.path.join(src_dir, f)).read()
|
||||||
|
contents = contents.replace(
|
||||||
|
'INSPECTOR_PROTOCOL_ENCODING_ENCODING_H_',
|
||||||
|
'V8_INSPECTOR_PROTOCOL_ENCODING_ENCODING_H_')
|
||||||
|
contents = contents.replace(
|
||||||
|
'namespace inspector_protocol_encoding',
|
||||||
|
'namespace v8_inspector_protocol_encoding')
|
||||||
|
open(os.path.join(dest_dir, f), 'w').write(contents)
|
||||||
|
shutil.copymode(os.path.join(src_dir, f), os.path.join(dest_dir, f))
|
||||||
|
for f in to_delete:
|
||||||
|
os.unlink(os.path.join(dest_dir, f))
|
||||||
|
head_revision = GetHeadRevision(upstream)
|
||||||
|
lines = open(os.path.join(dest_dir, 'README.v8')).readlines()
|
||||||
|
f = open(os.path.join(dest_dir, 'README.v8'), 'w')
|
||||||
|
for line in lines:
|
||||||
|
if line.startswith('Revision: '):
|
||||||
|
f.write('Revision: %s' % head_revision)
|
||||||
|
else:
|
||||||
|
f.write(line)
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main(sys.argv[1:]))
|
@ -203,12 +203,12 @@ void Frontend::flush()
|
|||||||
m_frontendChannel->flushProtocolNotifications();
|
m_frontendChannel->flushProtocolNotifications();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Frontend::sendRawNotification(String notification)
|
void Frontend::sendRawJSONNotification(String notification)
|
||||||
{
|
{
|
||||||
m_frontendChannel->sendProtocolNotification(InternalRawNotification::fromJSON(std::move(notification)));
|
m_frontendChannel->sendProtocolNotification(InternalRawNotification::fromJSON(std::move(notification)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Frontend::sendRawNotification(std::vector<uint8_t> notification)
|
void Frontend::sendRawCBORNotification(std::vector<uint8_t> notification)
|
||||||
{
|
{
|
||||||
m_frontendChannel->sendProtocolNotification(InternalRawNotification::fromBinary(std::move(notification)));
|
m_frontendChannel->sendProtocolNotification(InternalRawNotification::fromBinary(std::move(notification)));
|
||||||
}
|
}
|
||||||
|
@ -269,8 +269,8 @@ public:
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
void flush();
|
void flush();
|
||||||
void sendRawNotification(String);
|
void sendRawJSONNotification(String);
|
||||||
void sendRawNotification(std::vector<uint8_t>);
|
void sendRawCBORNotification(std::vector<uint8_t>);
|
||||||
private:
|
private:
|
||||||
FrontendChannel* m_frontendChannel;
|
FrontendChannel* m_frontendChannel;
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user