Make outout of cppbor::parse safely mutable am: 1a288db757 am: 69d2206312
Original change: https://android-review.googlesource.com/c/platform/system/libcppbor/+/2468182
Change-Id: I2320574766d4f5b9612df62065facadc494988a1
Signed-off-by: Automerger Merge Worker <[email protected]>
diff --git a/src/cppbor_parse.cpp b/src/cppbor_parse.cpp
index a221cf4..b5bdd80 100644
--- a/src/cppbor_parse.cpp
+++ b/src/cppbor_parse.cpp
@@ -16,8 +16,11 @@
#include "cppbor_parse.h"
+#include <memory>
#include <sstream>
#include <stack>
+#include <type_traits>
+#include "cppbor.h"
#ifndef __TRUSTY__
#include <android-base/logging.h>
@@ -110,8 +113,11 @@
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 IncompleteArray : public Array, public IncompleteItem {
@@ -126,6 +132,12 @@
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:
size_t mSize;
};
@@ -146,6 +158,10 @@
}
}
+ virtual std::unique_ptr<Item> finalize() && override {
+ return std::make_unique<Map>(std::move(*this));
+ }
+
private:
std::unique_ptr<Item> mKeyHeldForAdding;
size_t mSize;
@@ -159,8 +175,35 @@
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());
+ 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->asSemanticTag()) {
+#if __has_feature(cxx_rtti)
+ CHECK(dynamic_cast<IncompleteSemanticTag*>(item));
+#endif
+ return static_cast<IncompleteSemanticTag*>(item);
+ } else {
+ CHECK(false); // Impossible to get here.
+ }
+ return nullptr;
+ }
+
std::tuple<const uint8_t*, ParseClient*> handleEntries(size_t entryCount, const uint8_t* hdrBegin,
const uint8_t* pos, const uint8_t* end,
const std::string& typeName,
@@ -321,12 +364,15 @@
CHECK(item->isCompound() && item.get() == mParentStack.top());
mParentStack.pop();
+ IncompleteItem* incompleteItem = IncompleteItem::cast(std::move(item).get());
+ std::unique_ptr<Item> finalizedItem = std::move(*incompleteItem).finalize();
+
if (mParentStack.empty()) {
- mTheItem = std::move(item);
+ mTheItem = std::move(finalizedItem);
mPosition = end;
return nullptr; // We're done
} else {
- appendToLastParent(std::move(item));
+ appendToLastParent(std::move(finalizedItem));
return this;
}
}
@@ -346,21 +392,7 @@
private:
void appendToLastParent(std::unique_ptr<Item> item) {
auto parent = mParentStack.top();
-#if __has_feature(cxx_rtti)
- assert(dynamic_cast<IncompleteItem*>(parent));
-#endif
-
- IncompleteItem* parentItem{};
- if (parent->type() == ARRAY) {
- parentItem = static_cast<IncompleteArray*>(parent);
- } else if (parent->type() == MAP) {
- 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));
+ IncompleteItem::cast(parent)->add(std::move(item));
}
std::unique_ptr<Item> mTheItem;
diff --git a/tests/cppbor_test.cpp b/tests/cppbor_test.cpp
index b9a2f35..df2e017 100644
--- a/tests/cppbor_test.cpp
+++ b/tests/cppbor_test.cpp
@@ -1642,6 +1642,39 @@
EXPECT_EQ(arr[0]->asTstr()->value(), "hello");
}
+TEST(FullParserTest, MutableOutput) {
+ Array nestedArray("pizza", 31415);
+ Map nestedMap("array", std::move(nestedArray));
+ Array input(std::move(nestedMap));
+
+ auto [updatedItem, ignoredPos, ignoredMessage] = parse(input.encode());
+ updatedItem->asArray()->add(42);
+
+ // add some stuff to the map in our array
+ Map* parsedNestedMap = updatedItem->asArray()->get(0)->asMap();
+ ASSERT_NE(nullptr, parsedNestedMap);
+ parsedNestedMap->add("number", 10);
+ EXPECT_THAT(updatedItem->asArray()->get(0)->asMap()->get("number"), MatchesItem(Uint(10)));
+ parsedNestedMap->add(42, "the answer");
+ EXPECT_THAT(updatedItem->asArray()->get(0)->asMap()->get(42), MatchesItem(Tstr("the answer")));
+
+ // add some stuff to the array in the map that's in our array
+ Array* parsedNestedArray = parsedNestedMap->get("array")->asArray();
+ ASSERT_NE(nullptr, parsedNestedArray);
+ parsedNestedArray->add("pie");
+ EXPECT_THAT(
+ updatedItem->asArray()->get(0)->asMap()->get("array")->asArray()->get(2),
+ MatchesItem(Tstr("pie")));
+
+ // encode the mutated item, then ensure the CBOR is valid
+ const auto encodedUpdatedItem = updatedItem->encode();
+ auto [parsedUpdatedItem, pos, message] = parse(encodedUpdatedItem);
+ EXPECT_EQ("", message);
+ EXPECT_EQ(pos, encodedUpdatedItem.data() + encodedUpdatedItem.size());
+ ASSERT_NE(nullptr, parsedUpdatedItem);
+ EXPECT_THAT(parsedUpdatedItem, MatchesItem(ByRef(*updatedItem)));
+}
+
TEST(FullParserTest, Map) {
Map val("hello", -4, 3, Bstr("hi"));