Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f6fcd319 authored by Vikram Gaur's avatar Vikram Gaur Committed by Automerger Merge Worker
Browse files

Merge "Remove obsolete libcppbor code from identity module" am: dc34dfe8 am:...

Merge "Remove obsolete libcppbor code from identity module" am: dc34dfe8 am: 6b2625b4 am: c58c687e

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



Change-Id: I0a0de4f64c73ec70befbdebc14dcae5db3ca8e94
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 64017373 c58c687e
Loading
Loading
Loading
Loading
+0 −54
Original line number Diff line number Diff line
@@ -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"],
}
+0 −216
Original line number Diff line number Diff line
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.
+0 −827

File deleted.

Preview size limit exceeded, changes collapsed.

+0 −133
Original line number Diff line number Diff line
/*
 * 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

identity/support/src/cppbor.cpp

deleted100644 → 0
+0 −225
Original line number Diff line number Diff line
/*
 * 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
Loading