diff --git a/identity/support/Android.bp b/identity/support/Android.bp index 4e3d1f75e5..3096fe5f2c 100644 --- a/identity/support/Android.bp +++ b/identity/support/Android.bp @@ -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"], -} diff --git a/identity/support/include/cppbor/README.md b/identity/support/include/cppbor/README.md deleted file mode 100644 index 723bfcfbaf..0000000000 --- a/identity/support/include/cppbor/README.md +++ /dev/null @@ -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` convert to `Tstr`. -* `std::vector`, `std::pair` and `std::pair` 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 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 vec = // ... - Map val("key1", Array(Map("key_a", 99 "key_b", vec), "foo"), "key2", true); -std::vector 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 vec = // ... - Map val(); -val.add("key1", Array().add(Map().add("key_a", 99).add("key_b", vec)).add("foo")).add("key2", true); -std::vector 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 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 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. diff --git a/identity/support/include/cppbor/cppbor.h b/identity/support/include/cppbor/cppbor.h deleted file mode 100644 index af5d82e8bd..0000000000 --- a/identity/support/include/cppbor/cppbor.h +++ /dev/null @@ -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 -#include -#include -#include -#include -#include -#include - -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; - -/** - * 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 ::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 clone() const = 0; - - /** - * Encodes the Item into the provided OutputIterator. - */ - template ::iterator_category> - void encode(OutputIterator i) const { - return encode([&](uint8_t v) { *i++ = v; }); - } - - /** - * Encodes the Item into a new std::vector. - */ - std::vector encode() const { - std::vector 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 clone() const override { return std::make_unique(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::min(), -1]. It cannot - * express values in the range [std::numeric_limits::min() - 1, - * -std::numeric_limits::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 clone() const override { return std::make_unique(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 v) : mValue(std::move(v)) {} - - // Construct from a string - explicit Bstr(const std::string& v) - : mValue(reinterpret_cast(v.data()), - reinterpret_cast(v.data()) + v.size()) {} - - // Construct from a pointer/size pair - explicit Bstr(const std::pair& buf) - : mValue(buf.first, buf.first + buf.second) {} - - // Construct from a pair of iterators - template ::iterator_category, - typename = typename std::iterator_traits::iterator_category> - explicit Bstr(const std::pair& pair) : mValue(pair.first, pair.second) {} - - // Construct from an iterator range. - template ::iterator_category, - typename = typename std::iterator_traits::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& value() const { return mValue; } - - virtual std::unique_ptr clone() const override { return std::make_unique(mValue); } - - private: - void encodeValue(EncodeCallback encodeCallback) const; - - std::vector 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 ::iterator_category, - typename = typename std::iterator_traits::iterator_category> - explicit Tstr(const std::pair& pair) : mValue(pair.first, pair.second) {} - - // Construct from an iterator range - template ::iterator_category, - typename = typename std::iterator_traits::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 clone() const override { return std::make_unique(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> 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 - Array(Args&&... args); - - /** - * Append a single element to the Array, of any compatible type. - */ - template - Array& add(T&& v) &; - template - Array&& add(T&& v) &&; - - const std::unique_ptr& operator[](size_t index) const { return mEntries[index]; } - std::unique_ptr& operator[](size_t index) { return mEntries[index]; } - - MajorType type() const override { return kMajorType; } - const Array* asArray() const override { return this; } - - virtual std::unique_ptr 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 - Map(Args&&... args); - - /** - * Append a key/value pair to the Map, of any compatible types. - */ - template - Map& add(Key&& key, Value&& value) &; - template - Map&& add(Key&& key, Value&& value) &&; - - size_t size() const override { - assertInvariant(); - return mEntries.size() / 2; - } - - template - std::pair&, bool> get(Key key); - - std::pair&, const std::unique_ptr&> operator[]( - size_t index) const { - assertInvariant(); - return {mEntries[index * 2], mEntries[index * 2 + 1]}; - } - - std::pair&, std::unique_ptr&> 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 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 - 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& child() const { - assertInvariant(); - return mEntries[0]; - } - - std::unique_ptr& child() { - assertInvariant(); - return mEntries[0]; - } - - uint64_t value() const { return mValue; } - - uint64_t addlInfo() const override { return value(); } - - virtual std::unique_ptr clone() const override { - assertInvariant(); - return std::make_unique(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 clone() const override { return std::make_unique(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 clone() const override { return std::make_unique(); } -}; - -template -std::unique_ptr downcastItem(std::unique_ptr&& v) { - static_assert(std::is_base_of_v && !std::is_abstract_v, - "returned type is not an Item or is an abstract class"); - if (v && T::kMajorType == v->type()) { - if constexpr (std::is_base_of_v) { - if (T::kSimpleType != v->asSimple()->simpleType()) { - return nullptr; - } - } - return std::unique_ptr(static_cast(v.release())); - } else { - return nullptr; - } -} - -/** - * Details. Mostly you shouldn't have to look below, except perhaps at the docstring for makeItem. - */ -namespace details { - -template -struct is_iterator_pair_over : public std::false_type {}; - -template -struct is_iterator_pair_over< - std::pair, V, - typename std::enable_if_t::value_type>>> - : public std::true_type {}; - -template -struct is_unique_ptr_of_subclass_of_v : public std::false_type {}; - -template -struct is_unique_ptr_of_subclass_of_v, - typename std::enable_if_t>> - : 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 -struct is_text_type_v : public std::false_type {}; - -template -struct is_text_type_v< - T, typename std::enable_if_t< - /* case 1 */ // - std::is_same_v>, std::string> - /* case 2 */ // - || std::is_same_v>, std::string_view> - /* case 3 */ // - || std::is_same_v>, char*> // - || std::is_same_v>, const char*> - /* case 4 */ - || details::is_iterator_pair_over::value>> : public std::true_type {}; - -/** - * Construct a unique_ptr 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(d1), pair of iterators (d2) or pair - * (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 -std::unique_ptr makeItem(T v) { - Item* p = nullptr; - if constexpr (/* case a */ std::is_same_v) { - p = new Bool(v); - } else if constexpr (/* case b */ std::is_integral_v) { // b - if (v < 0) { - p = new Nint(v); - } else { - p = new Uint(static_cast(v)); - } - } else if constexpr (/* case c */ // - details::is_text_type_v::value) { - p = new Tstr(v); - } else if constexpr (/* case d1 */ // - std::is_same_v>, - std::vector> - /* case d2 */ // - || details::is_iterator_pair_over::value - /* case d3 */ // - || std::is_same_v>, - std::pair>) { - p = new Bstr(v); - } else if constexpr (/* case e1 */ // - std::is_pointer_v && - std::is_base_of_v>) { - p = v; - } else if constexpr (/* case e2 */ // - details::is_unique_ptr_of_subclass_of_v::value) { - p = v.release(); - } else if constexpr (/* case e3 */ // - std::is_base_of_v) { - p = new T(std::move(v)); - } else if constexpr (/* case f */ std::is_null_pointer_v) { - 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, "makeItem called with unsupported type"); - } - return std::unique_ptr(p); -} - -} // namespace details - -template >> || ...)>> -Array::Array(Args&&... args) { - mEntries.reserve(sizeof...(args)); - (mEntries.push_back(details::makeItem(std::forward(args))), ...); -} - -template -Array& Array::add(T&& v) & { - mEntries.push_back(details::makeItem(std::forward(v))); - return *this; -} - -template -Array&& Array::add(T&& v) && { - mEntries.push_back(details::makeItem(std::forward(v))); - return std::move(*this); -} - -template > -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))), ...); -} - -template -Map& Map::add(Key&& key, Value&& value) & { - mEntries.push_back(details::makeItem(std::forward(key))); - mEntries.push_back(details::makeItem(std::forward(value))); - return *this; -} - -template -Map&& Map::add(Key&& key, Value&& value) && { - this->add(std::forward(key), std::forward(value)); - return std::move(*this); -} - -template || - details::is_text_type_v::value>> -std::pair&, 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 -Semantic::Semantic(uint64_t value, T&& child) : mValue(value) { - mEntries.reserve(1); - mEntries.push_back(details::makeItem(std::forward(child))); -} - -} // namespace cppbor diff --git a/identity/support/include/cppbor/cppbor_parse.h b/identity/support/include/cppbor/cppbor_parse.h deleted file mode 100644 index 66cd5a3d62..0000000000 --- a/identity/support/include/cppbor/cppbor_parse.h +++ /dev/null @@ -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 /* 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& 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& 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, 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, 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 diff --git a/identity/support/src/cppbor.cpp b/identity/support/src/cppbor.cpp deleted file mode 100644 index d289985175..0000000000 --- a/identity/support/src/cppbor.cpp +++ /dev/null @@ -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 - -namespace cppbor { - -namespace { - -template ::value>> -Iterator writeBigEndian(T value, Iterator pos) { - for (unsigned i = 0; i < sizeof(value); ++i) { - *pos++ = static_cast(value >> (8 * (sizeof(value) - 1))); - value = static_cast(value << 8); - } - return pos; -} - -template ::value>> -void writeBigEndian(T value, std::function& cb) { - for (unsigned i = 0; i < sizeof(value); ++i) { - cb(static_cast(value >> (8 * (sizeof(value) - 1)))); - value = static_cast(value << 8); - } -} - -} // namespace - -size_t headerSize(uint64_t addlInfo) { - if (addlInfo < ONE_BYTE_LENGTH) return 1; - if (addlInfo <= std::numeric_limits::max()) return 2; - if (addlInfo <= std::numeric_limits::max()) return 3; - if (addlInfo <= std::numeric_limits::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(sz)) return nullptr; - switch (sz) { - case 1: - *pos++ = type | static_cast(addlInfo); - return pos; - case 2: - *pos++ = type | ONE_BYTE_LENGTH; - *pos++ = static_cast(addlInfo); - return pos; - case 3: - *pos++ = type | TWO_BYTE_LENGTH; - return writeBigEndian(static_cast(addlInfo), pos); - case 5: - *pos++ = type | FOUR_BYTE_LENGTH; - return writeBigEndian(static_cast(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(addlInfo)); - break; - case 2: - encodeCallback(type | ONE_BYTE_LENGTH); - encodeCallback(static_cast(addlInfo)); - break; - case 3: - encodeCallback(type | TWO_BYTE_LENGTH); - writeBigEndian(static_cast(addlInfo), encodeCallback); - break; - case 5: - encodeCallback(type | FOUR_BYTE_LENGTH); - writeBigEndian(static_cast(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(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(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(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 Map::clone() const { - assertInvariant(); - auto res = std::make_unique(); - for (size_t i = 0; i < mEntries.size(); i += 2) { - res->add(mEntries[i]->clone(), mEntries[i + 1]->clone()); - } - return res; -} - -std::unique_ptr Array::clone() const { - auto res = std::make_unique(); - 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 diff --git a/identity/support/src/cppbor_parse.cpp b/identity/support/src/cppbor_parse.cpp deleted file mode 100644 index c9ebb8ac1a..0000000000 --- a/identity/support/src/cppbor_parse.cpp +++ /dev/null @@ -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 -#include - -#define LOG_TAG "CppBor" -#include - -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 >> -std::tuple 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((result << 8) | *pos++); - } while (pos < intEnd); - return {true, result, pos}; -} - -std::tuple parseRecursively(const uint8_t* begin, const uint8_t* end, - ParseClient* parseClient); - -std::tuple handleUint(uint64_t value, const uint8_t* hdrBegin, - const uint8_t* hdrEnd, - ParseClient* parseClient) { - std::unique_ptr item = std::make_unique(value); - return {hdrEnd, - parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)}; -} - -std::tuple handleNint(uint64_t value, const uint8_t* hdrBegin, - const uint8_t* hdrEnd, - ParseClient* parseClient) { - if (value > std::numeric_limits::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 = std::make_unique(-1 - static_cast(value)); - return {hdrEnd, - parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)}; -} - -std::tuple handleBool(uint64_t value, const uint8_t* hdrBegin, - const uint8_t* hdrEnd, - ParseClient* parseClient) { - std::unique_ptr item = std::make_unique(value == TRUE); - return {hdrEnd, - parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)}; -} - -std::tuple handleNull(const uint8_t* hdrBegin, const uint8_t* hdrEnd, - ParseClient* parseClient) { - std::unique_ptr item = std::make_unique(); - return {hdrEnd, - parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)}; -} - -template -std::tuple 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(length)) { - parseClient->error(hdrBegin, insufficientLengthString(length, end - valueBegin, errLabel)); - return {hdrBegin, nullptr /* end parsing */}; - } - - std::unique_ptr item = std::make_unique(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) = 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) 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) 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) override { - mEntries.reserve(1); - mEntries.push_back(std::move(item)); - } -}; - -std::tuple 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 handleCompound( - std::unique_ptr 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 parseRecursively(const uint8_t* begin, const uint8_t* end, - ParseClient* parseClient) { - const uint8_t* pos = begin; - - MajorType type = static_cast(*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(pos, end, parseClient); - break; - - case TWO_BYTE_LENGTH: - std::tie(success, addlData, pos) = parseLength(pos, end, parseClient); - break; - - case FOUR_BYTE_LENGTH: - std::tie(success, addlData, pos) = parseLength(pos, end, parseClient); - break; - - case EIGHT_BYTE_LENGTH: - std::tie(success, addlData, pos) = parseLength(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(addlData, begin, pos, end, "byte string", parseClient); - - case TSTR: - return handleString(addlData, begin, pos, end, "text string", parseClient); - - case ARRAY: - return handleCompound(std::make_unique(addlData), addlData, begin, pos, - end, "array", parseClient); - - case MAP: - return handleCompound(std::make_unique(addlData), addlData * 2, begin, - pos, end, "map", parseClient); - - case SEMANTIC: - return handleCompound(std::make_unique(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, 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(item.get())); - mParentStack.push(static_cast(item.get())); - return this; - } else { - appendToLastParent(std::move(item)); - return this; - } - } - - virtual ParseClient* itemEnd(std::unique_ptr& 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 /* result */, const uint8_t* /* newPos */, - std::string /* errMsg */> - parseResult() { - std::unique_ptr p = std::move(mTheItem); - return {std::move(p), mPosition, std::move(mErrorMessage)}; - } - - private: - void appendToLastParent(std::unique_ptr item) { - auto parent = mParentStack.top(); - assert(dynamic_cast(parent)); - if (parent->type() == ARRAY) { - static_cast(parent)->add(std::move(item)); - } else if (parent->type() == MAP) { - static_cast(parent)->add(std::move(item)); - } else if (parent->type() == SEMANTIC) { - static_cast(parent)->add(std::move(item)); - } else { - CHECK(false); // Impossible to get here. - } - } - - std::unique_ptr mTheItem; - std::stack 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 /* 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 diff --git a/identity/support/tests/cppbor_test.cpp b/identity/support/tests/cppbor_test.cpp deleted file mode 100644 index 3eb5598961..0000000000 --- a/identity/support/tests/cppbor_test.cpp +++ /dev/null @@ -1,1013 +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 -#include - -#include -#include - -#include "cppbor.h" -#include "cppbor_parse.h" - -using namespace cppbor; -using namespace std; - -using ::testing::_; -using ::testing::AllOf; -using ::testing::ByRef; -using ::testing::InSequence; -using ::testing::IsNull; -using ::testing::NotNull; -using ::testing::Return; -using ::testing::Truly; -using ::testing::Unused; - -string hexDump(const string& str) { - stringstream s; - for (auto c : str) { - s << setfill('0') << setw(2) << hex << (static_cast(c) & 0xff); - } - return s.str(); -} - -TEST(SimpleValueTest, UnsignedValueSizes) { - // Check that unsigned integers encode to correct lengths, and that encodedSize() is correct. - vector> testCases{ - {0, 1}, - {1, 1}, - {23, 1}, - {24, 2}, - {255, 2}, - {256, 3}, - {65535, 3}, - {65536, 5}, - {4294967295, 5}, - {4294967296, 9}, - {std::numeric_limits::max(), 9}, - }; - for (auto& testCase : testCases) { - Uint val(testCase.first); - EXPECT_EQ(testCase.second, val.encodedSize()) << "Wrong size for value " << testCase.first; - EXPECT_EQ(val.encodedSize(), val.toString().size()) - << "encodedSize and encoding disagree for value " << testCase.first; - } -} - -TEST(SimpleValueTest, UnsignedValueEncodings) { - EXPECT_EQ("\x00"s, Uint(0u).toString()); - EXPECT_EQ("\x01"s, Uint(1u).toString()); - EXPECT_EQ("\x0a"s, Uint(10u).toString()); - EXPECT_EQ("\x17"s, Uint(23u).toString()); - EXPECT_EQ("\x18\x18"s, Uint(24u).toString()); - EXPECT_EQ("\x18\x19"s, Uint(25u).toString()); - EXPECT_EQ("\x18\x64"s, Uint(100u).toString()); - EXPECT_EQ("\x19\x03\xe8"s, Uint(1000u).toString()); - EXPECT_EQ("\x1a\x00\x0f\x42\x40"s, Uint(1000000u).toString()); - EXPECT_EQ("\x1b\x00\x00\x00\xe8\xd4\xa5\x10\x00"s, Uint(1000000000000u).toString()); - EXPECT_EQ("\x1B\x7f\xff\xff\xff\xff\xff\xff\xff"s, - Uint(std::numeric_limits::max()).toString()); -} - -TEST(SimpleValueTest, NegativeValueEncodings) { - EXPECT_EQ("\x20"s, Nint(-1).toString()); - EXPECT_EQ("\x28"s, Nint(-9).toString()); - EXPECT_EQ("\x29"s, Nint(-10).toString()); - EXPECT_EQ("\x36"s, Nint(-23).toString()); - EXPECT_EQ("\x37"s, Nint(-24).toString()); - EXPECT_EQ("\x38\x18"s, Nint(-25).toString()); - EXPECT_EQ("\x38\x62"s, Nint(-99).toString()); - EXPECT_EQ("\x38\x63"s, Nint(-100).toString()); - EXPECT_EQ("\x39\x03\xe6"s, Nint(-999).toString()); - EXPECT_EQ("\x39\x03\xe7"s, Nint(-1000).toString()); - EXPECT_EQ("\x3a\x00\x0f\x42\x3F"s, Nint(-1000000).toString()); - EXPECT_EQ("\x3b\x00\x00\x00\xe8\xd4\xa5\x0f\xff"s, Nint(-1000000000000).toString()); - EXPECT_EQ("\x3B\x7f\xff\xff\xff\xff\xff\xff\xff"s, - Nint(std::numeric_limits::min()).toString()); -} - -TEST(SimpleValueDeathTest, NegativeValueEncodings) { - EXPECT_DEATH(Nint(0), ""); - EXPECT_DEATH(Nint(1), ""); -} - -TEST(SimpleValueTest, BooleanEncodings) { - EXPECT_EQ("\xf4"s, Bool(false).toString()); - EXPECT_EQ("\xf5"s, Bool(true).toString()); -} - -TEST(SimpleValueTest, ByteStringEncodings) { - EXPECT_EQ("\x40", Bstr("").toString()); - EXPECT_EQ("\x41\x61", Bstr("a").toString()); - EXPECT_EQ("\x41\x41", Bstr("A").toString()); - EXPECT_EQ("\x44\x49\x45\x54\x46", Bstr("IETF").toString()); - EXPECT_EQ("\x42\x22\x5c", Bstr("\"\\").toString()); - EXPECT_EQ("\x42\xc3\xbc", Bstr("\xc3\xbc").toString()); - EXPECT_EQ("\x43\xe6\xb0\xb4", Bstr("\xe6\xb0\xb4").toString()); - EXPECT_EQ("\x44\xf0\x90\x85\x91", Bstr("\xf0\x90\x85\x91").toString()); - EXPECT_EQ("\x44\x01\x02\x03\x04", Bstr("\x01\x02\x03\x04").toString()); - EXPECT_EQ("\x44\x40\x40\x40\x40", Bstr("@@@@").toString()); -} - -TEST(SimpleValueTest, TextStringEncodings) { - EXPECT_EQ("\x60"s, Tstr("").toString()); - EXPECT_EQ("\x61\x61"s, Tstr("a").toString()); - EXPECT_EQ("\x61\x41"s, Tstr("A").toString()); - EXPECT_EQ("\x64\x49\x45\x54\x46"s, Tstr("IETF").toString()); - EXPECT_EQ("\x62\x22\x5c"s, Tstr("\"\\").toString()); - EXPECT_EQ("\x62\xc3\xbc"s, Tstr("\xc3\xbc").toString()); - EXPECT_EQ("\x63\xe6\xb0\xb4"s, Tstr("\xe6\xb0\xb4").toString()); - EXPECT_EQ("\x64\xf0\x90\x85\x91"s, Tstr("\xf0\x90\x85\x91").toString()); - EXPECT_EQ("\x64\x01\x02\x03\x04"s, Tstr("\x01\x02\x03\x04").toString()); -} - -TEST(IsIteratorPairOverTest, All) { - EXPECT_TRUE(( - details::is_iterator_pair_over, char>::value)); - EXPECT_TRUE((details::is_iterator_pair_over, - char>::value)); - EXPECT_TRUE((details::is_iterator_pair_over, - char>::value)); - EXPECT_TRUE((details::is_iterator_pair_over, char>::value)); - EXPECT_TRUE((details::is_iterator_pair_over, char>::value)); - EXPECT_TRUE((details::is_iterator_pair_over, char>::value)); - EXPECT_FALSE((details::is_iterator_pair_over, - uint8_t>::value)); - EXPECT_FALSE((details::is_iterator_pair_over, uint8_t>::value)); - EXPECT_TRUE((details::is_iterator_pair_over< - pair::iterator, vector::iterator>, uint8_t>::value)); - EXPECT_TRUE((details::is_iterator_pair_over< - pair::const_iterator, vector::iterator>, - uint8_t>::value)); - EXPECT_TRUE((details::is_iterator_pair_over< - pair::iterator, vector::const_iterator>, - uint8_t>::value)); - EXPECT_TRUE((details::is_iterator_pair_over, uint8_t>::value)); - EXPECT_TRUE((details::is_iterator_pair_over, uint8_t>::value)); - EXPECT_TRUE((details::is_iterator_pair_over, uint8_t>::value)); - EXPECT_FALSE((details::is_iterator_pair_over< - pair::iterator, vector::iterator>, char>::value)); - EXPECT_FALSE((details::is_iterator_pair_over, char>::value)); -} - -TEST(MakeEntryTest, Boolean) { - EXPECT_EQ("\xf4"s, details::makeItem(false)->toString()); -} - -TEST(MakeEntryTest, Integers) { - EXPECT_EQ("\x00"s, details::makeItem(static_cast(0))->toString()); - EXPECT_EQ("\x00"s, details::makeItem(static_cast(0))->toString()); - EXPECT_EQ("\x00"s, details::makeItem(static_cast(0))->toString()); - EXPECT_EQ("\x00"s, details::makeItem(static_cast(0))->toString()); - EXPECT_EQ("\x00"s, details::makeItem(static_cast(0))->toString()); - EXPECT_EQ("\x00"s, details::makeItem(static_cast(0))->toString()); - EXPECT_EQ("\x00"s, details::makeItem(static_cast(0))->toString()); - EXPECT_EQ("\x00"s, details::makeItem(static_cast(0))->toString()); - EXPECT_EQ("\x20"s, details::makeItem(static_cast(-1))->toString()); - EXPECT_EQ("\x20"s, details::makeItem(static_cast(-1))->toString()); - EXPECT_EQ("\x20"s, details::makeItem(static_cast(-1))->toString()); - EXPECT_EQ("\x20"s, details::makeItem(static_cast(-1))->toString()); - - EXPECT_EQ("\x1b\xff\xff\xff\xff\xff\xff\xff\xff"s, - details::makeItem(static_cast(std::numeric_limits::max())) - ->toString()); -} - -TEST(MakeEntryTest, StdStrings) { - string s1("hello"); - const string s2("hello"); - EXPECT_EQ("\x65\x68\x65\x6c\x6c\x6f"s, details::makeItem(s1)->toString()); // copy of string - EXPECT_EQ("\x65\x68\x65\x6c\x6c\x6f"s, - details::makeItem(s2)->toString()); // copy of const string - EXPECT_EQ("\x65\x68\x65\x6c\x6c\x6f"s, - details::makeItem(std::move(s1))->toString()); // move string - EXPECT_EQ(0U, s1.size()); // Prove string was moved, not copied. -} - -TEST(MakeEntryTest, StdStringViews) { - string_view s1("hello"); - const string_view s2("hello"); - EXPECT_EQ("\x65\x68\x65\x6c\x6c\x6f"s, details::makeItem(s1)->toString()); - EXPECT_EQ("\x65\x68\x65\x6c\x6c\x6f"s, details::makeItem(s2)->toString()); -} - -TEST(MakeEntryTest, CStrings) { - char s1[] = "hello"; - const char s2[] = "hello"; - const char* s3 = "hello"; - EXPECT_EQ("\x65\x68\x65\x6c\x6c\x6f"s, details::makeItem(s1)->toString()); - EXPECT_EQ("\x65\x68\x65\x6c\x6c\x6f"s, details::makeItem(s2)->toString()); - EXPECT_EQ("\x65\x68\x65\x6c\x6c\x6f"s, details::makeItem(s3)->toString()); -} - -TEST(MakeEntryTest, StringIteratorPairs) { - // Use iterators from string to prove that "real" iterators work - string s1 = "hello"s; - pair p1 = make_pair(s1.begin(), s1.end()); - - const pair p2 = p1; - EXPECT_EQ("\x65\x68\x65\x6c\x6c\x6f"s, details::makeItem(p1)->toString()); - EXPECT_EQ("\x65\x68\x65\x6c\x6c\x6f"s, details::makeItem(p2)->toString()); - - // Use char*s as iterators - const char* s2 = "hello"; - pair p3 = make_pair(s2, s2 + 5); - const pair p4 = p3; - EXPECT_EQ("\x65\x68\x65\x6c\x6c\x6f"s, details::makeItem(p3)->toString()); - EXPECT_EQ("\x65\x68\x65\x6c\x6c\x6f"s, details::makeItem(p4)->toString()); -} - -TEST(MakeEntryTest, ByteStrings) { - vector v1 = {0x00, 0x01, 0x02}; - const vector v2 = {0x00, 0x01, 0x02}; - EXPECT_EQ("\x43\x00\x01\x02"s, details::makeItem(v1)->toString()); // copy of vector - EXPECT_EQ("\x43\x00\x01\x02"s, details::makeItem(v2)->toString()); // copy of const vector - EXPECT_EQ("\x43\x00\x01\x02"s, details::makeItem(std::move(v1))->toString()); // move vector - EXPECT_EQ(0U, v1.size()); // Prove vector was moved, not copied. -} - -TEST(MakeEntryTest, ByteStringIteratorPairs) { - using vec = vector; - using iter = vec::iterator; - vec v1 = {0x00, 0x01, 0x02}; - pair p1 = make_pair(v1.begin(), v1.end()); - const pair p2 = make_pair(v1.begin(), v1.end()); - EXPECT_EQ("\x43\x00\x01\x02"s, details::makeItem(p1)->toString()); - EXPECT_EQ("\x43\x00\x01\x02"s, details::makeItem(p2)->toString()); - - // Use uint8_t*s as iterators - uint8_t v2[] = {0x00, 0x01, 0x02}; - uint8_t* v3 = v2; - pair p3 = make_pair(v2, v2 + 3); - const pair p4 = make_pair(v2, v2 + 3); - pair p5 = make_pair(v3, v3 + 3); - const pair p6 = make_pair(v3, v3 + 3); - EXPECT_EQ("\x43\x00\x01\x02"s, details::makeItem(p3)->toString()); - EXPECT_EQ("\x43\x00\x01\x02"s, details::makeItem(p4)->toString()); - EXPECT_EQ("\x43\x00\x01\x02"s, details::makeItem(p5)->toString()); - EXPECT_EQ("\x43\x00\x01\x02"s, details::makeItem(p6)->toString()); -} - -TEST(MakeEntryTest, ByteStringBuffers) { - uint8_t v1[] = {0x00, 0x01, 0x02}; - EXPECT_EQ("\x43\x00\x01\x02"s, details::makeItem(make_pair(v1, 3))->toString()); -} - -TEST(MakeEntryTest, ItemPointer) { - Uint* p1 = new Uint(0); - EXPECT_EQ("\x00"s, details::makeItem(p1)->toString()); - EXPECT_EQ("\x60"s, details::makeItem(new Tstr(string()))->toString()); -} - -TEST(MakeEntryTest, ItemReference) { - Tstr str("hello"s); - Tstr& strRef = str; - const Tstr& strConstRef = str; - EXPECT_EQ("\x65\x68\x65\x6c\x6c\x6f"s, details::makeItem(str)->toString()); - EXPECT_EQ("\x65\x68\x65\x6c\x6c\x6f"s, details::makeItem(strRef)->toString()); - EXPECT_EQ("\x65\x68\x65\x6c\x6c\x6f"s, details::makeItem(strConstRef)->toString()); - EXPECT_EQ("\x65\x68\x65\x6c\x6c\x6f"s, details::makeItem(std::move(str))->toString()); - EXPECT_EQ("\x60"s, details::makeItem(str)->toString()); // Prove that it moved - - EXPECT_EQ("\x00"s, details::makeItem(Uint(0))->toString()); - - EXPECT_EQ("\x43\x00\x01\x02"s, - details::makeItem(Bstr(vector{0x00, 0x01, 0x02}))->toString()); - - EXPECT_EQ("\x80"s, details::makeItem(Array())->toString()); - EXPECT_EQ("\xa0"s, details::makeItem(Map())->toString()); -} - -TEST(CompoundValueTest, ArrayOfInts) { - EXPECT_EQ("\x80"s, Array().toString()); - Array(Uint(0)).toString(); - - EXPECT_EQ("\x81\x00"s, Array(Uint(0U)).toString()); - EXPECT_EQ("\x82\x00\x01"s, Array(Uint(0), Uint(1)).toString()); - EXPECT_EQ("\x83\x00\x01\x38\x62"s, Array(Uint(0), Uint(1), Nint(-99)).toString()); - - EXPECT_EQ("\x81\x00"s, Array(0).toString()); - EXPECT_EQ("\x82\x00\x01"s, Array(0, 1).toString()); - EXPECT_EQ("\x83\x00\x01\x38\x62"s, Array(0, 1, -99).toString()); -} - -TEST(CompoundValueTest, MapOfInts) { - EXPECT_EQ("\xA0"s, Map().toString()); - EXPECT_EQ("\xA1\x00\x01"s, Map(Uint(0), Uint(1)).toString()); - // Maps with an odd number of arguments will fail to compile. Uncomment the next lines to test. - // EXPECT_EQ("\xA1\x00"s, Map(Int(0)).toString()); - // EXPECT_EQ("\xA1\x00\x01\x02"s, Map(Int(0), Int(1), Int(2)).toString()); -} - -TEST(CompoundValueTest, MixedArray) { - vector vec = {3, 2, 1}; - EXPECT_EQ("\x84\x01\x20\x43\x03\x02\x01\x65\x68\x65\x6C\x6C\x6F"s, - Array(Uint(1), Nint(-1), Bstr(vec), Tstr("hello")).toString()); - - EXPECT_EQ("\x84\x01\x20\x43\x03\x02\x01\x65\x68\x65\x6C\x6C\x6F"s, - Array(1, -1, vec, "hello").toString()); -} - -TEST(CompoundValueTest, MixedMap) { - vector vec = {3, 2, 1}; - EXPECT_EQ("\xA2\x01\x20\x43\x03\x02\x01\x65\x68\x65\x6C\x6C\x6F"s, - Map(Uint(1), Nint(-1), Bstr(vec), Tstr("hello")).toString()); - - EXPECT_EQ("\xA2\x01\x20\x43\x03\x02\x01\x65\x68\x65\x6C\x6C\x6F"s, - Map(1, -1, vec, "hello").toString()); -} - -TEST(CompoundValueTest, NestedStructures) { - vector vec = {3, 2, 1}; - - string expectedEncoding = - "\xA2\x66\x4F\x75\x74\x65\x72\x31\x82\xA2\x66\x49\x6E\x6E\x65\x72\x31\x18\x63\x66\x49" - "\x6E" - "\x6E\x65\x72\x32\x43\x03\x02\x01\x63\x66\x6F\x6F\x66\x4F\x75\x74\x65\x72\x32\x0A"s; - - // Do it with explicitly-created Items - EXPECT_EQ(expectedEncoding, - Map(Tstr("Outer1"), - Array( // - Map(Tstr("Inner1"), Uint(99), Tstr("Inner2"), Bstr(vec)), Tstr("foo")), - Tstr("Outer2"), // - Uint(10)) - .toString()); - EXPECT_EQ(3U, vec.size()); - - // Now just use convertible types - EXPECT_EQ(expectedEncoding, Map("Outer1", - Array(Map("Inner1", 99, // - "Inner2", vec), - "foo"), - "Outer2", 10) - .toString()); - EXPECT_EQ(3U, vec.size()); - - // Finally, do it with the .add() method. This is slightly less efficient, but has the - // advantage you can build a structure up incrementally, or somewhat fluently if you like. - // First, fluently. - EXPECT_EQ(expectedEncoding, Map().add("Outer1", Array().add(Map() // - .add("Inner1", 99) - .add("Inner2", vec)) - .add("foo")) - .add("Outer2", 10) - .toString()); - EXPECT_EQ(3U, vec.size()); - - // Next, more incrementally - Array arr; - arr.add(Map() // - .add("Inner1", 99) - .add("Inner2", vec)) - .add("foo"); - EXPECT_EQ(3U, vec.size()); - - Map m; - m.add("Outer1", std::move(arr)); // Moving is necessary; Map and Array cannot be copied. - m.add("Outer2", 10); - auto s = m.toString(); - EXPECT_EQ(expectedEncoding, s); -} - -TEST(EncodingMethodsTest, AllVariants) { - Map map; - map.add("key1", Array().add(Map() // - .add("key_a", 9999999) - .add("key_b", std::vector{0x01, 0x02, 0x03}) - .add("key_c", std::numeric_limits::max()) - .add("key_d", std::numeric_limits::min())) - .add("foo")) - .add("key2", true); - - std::vector buf; - buf.resize(map.encodedSize()); - - EXPECT_EQ(buf.data() + buf.size(), map.encode(buf.data(), buf.data() + buf.size())); - - EXPECT_EQ(buf, map.encode()); - - std::vector buf2; - map.encode(std::back_inserter(buf2)); - EXPECT_EQ(buf, buf2); - - auto iter = buf.begin(); - map.encode([&](uint8_t c) { EXPECT_EQ(c, *iter++); }); -} - -TEST(EncodingMethodsTest, UintWithTooShortBuf) { - Uint val(100000); - vector buf(val.encodedSize() - 1); - EXPECT_EQ(nullptr, val.encode(buf.data(), buf.data() + buf.size())); -} - -TEST(EncodingMethodsTest, TstrWithTooShortBuf) { - Tstr val("01234567890123456789012345"s); - vector buf(1); - EXPECT_EQ(nullptr, val.encode(buf.data(), buf.data() + buf.size())); - - buf.resize(val.encodedSize() - 1); - EXPECT_EQ(nullptr, val.encode(buf.data(), buf.data() + buf.size())); -} - -TEST(EncodingMethodsTest, BstrWithTooShortBuf) { - Bstr val("01234567890123456789012345"s); - vector buf(1); - EXPECT_EQ(nullptr, val.encode(buf.data(), buf.data() + buf.size())); - - buf.resize(val.encodedSize() - 1); - EXPECT_EQ(nullptr, val.encode(buf.data(), buf.data() + buf.size())); -} - -TEST(EncodingMethodsTest, ArrayWithTooShortBuf) { - Array val("a", 5, -100); - - std::vector buf(val.encodedSize() - 1); - EXPECT_EQ(nullptr, val.encode(buf.data(), buf.data() + buf.size())); -} - -TEST(EncodingMethodsTest, MapWithTooShortBuf) { - Map map; - map.add("key1", Array().add(Map() // - .add("key_a", 99) - .add("key_b", std::vector{0x01, 0x02, 0x03})) - .add("foo")) - .add("key2", true); - - std::vector buf(map.encodedSize() - 1); - EXPECT_EQ(nullptr, map.encode(buf.data(), buf.data() + buf.size())); -} - -TEST(EqualityTest, Uint) { - Uint val(99); - EXPECT_EQ(val, Uint(99)); - - EXPECT_NE(val, Uint(98)); - EXPECT_NE(val, Nint(-1)); - EXPECT_NE(val, Tstr("99")); - EXPECT_NE(val, Bstr("99")); - EXPECT_NE(val, Bool(false)); - EXPECT_NE(val, Array(99, 1)); - EXPECT_NE(val, Map(99, 1)); -} - -TEST(EqualityTest, Nint) { - Nint val(-1); - EXPECT_EQ(val, Nint(-1)); - - EXPECT_NE(val, Uint(99)); - EXPECT_NE(val, Nint(-4)); - EXPECT_NE(val, Tstr("99")); - EXPECT_NE(val, Bstr("99")); - EXPECT_NE(val, Bool(false)); - EXPECT_NE(val, Array(99)); - EXPECT_NE(val, Map(99, 1)); -} - -TEST(EqualityTest, Tstr) { - Tstr val("99"); - EXPECT_EQ(val, Tstr("99")); - - EXPECT_NE(val, Uint(99)); - EXPECT_NE(val, Nint(-1)); - EXPECT_NE(val, Nint(-4)); - EXPECT_NE(val, Tstr("98")); - EXPECT_NE(val, Bstr("99")); - EXPECT_NE(val, Bool(false)); - EXPECT_NE(val, Array(99, 1)); - EXPECT_NE(val, Map(99, 1)); -} - -TEST(EqualityTest, Bstr) { - Bstr val("99"); - EXPECT_EQ(val, Bstr("99")); - - EXPECT_NE(val, Uint(99)); - EXPECT_NE(val, Nint(-1)); - EXPECT_NE(val, Nint(-4)); - EXPECT_NE(val, Tstr("99")); - EXPECT_NE(val, Bstr("98")); - EXPECT_NE(val, Bool(false)); - EXPECT_NE(val, Array(99, 1)); - EXPECT_NE(val, Map(99, 1)); -} - -TEST(EqualityTest, Bool) { - Bool val(false); - EXPECT_EQ(val, Bool(false)); - - EXPECT_NE(val, Uint(99)); - EXPECT_NE(val, Nint(-1)); - EXPECT_NE(val, Nint(-4)); - EXPECT_NE(val, Tstr("99")); - EXPECT_NE(val, Bstr("98")); - EXPECT_NE(val, Bool(true)); - EXPECT_NE(val, Array(99, 1)); - EXPECT_NE(val, Map(99, 1)); -} - -TEST(EqualityTest, Array) { - Array val(99, 1); - EXPECT_EQ(val, Array(99, 1)); - - EXPECT_NE(val, Uint(99)); - EXPECT_NE(val, Nint(-1)); - EXPECT_NE(val, Nint(-4)); - EXPECT_NE(val, Tstr("99")); - EXPECT_NE(val, Bstr("98")); - EXPECT_NE(val, Bool(true)); - EXPECT_NE(val, Array(99, 2)); - EXPECT_NE(val, Array(98, 1)); - EXPECT_NE(val, Array(99, 1, 2)); - EXPECT_NE(val, Map(99, 1)); -} - -TEST(EqualityTest, Map) { - Map val(99, 1); - EXPECT_EQ(val, Map(99, 1)); - - EXPECT_NE(val, Uint(99)); - EXPECT_NE(val, Nint(-1)); - EXPECT_NE(val, Nint(-4)); - EXPECT_NE(val, Tstr("99")); - EXPECT_NE(val, Bstr("98")); - EXPECT_NE(val, Bool(true)); - EXPECT_NE(val, Array(99, 1)); - EXPECT_NE(val, Map(99, 2)); - EXPECT_NE(val, Map(99, 1, 99, 2)); -} - -TEST(ConvertTest, Uint) { - unique_ptr item = details::makeItem(10); - - EXPECT_EQ(UINT, item->type()); - EXPECT_NE(nullptr, item->asInt()); - EXPECT_NE(nullptr, item->asUint()); - EXPECT_EQ(nullptr, item->asNint()); - EXPECT_EQ(nullptr, item->asTstr()); - EXPECT_EQ(nullptr, item->asBstr()); - EXPECT_EQ(nullptr, item->asSimple()); - EXPECT_EQ(nullptr, item->asMap()); - EXPECT_EQ(nullptr, item->asArray()); - - EXPECT_EQ(10, item->asInt()->value()); - EXPECT_EQ(10, item->asUint()->value()); -} - -TEST(ConvertTest, Nint) { - unique_ptr item = details::makeItem(-10); - - EXPECT_EQ(NINT, item->type()); - EXPECT_NE(nullptr, item->asInt()); - EXPECT_EQ(nullptr, item->asUint()); - EXPECT_NE(nullptr, item->asNint()); - EXPECT_EQ(nullptr, item->asTstr()); - EXPECT_EQ(nullptr, item->asBstr()); - EXPECT_EQ(nullptr, item->asSimple()); - EXPECT_EQ(nullptr, item->asMap()); - EXPECT_EQ(nullptr, item->asArray()); - - EXPECT_EQ(-10, item->asInt()->value()); - EXPECT_EQ(-10, item->asNint()->value()); -} - -TEST(ConvertTest, Tstr) { - unique_ptr item = details::makeItem("hello"); - - EXPECT_EQ(TSTR, item->type()); - EXPECT_EQ(nullptr, item->asInt()); - EXPECT_EQ(nullptr, item->asUint()); - EXPECT_EQ(nullptr, item->asNint()); - EXPECT_NE(nullptr, item->asTstr()); - EXPECT_EQ(nullptr, item->asBstr()); - EXPECT_EQ(nullptr, item->asSimple()); - EXPECT_EQ(nullptr, item->asMap()); - EXPECT_EQ(nullptr, item->asArray()); - - EXPECT_EQ("hello"s, item->asTstr()->value()); -} - -TEST(ConvertTest, Bstr) { - vector vec{0x23, 0x24, 0x22}; - unique_ptr item = details::makeItem(vec); - - EXPECT_EQ(BSTR, item->type()); - EXPECT_EQ(nullptr, item->asInt()); - EXPECT_EQ(nullptr, item->asUint()); - EXPECT_EQ(nullptr, item->asNint()); - EXPECT_EQ(nullptr, item->asTstr()); - EXPECT_NE(nullptr, item->asBstr()); - EXPECT_EQ(nullptr, item->asSimple()); - EXPECT_EQ(nullptr, item->asMap()); - EXPECT_EQ(nullptr, item->asArray()); - - EXPECT_EQ(vec, item->asBstr()->value()); -} - -TEST(ConvertTest, Bool) { - unique_ptr item = details::makeItem(false); - - EXPECT_EQ(SIMPLE, item->type()); - EXPECT_EQ(nullptr, item->asInt()); - EXPECT_EQ(nullptr, item->asUint()); - EXPECT_EQ(nullptr, item->asNint()); - EXPECT_EQ(nullptr, item->asTstr()); - EXPECT_EQ(nullptr, item->asBstr()); - EXPECT_NE(nullptr, item->asSimple()); - EXPECT_EQ(nullptr, item->asMap()); - EXPECT_EQ(nullptr, item->asArray()); - - EXPECT_EQ(BOOLEAN, item->asSimple()->simpleType()); - EXPECT_NE(nullptr, item->asSimple()->asBool()); - - EXPECT_FALSE(item->asSimple()->asBool()->value()); -} - -TEST(ConvertTest, Map) { - unique_ptr item(new Map); - - EXPECT_EQ(MAP, item->type()); - EXPECT_EQ(nullptr, item->asInt()); - EXPECT_EQ(nullptr, item->asUint()); - EXPECT_EQ(nullptr, item->asNint()); - EXPECT_EQ(nullptr, item->asTstr()); - EXPECT_EQ(nullptr, item->asBstr()); - EXPECT_EQ(nullptr, item->asSimple()); - EXPECT_NE(nullptr, item->asMap()); - EXPECT_EQ(nullptr, item->asArray()); - - EXPECT_EQ(0U, item->asMap()->size()); -} - -TEST(ConvertTest, Array) { - unique_ptr item(new Array); - - EXPECT_EQ(ARRAY, item->type()); - EXPECT_EQ(nullptr, item->asInt()); - EXPECT_EQ(nullptr, item->asUint()); - EXPECT_EQ(nullptr, item->asNint()); - EXPECT_EQ(nullptr, item->asTstr()); - EXPECT_EQ(nullptr, item->asBstr()); - EXPECT_EQ(nullptr, item->asSimple()); - EXPECT_EQ(nullptr, item->asMap()); - EXPECT_NE(nullptr, item->asArray()); - - EXPECT_EQ(0U, item->asArray()->size()); -} - -class MockParseClient : public ParseClient { - public: - MOCK_METHOD4(item, ParseClient*(std::unique_ptr& item, const uint8_t* hdrBegin, - const uint8_t* valueBegin, const uint8_t* end)); - MOCK_METHOD4(itemEnd, ParseClient*(std::unique_ptr& item, const uint8_t* hdrBegin, - const uint8_t* valueBegin, const uint8_t* end)); - MOCK_METHOD2(error, void(const uint8_t* position, const std::string& errorMessage)); -}; - -MATCHER_P(IsType, value, std::string("Type ") + (negation ? "doesn't match" : "matches")) { - return arg->type() == value; -} - -MATCHER_P(MatchesItem, value, "") { - return arg && *arg == value; -} - -MATCHER_P(IsArrayOfSize, value, "") { - return arg->type() == ARRAY && arg->asArray()->size() == value; -} - -MATCHER_P(IsMapOfSize, value, "") { - return arg->type() == MAP && arg->asMap()->size() == value; -} - -TEST(StreamParseTest, Uint) { - MockParseClient mpc; - - Uint val(100); - auto encoded = val.encode(); - uint8_t* encBegin = encoded.data(); - uint8_t* encEnd = encoded.data() + encoded.size(); - - EXPECT_CALL(mpc, item(MatchesItem(val), encBegin, encEnd, encEnd)).WillOnce(Return(&mpc)); - EXPECT_CALL(mpc, itemEnd(_, _, _, _)).Times(0); - EXPECT_CALL(mpc, error(_, _)).Times(0); - - parse(encoded.data(), encoded.data() + encoded.size(), &mpc); -} - -TEST(StreamParseTest, Nint) { - MockParseClient mpc; - - Nint val(-10); - auto encoded = val.encode(); - uint8_t* encBegin = encoded.data(); - uint8_t* encEnd = encoded.data() + encoded.size(); - - EXPECT_CALL(mpc, item(MatchesItem(val), encBegin, encEnd, encEnd)).WillOnce(Return(&mpc)); - - EXPECT_CALL(mpc, itemEnd(_, _, _, _)).Times(0); - EXPECT_CALL(mpc, error(_, _)).Times(0); - - parse(encoded.data(), encoded.data() + encoded.size(), &mpc); -} - -TEST(StreamParseTest, Bool) { - MockParseClient mpc; - - Bool val(true); - auto encoded = val.encode(); - uint8_t* encBegin = encoded.data(); - uint8_t* encEnd = encoded.data() + encoded.size(); - - EXPECT_CALL(mpc, item(MatchesItem(val), encBegin, encEnd, encEnd)).WillOnce(Return(&mpc)); - EXPECT_CALL(mpc, itemEnd(_, _, _, _)).Times(0); - EXPECT_CALL(mpc, error(_, _)).Times(0); - - parse(encoded.data(), encoded.data() + encoded.size(), &mpc); -} - -TEST(StreamParseTest, Tstr) { - MockParseClient mpc; - - Tstr val("Hello"); - auto encoded = val.encode(); - uint8_t* encBegin = encoded.data(); - uint8_t* encEnd = encoded.data() + encoded.size(); - - EXPECT_CALL(mpc, item(MatchesItem(val), encBegin, encBegin + 1, encEnd)).WillOnce(Return(&mpc)); - EXPECT_CALL(mpc, itemEnd(_, _, _, _)).Times(0); - EXPECT_CALL(mpc, error(_, _)).Times(0); - - parse(encoded.data(), encoded.data() + encoded.size(), &mpc); -} - -TEST(StreamParseTest, Bstr) { - MockParseClient mpc; - - Bstr val("Hello"); - auto encoded = val.encode(); - uint8_t* encBegin = encoded.data(); - uint8_t* encEnd = encoded.data() + encoded.size(); - - EXPECT_CALL(mpc, item(MatchesItem(val), encBegin, encBegin + 1, encEnd)).WillOnce(Return(&mpc)); - EXPECT_CALL(mpc, itemEnd(_, _, _, _)).Times(0); - EXPECT_CALL(mpc, error(_, _)).Times(0); - - parse(encoded.data(), encoded.data() + encoded.size(), &mpc); -} - -TEST(StreamParseTest, Array) { - MockParseClient mpc; - - Array val("Hello", 4, Array(-9, "Goodbye"), std::numeric_limits::max()); - ASSERT_NE(val[2]->asArray(), nullptr); - const Array& interior = *(val[2]->asArray()); - auto encoded = val.encode(); - uint8_t* encBegin = encoded.data(); - uint8_t* encEnd = encoded.data() + encoded.size(); - - { - InSequence s; - const uint8_t* pos = encBegin; - EXPECT_CALL(mpc, item(IsArrayOfSize(val.size()), pos, pos + 1, pos + 1)) - .WillOnce(Return(&mpc)); - ++pos; - EXPECT_CALL(mpc, item(MatchesItem(ByRef(*val[0])), pos, pos + 1, pos + 6)) - .WillOnce(Return(&mpc)); - pos += 6; - EXPECT_CALL(mpc, item(MatchesItem(ByRef(*val[1])), pos, pos + 1, pos + 1)) - .WillOnce(Return(&mpc)); - ++pos; - const uint8_t* innerArrayBegin = pos; - EXPECT_CALL(mpc, item(IsArrayOfSize(interior.size()), pos, pos + 1, pos + 1)) - .WillOnce(Return(&mpc)); - ++pos; - EXPECT_CALL(mpc, item(MatchesItem(ByRef(*interior[0])), pos, pos + 1, pos + 1)) - .WillOnce(Return(&mpc)); - ++pos; - EXPECT_CALL(mpc, item(MatchesItem(ByRef(*interior[1])), pos, pos + 1, pos + 8)) - .WillOnce(Return(&mpc)); - pos += 8; - EXPECT_CALL(mpc, itemEnd(IsArrayOfSize(interior.size()), innerArrayBegin, - innerArrayBegin + 1, pos)) - .WillOnce(Return(&mpc)); - EXPECT_CALL(mpc, item(MatchesItem(ByRef(*val[3])), pos, pos + 9, pos + 9)) - .WillOnce(Return(&mpc)); - EXPECT_CALL(mpc, itemEnd(IsArrayOfSize(val.size()), encBegin, encBegin + 1, encEnd)) - .WillOnce(Return(&mpc)); - } - - EXPECT_CALL(mpc, error(_, _)) // - .Times(0); - - parse(encoded.data(), encoded.data() + encoded.size(), &mpc); -} - -TEST(StreamParseTest, Map) { - MockParseClient mpc; - - Map val("Hello", 4, Array(-9, "Goodbye"), std::numeric_limits::max()); - ASSERT_NE(val[1].first->asArray(), nullptr); - const Array& interior = *(val[1].first->asArray()); - auto encoded = val.encode(); - uint8_t* encBegin = encoded.data(); - uint8_t* encEnd = encoded.data() + encoded.size(); - - { - InSequence s; - const uint8_t* pos = encBegin; - EXPECT_CALL(mpc, item(_, pos, pos + 1, pos + 1)).WillOnce(Return(&mpc)); - ++pos; - EXPECT_CALL(mpc, item(MatchesItem(ByRef(*val[0].first)), pos, pos + 1, pos + 6)) - .WillOnce(Return(&mpc)); - pos += 6; - EXPECT_CALL(mpc, item(MatchesItem(ByRef(*val[0].second)), pos, pos + 1, pos + 1)) - .WillOnce(Return(&mpc)); - ++pos; - const uint8_t* innerArrayBegin = pos; - EXPECT_CALL(mpc, item(IsArrayOfSize(interior.size()), pos, pos + 1, pos + 1)) - .WillOnce(Return(&mpc)); - ++pos; - EXPECT_CALL(mpc, item(MatchesItem(ByRef(*interior[0])), pos, pos + 1, pos + 1)) - .WillOnce(Return(&mpc)); - ++pos; - EXPECT_CALL(mpc, item(MatchesItem(ByRef(*interior[1])), pos, pos + 1, pos + 8)) - .WillOnce(Return(&mpc)); - pos += 8; - EXPECT_CALL(mpc, itemEnd(IsArrayOfSize(interior.size()), innerArrayBegin, - innerArrayBegin + 1, pos)) - .WillOnce(Return(&mpc)); - EXPECT_CALL(mpc, item(MatchesItem(ByRef(*val[1].second)), pos, pos + 9, pos + 9)) - .WillOnce(Return(&mpc)); - EXPECT_CALL(mpc, itemEnd(IsMapOfSize(val.size()), encBegin, encBegin + 1, encEnd)) - .WillOnce(Return(&mpc)); - } - - EXPECT_CALL(mpc, error(_, _)) // - .Times(0); - - parse(encoded.data(), encoded.data() + encoded.size(), &mpc); -} - -TEST(StreamParseTest, Semantic) { - MockParseClient mpc; - - vector encoded; - auto iter = back_inserter(encoded); - encodeHeader(SEMANTIC, 0, iter); - Uint(999).encode(iter); - - EXPECT_CALL(mpc, item(_, _, _, _)).Times(0); - EXPECT_CALL(mpc, itemEnd(_, _, _, _)).Times(0); - EXPECT_CALL(mpc, error(encoded.data(), "Semantic tags not supported")); - - parse(encoded.data(), encoded.data() + encoded.size(), &mpc); -} - -TEST(FullParserTest, Uint) { - Uint val(10); - - auto [item, pos, message] = parse(val.encode()); - EXPECT_THAT(item, MatchesItem(val)); -} - -TEST(FullParserTest, Nint) { - Nint val(-10); - - auto [item, pos, message] = parse(val.encode()); - EXPECT_THAT(item, MatchesItem(val)); - - vector minNint = {0x3B, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - - std::tie(item, pos, message) = parse(minNint); - EXPECT_THAT(item, NotNull()); - EXPECT_EQ(item->asNint()->value(), std::numeric_limits::min()); -} - -TEST(FullParserTest, NintOutOfRange) { - vector outOfRangeNint = {0x3B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - - auto [item, pos, message] = parse(outOfRangeNint); - EXPECT_THAT(item, IsNull()); - EXPECT_EQ(pos, outOfRangeNint.data()); - EXPECT_EQ(message, "NINT values that don't fit in int64_t are not supported."); -} - -TEST(FullParserTest, Tstr) { - Tstr val("Hello"); - - auto [item, pos, message] = parse(val.encode()); - EXPECT_THAT(item, MatchesItem(val)); -} - -TEST(FullParserTest, Bstr) { - Bstr val("\x00\x01\0x02"s); - - auto [item, pos, message] = parse(val.encode()); - EXPECT_THAT(item, MatchesItem(val)); -} - -TEST(FullParserTest, Array) { - Array val("hello", -4, 3); - - auto encoded = val.encode(); - auto [item, pos, message] = parse(encoded); - EXPECT_THAT(item, MatchesItem(ByRef(val))); - EXPECT_EQ(pos, encoded.data() + encoded.size()); - EXPECT_EQ("", message); - - // We've already checked it all, but walk it just for fun. - ASSERT_NE(nullptr, item->asArray()); - const Array& arr = *(item->asArray()); - ASSERT_EQ(arr[0]->type(), TSTR); - EXPECT_EQ(arr[0]->asTstr()->value(), "hello"); -} - -TEST(FullParserTest, Map) { - Map val("hello", -4, 3, Bstr("hi")); - - auto [item, pos, message] = parse(val.encode()); - EXPECT_THAT(item, MatchesItem(ByRef(val))); -} - -TEST(FullParserTest, Complex) { - vector vec = {0x01, 0x02, 0x08, 0x03}; - Map val("Outer1", - Array(Map("Inner1", 99, // - "Inner2", vec), - "foo"), - "Outer2", 10); - - std::unique_ptr item; - const uint8_t* pos; - std::string message; - std::tie(item, pos, message) = parse(val.encode()); - EXPECT_THAT(item, MatchesItem(ByRef(val))); -} - -TEST(FullParserTest, IncompleteUint) { - Uint val(1000); - - auto encoding = val.encode(); - auto [item, pos, message] = parse(encoding.data(), encoding.size() - 1); - EXPECT_EQ(nullptr, item.get()); - EXPECT_EQ(encoding.data(), pos); - EXPECT_EQ("Need 2 byte(s) for length field, have 1.", message); -} - -TEST(FullParserTest, IncompleteString) { - Tstr val("hello"); - - auto encoding = val.encode(); - auto [item, pos, message] = parse(encoding.data(), encoding.size() - 2); - EXPECT_EQ(nullptr, item.get()); - EXPECT_EQ(encoding.data(), pos); - EXPECT_EQ("Need 5 byte(s) for text string, have 3.", message); -} - -TEST(FullParserTest, ArrayWithInsufficientEntries) { - Array val(1, 2, 3, 4); - - auto encoding = val.encode(); - auto [item, pos, message] = parse(encoding.data(), encoding.size() - 1); - EXPECT_EQ(nullptr, item.get()); - EXPECT_EQ(encoding.data(), pos); - EXPECT_EQ("Not enough entries for array.", message); -} - -TEST(FullParserTest, ArrayWithTruncatedEntry) { - Array val(1, 2, 3, 400000); - - auto encoding = val.encode(); - auto [item, pos, message] = parse(encoding.data(), encoding.size() - 1); - EXPECT_EQ(nullptr, item.get()); - EXPECT_EQ(encoding.data() + encoding.size() - 5, pos); - EXPECT_EQ("Need 4 byte(s) for length field, have 3.", message); -} - -TEST(FullParserTest, MapWithTruncatedEntry) { - Map val(1, 2, 300000, 4); - - auto encoding = val.encode(); - auto [item, pos, message] = parse(encoding.data(), encoding.size() - 2); - EXPECT_EQ(nullptr, item.get()); - EXPECT_EQ(encoding.data() + 3, pos); - EXPECT_EQ("Need 4 byte(s) for length field, have 3.", message); -} -int main(int argc, char** argv) { - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -}