| /* | |
| * Copyright 2019 Google LLC | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * https://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| #include "cppbor_parse.h" | |
| #include <algorithm> | |
| #include <cstdint> | |
| #include <cstring> | |
| #include <memory> | |
| #include <optional> | |
| #include <sstream> | |
| #include <stack> | |
| #include <type_traits> | |
| #include "cppbor.h" | |
| #ifndef __TRUSTY__ | |
| #include <android-base/logging.h> | |
| #define LOG_TAG "CppBor" | |
| #else | |
| #define CHECK(x) (void)(x) | |
| #endif | |
| namespace cppbor { | |
| namespace { | |
| const unsigned kMaxParseDepth = 1000; | |
| const size_t kMaxReserveSize = 8192; | |
| std::string insufficientLengthString(size_t bytesNeeded, size_t bytesAvail, | |
| const std::string& type) { | |
| char buf[1024]; | |
| snprintf(buf, sizeof(buf), "Need %zu byte(s) for %s, have %zu.", bytesNeeded, type.c_str(), | |
| bytesAvail); | |
| return std::string(buf); | |
| } | |
| template <typename T, typename = std::enable_if_t<std::is_unsigned_v<T>>> | |
| std::tuple<bool, uint64_t, const uint8_t*> parseLength(const uint8_t* pos, const uint8_t* end, | |
| ParseClient* parseClient) { | |
| if (pos + sizeof(T) > end) { | |
| parseClient->error(pos - 1, insufficientLengthString(sizeof(T), end - pos, "length field")); | |
| return {false, 0, pos}; | |
| } | |
| const uint8_t* intEnd = pos + sizeof(T); | |
| T result = 0; | |
| do { | |
| result = static_cast<T>((result << 8) | *pos++); | |
| } while (pos < intEnd); | |
| return {true, result, pos}; | |
| } | |
| std::tuple<const uint8_t*, ParseClient*> parseRecursively(const uint8_t* begin, const uint8_t* end, | |
| bool emitViews, ParseClient* parseClient, | |
| unsigned depth); | |
| std::tuple<const uint8_t*, ParseClient*> handleUint(uint64_t value, const uint8_t* hdrBegin, | |
| const uint8_t* hdrEnd, | |
| ParseClient* parseClient) { | |
| std::unique_ptr<Item> item = std::make_unique<Uint>(value); | |
| return {hdrEnd, | |
| parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)}; | |
| } | |
| std::tuple<const uint8_t*, ParseClient*> handleNint(uint64_t value, const uint8_t* hdrBegin, | |
| const uint8_t* hdrEnd, | |
| ParseClient* parseClient) { | |
| if (value > std::numeric_limits<int64_t>::max()) { | |
| parseClient->error(hdrBegin, "NINT values that don't fit in int64_t are not supported."); | |
| return {hdrBegin, nullptr /* end parsing */}; | |
| } | |
| std::unique_ptr<Item> item = std::make_unique<Nint>(-1 - static_cast<int64_t>(value)); | |
| return {hdrEnd, | |
| parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)}; | |
| } | |
| std::tuple<const uint8_t*, ParseClient*> handleBool(uint64_t value, const uint8_t* hdrBegin, | |
| const uint8_t* hdrEnd, | |
| ParseClient* parseClient) { | |
| std::unique_ptr<Item> item = std::make_unique<Bool>(value == TRUE); | |
| return {hdrEnd, | |
| parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)}; | |
| } | |
| std::tuple<const uint8_t*, ParseClient*> handleNull(const uint8_t* hdrBegin, const uint8_t* hdrEnd, | |
| ParseClient* parseClient) { | |
| std::unique_ptr<Item> item = std::make_unique<Null>(); | |
| return {hdrEnd, | |
| parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)}; | |
| } | |
| #if defined(__STDC_IEC_559__) || FLT_MANT_DIG == 24 || __FLT_MANT_DIG__ == 24 | |
| std::tuple<const uint8_t*, ParseClient*> handleFloat(uint32_t value, const uint8_t* hdrBegin, | |
| const uint8_t* hdrEnd, | |
| ParseClient* parseClient) { | |
| float f; | |
| std::memcpy(&f, &value, sizeof(float)); | |
| std::unique_ptr<Item> item = std::make_unique<Float>(f); | |
| return {hdrEnd, | |
| parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)}; | |
| } | |
| #endif // __STDC_IEC_559__ || FLT_MANT_DIG == 24 || __FLT_MANT_DIG__ == 24 | |
| #if defined(__STDC_IEC_559__) || DBL_MANT_DIG == 53 || __DBL_MANT_DIG__ == 53 | |
| std::tuple<const uint8_t*, ParseClient*> handleDouble(uint64_t value, const uint8_t* hdrBegin, | |
| const uint8_t* hdrEnd, | |
| ParseClient* parseClient) { | |
| double d; | |
| std::memcpy(&d, &value, sizeof(double)); | |
| std::unique_ptr<Item> item = std::make_unique<Double>(d); | |
| return {hdrEnd, | |
| parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)}; | |
| } | |
| #endif // __STDC_IEC_559__ || DBL_MANT_DIG == 53 || __DBL_MANT_DIG__ == 53 | |
| template <typename T> | |
| std::tuple<const uint8_t*, ParseClient*> handleString(uint64_t length, const uint8_t* hdrBegin, | |
| const uint8_t* valueBegin, const uint8_t* end, | |
| const std::string& errLabel, | |
| ParseClient* parseClient) { | |
| ssize_t signed_length = static_cast<ssize_t>(length); | |
| if (end - valueBegin < signed_length || signed_length < 0) { | |
| parseClient->error(hdrBegin, insufficientLengthString(length, end - valueBegin, errLabel)); | |
| return {hdrBegin, nullptr /* end parsing */}; | |
| } | |
| std::unique_ptr<Item> item = std::make_unique<T>(valueBegin, valueBegin + length); | |
| return {valueBegin + length, | |
| parseClient->item(item, hdrBegin, valueBegin, valueBegin + length)}; | |
| } | |
| std::tuple<const uint8_t*, ParseClient*> handleIncompleteString( | |
| std::unique_ptr<Item> item, const uint8_t* hdrBegin, const uint8_t* valueBegin, | |
| const uint8_t* end, const std::string& errLabel, bool emitViews, ParseClient* parseClient, | |
| unsigned depth) { | |
| parseClient = | |
| parseClient->item(item, hdrBegin, valueBegin, valueBegin /* don't know the end yet */); | |
| if (!parseClient) return {hdrBegin, nullptr}; | |
| const uint8_t* pos = valueBegin; | |
| while (true) { | |
| if (pos == end) { | |
| parseClient->error(hdrBegin, "Not enough entries for " + errLabel + "."); | |
| return {hdrBegin, nullptr /* end parsing */}; | |
| } | |
| if (*pos == 0xFF) { | |
| // We found a stop code. | |
| ++pos; | |
| break; | |
| } | |
| std::tie(pos, parseClient) = parseRecursively(pos, end, emitViews, parseClient, depth + 1); | |
| if (!parseClient) return {hdrBegin, nullptr}; | |
| } | |
| if (!parseClient) return {hdrBegin, nullptr}; | |
| return {pos, parseClient->itemEnd(item, hdrBegin, valueBegin, pos)}; | |
| } | |
| class IncompleteItem { | |
| public: | |
| static IncompleteItem* cast(Item* item); | |
| virtual ~IncompleteItem() {} | |
| virtual void add(std::unique_ptr<Item> item) = 0; | |
| virtual std::unique_ptr<Item> finalize() && = 0; | |
| }; | |
| class IncompleteBstr : public Bstr, public IncompleteItem { | |
| public: | |
| explicit IncompleteBstr() {} | |
| // The finalized version creates a copy which will not have this overridden. | |
| bool isCompound() const override { return true; } | |
| void add(std::unique_ptr<Item> item) override { | |
| if (item->type() == BSTR) { | |
| mValue.insert(mValue.end(), item->asBstr()->moveValue().begin(), | |
| item->asBstr()->moveValue().end()); | |
| } else { | |
| #ifndef __TRUSTY__ | |
| LOG(FATAL) << "Should not happen: Expected BSTR"; | |
| #endif // __TRUSTY__ | |
| } | |
| } | |
| std::unique_ptr<Item> finalize() && override { return std::make_unique<Bstr>(mValue); } | |
| }; | |
| class IncompleteTstr : public Tstr, public IncompleteItem { | |
| public: | |
| explicit IncompleteTstr() {} | |
| // The finalized version creates a copy which will not have this overridden. | |
| bool isCompound() const override { return true; } | |
| void add(std::unique_ptr<Item> item) override { | |
| if (item->type() == TSTR) { | |
| ss << item->asTstr()->moveValue(); | |
| } else { | |
| #ifndef __TRUSTY__ | |
| LOG(FATAL) << "Should not happen: Expected TSTR"; | |
| #endif // __TRUSTY__ | |
| } | |
| } | |
| std::unique_ptr<Item> finalize() && override { return std::make_unique<Tstr>(ss.str()); } | |
| private: | |
| std::stringstream ss; | |
| }; | |
| class IncompleteArray : public Array, public IncompleteItem { | |
| public: | |
| explicit IncompleteArray(std::optional<size_t> size) : mSize(size) {} | |
| // If the "complete" size is known, return it, otherwise return the current size. | |
| size_t size() const override { return mSize.value_or(Array::size()); } | |
| void add(std::unique_ptr<Item> item) override { | |
| if (mSize) mEntries.reserve(std::min(mSize.value(), kMaxReserveSize)); | |
| mEntries.push_back(std::move(item)); | |
| } | |
| virtual std::unique_ptr<Item> finalize() && override { | |
| // Use Array explicitly so the compiler picks the correct ctor overload | |
| Array* thisArray = this; | |
| return std::make_unique<Array>(std::move(*thisArray)); | |
| } | |
| private: | |
| std::optional<size_t> mSize; | |
| }; | |
| class IncompleteMap : public Map, public IncompleteItem { | |
| public: | |
| explicit IncompleteMap(std::optional<size_t> size) : mSize(size) {} | |
| // If the "complete" size is known, return it, otherwise return the current size. | |
| size_t size() const override { return mSize.value_or(Map::size()); } | |
| void add(std::unique_ptr<Item> item) override { | |
| if (mKeyHeldForAdding) { | |
| if (mSize) mEntries.reserve(std::min(mSize.value(), kMaxReserveSize)); | |
| mEntries.push_back({std::move(mKeyHeldForAdding), std::move(item)}); | |
| } else { | |
| mKeyHeldForAdding = std::move(item); | |
| } | |
| } | |
| virtual std::unique_ptr<Item> finalize() && override { | |
| return std::make_unique<Map>(std::move(*this)); | |
| } | |
| private: | |
| std::unique_ptr<Item> mKeyHeldForAdding; | |
| std::optional<size_t> mSize; | |
| }; | |
| class IncompleteSemanticTag : public SemanticTag, public IncompleteItem { | |
| public: | |
| explicit IncompleteSemanticTag(uint64_t value) : SemanticTag(value) {} | |
| // We return the "complete" size, rather than the actual size. | |
| size_t size() const override { return 1; } | |
| void add(std::unique_ptr<Item> item) override { mTaggedItem = std::move(item); } | |
| virtual std::unique_ptr<Item> finalize() && override { | |
| return std::make_unique<SemanticTag>(std::move(*this)); | |
| } | |
| }; | |
| IncompleteItem* IncompleteItem::cast(Item* item) { | |
| CHECK(item->isCompound()); | |
| // Semantic tag must be check first, because SemanticTag::type returns the wrapped item's type. | |
| if (item->asSemanticTag()) { | |
| #if __has_feature(cxx_rtti) | |
| CHECK(dynamic_cast<IncompleteSemanticTag*>(item)); | |
| #endif | |
| return static_cast<IncompleteSemanticTag*>(item); | |
| } else if (item->type() == ARRAY) { | |
| #if __has_feature(cxx_rtti) | |
| CHECK(dynamic_cast<IncompleteArray*>(item)); | |
| #endif | |
| return static_cast<IncompleteArray*>(item); | |
| } else if (item->type() == MAP) { | |
| #if __has_feature(cxx_rtti) | |
| CHECK(dynamic_cast<IncompleteMap*>(item)); | |
| #endif | |
| return static_cast<IncompleteMap*>(item); | |
| } else if (item->type() == BSTR) { | |
| #if __has_feature(cxx_rtti) | |
| CHECK(dynamic_cast<IncompleteBstr*>(item)); | |
| #endif | |
| return static_cast<IncompleteBstr*>(item); | |
| } else if (item->type() == TSTR) { | |
| #if __has_feature(cxx_rtti) | |
| CHECK(dynamic_cast<IncompleteTstr*>(item)); | |
| #endif | |
| return static_cast<IncompleteTstr*>(item); | |
| } else { | |
| CHECK(false); // Impossible to get here. | |
| } | |
| return nullptr; | |
| } | |
| std::tuple<const uint8_t*, ParseClient*> handleEntries(std::optional<size_t> entryCount, | |
| const uint8_t* hdrBegin, const uint8_t* pos, | |
| const uint8_t* end, | |
| const std::string& typeName, bool emitViews, | |
| ParseClient* parseClient, unsigned depth) { | |
| while (entryCount.value_or(1) > 0) { | |
| if (entryCount.has_value()) { | |
| --*entryCount; | |
| } | |
| if (pos == end) { | |
| parseClient->error(hdrBegin, "Not enough entries for " + typeName + "."); | |
| return {hdrBegin, nullptr /* end parsing */}; | |
| } | |
| if (!entryCount.has_value() && *pos == 0xFF) { | |
| // We're in an indeterminate-length object and found a stop code. | |
| ++pos; | |
| break; | |
| } | |
| std::tie(pos, parseClient) = parseRecursively(pos, end, emitViews, parseClient, depth + 1); | |
| if (!parseClient) return {hdrBegin, nullptr}; | |
| } | |
| return {pos, parseClient}; | |
| } | |
| std::tuple<const uint8_t*, ParseClient*> handleCompound( | |
| std::unique_ptr<Item> item, std::optional<uint64_t> entryCount, const uint8_t* hdrBegin, | |
| const uint8_t* valueBegin, const uint8_t* end, const std::string& typeName, bool emitViews, | |
| ParseClient* parseClient, unsigned depth) { | |
| parseClient = | |
| parseClient->item(item, hdrBegin, valueBegin, valueBegin /* don't know the end yet */); | |
| if (!parseClient) return {hdrBegin, nullptr}; | |
| const uint8_t* pos; | |
| std::tie(pos, parseClient) = handleEntries(entryCount, hdrBegin, valueBegin, end, typeName, | |
| emitViews, parseClient, depth); | |
| if (!parseClient) return {hdrBegin, nullptr}; | |
| return {pos, parseClient->itemEnd(item, hdrBegin, valueBegin, pos)}; | |
| } | |
| std::tuple<const uint8_t*, ParseClient*> parseRecursively(const uint8_t* begin, const uint8_t* end, | |
| bool emitViews, ParseClient* parseClient, | |
| unsigned depth) { | |
| if (begin == end) { | |
| parseClient->error( | |
| begin, "Input buffer is empty. Begin and end cannot point to the same location."); | |
| return {begin, nullptr}; | |
| } | |
| // Limit recursion depth to avoid overflowing the stack. | |
| if (depth > kMaxParseDepth) { | |
| parseClient->error(begin, | |
| "Max depth reached. Cannot parse CBOR structures with more " | |
| "than " + | |
| std::to_string(kMaxParseDepth) + " levels."); | |
| return {begin, nullptr}; | |
| } | |
| const uint8_t* pos = begin; | |
| MajorType type = static_cast<MajorType>(*pos & 0xE0); | |
| uint8_t tagInt = *pos & 0x1F; | |
| ++pos; | |
| bool success = true; | |
| std::optional<uint64_t> addlData; | |
| if (tagInt < ONE_BYTE_LENGTH) { | |
| addlData = tagInt; | |
| } else if (tagInt > EIGHT_BYTE_LENGTH && tagInt != INDEFINITE_LENGTH) { | |
| parseClient->error(begin, "Reserved additional information value."); | |
| return {begin, nullptr}; | |
| } else { | |
| switch (tagInt) { | |
| case ONE_BYTE_LENGTH: | |
| std::tie(success, addlData, pos) = parseLength<uint8_t>(pos, end, parseClient); | |
| break; | |
| case TWO_BYTE_LENGTH: | |
| std::tie(success, addlData, pos) = parseLength<uint16_t>(pos, end, parseClient); | |
| break; | |
| case FOUR_BYTE_LENGTH: | |
| std::tie(success, addlData, pos) = parseLength<uint32_t>(pos, end, parseClient); | |
| break; | |
| case EIGHT_BYTE_LENGTH: | |
| std::tie(success, addlData, pos) = parseLength<uint64_t>(pos, end, parseClient); | |
| break; | |
| case INDEFINITE_LENGTH: | |
| // View only strings are not yet supported due to their disjoint nature. | |
| if (type != ARRAY && type != MAP && !(type == BSTR && !emitViews) && | |
| !(type == TSTR && !emitViews)) { | |
| parseClient->error(begin, "Unsupported indefinite length item."); | |
| return {begin, nullptr}; | |
| } | |
| addlData = std::nullopt; | |
| 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: | |
| if (!addlData.has_value()) { | |
| return handleIncompleteString(std::make_unique<IncompleteBstr>(), begin, pos, end, | |
| "byte string", emitViews, parseClient, depth); | |
| } else if (emitViews) { | |
| return handleString<ViewBstr>(*addlData, begin, pos, end, "byte string", | |
| parseClient); | |
| } else { | |
| return handleString<Bstr>(*addlData, begin, pos, end, "byte string", parseClient); | |
| } | |
| case TSTR: | |
| if (!addlData.has_value()) { | |
| return handleIncompleteString(std::make_unique<IncompleteTstr>(), begin, pos, end, | |
| "text string", emitViews, parseClient, depth); | |
| } else if (emitViews) { | |
| return handleString<ViewTstr>(*addlData, begin, pos, end, "text string", | |
| parseClient); | |
| } else { | |
| return handleString<Tstr>(*addlData, begin, pos, end, "text string", parseClient); | |
| } | |
| case ARRAY: | |
| return handleCompound(std::make_unique<IncompleteArray>(addlData), addlData, begin, pos, | |
| end, "array", emitViews, parseClient, depth); | |
| case MAP: | |
| return handleCompound(std::make_unique<IncompleteMap>(addlData), | |
| addlData.has_value() ? *addlData * 2 : addlData, begin, pos, end, | |
| "map", emitViews, parseClient, depth); | |
| case SEMANTIC: | |
| return handleCompound(std::make_unique<IncompleteSemanticTag>(*addlData), 1, begin, pos, | |
| end, "semantic", emitViews, parseClient, depth); | |
| case SIMPLE: | |
| switch (tagInt) { | |
| case TRUE: | |
| case FALSE: | |
| return handleBool(*addlData, begin, pos, parseClient); | |
| case FLOAT_V: | |
| #if defined(__STDC_IEC_559__) || FLT_MANT_DIG == 24 || __FLT_MANT_DIG__ == 24 | |
| return handleFloat(*addlData, begin, pos, parseClient); | |
| #else | |
| parseClient->error(begin, "Value float is not supported for platform."); | |
| return {begin, nullptr}; | |
| #endif // __STDC_IEC_559__ || FLT_MANT_DIG == 24 || __FLT_MANT_DIG__ == 24 | |
| case DOUBLE_V: | |
| #if defined(__STDC_IEC_559__) || DBL_MANT_DIG == 53 || __DBL_MANT_DIG__ == 53 | |
| return handleDouble(*addlData, begin, pos, parseClient); | |
| #else | |
| parseClient->error(begin, "Value double is not supported for platform."); | |
| return {begin, nullptr}; | |
| #endif // __STDC_IEC_559__ || DBL_MANT_DIG == 53 || __DBL_MANT_DIG__ == 53 | |
| case NULL_V: | |
| return handleNull(begin, pos, parseClient); | |
| default: | |
| parseClient->error(begin, "Unsupported half-floating-point or simple value."); | |
| return {begin, nullptr}; | |
| } | |
| } | |
| CHECK(false); // Impossible to get here. | |
| return {}; | |
| } | |
| class FullParseClient : public ParseClient { | |
| public: | |
| virtual ParseClient* item(std::unique_ptr<Item>& item, const uint8_t*, const uint8_t*, | |
| const uint8_t* end) override { | |
| if (mParentStack.empty() && !item->isCompound()) { | |
| // This is the first and only item. | |
| mTheItem = std::move(item); | |
| mPosition = end; | |
| return nullptr; // We're done. | |
| } | |
| if (item->isCompound()) { | |
| // Starting a new compound data item, i.e. a new parent. Save it on the parent stack. | |
| // It's safe to save a raw pointer because the unique_ptr is guaranteed to stay in | |
| // existence until the corresponding itemEnd() call. | |
| mParentStack.push(item.get()); | |
| return this; | |
| } else { | |
| return appendToLastParent(std::move(item)); | |
| } | |
| } | |
| virtual ParseClient* itemEnd(std::unique_ptr<Item>& item, const uint8_t*, const uint8_t*, | |
| const uint8_t* end) override { | |
| CHECK(item->isCompound() && item.get() == mParentStack.top()); | |
| mParentStack.pop(); | |
| IncompleteItem* incompleteItem = IncompleteItem::cast(item.get()); | |
| std::unique_ptr<Item> finalizedItem = std::move(*incompleteItem).finalize(); | |
| if (mParentStack.empty()) { | |
| mTheItem = std::move(finalizedItem); | |
| mPosition = end; | |
| return nullptr; // We're done | |
| } else { | |
| return appendToLastParent(std::move(finalizedItem)); | |
| } | |
| } | |
| virtual void error(const uint8_t* position, const std::string& errorMessage) override { | |
| mPosition = position; | |
| mErrorMessage = errorMessage; | |
| } | |
| std::tuple<std::unique_ptr<Item> /* result */, const uint8_t* /* newPos */, | |
| std::string /* errMsg */> | |
| parseResult() { | |
| std::unique_ptr<Item> p = std::move(mTheItem); | |
| return {std::move(p), mPosition, std::move(mErrorMessage)}; | |
| } | |
| private: | |
| ParseClient* appendToLastParent(std::unique_ptr<Item> item) { | |
| auto parent = mParentStack.top(); | |
| switch (parent->type()) { | |
| case BSTR: | |
| if (item->type() != BSTR) { | |
| mErrorMessage += "Expected BSTR in indefinite-length string."; | |
| return nullptr; | |
| } | |
| IncompleteItem::cast(parent)->add(std::move(item)); | |
| break; | |
| case TSTR: | |
| if (item->type() != TSTR) { | |
| mErrorMessage += "Expected TSTR in indefinite-length string."; | |
| return nullptr; | |
| } | |
| IncompleteItem::cast(parent)->add(std::move(item)); | |
| break; | |
| default: | |
| IncompleteItem::cast(parent)->add(std::move(item)); | |
| break; | |
| } | |
| return this; | |
| } | |
| std::unique_ptr<Item> mTheItem; | |
| std::stack<Item*> mParentStack; | |
| const uint8_t* mPosition = nullptr; | |
| std::string mErrorMessage; | |
| }; | |
| } // anonymous namespace | |
| void parse(const uint8_t* begin, const uint8_t* end, ParseClient* parseClient) { | |
| parseRecursively(begin, end, false, parseClient, 0); | |
| } | |
| std::tuple<std::unique_ptr<Item> /* result */, const uint8_t* /* newPos */, | |
| std::string /* errMsg */> | |
| parse(const uint8_t* begin, const uint8_t* end) { | |
| FullParseClient parseClient; | |
| parse(begin, end, &parseClient); | |
| return parseClient.parseResult(); | |
| } | |
| void parseWithViews(const uint8_t* begin, const uint8_t* end, ParseClient* parseClient) { | |
| parseRecursively(begin, end, true, parseClient, 0); | |
| } | |
| std::tuple<std::unique_ptr<Item> /* result */, const uint8_t* /* newPos */, | |
| std::string /* errMsg */> | |
| parseWithViews(const uint8_t* begin, const uint8_t* end) { | |
| FullParseClient parseClient; | |
| parseWithViews(begin, end, &parseClient); | |
| return parseClient.parseResult(); | |
| } | |
| } // namespace cppbor |