Remove CompoundItem.

Refactor to remove CompoundItem. It was a way to share some common
code between Array and Map, and later Semantic, but the
representations of those classes need to diverge so it adds no value.

Test: cppbor_test_external
Change-Id: I986e90c1d212f6d81debe4b2f650ba68f065a6ed
diff --git a/include/cppbor/cppbor.h b/include/cppbor/cppbor.h
index 5ee055e..b6026ba 100644
--- a/include/cppbor/cppbor.h
+++ b/include/cppbor/cppbor.h
@@ -419,33 +419,6 @@
     std::string mValue;
 };
 
-/**
- * CompoundItem is an abstract Item that provides common functionality for Items that contain other
- * items, i.e. Arrays (CBOR type 4) and Maps (CBOR type 5).
- */
-class CompoundItem : public Item {
-  public:
-    bool operator==(const CompoundItem& other) const&;
-
-    virtual size_t size() const { return mEntries.size(); }
-
-    bool isCompound() const override { return true; }
-
-    size_t encodedSize() const override {
-        return std::accumulate(mEntries.begin(), mEntries.end(), headerSize(size()),
-                               [](size_t sum, auto& entry) { return sum + entry->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;
-
-    virtual uint64_t addlInfo() const = 0;
-
-  protected:
-    std::vector<std::unique_ptr<Item>> mEntries;
-};
-
 /*
  * Array is a concrete Item that implements CBOR major type 4.
  *
@@ -453,7 +426,7 @@
  * move-only ensures that they're never copied accidentally.  If you actually want to copy an Array,
  * use the clone() method.
  */
-class Array : public CompoundItem {
+class Array : public Item {
   public:
     static constexpr MajorType kMajorType = ARRAY;
 
@@ -463,6 +436,8 @@
     Array& operator=(const Array&) = delete;
     Array& operator=(Array&&) = default;
 
+    bool operator==(const Array& other) const&;
+
     /**
      * Construct an Array from a variable number of arguments of different types.  See
      * details::makeItem below for details on what types may be provided.  In general, this accepts
@@ -480,6 +455,19 @@
     template <typename T>
     Array&& add(T&& v) &&;
 
+    bool isCompound() const override { return true; }
+
+    virtual size_t size() const { return mEntries.size(); }
+
+    size_t encodedSize() const override {
+        return std::accumulate(mEntries.begin(), mEntries.end(), headerSize(size()),
+                               [](size_t sum, auto& entry) { return sum + entry->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;
+
     const std::unique_ptr<Item>& operator[](size_t index) const { return get(index); }
     std::unique_ptr<Item>& operator[](size_t index) { return get(index); }
 
@@ -491,7 +479,8 @@
 
     std::unique_ptr<Item> clone() const override;
 
-    uint64_t addlInfo() const override { return size(); }
+  protected:
+    std::vector<std::unique_ptr<Item>> mEntries;
 };
 
 /*
@@ -501,7 +490,7 @@
  * move-only ensures that they're never copied accidentally.  If you actually want to copy a
  * Map, use the clone() method.
  */
-class Map : public CompoundItem {
+class Map : public Item {
   public:
     static constexpr MajorType kMajorType = MAP;
 
@@ -511,6 +500,8 @@
     Map& operator=(const Map& other) = delete;
     Map& operator=(Map&&) = default;
 
+    bool operator==(const Map& other) const&;
+
     /**
      * Construct a Map from a variable number of arguments of different types.  An even number of
      * arguments must be provided (this is verified statically). See details::makeItem below for
@@ -529,11 +520,22 @@
     template <typename Key, typename Value>
     Map&& add(Key&& key, Value&& value) &&;
 
-    size_t size() const override {
+    bool isCompound() const override { return true; }
+
+    virtual size_t size() const  {
         assertInvariant();
         return mEntries.size() / 2;
     }
 
+    size_t encodedSize() const override {
+        return std::accumulate(mEntries.begin(), mEntries.end(), headerSize(size()),
+                               [](size_t sum, auto& entry) { return sum + entry->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;
+
     template <typename Key, typename Enable>
     const std::unique_ptr<Item>& get(Key key) const;
 
@@ -569,13 +571,14 @@
 
     std::unique_ptr<Item> clone() const override;
 
-    uint64_t addlInfo() const override { return size(); }
+  protected:
+    std::vector<std::unique_ptr<Item>> mEntries;
 
   private:
     void assertInvariant() const;
 };
 
-class Semantic : public CompoundItem {
+class Semantic : public Item {
   public:
     static constexpr MajorType kMajorType = SEMANTIC;
 
@@ -587,7 +590,11 @@
     Semantic& operator=(const Semantic& other) = delete;
     Semantic& operator=(Semantic&&) = default;
 
-    size_t size() const override {
+    bool operator==(const Semantic& other) const&;
+
+    bool isCompound() const override { return true; }
+
+    virtual size_t size() const  {
         assertInvariant();
         return 1;
     }
@@ -597,6 +604,10 @@
                                [](size_t sum, auto& entry) { return sum + entry->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; }
     const Semantic* asSemantic() const override { return this; }
 
@@ -612,8 +623,6 @@
 
     uint64_t value() const { return mValue; }
 
-    uint64_t addlInfo() const override { return value(); }
-
     std::unique_ptr<Item> clone() const override {
         assertInvariant();
         return std::make_unique<Semantic>(mValue, mEntries[0]->clone());
@@ -623,6 +632,7 @@
     Semantic() = default;
     Semantic(uint64_t value) : mValue(value) {}
     uint64_t mValue;
+    std::vector<std::unique_ptr<Item>> mEntries;
 
   private:
     void assertInvariant() const;
diff --git a/src/cppbor.cpp b/src/cppbor.cpp
index 50e98ca..632dd0b 100644
--- a/src/cppbor.cpp
+++ b/src/cppbor.cpp
@@ -52,7 +52,7 @@
     }
 }
 
-bool cborAreAllElementsNonCompound(const CompoundItem* compoundItem) {
+bool cborAreAllElementsNonCompound(const Item* compoundItem) {
     if (compoundItem->type() == ARRAY) {
         const Array* array = compoundItem->asArray();
         for (size_t n = 0; n < array->size(); n++) {
@@ -365,17 +365,16 @@
     }
 }
 
-bool CompoundItem::operator==(const CompoundItem& other) const& {
-    return type() == other.type()             //
-           && addlInfo() == other.addlInfo()  //
+bool Array::operator==(const Array& other) const& {
+    return size() == other.size()
            // Can't use vector::operator== because the contents are pointers.  std::equal lets us
            // provide a predicate that does the dereferencing.
            && std::equal(mEntries.begin(), mEntries.end(), other.mEntries.begin(),
                          [](auto& a, auto& b) -> bool { return *a == *b; });
 }
 
-uint8_t* CompoundItem::encode(uint8_t* pos, const uint8_t* end) const {
-    pos = encodeHeader(addlInfo(), pos, end);
+uint8_t* Array::encode(uint8_t* pos, const uint8_t* end) const {
+    pos = encodeHeader(size(), pos, end);
     if (!pos) return nullptr;
     for (auto& entry : mEntries) {
         pos = entry->encode(pos, end);
@@ -384,8 +383,41 @@
     return pos;
 }
 
-void CompoundItem::encode(EncodeCallback encodeCallback) const {
-    encodeHeader(addlInfo(), encodeCallback);
+void Array::encode(EncodeCallback encodeCallback) const {
+    encodeHeader(size(), encodeCallback);
+    for (auto& entry : mEntries) {
+        entry->encode(encodeCallback);
+    }
+}
+
+std::unique_ptr<Item> Array::clone() const {
+    auto res = std::make_unique<Array>();
+    for (size_t i = 0; i < mEntries.size(); i++) {
+        res->add(mEntries[i]->clone());
+    }
+    return res;
+}
+
+bool Map::operator==(const Map& other) const& {
+    return size() == other.size()
+           // Can't use vector::operator== because the contents are pointers.  std::equal lets us
+           // provide a predicate that does the dereferencing.
+           && std::equal(mEntries.begin(), mEntries.end(), other.mEntries.begin(),
+                         [](auto& a, auto& b) -> bool { return *a == *b; });
+}
+
+uint8_t* Map::encode(uint8_t* pos, const uint8_t* end) const {
+    pos = encodeHeader(size(), pos, end);
+    if (!pos) return nullptr;
+    for (auto& entry : mEntries) {
+        pos = entry->encode(pos, end);
+        if (!pos) return nullptr;
+    }
+    return pos;
+}
+
+void Map::encode(EncodeCallback encodeCallback) const {
+    encodeHeader(size(), encodeCallback);
     for (auto& entry : mEntries) {
         entry->encode(encodeCallback);
     }
@@ -455,12 +487,20 @@
     return res;
 }
 
-std::unique_ptr<Item> Array::clone() const {
-    auto res = std::make_unique<Array>();
-    for (size_t i = 0; i < mEntries.size(); i++) {
-        res->add(mEntries[i]->clone());
-    }
-    return res;
+bool Semantic::operator==(const Semantic& other) const& {
+    assertInvariant();
+    return *mEntries.front() == *other.mEntries.front();
+}
+
+uint8_t* Semantic::encode(uint8_t* pos, const uint8_t* end) const {
+    pos = encodeHeader(value(), pos, end);
+    if (!pos) return nullptr;
+    return mEntries.front()->encode(pos, end);
+}
+
+void Semantic::encode(EncodeCallback encodeCallback) const {
+    encodeHeader(value(), encodeCallback);
+    mEntries.front()->encode(encodeCallback);
 }
 
 void Semantic::assertInvariant() const {
diff --git a/src/cppbor_parse.cpp b/src/cppbor_parse.cpp
index 357b9ee..488f8c7 100644
--- a/src/cppbor_parse.cpp
+++ b/src/cppbor_parse.cpp
@@ -280,10 +280,7 @@
             // 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.
-#if __has_feature(cxx_rtti)
-            assert(dynamic_cast<CompoundItem*>(item.get()));
-#endif
-            mParentStack.push(static_cast<CompoundItem*>(item.get()));
+            mParentStack.push(item.get());
             return this;
         } else {
             appendToLastParent(std::move(item));
@@ -336,7 +333,7 @@
     }
 
     std::unique_ptr<Item> mTheItem;
-    std::stack<CompoundItem*> mParentStack;
+    std::stack<Item*> mParentStack;
     const uint8_t* mPosition = nullptr;
     std::string mErrorMessage;
 };