Merge "Remove obsolete libcppbor code from identity module" am: dc34dfe8ed am: 6b2625b454

Original change: https://android-review.googlesource.com/c/platform/hardware/interfaces/+/2136016

Change-Id: I0d5d33d8493e912947b84bcbe73433a0f489c1ca
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Vikram Gaur
2022-12-19 17:06:02 +00:00
committed by Automerger Merge Worker
7 changed files with 0 additions and 2819 deletions

View File

@@ -65,57 +65,3 @@ cc_test {
],
test_suites: ["general-tests"],
}
// --
cc_library {
name: "libcppbor",
vendor_available: true,
host_supported: true,
srcs: [
"src/cppbor.cpp",
"src/cppbor_parse.cpp",
],
export_include_dirs: [
"include/cppbor",
],
shared_libs: [
"libbase",
],
}
cc_test {
name: "cppbor_test",
tidy_timeout_srcs: [
"tests/cppbor_test.cpp",
],
srcs: [
"tests/cppbor_test.cpp",
],
shared_libs: [
"libcppbor_external",
"libbase",
],
static_libs: [
"libgmock",
],
test_suites: ["general-tests"],
}
cc_test_host {
name: "cppbor_host_test",
tidy_timeout_srcs: [
"tests/cppbor_test.cpp",
],
srcs: [
"tests/cppbor_test.cpp",
],
shared_libs: [
"libcppbor_external",
"libbase",
],
static_libs: [
"libgmock",
],
test_suites: ["general-tests"],
}

View File

@@ -1,216 +0,0 @@
CppBor: A Modern C++ CBOR Parser and Generator
==============================================
CppBor 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
CppBor requires C++-17.
## CBOR representation
CppBor 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 CppBor will rarely use most of these classes
when generating CBOR encodings. This is because CppBor 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
CppBor `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
CppBor 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.

View File

@@ -1,827 +0,0 @@
/*
* Copyright (c) 2019, The Android Open Source Project
*
* 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.
*/
#pragma once
#include <cstdint>
#include <functional>
#include <iterator>
#include <memory>
#include <numeric>
#include <string>
#include <vector>
namespace cppbor {
enum MajorType : uint8_t {
UINT = 0 << 5,
NINT = 1 << 5,
BSTR = 2 << 5,
TSTR = 3 << 5,
ARRAY = 4 << 5,
MAP = 5 << 5,
SEMANTIC = 6 << 5,
SIMPLE = 7 << 5,
};
enum SimpleType {
BOOLEAN,
NULL_T, // Only two supported, as yet.
};
enum SpecialAddlInfoValues : uint8_t {
FALSE = 20,
TRUE = 21,
NULL_V = 22,
ONE_BYTE_LENGTH = 24,
TWO_BYTE_LENGTH = 25,
FOUR_BYTE_LENGTH = 26,
EIGHT_BYTE_LENGTH = 27,
};
class Item;
class Uint;
class Nint;
class Int;
class Tstr;
class Bstr;
class Simple;
class Bool;
class Array;
class Map;
class Null;
class Semantic;
/**
* Returns the size of a CBOR header that contains the additional info value addlInfo.
*/
size_t headerSize(uint64_t addlInfo);
/**
* Encodes a CBOR header with the specified type and additional info into the range [pos, end).
* Returns a pointer to one past the last byte written, or nullptr if there isn't sufficient space
* to write the header.
*/
uint8_t* encodeHeader(MajorType type, uint64_t addlInfo, uint8_t* pos, const uint8_t* end);
using EncodeCallback = std::function<void(uint8_t)>;
/**
* Encodes a CBOR header with the specified type and additional info, passing each byte in turn to
* encodeCallback.
*/
void encodeHeader(MajorType type, uint64_t addlInfo, EncodeCallback encodeCallback);
/**
* Encodes a CBOR header with the specified type and additional info, writing each byte to the
* provided OutputIterator.
*/
template <typename OutputIterator,
typename = std::enable_if_t<std::is_base_of_v<
std::output_iterator_tag,
typename std::iterator_traits<OutputIterator>::iterator_category>>>
void encodeHeader(MajorType type, uint64_t addlInfo, OutputIterator iter) {
return encodeHeader(type, addlInfo, [&](uint8_t v) { *iter++ = v; });
}
/**
* Item represents a CBOR-encodeable data item. Item is an abstract interface with a set of virtual
* methods that allow encoding of the item or conversion to the appropriate derived type.
*/
class Item {
public:
virtual ~Item() {}
/**
* Returns the CBOR type of the item.
*/
virtual MajorType type() const = 0;
// These methods safely downcast an Item to the appropriate subclass.
virtual const Int* asInt() const { return nullptr; }
virtual const Uint* asUint() const { return nullptr; }
virtual const Nint* asNint() const { return nullptr; }
virtual const Tstr* asTstr() const { return nullptr; }
virtual const Bstr* asBstr() const { return nullptr; }
virtual const Simple* asSimple() const { return nullptr; }
virtual const Map* asMap() const { return nullptr; }
virtual const Array* asArray() const { return nullptr; }
virtual const Semantic* asSemantic() const { return nullptr; }
/**
* Returns true if this is a "compound" item, i.e. one that contains one or more other items.
*/
virtual bool isCompound() const { return false; }
bool operator==(const Item& other) const&;
bool operator!=(const Item& other) const& { return !(*this == other); }
/**
* Returns the number of bytes required to encode this Item into CBOR. Note that if this is a
* complex Item, calling this method will require walking the whole tree.
*/
virtual size_t encodedSize() const = 0;
/**
* Encodes the Item into buffer referenced by range [*pos, end). Returns a pointer to one past
* the last position written. Returns nullptr if there isn't enough space to encode.
*/
virtual uint8_t* encode(uint8_t* pos, const uint8_t* end) const = 0;
/**
* Encodes the Item by passing each encoded byte to encodeCallback.
*/
virtual void encode(EncodeCallback encodeCallback) const = 0;
/**
* Clones the Item
*/
virtual std::unique_ptr<Item> clone() const = 0;
/**
* Encodes the Item into the provided OutputIterator.
*/
template <typename OutputIterator,
typename = typename std::iterator_traits<OutputIterator>::iterator_category>
void encode(OutputIterator i) const {
return encode([&](uint8_t v) { *i++ = v; });
}
/**
* Encodes the Item into a new std::vector<uint8_t>.
*/
std::vector<uint8_t> encode() const {
std::vector<uint8_t> retval;
retval.reserve(encodedSize());
encode(std::back_inserter(retval));
return retval;
}
/**
* Encodes the Item into a new std::string.
*/
std::string toString() const {
std::string retval;
retval.reserve(encodedSize());
encode([&](uint8_t v) { retval.push_back(v); });
return retval;
}
/**
* Encodes only the header of the Item.
*/
inline uint8_t* encodeHeader(uint64_t addlInfo, uint8_t* pos, const uint8_t* end) const {
return ::cppbor::encodeHeader(type(), addlInfo, pos, end);
}
/**
* Encodes only the header of the Item.
*/
inline void encodeHeader(uint64_t addlInfo, EncodeCallback encodeCallback) const {
::cppbor::encodeHeader(type(), addlInfo, encodeCallback);
}
};
/**
* Int is an abstraction that allows Uint and Nint objects to be manipulated without caring about
* the sign.
*/
class Int : public Item {
public:
bool operator==(const Int& other) const& { return value() == other.value(); }
virtual int64_t value() const = 0;
const Int* asInt() const override { return this; }
};
/**
* Uint is a concrete Item that implements CBOR major type 0.
*/
class Uint : public Int {
public:
static constexpr MajorType kMajorType = UINT;
explicit Uint(uint64_t v) : mValue(v) {}
bool operator==(const Uint& other) const& { return mValue == other.mValue; }
MajorType type() const override { return kMajorType; }
const Uint* asUint() const override { return this; }
size_t encodedSize() const override { return headerSize(mValue); }
int64_t value() const override { return mValue; }
uint64_t unsignedValue() const { return mValue; }
using Item::encode;
uint8_t* encode(uint8_t* pos, const uint8_t* end) const override {
return encodeHeader(mValue, pos, end);
}
void encode(EncodeCallback encodeCallback) const override {
encodeHeader(mValue, encodeCallback);
}
virtual std::unique_ptr<Item> clone() const override { return std::make_unique<Uint>(mValue); }
private:
uint64_t mValue;
};
/**
* Nint is a concrete Item that implements CBOR major type 1.
* Note that it is incapable of expressing the full range of major type 1 values, becaue it can only
* express values that fall into the range [std::numeric_limits<int64_t>::min(), -1]. It cannot
* express values in the range [std::numeric_limits<int64_t>::min() - 1,
* -std::numeric_limits<uint64_t>::max()].
*/
class Nint : public Int {
public:
static constexpr MajorType kMajorType = NINT;
explicit Nint(int64_t v);
bool operator==(const Nint& other) const& { return mValue == other.mValue; }
MajorType type() const override { return kMajorType; }
const Nint* asNint() const override { return this; }
size_t encodedSize() const override { return headerSize(addlInfo()); }
int64_t value() const override { return mValue; }
using Item::encode;
uint8_t* encode(uint8_t* pos, const uint8_t* end) const override {
return encodeHeader(addlInfo(), pos, end);
}
void encode(EncodeCallback encodeCallback) const override {
encodeHeader(addlInfo(), encodeCallback);
}
virtual std::unique_ptr<Item> clone() const override { return std::make_unique<Nint>(mValue); }
private:
uint64_t addlInfo() const { return -1LL - mValue; }
int64_t mValue;
};
/**
* Bstr is a concrete Item that implements major type 2.
*/
class Bstr : public Item {
public:
static constexpr MajorType kMajorType = BSTR;
// Construct from a vector
explicit Bstr(std::vector<uint8_t> v) : mValue(std::move(v)) {}
// Construct from a string
explicit Bstr(const std::string& v)
: mValue(reinterpret_cast<const uint8_t*>(v.data()),
reinterpret_cast<const uint8_t*>(v.data()) + v.size()) {}
// Construct from a pointer/size pair
explicit Bstr(const std::pair<const uint8_t*, size_t>& buf)
: mValue(buf.first, buf.first + buf.second) {}
// Construct from a pair of iterators
template <typename I1, typename I2,
typename = typename std::iterator_traits<I1>::iterator_category,
typename = typename std::iterator_traits<I2>::iterator_category>
explicit Bstr(const std::pair<I1, I2>& pair) : mValue(pair.first, pair.second) {}
// Construct from an iterator range.
template <typename I1, typename I2,
typename = typename std::iterator_traits<I1>::iterator_category,
typename = typename std::iterator_traits<I2>::iterator_category>
Bstr(I1 begin, I2 end) : mValue(begin, end) {}
bool operator==(const Bstr& other) const& { return mValue == other.mValue; }
MajorType type() const override { return kMajorType; }
const Bstr* asBstr() const override { return this; }
size_t encodedSize() const override { return headerSize(mValue.size()) + mValue.size(); }
using Item::encode;
uint8_t* encode(uint8_t* pos, const uint8_t* end) const override;
void encode(EncodeCallback encodeCallback) const override {
encodeHeader(mValue.size(), encodeCallback);
encodeValue(encodeCallback);
}
const std::vector<uint8_t>& value() const { return mValue; }
virtual std::unique_ptr<Item> clone() const override { return std::make_unique<Bstr>(mValue); }
private:
void encodeValue(EncodeCallback encodeCallback) const;
std::vector<uint8_t> mValue;
};
/**
* Bstr is a concrete Item that implements major type 3.
*/
class Tstr : public Item {
public:
static constexpr MajorType kMajorType = TSTR;
// Construct from a string
explicit Tstr(std::string v) : mValue(std::move(v)) {}
// Construct from a string_view
explicit Tstr(const std::string_view& v) : mValue(v) {}
// Construct from a C string
explicit Tstr(const char* v) : mValue(std::string(v)) {}
// Construct from a pair of iterators
template <typename I1, typename I2,
typename = typename std::iterator_traits<I1>::iterator_category,
typename = typename std::iterator_traits<I2>::iterator_category>
explicit Tstr(const std::pair<I1, I2>& pair) : mValue(pair.first, pair.second) {}
// Construct from an iterator range
template <typename I1, typename I2,
typename = typename std::iterator_traits<I1>::iterator_category,
typename = typename std::iterator_traits<I2>::iterator_category>
Tstr(I1 begin, I2 end) : mValue(begin, end) {}
bool operator==(const Tstr& other) const& { return mValue == other.mValue; }
MajorType type() const override { return kMajorType; }
const Tstr* asTstr() const override { return this; }
size_t encodedSize() const override { return headerSize(mValue.size()) + mValue.size(); }
using Item::encode;
uint8_t* encode(uint8_t* pos, const uint8_t* end) const override;
void encode(EncodeCallback encodeCallback) const override {
encodeHeader(mValue.size(), encodeCallback);
encodeValue(encodeCallback);
}
const std::string& value() const { return mValue; }
virtual std::unique_ptr<Item> clone() const override { return std::make_unique<Tstr>(mValue); }
private:
void encodeValue(EncodeCallback encodeCallback) const;
std::string mValue;
};
/**
* CompoundItem is an abstract Item that provides common functionality for Items that contain other
* items, i.e. Arrays (CBOR type 4) and Maps (CBOR type 5).
*/
class CompoundItem : public Item {
public:
bool operator==(const CompoundItem& other) const&;
virtual size_t size() const { return mEntries.size(); }
bool isCompound() const override { return true; }
size_t encodedSize() const override {
return std::accumulate(mEntries.begin(), mEntries.end(), headerSize(size()),
[](size_t sum, auto& entry) { return sum + entry->encodedSize(); });
}
using Item::encode; // Make base versions visible.
uint8_t* encode(uint8_t* pos, const uint8_t* end) const override;
void encode(EncodeCallback encodeCallback) const override;
virtual uint64_t addlInfo() const = 0;
protected:
std::vector<std::unique_ptr<Item>> mEntries;
};
/*
* Array is a concrete Item that implements CBOR major type 4.
*
* Note that Arrays are not copyable. This is because copying them is expensive and making them
* move-only ensures that they're never copied accidentally. If you actually want to copy an Array,
* use the clone() method.
*/
class Array : public CompoundItem {
public:
static constexpr MajorType kMajorType = ARRAY;
Array() = default;
Array(const Array& other) = delete;
Array(Array&&) = default;
Array& operator=(const Array&) = delete;
Array& operator=(Array&&) = default;
/**
* Construct an Array from a variable number of arguments of different types. See
* details::makeItem below for details on what types may be provided. In general, this accepts
* all of the types you'd expect and doest the things you'd expect (integral values are addes as
* Uint or Nint, std::string and char* are added as Tstr, bools are added as Bool, etc.).
*/
template <typename... Args, typename Enable>
Array(Args&&... args);
/**
* Append a single element to the Array, of any compatible type.
*/
template <typename T>
Array& add(T&& v) &;
template <typename T>
Array&& add(T&& v) &&;
const std::unique_ptr<Item>& operator[](size_t index) const { return mEntries[index]; }
std::unique_ptr<Item>& operator[](size_t index) { return mEntries[index]; }
MajorType type() const override { return kMajorType; }
const Array* asArray() const override { return this; }
virtual std::unique_ptr<Item> clone() const override;
uint64_t addlInfo() const override { return size(); }
};
/*
* Map is a concrete Item that implements CBOR major type 5.
*
* Note that Maps are not copyable. This is because copying them is expensive and making them
* move-only ensures that they're never copied accidentally. If you actually want to copy a
* Map, use the clone() method.
*/
class Map : public CompoundItem {
public:
static constexpr MajorType kMajorType = MAP;
Map() = default;
Map(const Map& other) = delete;
Map(Map&&) = default;
Map& operator=(const Map& other) = delete;
Map& operator=(Map&&) = default;
/**
* Construct a Map from a variable number of arguments of different types. An even number of
* arguments must be provided (this is verified statically). See details::makeItem below for
* details on what types may be provided. In general, this accepts all of the types you'd
* expect and doest the things you'd expect (integral values are addes as Uint or Nint,
* std::string and char* are added as Tstr, bools are added as Bool, etc.).
*/
template <typename... Args, typename Enable>
Map(Args&&... args);
/**
* Append a key/value pair to the Map, of any compatible types.
*/
template <typename Key, typename Value>
Map& add(Key&& key, Value&& value) &;
template <typename Key, typename Value>
Map&& add(Key&& key, Value&& value) &&;
size_t size() const override {
assertInvariant();
return mEntries.size() / 2;
}
template <typename Key, typename Enable>
std::pair<std::unique_ptr<Item>&, bool> get(Key key);
std::pair<const std::unique_ptr<Item>&, const std::unique_ptr<Item>&> operator[](
size_t index) const {
assertInvariant();
return {mEntries[index * 2], mEntries[index * 2 + 1]};
}
std::pair<std::unique_ptr<Item>&, std::unique_ptr<Item>&> operator[](size_t index) {
assertInvariant();
return {mEntries[index * 2], mEntries[index * 2 + 1]};
}
MajorType type() const override { return kMajorType; }
const Map* asMap() const override { return this; }
virtual std::unique_ptr<Item> clone() const override;
uint64_t addlInfo() const override { return size(); }
private:
void assertInvariant() const;
};
class Semantic : public CompoundItem {
public:
static constexpr MajorType kMajorType = SEMANTIC;
template <typename T>
explicit Semantic(uint64_t value, T&& child);
Semantic(const Semantic& other) = delete;
Semantic(Semantic&&) = default;
Semantic& operator=(const Semantic& other) = delete;
Semantic& operator=(Semantic&&) = default;
size_t size() const override {
assertInvariant();
return 1;
}
size_t encodedSize() const override {
return std::accumulate(mEntries.begin(), mEntries.end(), headerSize(mValue),
[](size_t sum, auto& entry) { return sum + entry->encodedSize(); });
}
MajorType type() const override { return kMajorType; }
const Semantic* asSemantic() const override { return this; }
const std::unique_ptr<Item>& child() const {
assertInvariant();
return mEntries[0];
}
std::unique_ptr<Item>& child() {
assertInvariant();
return mEntries[0];
}
uint64_t value() const { return mValue; }
uint64_t addlInfo() const override { return value(); }
virtual std::unique_ptr<Item> clone() const override {
assertInvariant();
return std::make_unique<Semantic>(mValue, mEntries[0]->clone());
}
protected:
Semantic() = default;
Semantic(uint64_t value) : mValue(value) {}
uint64_t mValue;
private:
void assertInvariant() const;
};
/**
* Simple is abstract Item that implements CBOR major type 7. It is intended to be subclassed to
* create concrete Simple types. At present only Bool is provided.
*/
class Simple : public Item {
public:
static constexpr MajorType kMajorType = SIMPLE;
bool operator==(const Simple& other) const&;
virtual SimpleType simpleType() const = 0;
MajorType type() const override { return kMajorType; }
const Simple* asSimple() const override { return this; }
virtual const Bool* asBool() const { return nullptr; };
virtual const Null* asNull() const { return nullptr; };
};
/**
* Bool is a concrete type that implements CBOR major type 7, with additional item values for TRUE
* and FALSE.
*/
class Bool : public Simple {
public:
static constexpr SimpleType kSimpleType = BOOLEAN;
explicit Bool(bool v) : mValue(v) {}
bool operator==(const Bool& other) const& { return mValue == other.mValue; }
SimpleType simpleType() const override { return kSimpleType; }
const Bool* asBool() const override { return this; }
size_t encodedSize() const override { return 1; }
using Item::encode;
uint8_t* encode(uint8_t* pos, const uint8_t* end) const override {
return encodeHeader(mValue ? TRUE : FALSE, pos, end);
}
void encode(EncodeCallback encodeCallback) const override {
encodeHeader(mValue ? TRUE : FALSE, encodeCallback);
}
bool value() const { return mValue; }
virtual std::unique_ptr<Item> clone() const override { return std::make_unique<Bool>(mValue); }
private:
bool mValue;
};
/**
* Null is a concrete type that implements CBOR major type 7, with additional item value for NULL
*/
class Null : public Simple {
public:
static constexpr SimpleType kSimpleType = NULL_T;
explicit Null() {}
SimpleType simpleType() const override { return kSimpleType; }
const Null* asNull() const override { return this; }
size_t encodedSize() const override { return 1; }
using Item::encode;
uint8_t* encode(uint8_t* pos, const uint8_t* end) const override {
return encodeHeader(NULL_V, pos, end);
}
void encode(EncodeCallback encodeCallback) const override {
encodeHeader(NULL_V, encodeCallback);
}
virtual std::unique_ptr<Item> clone() const override { return std::make_unique<Null>(); }
};
template <typename T>
std::unique_ptr<T> downcastItem(std::unique_ptr<Item>&& v) {
static_assert(std::is_base_of_v<Item, T> && !std::is_abstract_v<T>,
"returned type is not an Item or is an abstract class");
if (v && T::kMajorType == v->type()) {
if constexpr (std::is_base_of_v<Simple, T>) {
if (T::kSimpleType != v->asSimple()->simpleType()) {
return nullptr;
}
}
return std::unique_ptr<T>(static_cast<T*>(v.release()));
} else {
return nullptr;
}
}
/**
* Details. Mostly you shouldn't have to look below, except perhaps at the docstring for makeItem.
*/
namespace details {
template <typename T, typename V, typename Enable = void>
struct is_iterator_pair_over : public std::false_type {};
template <typename I1, typename I2, typename V>
struct is_iterator_pair_over<
std::pair<I1, I2>, V,
typename std::enable_if_t<std::is_same_v<V, typename std::iterator_traits<I1>::value_type>>>
: public std::true_type {};
template <typename T, typename V, typename Enable = void>
struct is_unique_ptr_of_subclass_of_v : public std::false_type {};
template <typename T, typename P>
struct is_unique_ptr_of_subclass_of_v<T, std::unique_ptr<P>,
typename std::enable_if_t<std::is_base_of_v<T, P>>>
: public std::true_type {};
/* check if type is one of std::string (1), std::string_view (2), null-terminated char* (3) or pair
* of iterators (4)*/
template <typename T, typename Enable = void>
struct is_text_type_v : public std::false_type {};
template <typename T>
struct is_text_type_v<
T, typename std::enable_if_t<
/* case 1 */ //
std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, std::string>
/* case 2 */ //
|| std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, std::string_view>
/* case 3 */ //
|| std::is_same_v<std::remove_cv_t<std::decay_t<T>>, char*> //
|| std::is_same_v<std::remove_cv_t<std::decay_t<T>>, const char*>
/* case 4 */
|| details::is_iterator_pair_over<T, char>::value>> : public std::true_type {};
/**
* Construct a unique_ptr<Item> from many argument types. Accepts:
*
* (a) booleans;
* (b) integers, all sizes and signs;
* (c) text strings, as defined by is_text_type_v above;
* (d) byte strings, as std::vector<uint8_t>(d1), pair of iterators (d2) or pair<uint8_t*, size_T>
* (d3); and
* (e) Item subclass instances, including Array and Map. Items may be provided by naked pointer
* (e1), unique_ptr (e2), reference (e3) or value (e3). If provided by reference or value, will
* be moved if possible. If provided by pointer, ownership is taken.
* (f) null pointer;
*/
template <typename T>
std::unique_ptr<Item> makeItem(T v) {
Item* p = nullptr;
if constexpr (/* case a */ std::is_same_v<T, bool>) {
p = new Bool(v);
} else if constexpr (/* case b */ std::is_integral_v<T>) { // b
if (v < 0) {
p = new Nint(v);
} else {
p = new Uint(static_cast<uint64_t>(v));
}
} else if constexpr (/* case c */ //
details::is_text_type_v<T>::value) {
p = new Tstr(v);
} else if constexpr (/* case d1 */ //
std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>,
std::vector<uint8_t>>
/* case d2 */ //
|| details::is_iterator_pair_over<T, uint8_t>::value
/* case d3 */ //
|| std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>,
std::pair<uint8_t*, size_t>>) {
p = new Bstr(v);
} else if constexpr (/* case e1 */ //
std::is_pointer_v<T> &&
std::is_base_of_v<Item, std::remove_pointer_t<T>>) {
p = v;
} else if constexpr (/* case e2 */ //
details::is_unique_ptr_of_subclass_of_v<Item, T>::value) {
p = v.release();
} else if constexpr (/* case e3 */ //
std::is_base_of_v<Item, T>) {
p = new T(std::move(v));
} else if constexpr (/* case f */ std::is_null_pointer_v<T>) {
p = new Null();
} else {
// It's odd that this can't be static_assert(false), since it shouldn't be evaluated if one
// of the above ifs matches. But static_assert(false) always triggers.
static_assert(std::is_same_v<T, bool>, "makeItem called with unsupported type");
}
return std::unique_ptr<Item>(p);
}
} // namespace details
template <typename... Args,
/* Prevent use as copy ctor */ typename = std::enable_if_t<
(sizeof...(Args)) != 1 ||
!(std::is_same_v<Array, std::remove_cv_t<std::remove_reference_t<Args>>> || ...)>>
Array::Array(Args&&... args) {
mEntries.reserve(sizeof...(args));
(mEntries.push_back(details::makeItem(std::forward<Args>(args))), ...);
}
template <typename T>
Array& Array::add(T&& v) & {
mEntries.push_back(details::makeItem(std::forward<T>(v)));
return *this;
}
template <typename T>
Array&& Array::add(T&& v) && {
mEntries.push_back(details::makeItem(std::forward<T>(v)));
return std::move(*this);
}
template <typename... Args,
/* Prevent use as copy ctor */ typename = std::enable_if_t<(sizeof...(Args)) != 1>>
Map::Map(Args&&... args) {
static_assert((sizeof...(Args)) % 2 == 0, "Map must have an even number of entries");
mEntries.reserve(sizeof...(args));
(mEntries.push_back(details::makeItem(std::forward<Args>(args))), ...);
}
template <typename Key, typename Value>
Map& Map::add(Key&& key, Value&& value) & {
mEntries.push_back(details::makeItem(std::forward<Key>(key)));
mEntries.push_back(details::makeItem(std::forward<Value>(value)));
return *this;
}
template <typename Key, typename Value>
Map&& Map::add(Key&& key, Value&& value) && {
this->add(std::forward<Key>(key), std::forward<Value>(value));
return std::move(*this);
}
template <typename Key, typename = std::enable_if_t<std::is_integral_v<Key> ||
details::is_text_type_v<Key>::value>>
std::pair<std::unique_ptr<Item>&, bool> Map::get(Key key) {
assertInvariant();
auto keyItem = details::makeItem(key);
for (size_t i = 0; i < mEntries.size(); i += 2) {
if (*keyItem == *mEntries[i]) {
return {mEntries[i + 1], true};
}
}
return {keyItem, false};
}
template <typename T>
Semantic::Semantic(uint64_t value, T&& child) : mValue(value) {
mEntries.reserve(1);
mEntries.push_back(details::makeItem(std::forward<T>(child)));
}
} // namespace cppbor

View File

@@ -1,133 +0,0 @@
/*
* Copyright (c) 2019, The Android Open Source Project
*
* 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.
*/
#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 remining 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 remining 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 remining 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);
}
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

View File

@@ -1,225 +0,0 @@
/*
* Copyright (c) 2019, The Android Open Source Project
*
* 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.
*/
#include "cppbor.h"
#include "cppbor_parse.h"
#define LOG_TAG "CppBor"
#include <android-base/logging.h>
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);
}
}
} // 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:
return *asBstr() == *(other.asBstr());
case TSTR:
return *asTstr() == *(other.asTstr());
case ARRAY:
return *asArray() == *(other.asArray());
case MAP:
return *asMap() == *(other.asMap());
case SIMPLE:
return *asSimple() == *(other.asSimple());
case SEMANTIC:
return *asSemantic() == *(other.asSemantic());
default:
CHECK(false); // Impossible to get here.
return false;
}
}
Nint::Nint(int64_t v) : mValue(v) {
CHECK(v < 0) << "Only negative values allowed";
}
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 CompoundItem::operator==(const CompoundItem& other) const& {
return type() == other.type() //
&& addlInfo() == other.addlInfo() //
// 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(),
[](auto& a, auto& b) -> bool { return *a == *b; });
}
uint8_t* CompoundItem::encode(uint8_t* pos, const uint8_t* end) const {
pos = encodeHeader(addlInfo(), pos, end);
if (!pos) return nullptr;
for (auto& entry : mEntries) {
pos = entry->encode(pos, end);
if (!pos) return nullptr;
}
return pos;
}
void CompoundItem::encode(EncodeCallback encodeCallback) const {
encodeHeader(addlInfo(), encodeCallback);
for (auto& entry : mEntries) {
entry->encode(encodeCallback);
}
}
void Map::assertInvariant() const {
CHECK(mEntries.size() % 2 == 0);
}
std::unique_ptr<Item> Map::clone() const {
assertInvariant();
auto res = std::make_unique<Map>();
for (size_t i = 0; i < mEntries.size(); i += 2) {
res->add(mEntries[i]->clone(), mEntries[i + 1]->clone());
}
return res;
}
std::unique_ptr<Item> Array::clone() const {
auto res = std::make_unique<Array>();
for (size_t i = 0; i < mEntries.size(); i++) {
res->add(mEntries[i]->clone());
}
return res;
}
void Semantic::assertInvariant() const {
CHECK(mEntries.size() == 1);
}
} // namespace cppbor

View File

@@ -1,351 +0,0 @@
/*
* Copyright (c) 2019, The Android Open Source Project
*
* 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.
*/
#include "cppbor_parse.h"
#include <sstream>
#include <stack>
#define LOG_TAG "CppBor"
#include <android-base/logging.h>
namespace cppbor {
namespace {
std::string insufficientLengthString(size_t bytesNeeded, size_t bytesAvail,
const std::string& type) {
std::stringstream errStream;
errStream << "Need " << bytesNeeded << " byte(s) for " << type << ", have " << bytesAvail
<< ".";
return errStream.str();
}
template <typename T, typename = std::enable_if_t<std::is_unsigned_v<T>>>
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,
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<uint64_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) {
if (end - valueBegin < static_cast<ssize_t>(length)) {
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:
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:
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 {
mEntries.reserve(mSize * 2);
mEntries.push_back(std::move(item));
}
private:
size_t mSize;
};
class IncompleteSemantic : public Semantic, public IncompleteItem {
public:
IncompleteSemantic(uint64_t value) : Semantic(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 {
mEntries.reserve(1);
mEntries.push_back(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,
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, 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,
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, 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,
ParseClient* parseClient) {
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 || tagInt > EIGHT_BYTE_LENGTH) {
addlData = tagInt;
} 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:
CHECK(false); // It's impossible to get here
break;
}
}
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", parseClient);
case MAP:
return handleCompound(std::make_unique<IncompleteMap>(addlData), addlData * 2, begin,
pos, end, "map", parseClient);
case SEMANTIC:
return handleCompound(std::make_unique<IncompleteSemantic>(addlData), 1, begin, pos,
end, "semantic", parseClient);
case SIMPLE:
switch (addlData) {
case TRUE:
case FALSE:
return handleBool(addlData, begin, pos, parseClient);
case NULL_V:
return handleNull(begin, pos, parseClient);
}
}
CHECK(false); // Impossible to get here.
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.
assert(dynamic_cast<CompoundItem*>(item.get()));
mParentStack.push(static_cast<CompoundItem*>(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 {
CHECK(item->isCompound() && item.get() == mParentStack.top());
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();
assert(dynamic_cast<IncompleteItem*>(parent));
if (parent->type() == ARRAY) {
static_cast<IncompleteArray*>(parent)->add(std::move(item));
} else if (parent->type() == MAP) {
static_cast<IncompleteMap*>(parent)->add(std::move(item));
} else if (parent->type() == SEMANTIC) {
static_cast<IncompleteSemantic*>(parent)->add(std::move(item));
} else {
CHECK(false); // Impossible to get here.
}
}
std::unique_ptr<Item> mTheItem;
std::stack<CompoundItem*> 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, 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