diff options
author | Shawn Willden <swillden@google.com> | 2020-12-16 03:49:52 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2020-12-16 03:49:52 +0000 |
commit | 01589c6c88e710647bd66bafcf8838933a081446 (patch) | |
tree | 57e62d291329e2b3fea02b07897a98e6a0a3e9b3 | |
parent | 7aa436b8cae4c99277aab05ac2380e8228f71623 (diff) | |
parent | 315d859ec47ec349348cc400973ab4d5f5973332 (diff) | |
download | libcppbor-01589c6c88e710647bd66bafcf8838933a081446.tar.gz |
Change semantic tagging. am: 315d859ec4
Original change: https://android-review.googlesource.com/c/platform/external/libcppbor/+/1515385
MUST ONLY BE SUBMITTED BY AUTOMERGER
Change-Id: Ic98c01b5918eda13e21f0ff8d5343f98d5286914
-rw-r--r-- | include/cppbor/cppbor.h | 117 | ||||
-rw-r--r-- | src/cppbor.cpp | 68 | ||||
-rw-r--r-- | src/cppbor_parse.cpp | 22 | ||||
-rw-r--r-- | tests/cppbor_test.cpp | 187 |
4 files changed, 267 insertions, 127 deletions
diff --git a/include/cppbor/cppbor.h b/include/cppbor/cppbor.h index f9d371e..1409bf8 100644 --- a/include/cppbor/cppbor.h +++ b/include/cppbor/cppbor.h @@ -63,7 +63,7 @@ class Bool; class Array; class Map; class Null; -class Semantic; +class SemanticTag; class EncodedItem; /** @@ -128,8 +128,39 @@ class Item { const Map* asMap() const { return const_cast<Item*>(this)->asMap(); } virtual Array* asArray() { return nullptr; } const Array* asArray() const { return const_cast<Item*>(this)->asArray(); } - virtual Semantic* asSemantic() { return nullptr; } - const Semantic* asSemantic() const { return const_cast<Item*>(this)->asSemantic(); } + + // Like those above, these methods safely downcast an Item when it's actually a SemanticTag. + // However, if you think you want to use these methods, you probably don't. Typically, the way + // you should handle tagged Items is by calling the appropriate method above (e.g. asInt()) + // which will return a pointer to the tagged Item, rather than the tag itself. If you want to + // find out if the Item* you're holding is to something with one or more tags applied, see + // semanticTagCount() and semanticTag() below. + virtual SemanticTag* asSemanticTag() { return nullptr; } + const SemanticTag* asSemanticTag() const { return const_cast<Item*>(this)->asSemanticTag(); } + + /** + * Returns the number of semantic tags prefixed to this Item. + */ + virtual size_t semanticTagCount() const { return 0; } + + /** + * Returns the semantic tag at the specified nesting level `nesting`, iff `nesting` is less than + * the value returned by semanticTagCount(). + * + * CBOR tags are "nested" by applying them in sequence. The "rightmost" tag is the "inner" tag. + * That is, given: + * + * 4(5(6("AES"))) which encodes as C1 C2 C3 63 414553 + * + * The tstr "AES" is tagged with 6. The combined entity ("AES" tagged with 6) is tagged with 5, + * etc. So in this example, semanticTagCount() would return 3, and semanticTag(0) would return + * 5 semanticTag(1) would return 5 and semanticTag(2) would return 4. For values of n > 2, + * semanticTag(n) will return 0, but this is a meaningless value. + * + * If this layering is confusing, you probably don't have to worry about it. Nested tagging does + * not appear to be common, so semanticTag(0) is the only one you'll use. + */ + virtual uint64_t semanticTag(size_t /* nesting */ = 0) const { return 0; } /** * Returns true if this is a "compound" item, i.e. one that contains one or more other items. @@ -614,64 +645,58 @@ class Map : public Item { bool mCanonicalized = false; }; -class Semantic : public Item { +class SemanticTag : public Item { public: static constexpr MajorType kMajorType = SEMANTIC; template <typename T> - explicit Semantic(uint64_t value, T&& child); - - Semantic(const Semantic& other) = delete; - Semantic(Semantic&&) = default; - Semantic& operator=(const Semantic& other) = delete; - Semantic& operator=(Semantic&&) = default; - - bool operator==(const Semantic& other) const&; + SemanticTag(uint64_t tagValue, T&& taggedItem); + SemanticTag(const SemanticTag& other) = delete; + SemanticTag(SemanticTag&&) = default; + SemanticTag& operator=(const SemanticTag& other) = delete; + SemanticTag& operator=(SemanticTag&&) = default; + + bool operator==(const SemanticTag& other) const& { + return mValue == other.mValue && *mTaggedItem == *other.mTaggedItem; + } bool isCompound() const override { return true; } - virtual size_t size() const { - assertInvariant(); - return 1; - } + virtual size_t size() const { 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(); }); - } + // Encoding returns the tag + enclosed Item. + size_t encodedSize() const override { return headerSize(mValue) + mTaggedItem->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; - MajorType type() const override { return kMajorType; } - Semantic* asSemantic() override { return this; } - - const std::unique_ptr<Item>& child() const { - assertInvariant(); - return mEntries[0]; - } - - std::unique_ptr<Item>& child() { - assertInvariant(); - return mEntries[0]; - } + // type() is a bit special. In normal usage it should return the wrapped type, but during + // parsing when we haven't yet parsed the tagged item, it needs to return SEMANTIC. + MajorType type() const override { return mTaggedItem ? mTaggedItem->type() : SEMANTIC; } + SemanticTag* asSemanticTag() override { return this; } + + // Type information reflects the enclosed Item. Note that if the immediately-enclosed Item is + // another tag, these methods will recurse down to the non-tag Item. + Int* asInt() override { return mTaggedItem->asInt(); } + Uint* asUint() override { return mTaggedItem->asUint(); } + Nint* asNint() override { return mTaggedItem->asNint(); } + Tstr* asTstr() override { return mTaggedItem->asTstr(); } + Bstr* asBstr() override { return mTaggedItem->asBstr(); } + Simple* asSimple() override { return mTaggedItem->asSimple(); } + Map* asMap() override { return mTaggedItem->asMap(); } + Array* asArray() override { return mTaggedItem->asArray(); } - uint64_t value() const { return mValue; } + std::unique_ptr<Item> clone() const override; - std::unique_ptr<Item> clone() const override { - assertInvariant(); - return std::make_unique<Semantic>(mValue, mEntries[0]->clone()); - } + size_t semanticTagCount() const override; + uint64_t semanticTag(size_t nesting = 0) const override; protected: - Semantic() = default; - Semantic(uint64_t value) : mValue(value) {} + SemanticTag() = default; + SemanticTag(uint64_t value) : mValue(value) {} uint64_t mValue; - std::vector<std::unique_ptr<Item>> mEntries; - - private: - void assertInvariant() const; + std::unique_ptr<Item> mTaggedItem; }; /** @@ -955,9 +980,7 @@ const std::unique_ptr<Item>& Map::get(Key key) const { } template <typename T> -Semantic::Semantic(uint64_t value, T&& child) : mValue(value) { - mEntries.reserve(1); - mEntries.push_back(details::makeItem(std::forward<T>(child))); -} +SemanticTag::SemanticTag(uint64_t value, T&& taggedItem) + : mValue(value), mTaggedItem(details::makeItem(std::forward<T>(taggedItem))) {} } // namespace cppbor diff --git a/src/cppbor.cpp b/src/cppbor.cpp index dc34985..f113a32 100644 --- a/src/cppbor.cpp +++ b/src/cppbor.cpp @@ -98,7 +98,18 @@ bool prettyPrintInternal(const Item* item, string& out, size_t indent, size_t ma string indentString(indent, ' '); + size_t tagCount = item->semanticTagCount(); + while (tagCount > 0) { + --tagCount; + snprintf(buf, sizeof(buf), "tag %" PRIu64 " ", item->semanticTag(tagCount)); + out.append(buf); + } + switch (item->type()) { + case SEMANTIC: + // Handled above. + break; + case UINT: snprintf(buf, sizeof(buf), "%" PRIu64, item->asUint()->unsignedValue()); out.append(buf); @@ -205,14 +216,6 @@ bool prettyPrintInternal(const Item* item, string& out, size_t indent, size_t ma } } break; - case SEMANTIC: { - const Semantic* semantic = item->asSemantic(); - snprintf(buf, sizeof(buf), "tag %" PRIu64 " ", semantic->value()); - out.append(buf); - prettyPrintInternal(semantic->child().get(), out, indent, maxBStrSize, - mapKeysToNotPrint); - } break; - case SIMPLE: const Bool* asBool = item->asSimple()->asBool(); const Null* asNull = item->asSimple()->asNull(); @@ -313,7 +316,7 @@ bool Item::operator==(const Item& other) const& { case SIMPLE: return *asSimple() == *(other.asSimple()); case SEMANTIC: - return *asSemantic() == *(other.asSemantic()); + return *asSemanticTag() == *(other.asSemanticTag()); default: CHECK(false); // Impossible to get here. return false; @@ -459,7 +462,9 @@ void recursivelyCanonicalize(std::unique_ptr<Item>& item) { return; case SEMANTIC: - recursivelyCanonicalize(item->asSemantic()->child()); + // This can't happen. SemanticTags delegate their type() method to the contained Item's + // type. + assert(false); return; } } @@ -492,24 +497,45 @@ std::unique_ptr<Item> Map::clone() const { return res; } -bool Semantic::operator==(const Semantic& other) const& { - assertInvariant(); - return *mEntries.front() == *other.mEntries.front(); +std::unique_ptr<Item> SemanticTag::clone() const { + return std::make_unique<SemanticTag>(mValue, mTaggedItem->clone()); } -uint8_t* Semantic::encode(uint8_t* pos, const uint8_t* end) const { - pos = encodeHeader(value(), pos, end); +uint8_t* SemanticTag::encode(uint8_t* pos, const uint8_t* end) const { + // Can't use the encodeHeader() method that calls type() to get the major type, since that will + // return the tagged Item's type. + pos = ::cppbor::encodeHeader(kMajorType, mValue, pos, end); if (!pos) return nullptr; - return mEntries.front()->encode(pos, end); + return mTaggedItem->encode(pos, end); +} + +void SemanticTag::encode(EncodeCallback encodeCallback) const { + // Can't use the encodeHeader() method that calls type() to get the major type, since that will + // return the tagged Item's type. + ::cppbor::encodeHeader(kMajorType, mValue, encodeCallback); + mTaggedItem->encode(encodeCallback); } -void Semantic::encode(EncodeCallback encodeCallback) const { - encodeHeader(value(), encodeCallback); - mEntries.front()->encode(encodeCallback); +size_t SemanticTag::semanticTagCount() const { + size_t levelCount = 1; // Count this level. + const SemanticTag* cur = this; + while (cur->mTaggedItem && (cur = cur->mTaggedItem->asSemanticTag()) != nullptr) ++levelCount; + return levelCount; } -void Semantic::assertInvariant() const { - CHECK(mEntries.size() == 1); +uint64_t SemanticTag::semanticTag(size_t nesting) const { + // Getting the value of a specific nested tag is a bit tricky, because we start with the outer + // tag and don't know how many are inside. We count the number of nesting levels to find out + // how many there are in total, then to get the one we want we have to walk down levelCount - + // nesting steps. + size_t levelCount = semanticTagCount(); + if (nesting >= levelCount) return 0; + + levelCount -= nesting; + const SemanticTag* cur = this; + while (--levelCount > 0) cur = cur->mTaggedItem->asSemanticTag(); + + return cur->mValue; } string prettyPrint(const Item* item, size_t maxBStrSize, const vector<string>& mapKeysToNotPrint) { diff --git a/src/cppbor_parse.cpp b/src/cppbor_parse.cpp index 2736b71..42d74fb 100644 --- a/src/cppbor_parse.cpp +++ b/src/cppbor_parse.cpp @@ -149,17 +149,14 @@ class IncompleteMap : public Map, public IncompleteItem { size_t mSize; }; -class IncompleteSemantic : public Semantic, public IncompleteItem { +class IncompleteSemanticTag : public SemanticTag, public IncompleteItem { public: - explicit IncompleteSemantic(uint64_t value) : Semantic(value) {} + 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 { - mEntries.reserve(1); - mEntries.push_back(std::move(item)); - } + void add(std::unique_ptr<Item> item) override { mTaggedItem = std::move(item); } }; std::tuple<const uint8_t*, ParseClient*> handleEntries(size_t entryCount, const uint8_t* hdrBegin, @@ -254,7 +251,7 @@ std::tuple<const uint8_t*, ParseClient*> parseRecursively(const uint8_t* begin, pos, end, "map", parseClient); case SEMANTIC: - return handleCompound(std::make_unique<IncompleteSemantic>(addlData), 1, begin, pos, + return handleCompound(std::make_unique<IncompleteSemanticTag>(addlData), 1, begin, pos, end, "semantic", parseClient); case SIMPLE: @@ -326,15 +323,18 @@ class FullParseClient : public ParseClient { #if __has_feature(cxx_rtti) assert(dynamic_cast<IncompleteItem*>(parent)); #endif + + IncompleteItem* parentItem{}; if (parent->type() == ARRAY) { - static_cast<IncompleteArray*>(parent)->add(std::move(item)); + parentItem = static_cast<IncompleteArray*>(parent); } else if (parent->type() == MAP) { - static_cast<IncompleteMap*>(parent)->add(std::move(item)); - } else if (parent->type() == SEMANTIC) { - static_cast<IncompleteSemantic*>(parent)->add(std::move(item)); + parentItem = static_cast<IncompleteMap*>(parent); + } else if (parent->asSemanticTag()) { + parentItem = static_cast<IncompleteSemanticTag*>(parent); } else { CHECK(false); // Impossible to get here. } + parentItem->add(std::move(item)); } std::unique_ptr<Item> mTheItem; diff --git a/tests/cppbor_test.cpp b/tests/cppbor_test.cpp index b45d804..4d9a1f0 100644 --- a/tests/cppbor_test.cpp +++ b/tests/cppbor_test.cpp @@ -140,7 +140,17 @@ TEST(SimpleValueTest, TextStringEncodings) { TEST(SimpleValueTest, SemanticTagEncoding) { EXPECT_EQ("\xDB\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x63\x41\x45\x53"s, - Semantic(std::numeric_limits<uint64_t>::max(), "AES").toString()); + SemanticTag(std::numeric_limits<uint64_t>::max(), "AES").toString()); +} + +TEST(SimpleValueTest, NestedSemanticTagEncoding) { + auto tripleTagged = + SemanticTag(254, + SemanticTag(1, // + SemanticTag(std::numeric_limits<uint64_t>::max(), // + "AES"))); + EXPECT_EQ("\xD8\xFE\xC1\xDB\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x63\x41\x45\x53"s, + tripleTagged.toString()); } TEST(IsIteratorPairOverTest, All) { @@ -176,7 +186,8 @@ TEST(IsUniquePtrSubclassOf, All) { EXPECT_TRUE((details::is_unique_ptr_of_subclass_of_v<Item, std::unique_ptr<Bool>>::value)); EXPECT_TRUE((details::is_unique_ptr_of_subclass_of_v<Item, std::unique_ptr<Map>>::value)); EXPECT_TRUE((details::is_unique_ptr_of_subclass_of_v<Item, std::unique_ptr<Array>>::value)); - EXPECT_TRUE((details::is_unique_ptr_of_subclass_of_v<Item, std::unique_ptr<Semantic>>::value)); + EXPECT_TRUE( + (details::is_unique_ptr_of_subclass_of_v<Item, std::unique_ptr<SemanticTag>>::value)); EXPECT_FALSE( (details::is_unique_ptr_of_subclass_of_v<std::string, std::unique_ptr<Bool>>::value)); EXPECT_FALSE(( @@ -416,7 +427,7 @@ TEST(EncodingMethodsTest, AllVariants) { .add("key_d", std::numeric_limits<int16_t>::min())) .add("foo")) .add("key2", true) - .add("key3", Semantic(987654321, "Zhai gana test")); + .add("key3", SemanticTag(1, SemanticTag(987654321, "Zhai gana test"))); std::vector<uint8_t> buf; buf.resize(map.encodedSize()); @@ -476,9 +487,9 @@ TEST(EncodingMethodsTest, MapWithTooShortBuf) { EXPECT_EQ(nullptr, map.encode(buf.data(), buf.data() + buf.size())); } -TEST(EncodingMethodsTest, SemanticWithTooShortBuf) { - Semantic tag(4321, Array().add(Array().add("Qaiyrly kesh!").add("Kesh zharyq!").add("431")) - .add(Map().add("kilt_1", 777).add("kilt_2", 999))); +TEST(EncodingMethodsTest, SemanticTagWithTooShortBuf) { + SemanticTag tag(4321, Array().add(Array().add("Qaiyrly kesh!").add("Kesh zharyq!").add("431")) + .add(Map().add("kilt_1", 777).add("kilt_2", 999))); std::vector<uint8_t> buf(tag.encodedSize() - 1); EXPECT_EQ(nullptr, tag.encode(buf.data(), buf.data() + buf.size())); } @@ -597,9 +608,24 @@ TEST(EqualityTest, Null) { EXPECT_NE(val, Map(99, 1, 99, 2)); } -TEST(EqualityTest, Semantic) { - Semantic val(215, Bstr("asd")); - EXPECT_EQ(val, Semantic(215, Bstr("asd"))); +TEST(EqualityTest, SemanticTag) { + SemanticTag val(215, Bstr("asd")); + EXPECT_EQ(val, SemanticTag(215, Bstr("asd"))); + + 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, Null()); +} + +TEST(EqualityTest, NestedSemanticTag) { + SemanticTag val(238238, SemanticTag(215, Bstr("asd"))); + EXPECT_EQ(val, SemanticTag(238238, SemanticTag(215, Bstr("asd")))); EXPECT_NE(val, Uint(99)); EXPECT_NE(val, Nint(-1)); @@ -731,21 +757,58 @@ TEST(ConvertTest, Array) { EXPECT_EQ(0U, item->asArray()->size()); } -TEST(ConvertTest, Semantic) { - unique_ptr<Item> item(new Semantic(1, "DSA")); +TEST(ConvertTest, SemanticTag) { + unique_ptr<Item> item(new SemanticTag(10, "DSA")); - EXPECT_EQ(SEMANTIC, item->type()); + EXPECT_EQ(TSTR, item->type()); + EXPECT_EQ(nullptr, item->asInt()); + EXPECT_EQ(nullptr, item->asUint()); + EXPECT_EQ(nullptr, item->asNint()); + EXPECT_EQ(nullptr, item->asBstr()); + EXPECT_EQ(nullptr, item->asSimple()); + EXPECT_EQ(nullptr, item->asMap()); + EXPECT_EQ(nullptr, item->asArray()); + + // Both asTstr() (the contained type) and asSemanticTag() return non-null. + EXPECT_NE(nullptr, item->asTstr()); + EXPECT_NE(nullptr, item->asSemanticTag()); + + // asTtr() and asSemanticTag() actually return different objects. + EXPECT_NE(static_cast<Item*>(item->asTstr()), static_cast<Item*>(item->asSemanticTag())); + + EXPECT_EQ(1U, item->asSemanticTag()->size()); + EXPECT_EQ("DSA", item->asTstr()->value()); + + EXPECT_EQ(1U, item->semanticTagCount()); + EXPECT_EQ(10U, item->semanticTag()); +} + +TEST(ConvertTest, NestedSemanticTag) { + unique_ptr<Item> item(new SemanticTag(40, new SemanticTag(10, "DSA"))); + + EXPECT_EQ(TSTR, 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_EQ(nullptr, item->asArray()); - EXPECT_NE(nullptr, item->asSemantic()); - EXPECT_EQ(1U, item->asSemantic()->size()); + // Both asTstr() (the contained type) and asSemanticTag() return non-null. + EXPECT_NE(nullptr, item->asTstr()); + EXPECT_NE(nullptr, item->asSemanticTag()); + + // asTtr() and asSemanticTag() actually return different objects. Note that there's no way to + // get a pointer to the "inner" SemanticTag object. There shouldn't be any need to. + EXPECT_NE(static_cast<Item*>(item->asTstr()), static_cast<Item*>(item->asSemanticTag())); + + EXPECT_EQ(1U, item->asSemanticTag()->size()); + EXPECT_EQ("DSA", item->asTstr()->value()); + + EXPECT_EQ(2U, item->semanticTagCount()); + EXPECT_EQ(10U, item->semanticTag(0)); + EXPECT_EQ(40U, item->semanticTag(1)); } TEST(ConvertTest, Null) { @@ -772,7 +835,6 @@ TEST(CloningTest, Uint) { EXPECT_EQ(clone->type(), UINT); EXPECT_NE(clone->asUint(), nullptr); EXPECT_EQ(item, *clone->asUint()); - std::move(item); EXPECT_EQ(*clone->asUint(), Uint(10)); } @@ -782,7 +844,6 @@ TEST(CloningTest, Nint) { EXPECT_EQ(clone->type(), NINT); EXPECT_NE(clone->asNint(), nullptr); EXPECT_EQ(item, *clone->asNint()); - std::move(item); EXPECT_EQ(*clone->asNint(), Nint(-1000000)); } @@ -792,7 +853,6 @@ TEST(CloningTest, Tstr) { EXPECT_EQ(clone->type(), TSTR); EXPECT_NE(clone->asTstr(), nullptr); EXPECT_EQ(item, *clone->asTstr()); - std::move(item); EXPECT_EQ(*clone->asTstr(), Tstr("qwertyasdfgh")); } @@ -802,23 +862,18 @@ TEST(CloningTest, Bstr) { EXPECT_EQ(clone->type(), BSTR); EXPECT_NE(clone->asBstr(), nullptr); EXPECT_EQ(item, *clone->asBstr()); - std::move(item); EXPECT_EQ(*clone->asBstr(), Bstr(std::vector<uint8_t>{1, 2, 3, 255, 0})); } TEST(CloningTest, Array) { Array item(-1000000, 22222222, "item", Map(1, 2, 4, Array(1, "das", true, nullptr)), - Semantic(16, "DATA")), + SemanticTag(16, "DATA")), copy(-1000000, 22222222, "item", Map(1, 2, 4, Array(1, "das", true, nullptr)), - Semantic(16, "DATA")); + SemanticTag(16, "DATA")); auto clone = item.clone(); EXPECT_EQ(clone->type(), ARRAY); EXPECT_NE(clone->asArray(), nullptr); EXPECT_EQ(item, *clone->asArray()); - std::move(item[0]); - std::move(item[1]); - std::move(item[3]); - std::move(item); EXPECT_EQ(*clone->asArray(), copy); } @@ -829,10 +884,6 @@ TEST(CloningTest, Map) { EXPECT_EQ(clone->type(), MAP); EXPECT_NE(clone->asMap(), nullptr); EXPECT_EQ(item, *clone->asMap()); - auto& [key, value] = item[0]; - std::move(key); - std::move(value); - std::move(item); EXPECT_EQ(*clone->asMap(), copy); } @@ -844,7 +895,6 @@ TEST(CloningTest, Bool) { EXPECT_EQ(clone->asSimple()->simpleType(), BOOLEAN); EXPECT_NE(clone->asSimple()->asBool(), nullptr); EXPECT_EQ(item, *clone->asSimple()->asBool()); - std::move(item); EXPECT_EQ(*clone->asSimple()->asBool(), Bool(true)); } @@ -856,19 +906,52 @@ TEST(CloningTest, Null) { EXPECT_EQ(clone->asSimple()->simpleType(), NULL_T); EXPECT_NE(clone->asSimple()->asNull(), nullptr); EXPECT_EQ(item, *clone->asSimple()->asNull()); - std::move(item); EXPECT_EQ(*clone->asSimple()->asNull(), Null()); } -TEST(CloningTest, Semantic) { - Semantic item(96, Array(1, 2, 3, "entry", Map("key", "value"))), - copy(96, Array(1, 2, 3, "entry", Map("key", "value"))); +TEST(CloningTest, SemanticTag) { + SemanticTag item(96, Array(1, 2, 3, "entry", Map("key", "value"))); + SemanticTag copy(96, Array(1, 2, 3, "entry", Map("key", "value"))); + + auto clone = item.clone(); + EXPECT_EQ(clone->type(), ARRAY); + EXPECT_NE(clone->asSemanticTag(), nullptr); + EXPECT_EQ(item, *clone->asSemanticTag()); + EXPECT_EQ(*clone->asSemanticTag(), copy); +} + +TEST(CloningTest, NestedSemanticTag) { + SemanticTag item(20, // + SemanticTag(30, // + SemanticTag(96, // + Array(1, 2, 3, "entry", Map("key", "value"))))); + SemanticTag copy(20, // + SemanticTag(30, // + SemanticTag(96, // + Array(1, 2, 3, "entry", Map("key", "value"))))); + auto clone = item.clone(); - EXPECT_EQ(clone->type(), SEMANTIC); - EXPECT_NE(clone->asSemantic(), nullptr); - EXPECT_EQ(item, *clone->asSemantic()); - std::move(item); - EXPECT_EQ(*clone->asSemantic(), copy); + EXPECT_EQ(clone->type(), ARRAY); + EXPECT_NE(clone->asSemanticTag(), nullptr); + EXPECT_EQ(item, *clone->asSemanticTag()); + EXPECT_EQ(*clone->asSemanticTag(), copy); +} + +TEST(PrettyPrintingTest, NestedSemanticTag) { + SemanticTag item(20, // + SemanticTag(30, // + SemanticTag(96, // + Array(1, 2, 3, "entry", Map("key", "value"))))); + EXPECT_EQ(prettyPrint(&item), + "tag 20 tag 30 tag 96 [\n" + " 1,\n" + " 2,\n" + " 3,\n" + " 'entry',\n" + " {\n" + " 'key' : 'value',\n" + " },\n" + "]"); } TEST(MapCanonicalizationTest, CanonicalizationTest) { @@ -1051,7 +1134,7 @@ MATCHER_P(IsArrayOfSize, value, "") { } MATCHER_P(IsSemanticTagOfValue, value, "") { - return arg->type() == SEMANTIC && arg->asSemantic()->value() == value; + return arg->semanticTagCount() == 1 && arg->semanticTag() == value; } MATCHER_P(IsMapOfSize, value, "") { @@ -1196,19 +1279,19 @@ TEST(StreamParseTest, Array) { parse(encoded.data(), encoded.data() + encoded.size(), &mpc); } -TEST(StreamParseTest, Semantic) { +TEST(StreamParseTest, SemanticTag) { MockParseClient mpc; - Semantic val(15, Array(-5, "Hi")); + SemanticTag val(15, Array(-5, "Hi")); auto encoded = val.encode(); - ASSERT_NE(val.child()->asArray(), nullptr); - const Array& array = *(val.child()->asArray()); + ASSERT_NE(val.asArray(), nullptr); + const Array& array = *(val.asArray()); uint8_t* encBegin = encoded.data(); uint8_t* encEnd = encoded.data() + encoded.size(); { InSequence s; const uint8_t* pos = encBegin; - EXPECT_CALL(mpc, item(IsSemanticTagOfValue(val.value()), pos, pos + 1, pos + 1)) + EXPECT_CALL(mpc, item(IsSemanticTagOfValue(val.semanticTag()), pos, pos + 1, pos + 1)) .WillOnce(Return(&mpc)); ++pos; const uint8_t* innerArrayBegin = pos; @@ -1224,7 +1307,8 @@ TEST(StreamParseTest, Semantic) { EXPECT_CALL(mpc, itemEnd(IsArrayOfSize(array.size()), innerArrayBegin, innerArrayBegin + 1, pos)) .WillOnce(Return(&mpc)); - EXPECT_CALL(mpc, itemEnd(IsSemanticTagOfValue(val.value()), encBegin, encBegin + 1, encEnd)) + EXPECT_CALL(mpc, itemEnd(IsSemanticTagOfValue(val.semanticTag()), encBegin, encBegin + 1, + encEnd)) .WillOnce(Return(&mpc)); } @@ -1352,8 +1436,15 @@ TEST(FullParserTest, Map) { EXPECT_THAT(item, MatchesItem(ByRef(val))); } -TEST(FullParserTest, Semantic) { - Semantic val(99, "Salem"); +TEST(FullParserTest, SemanticTag) { + SemanticTag val(99, "Salem"); + + auto [item, pos, message] = parse(val.encode()); + EXPECT_THAT(item, MatchesItem(ByRef(val))); +} + +TEST(FullParserTest, NestedSemanticTag) { + SemanticTag val(10, SemanticTag(99, "Salem")); auto [item, pos, message] = parse(val.encode()); EXPECT_THAT(item, MatchesItem(ByRef(val))); |