Add support for indeterminate length Arrays and Maps.

Change-Id: I15c64278b3eb9c697cd927bb4191dd7ca7c5ac21
diff --git a/README.md b/README.md
index b23cc0f..6463766 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,8 @@
 CBOR, nor (yet) support validation against CDDL schemata, though both
 are planned.  CBOR features that aren't supported include:
 
-* Indefinite length values
+* Parsing Indefinite length values for major types 2 (byte string) and 3 (text string)
+* Writing Indefinite length values
 * Semantic tagging
 * Floating point
 
diff --git a/include/cppbor/cppbor.h b/include/cppbor/cppbor.h
index 2362e3c..0589e0c 100644
--- a/include/cppbor/cppbor.h
+++ b/include/cppbor/cppbor.h
@@ -68,6 +68,7 @@
     TWO_BYTE_LENGTH = 25,
     FOUR_BYTE_LENGTH = 26,
     EIGHT_BYTE_LENGTH = 27,
+    INDEFINITE_LENGTH = 31,
 };
 
 class Item;
diff --git a/src/cppbor_parse.cpp b/src/cppbor_parse.cpp
index c3fa070..fdfd7bf 100644
--- a/src/cppbor_parse.cpp
+++ b/src/cppbor_parse.cpp
@@ -127,8 +127,10 @@
   public:
     explicit IncompleteArray(size_t size) : mSize(size) {}
 
-    // We return the "complete" size, rather than the actual size.
-    size_t size() const override { return mSize; }
+    // If the "complete" size is known, return it, otherwise return the current size.
+    size_t size() const override {
+        return mSize == SIZE_MAX ? Array::size() : mSize;
+    }
 
     void add(std::unique_ptr<Item> item) override {
         mEntries.push_back(std::move(item));
@@ -148,8 +150,10 @@
   public:
     explicit IncompleteMap(size_t size) : mSize(size) {}
 
-    // We return the "complete" size, rather than the actual size.
-    size_t size() const override { return mSize; }
+    // If the "complete" size is known, return it, otherwise return the current size.
+    size_t size() const override {
+        return mSize == SIZE_MAX ? Map::size() : mSize;
+    }
 
     void add(std::unique_ptr<Item> item) override {
         if (mKeyHeldForAdding) {
@@ -216,6 +220,11 @@
             parseClient->error(hdrBegin, "Not enough entries for " + typeName + ".");
             return {hdrBegin, nullptr /* end parsing */};
         }
+        if (*pos == 0xFF) {
+            // Next character is the "break" Stop Code
+            ++pos;
+            break;
+        }
         std::tie(pos, parseClient) = parseRecursively(pos, end, emitViews, parseClient, depth + 1);
         if (!parseClient) return {hdrBegin, nullptr};
     }
@@ -265,7 +274,9 @@
 
     bool success = true;
     uint64_t addlData;
-    if (tagInt < ONE_BYTE_LENGTH) {
+    if ((type == ARRAY || type == MAP) && tagInt == INDEFINITE_LENGTH) {
+        addlData = SIZE_MAX;
+    } else if (tagInt < ONE_BYTE_LENGTH) {
         addlData = tagInt;
     } else if (tagInt > EIGHT_BYTE_LENGTH) {
         parseClient->error(
diff --git a/tests/cppbor_test.cpp b/tests/cppbor_test.cpp
index fadcaa9..62fd5db 100644
--- a/tests/cppbor_test.cpp
+++ b/tests/cppbor_test.cpp
@@ -1862,14 +1862,55 @@
               message);
 }
 
-TEST(FullParserTest, IndefiniteArray) {
-    vector<uint8_t> indefiniteArray = {0x7F};
+TEST(FullParserTest, IndefiniteArrayEmpty) {
+    Bstr encoding("\x9F\xFF");
+    string expected = Array().toString();
 
-    auto [item, pos, message] = parse(indefiniteArray);
-    EXPECT_THAT(item, IsNull());
-    EXPECT_EQ(pos, indefiniteArray.data());
-    EXPECT_EQ("Reserved additional information value or unsupported indefinite length item.",
-              message);
+    auto [item, pos, message] = parse(&encoding);
+    EXPECT_EQ(expected, item->toString());
+}
+
+TEST(FullParserTest, IndefiniteArrayWithOneNumber) {
+    Bstr encoding("\x9F\x01\xFF");
+    string expected = Array(Uint(1)).toString();
+
+    auto [item, pos, message] = parse(&encoding);
+    EXPECT_EQ(expected, item->toString());
+}
+
+TEST(FullParserTest, IndefiniteArrayOfArray) {
+    Bstr encoding("\x9F\x9F\x01\xFF\xFF");
+
+    Array nested;
+    nested.add(Array(Uint(1)));
+    string expected = nested.toString();
+
+    auto [item, pos, message] = parse(&encoding);
+    EXPECT_EQ(expected, item->toString());
+}
+
+TEST(FullParserTest, IndefiniteMapEmpty) {
+    Bstr encoding("\xBF\xFF");
+    string expected = Map().toString();
+
+    auto [item, pos, message] = parse(&encoding);
+    EXPECT_EQ(expected, item->toString());
+}
+
+TEST(FullParserTest, IndefiniteMapsNested) {
+    Bstr encoding("\xBF\x01\xBF\xFF\xFF");
+    string expected = Map(Uint(1), Map()).toString();
+
+    auto [item, pos, message] = parse(&encoding);
+    EXPECT_EQ(expected, item->toString());
+}
+
+TEST(FullParserTest, IndefiniteMapWithOneEntry) {
+    Bstr encoding("\xBF\x01\x05\xFF");
+    string expected = Map(Uint(1), Uint(5)).toString();
+
+    auto [item, pos, message] = parse(&encoding);
+    EXPECT_EQ(expected, item->toString());
 }
 
 TEST(FullParserTest, UnassignedSimpleValue) {