aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShawn Willden <swillden@google.com>2020-12-16 04:52:33 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2020-12-16 04:52:33 +0000
commitb1e83192151240f0b38039c96db1e4c2318fed0f (patch)
tree57e62d291329e2b3fea02b07897a98e6a0a3e9b3
parent4b1ec2b43b6f92c79d0a48b98fe90697d8549aa1 (diff)
parentc285326084f649cdc634336dad74f8b2988f2532 (diff)
downloadlibcppbor-b1e83192151240f0b38039c96db1e4c2318fed0f.tar.gz
Change semantic tagging. am: 315d859ec4 am: 01589c6c88 am: c285326084
Original change: https://android-review.googlesource.com/c/platform/external/libcppbor/+/1515385 MUST ONLY BE SUBMITTED BY AUTOMERGER Change-Id: I9d7b8d5850ea9d48126643d7e1a77344f859e6e1
-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)));