Source release 19.1.0

This commit is contained in:
Matt Feddersen
2024-03-28 19:21:54 -07:00
parent 28ec8548c6
commit b8bdfccebe
182 changed files with 10645 additions and 2040 deletions

22
third_party/libcppbor.gyp vendored Normal file
View File

@@ -0,0 +1,22 @@
{
'targets': [
{
'target_name': 'cppbor',
'type': 'static_library',
'sources': [
'libcppbor/src/cppbor.cpp',
'libcppbor/src/cppbor_parse.cpp',
],
'include_dirs': [
'libcppbor/include',
'libcppbor/include/cppbor',
],
'direct_dependent_settings': {
'include_dirs': [
'libcppbor/include',
'libcppbor/include/cppbor',
],
},
},
],
}

95
third_party/libcppbor/Android.bp vendored Normal file
View File

@@ -0,0 +1,95 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package {
default_applicable_licenses: ["external_libcppbor_license"],
}
// Added automatically by a large-scale-change
// See: http://go/android-license-faq
license {
name: "external_libcppbor_license",
visibility: [":__subpackages__"],
license_kinds: [
"SPDX-license-identifier-Apache-2.0",
],
license_text: [
"LICENSE",
],
}
cc_defaults {
name: "libcppbor_defaults",
cflags: [
"-Wall",
"-Wextra",
"-Werror",
],
}
cc_library {
name: "libcppbor_external",
defaults: [
"libcppbor_defaults",
],
vendor_available: true,
host_supported: true,
srcs: [
"src/cppbor.cpp",
"src/cppbor_parse.cpp",
],
export_include_dirs: [
"include/cppbor",
],
shared_libs: [
"libbase",
"libcrypto",
]
}
cc_test {
name: "cppbor_test_external",
defaults: [
"libcppbor_defaults",
],
srcs: [
"tests/cppbor_test.cpp"
],
shared_libs: [
"libcppbor_external",
"libbase",
],
static_libs: [
"libgmock",
],
test_suites: ["general-tests"],
}
cc_test_host {
name: "cppbor_host_test_external",
defaults: [
"libcppbor_defaults",
],
srcs: [
"tests/cppbor_test.cpp"
],
shared_libs: [
"libcppbor_external",
"libbase",
],
static_libs: [
"libgmock",
],
test_suites: ["general-tests"],
}

202
third_party/libcppbor/LICENSE vendored Normal file
View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

3
third_party/libcppbor/METADATA vendored Normal file
View File

@@ -0,0 +1,3 @@
third_party {
license_type: NOTICE
}

226
third_party/libcppbor/README.md vendored Normal file
View File

@@ -0,0 +1,226 @@
LibCppBor: A Modern C++ CBOR Parser and Generator
==============================================
TODO(b/254108623):
This is a modified version of LibCppBor and is C++-14 compliant. The released
version can be found at
https://android.googlesource.com/platform/external/libcppbor, which requires
C++-17. This is a reminder of refreshing the library with the latest source
above once we officially move to C++-17.
LibCppBor provides a natural and easy-to-use syntax for constructing and
parsing CBOR messages. It does not (yet) support all features of
CBOR, nor (yet) support validation against CDDL schemata, though both
are planned. CBOR features that aren't supported include:
* Indefinite length values
* Semantic tagging
* Floating point
LibCppBor requires C++-17.
## CBOR representation
LibCppBor represents CBOR data items as instances of the `Item` class or,
more precisely, as instances of subclasses of `Item`, since `Item` is a
pure interface. The subclasses of `Item` correspond almost one-to-one
with CBOR major types, and are named to match the CDDL names to which
they correspond. They are:
* `Uint` corresponds to major type 0, and can hold unsigned integers
up through (2^64 - 1).
* `Nint` corresponds to major type 1. It can only hold values from -1
to -(2^63 - 1), since it's internal representation is an int64_t.
This can be fixed, but it seems unlikely that applications will need
the omitted range from -(2^63) to (2^64 - 1), since it's
inconvenient to represent them in many programming languages.
* `Int` is an abstract base of `Uint` and `Nint` that facilitates
working with all signed integers representable with int64_t.
* `Bstr` corresponds to major type 2, a byte string.
* `Tstr` corresponds to major type 3, a text string.
* `Array` corresponds to major type 4, an Array. It holds a
variable-length array of `Item`s.
* `Map` corresponds to major type 5, a Map. It holds a
variable-length array of pairs of `Item`s.
* `Simple` corresponds to major type 7. It's an abstract class since
items require more specific type.
* `Bool` is the only currently-implemented subclass of `Simple`.
Note that major type 6, semantic tag, is not yet implemented.
In practice, users of LibCppBor will rarely use most of these classes
when generating CBOR encodings. This is because LibCppBor provides
straightforward conversions from the obvious normal C++ types.
Specifically, the following conversions are provided in appropriate
contexts:
* Signed and unsigned integers convert to `Uint` or `Nint`, as
appropriate.
* `std::string`, `std::string_view`, `const char*` and
`std::pair<char iterator, char iterator>` convert to `Tstr`.
* `std::vector<uint8_t>`, `std::pair<uint8_t iterator, uint8_t
iterator>` and `std::pair<uint8_t*, size_t>` convert to `Bstr`.
* `bool` converts to `Bool`.
## CBOR generation
### Complete tree generation
The set of `encode` methods in `Item` provide the interface for
producing encoded CBOR. The basic process for "complete tree"
generation (as opposed to "incremental" generation, which is discussed
below) is to construct an `Item` which models the data to be encoded,
and then call one of the `encode` methods, whichever is convenient for
the encoding destination. A trivial example:
```
cppbor::Uint val(0);
std::vector<uint8_t> encoding = val.encode();
```
It's relatively rare that single values are encoded as above. More often, the
"root" data item will be an `Array` or `Map` which contains a more complex structure.For example
:
``` using cppbor::Map;
using cppbor::Array;
std::vector<uint8_t> vec = // ...
Map val("key1", Array(Map("key_a", 99 "key_b", vec), "foo"), "key2", true);
std::vector<uint8_t> encoding = val.encode();
```
This creates a map with two entries, with `Tstr` keys "Outer1" and
"Outer2", respectively. The "Outer1" entry has as its value an
`Array` containing a `Map` and a `Tstr`. The "Outer2" entry has a
`Bool` value.
This example demonstrates how automatic conversion of C++ types to
LibCppBor `Item` subclass instances is done. Where the caller provides a
C++ or C string, a `Tstr` entry is added. Where the caller provides
an integer literal or variable, a `Uint` or `Nint` is added, depending
on whether the value is positive or negative.
As an alternative, a more fluent-style API is provided for building up
structures. For example:
```
using cppbor::Map;
using cppbor::Array;
std::vector<uint8_t> vec = // ...
Map val();
val.add("key1", Array().add(Map().add("key_a", 99).add("key_b", vec)).add("foo")).add("key2", true);
std::vector<uint8_t> encoding = val.encode();
```
An advantage of this interface over the constructor -
based creation approach above is that it need not be done all at once.
The `add` methods return a reference to the object added to to allow calls to be chained,
but chaining is not necessary; calls can be made
sequentially, as the data to add is available.
#### `encode` methods
There are several variations of `Item::encode`, all of which
accomplish the same task but output the encoded data in different
ways, and with somewhat different performance characteristics. The
provided options are:
* `bool encode(uint8\_t** pos, const uint8\_t* end)` encodes into the
buffer referenced by the range [`*pos`, end). `*pos` is moved. If
the encoding runs out of buffer space before finishing, the method
returns false. This is the most efficient way to encode, into an
already-allocated buffer.
* `void encode(EncodeCallback encodeCallback)` calls `encodeCallback`
for each encoded byte. It's the responsibility of the implementor
of the callback to behave safely in the event that the output buffer
(if applicable) is exhausted. This is less efficient than the prior
method because it imposes an additional function call for each byte.
* `template </*...*/> void encode(OutputIterator i)`
encodes into the provided iterator. SFINAE ensures that the
template doesn't match for non-iterators. The implementation
actually uses the callback-based method, plus has whatever overhead
the iterator adds.
* `std::vector<uint8_t> encode()` creates a new std::vector, reserves
sufficient capacity to hold the encoding, and inserts the encoded
bytes with a std::pushback_iterator and the previous method.
* `std::string toString()` does the same as the previous method, but
returns a string instead of a vector.
### Incremental generation
Incremental generation requires deeper understanding of CBOR, because
the library can't do as much to ensure that the output is valid. The
basic tool for intcremental generation is the `encodeHeader`
function. There are two variations, one which writes into a buffer,
and one which uses a callback. Both simply write out the bytes of a
header. To construct the same map as in the above examples,
incrementally, one might write:
```
using namespace cppbor; // For example brevity
std::vector encoding;
auto iter = std::back_inserter(result);
encodeHeader(MAP, 2 /* # of map entries */, iter);
std::string s = "key1";
encodeHeader(TSTR, s.size(), iter);
std::copy(s.begin(), s.end(), iter);
encodeHeader(ARRAY, 2 /* # of array entries */, iter);
Map().add("key_a", 99).add("key_b", vec).encode(iter)
s = "foo";
encodeHeader(TSTR, foo.size(), iter);
std::copy(s.begin(), s.end(), iter);
s = "key2";
encodeHeader(TSTR, foo.size(), iter);
std::copy(s.begin(), s.end(), iter);
encodeHeader(SIMPLE, TRUE, iter);
```
As the above example demonstrates, the styles can be mixed -- Note the
creation and encoding of the inner Map using the fluent style.
## Parsing
LibCppBor also supports parsing of encoded CBOR data, with the same
feature set as encoding. There are two basic approaches to parsing,
"full" and "stream"
### Full parsing
Full parsing means completely parsing a (possibly-compound) data
item from a byte buffer. The `parse` functions that do not take a
`ParseClient` pointer do this. They return a `ParseResult` which is a
tuple of three values:
* std::unique_ptr<Item> that points to the parsed item, or is nullptr
if there was a parse error.
* const uint8_t* that points to the byte after the end of the decoded
item, or to the first unparseable byte in the event of an error.
* std::string that is empty on success or contains an error message if
a parse error occurred.
Assuming a successful parse, you can then use `Item::type()` to
discover the type of the parsed item (e.g. MAP), and then use the
appropriate `Item::as*()` method (e.g. `Item::asMap()`) to get a
pointer to an interface which allows you to retrieve specific values.
### Stream parsing
Stream parsing is more complex, but more flexible. To use
StreamParsing, you must create your own subclass of `ParseClient` and
call one of the `parse` functions that accepts it. See the
`ParseClient` methods docstrings for details.
One unusual feature of stream parsing is that the `ParseClient`
callback methods not only provide the parsed Item, but also pointers
to the portion of the buffer that encode that Item. This is useful
if, for example, you want to find an element inside of a structure,
and then copy the encoding of that sub-structure, without bothering to
parse the rest.
The full parser is implemented with the stream parser.
### Disclaimer
This is not an officially supported Google product

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,150 @@
/*
* Copyright 2019 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "cppbor.h"
namespace cppbor {
using ParseResult = std::tuple<std::unique_ptr<Item> /* result */, const uint8_t* /* newPos */,
std::string /* errMsg */>;
/**
* Parse the first CBOR data item (possibly compound) from the range [begin, end).
*
* Returns a tuple of Item pointer, buffer pointer and error message. If parsing is successful, the
* Item pointer is non-null, the buffer pointer points to the first byte after the
* successfully-parsed item and the error message string is empty. If parsing fails, the Item
* pointer is null, the buffer pointer points to the first byte that was unparseable (the first byte
* of a data item header that is malformed in some way, e.g. an invalid value, or a length that is
* too large for the remaining buffer, etc.) and the string contains an error message describing the
* problem encountered.
*/
ParseResult parse(const uint8_t* begin, const uint8_t* end);
/**
* Parse the first CBOR data item (possibly compound) from the byte vector.
*
* Returns a tuple of Item pointer, buffer pointer and error message. If parsing is successful, the
* Item pointer is non-null, the buffer pointer points to the first byte after the
* successfully-parsed item and the error message string is empty. If parsing fails, the Item
* pointer is null, the buffer pointer points to the first byte that was unparseable (the first byte
* of a data item header that is malformed in some way, e.g. an invalid value, or a length that is
* too large for the remaining buffer, etc.) and the string contains an error message describing the
* problem encountered.
*/
inline ParseResult parse(const std::vector<uint8_t>& encoding) {
return parse(encoding.data(), encoding.data() + encoding.size());
}
/**
* Parse the first CBOR data item (possibly compound) from the range [begin, begin + size).
*
* Returns a tuple of Item pointer, buffer pointer and error message. If parsing is successful, the
* Item pointer is non-null, the buffer pointer points to the first byte after the
* successfully-parsed item and the error message string is empty. If parsing fails, the Item
* pointer is null, the buffer pointer points to the first byte that was unparseable (the first byte
* of a data item header that is malformed in some way, e.g. an invalid value, or a length that is
* too large for the remaining buffer, etc.) and the string contains an error message describing the
* problem encountered.
*/
inline ParseResult parse(const uint8_t* begin, size_t size) {
return parse(begin, begin + size);
}
/**
* Parse the first CBOR data item (possibly compound) from the value contained in a Bstr.
*
* Returns a tuple of Item pointer, buffer pointer and error message. If parsing is successful, the
* Item pointer is non-null, the buffer pointer points to the first byte after the
* successfully-parsed item and the error message string is empty. If parsing fails, the Item
* pointer is null, the buffer pointer points to the first byte that was unparseable (the first byte
* of a data item header that is malformed in some way, e.g. an invalid value, or a length that is
* too large for the remaining buffer, etc.) and the string contains an error message describing the
* problem encountered.
*/
inline ParseResult parse(const Bstr* bstr) {
if (!bstr)
return ParseResult(nullptr, nullptr, "Null Bstr pointer");
return parse(bstr->value());
}
class ParseClient;
/**
* Parse the CBOR data in the range [begin, end) in streaming fashion, calling methods on the
* provided ParseClient when elements are found.
*/
void parse(const uint8_t* begin, const uint8_t* end, ParseClient* parseClient);
/**
* Parse the CBOR data in the vector in streaming fashion, calling methods on the
* provided ParseClient when elements are found.
*/
inline void parse(const std::vector<uint8_t>& encoding, ParseClient* parseClient) {
return parse(encoding.data(), encoding.data() + encoding.size(), parseClient);
}
/**
* A pure interface that callers of the streaming parse functions must implement.
*/
class ParseClient {
public:
virtual ~ParseClient() {}
/**
* Called when an item is found. The Item pointer points to the found item; use type() and
* the appropriate as*() method to examine the value. hdrBegin points to the first byte of the
* header, valueBegin points to the first byte of the value and end points one past the end of
* the item. In the case of header-only items, such as integers, and compound items (ARRAY,
* MAP or SEMANTIC) whose end has not yet been found, valueBegin and end are equal and point to
* the byte past the header.
*
* Note that for compound types (ARRAY, MAP, and SEMANTIC), the Item will have no content. For
* Map and Array items, the size() method will return a correct value, but the index operators
* are unsafe, and the object cannot be safely compared with another Array/Map.
*
* The method returns a ParseClient*. In most cases "return this;" will be the right answer,
* but a different ParseClient may be returned, which the parser will begin using. If the method
* returns nullptr, parsing will be aborted immediately.
*/
virtual ParseClient* item(std::unique_ptr<Item>& item, const uint8_t* hdrBegin,
const uint8_t* valueBegin, const uint8_t* end) = 0;
/**
* Called when the end of a compound item (MAP or ARRAY) is found. The item argument will be
* the same one passed to the item() call -- and may be empty if item() moved its value out.
* hdrBegin, valueBegin and end point to the beginning of the item header, the beginning of the
* first contained value, and one past the end of the last contained value, respectively.
*
* Note that the Item will have no content.
*
* As with item(), itemEnd() can change the ParseClient by returning a different one, or end the
* parsing by returning nullptr;
*/
virtual ParseClient* itemEnd(std::unique_ptr<Item>& item, const uint8_t* hdrBegin,
const uint8_t* valueBegin, const uint8_t* end) = 0;
/**
* Called when parsing encounters an error. position is set to the first unparsed byte (one
* past the last successfully-parsed byte) and errorMessage contains an message explaining what
* sort of error occurred.
*/
virtual void error(const uint8_t* position, const std::string& errorMessage) = 0;
};
} // namespace cppbor

567
third_party/libcppbor/src/cppbor.cpp vendored Normal file
View File

@@ -0,0 +1,567 @@
/*
* Copyright 2019 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "cppbor.h"
#include <inttypes.h>
#include <limits>
using std::string;
using std::vector;
#define CHECK(x) (void)(x)
namespace cppbor {
namespace {
template <typename T, typename Iterator,
typename = std::enable_if<std::is_unsigned<T>::value>>
Iterator writeBigEndian(T value, Iterator pos) {
for (unsigned i = 0; i < sizeof(value); ++i) {
*pos++ = static_cast<uint8_t>(value >> (8 * (sizeof(value) - 1)));
value = static_cast<T>(value << 8);
}
return pos;
}
template <typename T, typename = std::enable_if<std::is_unsigned<T>::value>>
void writeBigEndian(T value, std::function<void(uint8_t)>& cb) {
for (unsigned i = 0; i < sizeof(value); ++i) {
cb(static_cast<uint8_t>(value >> (8 * (sizeof(value) - 1))));
value = static_cast<T>(value << 8);
}
}
bool cborAreAllElementsNonCompound(const Item* compoundItem) {
if (compoundItem->type() == ARRAY) {
const Array* array = compoundItem->asArray();
for (size_t n = 0; n < array->size(); n++) {
const Item* entry = (*array)[n].get();
switch (entry->type()) {
case ARRAY:
case MAP:
return false;
default:
break;
}
}
} else {
const Map* map = compoundItem->asMap();
for (auto& entry : *map) {
auto& keyEntry = entry.first;
auto& valueEntry = entry.second;
switch (keyEntry->type()) {
case ARRAY:
case MAP:
return false;
default:
break;
}
switch (valueEntry->type()) {
case ARRAY:
case MAP:
return false;
default:
break;
}
}
}
return true;
}
bool prettyPrintInternal(const Item* item, string& out, size_t indent,
size_t maxBStrSize,
const vector<string>& mapKeysToNotPrint) {
if (!item) {
out.append("<NULL>");
return false;
}
char buf[80];
string indentString(indent, ' ');
size_t tagCount = item->semanticTagCount();
while (tagCount > 0) {
--tagCount;
snprintf(buf, sizeof(buf), "tag %" PRIu64 " ", item->semanticTag(tagCount));
out.append(buf);
}
switch (item->type()) {
case SEMANTIC:
// Handled above.
break;
case UINT:
snprintf(buf, sizeof(buf), "%" PRIu64, item->asUint()->unsignedValue());
out.append(buf);
break;
case NINT:
snprintf(buf, sizeof(buf), "%" PRId64, item->asNint()->value());
out.append(buf);
break;
case BSTR: {
const uint8_t* valueData;
size_t valueSize;
const Bstr* bstr = item->asBstr();
if (bstr == nullptr) {
return false;
}
const vector<uint8_t>& value = bstr->value();
valueData = value.data();
valueSize = value.size();
out.append("{");
for (size_t n = 0; n < valueSize; n++) {
if (n > 0) {
out.append(", ");
}
snprintf(buf, sizeof(buf), "0x%02x", valueData[n]);
out.append(buf);
}
out.append("}");
} break;
case TSTR:
out.append("'");
{
// TODO: escape "'" characters
if (item->asTstr() != nullptr) {
out.append(item->asTstr()->value().c_str());
} else {
}
}
out.append("'");
break;
case ARRAY: {
const Array* array = item->asArray();
if (array->size() == 0) {
out.append("[]");
} else if (cborAreAllElementsNonCompound(array)) {
out.append("[");
for (size_t n = 0; n < array->size(); n++) {
if (!prettyPrintInternal((*array)[n].get(), out, indent + 2,
maxBStrSize, mapKeysToNotPrint)) {
return false;
}
out.append(", ");
}
out.append("]");
} else {
out.append("[\n" + indentString);
for (size_t n = 0; n < array->size(); n++) {
out.append(" ");
if (!prettyPrintInternal((*array)[n].get(), out, indent + 2,
maxBStrSize, mapKeysToNotPrint)) {
return false;
}
out.append(",\n" + indentString);
}
out.append("]");
}
} break;
case MAP: {
const Map* map = item->asMap();
if (map->size() == 0) {
out.append("{}");
} else {
out.append("{\n" + indentString);
for (auto& entry : *map) {
auto& map_key = entry.first;
auto& map_value = entry.second;
out.append(" ");
if (!prettyPrintInternal(map_key.get(), out, indent + 2, maxBStrSize,
mapKeysToNotPrint)) {
return false;
}
out.append(" : ");
if (map_key->type() == TSTR &&
std::find(mapKeysToNotPrint.begin(), mapKeysToNotPrint.end(),
map_key->asTstr()->value()) !=
mapKeysToNotPrint.end()) {
out.append("<not printed>");
} else {
if (!prettyPrintInternal(map_value.get(), out, indent + 2,
maxBStrSize, mapKeysToNotPrint)) {
return false;
}
}
out.append(",\n" + indentString);
}
out.append("}");
}
} break;
case SIMPLE:
const Bool* asBool = item->asSimple()->asBool();
const Null* asNull = item->asSimple()->asNull();
if (asBool != nullptr) {
out.append(asBool->value() ? "true" : "false");
} else if (asNull != nullptr) {
out.append("null");
} else {
return false;
}
break;
}
return true;
}
} // namespace
size_t headerSize(uint64_t addlInfo) {
if (addlInfo < ONE_BYTE_LENGTH) return 1;
if (addlInfo <= std::numeric_limits<uint8_t>::max()) return 2;
if (addlInfo <= std::numeric_limits<uint16_t>::max()) return 3;
if (addlInfo <= std::numeric_limits<uint32_t>::max()) return 5;
return 9;
}
uint8_t* encodeHeader(MajorType type, uint64_t addlInfo, uint8_t* pos,
const uint8_t* end) {
size_t sz = headerSize(addlInfo);
if (end - pos < static_cast<ssize_t>(sz)) return nullptr;
switch (sz) {
case 1:
*pos++ = type | static_cast<uint8_t>(addlInfo);
return pos;
case 2:
*pos++ = type | ONE_BYTE_LENGTH;
*pos++ = static_cast<uint8_t>(addlInfo);
return pos;
case 3:
*pos++ = type | TWO_BYTE_LENGTH;
return writeBigEndian(static_cast<uint16_t>(addlInfo), pos);
case 5:
*pos++ = type | FOUR_BYTE_LENGTH;
return writeBigEndian(static_cast<uint32_t>(addlInfo), pos);
case 9:
*pos++ = type | EIGHT_BYTE_LENGTH;
return writeBigEndian(addlInfo, pos);
default:
CHECK(false); // Impossible to get here.
return nullptr;
}
}
void encodeHeader(MajorType type, uint64_t addlInfo,
EncodeCallback encodeCallback) {
size_t sz = headerSize(addlInfo);
switch (sz) {
case 1:
encodeCallback(type | static_cast<uint8_t>(addlInfo));
break;
case 2:
encodeCallback(type | ONE_BYTE_LENGTH);
encodeCallback(static_cast<uint8_t>(addlInfo));
break;
case 3:
encodeCallback(type | TWO_BYTE_LENGTH);
writeBigEndian(static_cast<uint16_t>(addlInfo), encodeCallback);
break;
case 5:
encodeCallback(type | FOUR_BYTE_LENGTH);
writeBigEndian(static_cast<uint32_t>(addlInfo), encodeCallback);
break;
case 9:
encodeCallback(type | EIGHT_BYTE_LENGTH);
writeBigEndian(addlInfo, encodeCallback);
break;
default:
CHECK(false); // Impossible to get here.
}
}
bool Item::operator==(const Item& other) const& {
if (type() != other.type()) return false;
switch (type()) {
case UINT:
return *asUint() == *(other.asUint());
case NINT:
return *asNint() == *(other.asNint());
case BSTR:
if (asBstr() != nullptr && other.asBstr() != nullptr) {
return *asBstr() == *(other.asBstr());
}
// Interesting corner case: comparing a Bstr and ViewBstr with
// identical contents. The function currently returns false for
// this case.
// TODO: if it should return true, this needs a deep comparison
return false;
case TSTR:
if (asTstr() != nullptr && other.asTstr() != nullptr) {
return *asTstr() == *(other.asTstr());
}
// Same corner case as Bstr
return false;
case ARRAY:
return *asArray() == *(other.asArray());
case MAP:
return *asMap() == *(other.asMap());
case SIMPLE:
return *asSimple() == *(other.asSimple());
case SEMANTIC:
return *asSemanticTag() == *(other.asSemanticTag());
default:
CHECK(false); // Impossible to get here.
return false;
}
}
Nint::Nint(int64_t v) : mValue(v) { CHECK(v < 0); }
bool Simple::operator==(const Simple& other) const& {
if (simpleType() != other.simpleType()) return false;
switch (simpleType()) {
case BOOLEAN:
return *asBool() == *(other.asBool());
case NULL_T:
return true;
default:
CHECK(false); // Impossible to get here.
return false;
}
}
uint8_t* Bstr::encode(uint8_t* pos, const uint8_t* end) const {
pos = encodeHeader(mValue.size(), pos, end);
if (!pos || end - pos < static_cast<ptrdiff_t>(mValue.size())) return nullptr;
return std::copy(mValue.begin(), mValue.end(), pos);
}
void Bstr::encodeValue(EncodeCallback encodeCallback) const {
for (auto c : mValue) {
encodeCallback(c);
}
}
uint8_t* Tstr::encode(uint8_t* pos, const uint8_t* end) const {
pos = encodeHeader(mValue.size(), pos, end);
if (!pos || end - pos < static_cast<ptrdiff_t>(mValue.size())) return nullptr;
return std::copy(mValue.begin(), mValue.end(), pos);
}
void Tstr::encodeValue(EncodeCallback encodeCallback) const {
for (auto c : mValue) {
encodeCallback(static_cast<uint8_t>(c));
}
}
bool Array::operator==(const Array& other) const& {
return size() == other.size()
// Can't use vector::operator== because the contents are pointers.
// std::equal lets us provide a predicate that does the dereferencing.
&& std::equal(mEntries.begin(), mEntries.end(), other.mEntries.begin(),
[](const std::unique_ptr<cppbor::Item>& a,
const std::unique_ptr<cppbor::Item>& b) -> bool {
return *a == *b;
});
}
uint8_t* Array::encode(uint8_t* pos, const uint8_t* end) const {
pos = encodeHeader(size(), pos, end);
if (!pos) return nullptr;
for (auto& entry : mEntries) {
pos = entry->encode(pos, end);
if (!pos) return nullptr;
}
return pos;
}
void Array::encode(EncodeCallback encodeCallback) const {
encodeHeader(size(), encodeCallback);
for (auto& entry : mEntries) {
entry->encode(encodeCallback);
}
}
std::unique_ptr<Item> Array::clone() const {
auto res = make_unique<Array>();
for (size_t i = 0; i < mEntries.size(); i++) {
res->add(mEntries[i]->clone());
}
return res;
}
bool Map::operator==(const Map& other) const& {
return size() == other.size()
// Can't use vector::operator== because the contents are pairs of
// pointers. std::equal lets us provide a predicate that does the
// dereferencing.
&& std::equal(begin(), end(), other.begin(),
[](const entry_type& a, const entry_type& b) {
return *a.first == *b.first && *a.second == *b.second;
});
}
uint8_t* Map::encode(uint8_t* pos, const uint8_t* end) const {
pos = encodeHeader(size(), pos, end);
if (!pos) return nullptr;
for (auto& entry : mEntries) {
pos = entry.first->encode(pos, end);
if (!pos) return nullptr;
pos = entry.second->encode(pos, end);
if (!pos) return nullptr;
}
return pos;
}
void Map::encode(EncodeCallback encodeCallback) const {
encodeHeader(size(), encodeCallback);
for (auto& entry : mEntries) {
entry.first->encode(encodeCallback);
entry.second->encode(encodeCallback);
}
}
bool Map::keyLess(const Item* a, const Item* b) {
// CBOR map canonicalization rules are:
// 1. If two keys have different lengths, the shorter one sorts earlier.
if (a->encodedSize() < b->encodedSize()) return true;
if (a->encodedSize() > b->encodedSize()) return false;
// 2. If two keys have the same length, the one with the lower value in
// (byte-wise) lexical order sorts earlier. This requires encoding both
// items.
auto encodedA = a->encode();
auto encodedB = b->encode();
return std::lexicographical_compare(encodedA.begin(), encodedA.end(), //
encodedB.begin(), encodedB.end());
}
void recursivelyCanonicalize(std::unique_ptr<Item>& item) {
switch (item->type()) {
case UINT:
case NINT:
case BSTR:
case TSTR:
case SIMPLE:
return;
case ARRAY:
std::for_each(item->asArray()->begin(), item->asArray()->end(),
recursivelyCanonicalize);
return;
case MAP:
item->asMap()->canonicalize(true /* recurse */);
return;
case SEMANTIC:
// This can't happen. SemanticTags delegate their type() method to the
// contained Item's type.
assert(false);
return;
}
}
Map& Map::canonicalize(bool recurse) & {
if (recurse) {
for (auto& entry : mEntries) {
recursivelyCanonicalize(entry.first);
recursivelyCanonicalize(entry.second);
}
}
if (size() < 2 || mCanonicalized) {
// Trivially or already canonical; do nothing.
return *this;
}
std::sort(begin(), end(), [](const entry_type& a, const entry_type& b) {
return keyLess(a.first.get(), b.first.get());
});
mCanonicalized = true;
return *this;
}
std::unique_ptr<Item> Map::clone() const {
auto res = make_unique<Map>();
for (auto& entry : *this) {
auto& key = entry.first;
auto& value = entry.second;
res->add(key->clone(), value->clone());
}
res->mCanonicalized = mCanonicalized;
return res;
}
std::unique_ptr<Item> SemanticTag::clone() const {
return make_unique<SemanticTag>(mValue, mTaggedItem->clone());
}
uint8_t* SemanticTag::encode(uint8_t* pos, const uint8_t* end) const {
// Can't use the encodeHeader() method that calls type() to get the major
// type, since that will return the tagged Item's type.
pos = ::cppbor::encodeHeader(kMajorType, mValue, pos, end);
if (!pos) return nullptr;
return mTaggedItem->encode(pos, end);
}
void SemanticTag::encode(EncodeCallback encodeCallback) const {
// Can't use the encodeHeader() method that calls type() to get the major
// type, since that will return the tagged Item's type.
::cppbor::encodeHeader(kMajorType, mValue, encodeCallback);
mTaggedItem->encode(std::move(encodeCallback));
}
size_t SemanticTag::semanticTagCount() const {
size_t levelCount = 1; // Count this level.
const SemanticTag* cur = this;
while (cur->mTaggedItem &&
(cur = cur->mTaggedItem->asSemanticTag()) != nullptr)
++levelCount;
return levelCount;
}
uint64_t SemanticTag::semanticTag(size_t nesting) const {
// Getting the value of a specific nested tag is a bit tricky, because we
// start with the outer tag and don't know how many are inside. We count the
// number of nesting levels to find out how many there are in total, then to
// get the one we want we have to walk down levelCount - nesting steps.
size_t levelCount = semanticTagCount();
if (nesting >= levelCount) return 0;
levelCount -= nesting;
const SemanticTag* cur = this;
while (--levelCount > 0) cur = cur->mTaggedItem->asSemanticTag();
return cur->mValue;
}
string prettyPrint(const Item* item, size_t maxBStrSize,
const vector<string>& mapKeysToNotPrint) {
string out;
prettyPrintInternal(item, out, 0, maxBStrSize, mapKeysToNotPrint);
return out;
}
} // namespace cppbor

View File

@@ -0,0 +1,380 @@
/*
* Copyright 2019 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "cppbor_parse.h"
#include <limits>
#include <sstream>
#include <stack>
#ifndef __has_feature
#define __has_feature(x) 0
#endif
namespace cppbor {
namespace {
std::string insufficientLengthString(size_t bytesNeeded, size_t bytesAvail,
const std::string& type) {
char buf[1024];
snprintf(buf, sizeof(buf), "Need %zu byte(s) for %s, have %zu.", bytesNeeded, type.c_str(),
bytesAvail);
return std::string(buf);
}
template <typename T, typename = std::enable_if_t<std::is_unsigned<T>::value>>
std::tuple<bool, uint64_t, const uint8_t*> parseLength(const uint8_t* pos, const uint8_t* end,
ParseClient* parseClient) {
if (pos + sizeof(T) > end) {
parseClient->error(pos - 1, insufficientLengthString(sizeof(T), end - pos, "length field"));
return {false, 0, pos};
}
const uint8_t* intEnd = pos + sizeof(T);
T result = 0;
do {
result = static_cast<T>((result << 8) | *pos++);
} while (pos < intEnd);
return {true, result, pos};
}
std::tuple<const uint8_t*, ParseClient*> parseRecursively(const uint8_t* begin, const uint8_t* end,
bool emitViews, ParseClient* parseClient);
std::tuple<const uint8_t*, ParseClient*> handleUint(uint64_t value, const uint8_t* hdrBegin,
const uint8_t* hdrEnd,
ParseClient* parseClient) {
std::unique_ptr<Item> item = std::make_unique<Uint>(value);
return {hdrEnd,
parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)};
}
std::tuple<const uint8_t*, ParseClient*> handleNint(uint64_t value, const uint8_t* hdrBegin,
const uint8_t* hdrEnd,
ParseClient* parseClient) {
if (value > std::numeric_limits<int64_t>::max()) {
parseClient->error(hdrBegin, "NINT values that don't fit in int64_t are not supported.");
return {hdrBegin, nullptr /* end parsing */};
}
std::unique_ptr<Item> item = std::make_unique<Nint>(-1 - static_cast<int64_t>(value));
return {hdrEnd,
parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)};
}
std::tuple<const uint8_t*, ParseClient*> handleBool(uint64_t value, const uint8_t* hdrBegin,
const uint8_t* hdrEnd,
ParseClient* parseClient) {
std::unique_ptr<Item> item = std::make_unique<Bool>(value == TRUE);
return {hdrEnd,
parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)};
}
std::tuple<const uint8_t*, ParseClient*> handleNull(const uint8_t* hdrBegin, const uint8_t* hdrEnd,
ParseClient* parseClient) {
std::unique_ptr<Item> item = std::make_unique<Null>();
return {hdrEnd,
parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)};
}
template <typename T>
std::tuple<const uint8_t*, ParseClient*> handleString(uint64_t length, const uint8_t* hdrBegin,
const uint8_t* valueBegin, const uint8_t* end,
const std::string& errLabel,
ParseClient* parseClient) {
ssize_t signed_length = static_cast<ssize_t>(length);
if (end - valueBegin < signed_length || signed_length < 0) {
parseClient->error(hdrBegin, insufficientLengthString(length, end - valueBegin, errLabel));
return {hdrBegin, nullptr /* end parsing */};
}
std::unique_ptr<Item> item = std::make_unique<T>(valueBegin, valueBegin + length);
return {valueBegin + length,
parseClient->item(item, hdrBegin, valueBegin, valueBegin + length)};
}
class IncompleteItem {
public:
virtual ~IncompleteItem() {}
virtual void add(std::unique_ptr<Item> item) = 0;
};
class IncompleteArray : public Array, public IncompleteItem {
public:
explicit IncompleteArray(size_t size) : mSize(size) {}
// We return the "complete" size, rather than the actual size.
size_t size() const override { return mSize; }
void add(std::unique_ptr<Item> item) override {
mEntries.reserve(mSize);
mEntries.push_back(std::move(item));
}
private:
size_t mSize;
};
class IncompleteMap : public Map, public IncompleteItem {
public:
explicit IncompleteMap(size_t size) : mSize(size) {}
// We return the "complete" size, rather than the actual size.
size_t size() const override { return mSize; }
void add(std::unique_ptr<Item> item) override {
if (mKeyHeldForAdding) {
mEntries.reserve(mSize);
mEntries.push_back({std::move(mKeyHeldForAdding), std::move(item)});
} else {
mKeyHeldForAdding = std::move(item);
}
}
private:
std::unique_ptr<Item> mKeyHeldForAdding;
size_t mSize;
};
class IncompleteSemanticTag : public SemanticTag, public IncompleteItem {
public:
explicit IncompleteSemanticTag(uint64_t value) : SemanticTag(value) {}
// We return the "complete" size, rather than the actual size.
size_t size() const override { return 1; }
void add(std::unique_ptr<Item> item) override { mTaggedItem = std::move(item); }
};
std::tuple<const uint8_t*, ParseClient*> handleEntries(size_t entryCount, const uint8_t* hdrBegin,
const uint8_t* pos, const uint8_t* end,
const std::string& typeName,
bool emitViews,
ParseClient* parseClient) {
while (entryCount > 0) {
--entryCount;
if (pos == end) {
parseClient->error(hdrBegin, "Not enough entries for " + typeName + ".");
return {hdrBegin, nullptr /* end parsing */};
}
std::tie(pos, parseClient) = parseRecursively(pos, end, emitViews, parseClient);
if (!parseClient) return {hdrBegin, nullptr};
}
return {pos, parseClient};
}
std::tuple<const uint8_t*, ParseClient*> handleCompound(
std::unique_ptr<Item> item, uint64_t entryCount, const uint8_t* hdrBegin,
const uint8_t* valueBegin, const uint8_t* end, const std::string& typeName,
bool emitViews, ParseClient* parseClient) {
parseClient =
parseClient->item(item, hdrBegin, valueBegin, valueBegin /* don't know the end yet */);
if (!parseClient) return {hdrBegin, nullptr};
const uint8_t* pos;
std::tie(pos, parseClient) =
handleEntries(entryCount, hdrBegin, valueBegin, end, typeName, emitViews, parseClient);
if (!parseClient) return {hdrBegin, nullptr};
return {pos, parseClient->itemEnd(item, hdrBegin, valueBegin, pos)};
}
std::tuple<const uint8_t*, ParseClient*> parseRecursively(const uint8_t* begin, const uint8_t* end,
bool emitViews, ParseClient* parseClient) {
if (begin == end) {
parseClient->error(
begin,
"Input buffer is empty. Begin and end cannot point to the same location.");
return {begin, nullptr};
}
const uint8_t* pos = begin;
MajorType type = static_cast<MajorType>(*pos & 0xE0);
uint8_t tagInt = *pos & 0x1F;
++pos;
bool success = true;
uint64_t addlData;
if (tagInt < ONE_BYTE_LENGTH) {
addlData = tagInt;
} else if (tagInt > EIGHT_BYTE_LENGTH) {
parseClient->error(
begin,
"Reserved additional information value or unsupported indefinite length item.");
return {begin, nullptr};
} else {
switch (tagInt) {
case ONE_BYTE_LENGTH:
std::tie(success, addlData, pos) = parseLength<uint8_t>(pos, end, parseClient);
break;
case TWO_BYTE_LENGTH:
std::tie(success, addlData, pos) = parseLength<uint16_t>(pos, end, parseClient);
break;
case FOUR_BYTE_LENGTH:
std::tie(success, addlData, pos) = parseLength<uint32_t>(pos, end, parseClient);
break;
case EIGHT_BYTE_LENGTH:
std::tie(success, addlData, pos) = parseLength<uint64_t>(pos, end, parseClient);
break;
default:
// It's impossible to get here
parseClient->error(begin, "Invalid tag.");
return {};
}
}
if (!success) return {begin, nullptr};
switch (type) {
case UINT:
return handleUint(addlData, begin, pos, parseClient);
case NINT:
return handleNint(addlData, begin, pos, parseClient);
case BSTR:
return handleString<Bstr>(addlData, begin, pos, end, "byte string", parseClient);
case TSTR:
return handleString<Tstr>(addlData, begin, pos, end, "text string", parseClient);
case ARRAY:
return handleCompound(std::make_unique<IncompleteArray>(addlData), addlData, begin, pos,
end, "array", emitViews, parseClient);
case MAP:
return handleCompound(std::make_unique<IncompleteMap>(addlData), addlData * 2, begin,
pos, end, "map", emitViews, parseClient);
case SEMANTIC:
return handleCompound(std::make_unique<IncompleteSemanticTag>(addlData), 1, begin, pos,
end, "semantic", emitViews, parseClient);
case SIMPLE:
switch (addlData) {
case TRUE:
case FALSE:
return handleBool(addlData, begin, pos, parseClient);
case NULL_V:
return handleNull(begin, pos, parseClient);
default:
parseClient->error(begin, "Unsupported floating-point or simple value.");
return {begin, nullptr};
}
}
// Impossible to get here.
parseClient->error(begin, "Invalid type.");
return {};
}
class FullParseClient : public ParseClient {
public:
virtual ParseClient* item(std::unique_ptr<Item>& item, const uint8_t*, const uint8_t*,
const uint8_t* end) override {
if (mParentStack.empty() && !item->isCompound()) {
// This is the first and only item.
mTheItem = std::move(item);
mPosition = end;
return nullptr; // We're done.
}
if (item->isCompound()) {
// Starting a new compound data item, i.e. a new parent. Save it on the parent stack.
// It's safe to save a raw pointer because the unique_ptr is guaranteed to stay in
// existence until the corresponding itemEnd() call.
mParentStack.push(item.get());
return this;
} else {
appendToLastParent(std::move(item));
return this;
}
}
virtual ParseClient* itemEnd(std::unique_ptr<Item>& item, const uint8_t*, const uint8_t*,
const uint8_t* end) override {
if (!item->isCompound() || item.get() != mParentStack.top()) {
return nullptr;
}
mParentStack.pop();
if (mParentStack.empty()) {
mTheItem = std::move(item);
mPosition = end;
return nullptr; // We're done
} else {
appendToLastParent(std::move(item));
return this;
}
}
virtual void error(const uint8_t* position, const std::string& errorMessage) override {
mPosition = position;
mErrorMessage = errorMessage;
}
std::tuple<std::unique_ptr<Item> /* result */, const uint8_t* /* newPos */,
std::string /* errMsg */>
parseResult() {
std::unique_ptr<Item> p = std::move(mTheItem);
return {std::move(p), mPosition, std::move(mErrorMessage)};
}
private:
void appendToLastParent(std::unique_ptr<Item> item) {
auto parent = mParentStack.top();
#if __has_feature(cxx_rtti)
assert(dynamic_cast<IncompleteItem*>(parent));
#endif
IncompleteItem* parentItem{};
if (parent->type() == ARRAY) {
parentItem = static_cast<IncompleteArray*>(parent);
} else if (parent->type() == MAP) {
parentItem = static_cast<IncompleteMap*>(parent);
} else if (parent->asSemanticTag()) {
parentItem = static_cast<IncompleteSemanticTag*>(parent);
} else {
// Impossible to get here.
}
parentItem->add(std::move(item));
}
std::unique_ptr<Item> mTheItem;
std::stack<Item*> mParentStack;
const uint8_t* mPosition = nullptr;
std::string mErrorMessage;
};
} // anonymous namespace
void parse(const uint8_t* begin, const uint8_t* end, ParseClient* parseClient) {
parseRecursively(begin, end, false, parseClient);
}
std::tuple<std::unique_ptr<Item> /* result */, const uint8_t* /* newPos */,
std::string /* errMsg */>
parse(const uint8_t* begin, const uint8_t* end) {
FullParseClient parseClient;
parse(begin, end, &parseClient);
return parseClient.parseResult();
}
} // namespace cppbor

File diff suppressed because it is too large Load Diff