aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShawn Willden <swillden@google.com>2020-11-25 15:46:34 -0700
committerShawn Willden <swillden@google.com>2020-12-14 10:02:02 -0700
commit315d859ec47ec349348cc400973ab4d5f5973332 (patch)
tree57e62d291329e2b3fea02b07897a98e6a0a3e9b3
parentf79067125cdf2977226a264180fa193895df0ef6 (diff)
downloadlibcppbor-315d859ec47ec349348cc400973ab4d5f5973332.tar.gz
Change semantic tagging.
Semantic tagging in libcppbor was a bit cumbersome to use, with tags treated as separate items, requiring code that analyzes tagged data to pay attention to the tags. Among other issues, that violates the intention of semantic tagging in CBOR, which, per the RFC, does not require decoders to understand tags. This CL changes that behavior so that code that walks a parsed Item tree will not "see" the tags unless it looks for them, by calling "Item::semanticTagCount()" and then "Item::semanticTag()". Nested tags are supported. Test: cppbor_test_external Change-Id: Ifa99475fd0d9f369f3e379251979446a2ec262b5
-rw-r--r--include/cppbor/cppbor.h117
-rw-r--r--src/cppbor.cpp68
-rw-r--r--src/cppbor_parse.cpp22
-rw-r--r--tests/cppbor_test.cpp187
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)));