Improve documentation for encode size calculation mode (#123)

This adds the constant SizeCalculateUsefulBuf which can be passed to QCBOR Encode and to UsefulOutBuf to indicate only size calculation should be performed, that no encoded CBOR is to be output.

The documentation for this mode is improved, an example is added and test cases are added.

Co-authored-by: Laurence Lundblade <lgl@securitytheory.com>
diff --git a/example.c b/example.c
index 02345ec..80b2149 100644
--- a/example.c
+++ b/example.c
@@ -147,8 +147,13 @@
  * @c Buffer must be big enough to hold the output. If it is not @ref
  * NULLUsefulBufC will be returned. @ref NULLUsefulBufC will be
  * returned for any other encoding errors.
+ *
+ * This can be called with @c Buffer set to @ref SizeCalculateUsefulBuf
+ * in which case the size of the encoded engine will be calculated,
+ * but no actual encoded CBOR will be output. The calculated size is
+ * in @c .len of the returned @ref UsefulBufC.
  */
-UsefulBufC EncodeEngineDefiniteLength(const CarEngine *pEngine, UsefulBuf Buffer)
+UsefulBufC EncodeEngine(const CarEngine *pEngine, UsefulBuf Buffer)
 {
    /* Set up the encoding context with the output buffer */
     QCBOREncodeContext EncodeCtx;
@@ -333,7 +338,7 @@
    EngineInit(&InitialEngine);
 
    /* Encode the engine structure. */
-   EncodedEngine = EncodeEngineDefiniteLength(&InitialEngine, EngineBuffer);
+   EncodedEngine = EncodeEngine(&InitialEngine, EngineBuffer);
    if(UsefulBuf_IsNULLC(EncodedEngine)) {
       printf("Engine encode failed\n");
       goto Done;
@@ -353,6 +358,25 @@
       printf("Example: Spiffy Engine Decode comparison fail\n");
    }
 
+
+   /* Further example of how to calculate the encoded size, then allocate */
+   UsefulBufC EncodedEngineSize;
+   EncodedEngineSize = EncodeEngine(&InitialEngine, SizeCalculateUsefulBuf);
+   if(UsefulBuf_IsNULLC(EncodedEngine)) {
+      printf("Engine encode size calculation failed\n");
+      goto Done;
+   }
+   (void)EncodedEngineSize; /* Supress unsed variable warning */
+   /* Here malloc could be called to allocate a buffer. Then
+    * EncodeEngine() can be called a second time to actually
+    * encode. (The actual code is not live here to avoid a
+    * dependency on malloc()).
+    *  UsefulBuf  MallocedBuffer;
+    *  MallocedBuffer.len = EncodedEngineSize.len;
+    *  MallocedBuffer.ptr = malloc(EncodedEngineSize.len);
+    *  EncodedEngine = EncodeEngine(&InitialEngine, MallocedBuffer);
+    */
+
 Done:
    printf("\n");
 
diff --git a/inc/qcbor/UsefulBuf.h b/inc/qcbor/UsefulBuf.h
index fb3c12b..1662fcc 100644
--- a/inc/qcbor/UsefulBuf.h
+++ b/inc/qcbor/UsefulBuf.h
@@ -42,6 +42,7 @@
 
  when         who             what, where, why
  --------     ----            --------------------------------------------------
+ 9/21/2021    llundbla        Clarify UsefulOutBuf size calculation mode
  8/8/2021     dthaler/llundbla Work with C++ without compiler extensions
  5/11/2021    llundblade      Improve comments and comment formatting.
  3/6/2021     mcr/llundblade  Fix warnings related to --Wcast-qual
@@ -793,17 +794,13 @@
  *    - Call UsefulOutBuf_OutUBuf() or UsefulOutBuf_CopyOut() to see
  *      there were no errors and to get the serialized output bytes.
  *
- * @ref UsefulOutBuf can be used in a size calculation mode to
- * calculate the size of output that would be generated. This is
+ * @ref UsefulOutBuf can be used in a mode to calculate the size of
+ * what would be output without actually outputting anything.  This is
  * useful to calculate the size of a buffer that is to be allocated to
- * hold the output. To use @ref UsefulOutBuf in this mode, call
- * UsefulOutBuf_Init() with the @c Storage @ref UsefulBuf as
- * @c (UsefulBuf){NULL, MAX_UINT32}. Then call all the Insert and Add
- * functions. No attempt will be made to actually copy data, so only
- * the lengths have to be valid for inputs to these calls.
+ * hold the output. See @ref SizeCalculateUsefulBuf.
  *
  * Methods like UsefulOutBuf_InsertUint64() always output in network
- * bytes order (big endian).
+ * byte order (big endian).
  *
  * The possible errors are:
  *
@@ -840,6 +837,23 @@
 
 
 /**
+ * This is a @ref UsefulBuf value that can be passed to
+ * UsefulOutBuf_Init() to have it calculate the size of the output
+ * buffer needed. Pass this for @c Storage, call all the append and
+ * insert functions normally, then call UsefulOutBuf_OutUBuf(). The
+ * returned @ref UsefulBufC has the size.
+ *
+ * As one can see, this is just a NULL pointer and very large size.
+ * The NULL pointer tells UsefulOutputBuf to not copy any data.
+ */
+#ifdef __cplusplus
+#define SizeCalculateUsefulBuf {NULL, SIZE_MAX}
+#else
+#define SizeCalculateUsefulBuf ((UsefulBuf) {NULL, SIZE_MAX})
+#endif
+
+
+/**
  * @brief Initialize and supply the actual output buffer.
  *
  * @param[out] pUOutBuf  The @ref UsefulOutBuf to initialize.
@@ -849,6 +863,10 @@
  * current position to the beginning of the buffer and clears the
  * error state.
  *
+ * See @ref SizeCalculateUsefulBuf for instructions on how to
+ * initialize a @ref UsefulOutBuf to calculate the size that would be
+ * output without actually outputting.
+ *
  * This must be called before the @ref UsefulOutBuf is used.
  */
 void UsefulOutBuf_Init(UsefulOutBuf *pUOutBuf, UsefulBuf Storage);
diff --git a/inc/qcbor/qcbor_common.h b/inc/qcbor/qcbor_common.h
index 51191e2..8b161de 100644
--- a/inc/qcbor/qcbor_common.h
+++ b/inc/qcbor/qcbor_common.h
@@ -288,8 +288,9 @@
        and 31. */
    QCBOR_ERR_ENCODE_UNSUPPORTED = 2,
 
-   /** During encoding, the length of the encoded CBOR exceeded @c
-       UINT32_MAX. */
+   /** During encoding, the length of the encoded CBOR exceeded
+       QCBOR_MAX_ARRAY_OFFSET, which is slightly less than
+       @c UINT32_MAX. */
    QCBOR_ERR_BUFFER_TOO_LARGE = 3,
 
    /** During encoding, the array or map nesting was deeper than this
diff --git a/inc/qcbor/qcbor_encode.h b/inc/qcbor/qcbor_encode.h
index 202a98f..0abc594 100644
--- a/inc/qcbor/qcbor_encode.h
+++ b/inc/qcbor/qcbor_encode.h
@@ -167,15 +167,14 @@
  ## Encoding
 
  A common encoding usage mode is to invoke the encoding twice. First
- with no output buffer to compute the length of the needed output
- buffer. Then the correct sized output buffer is allocated. Last the
- encoder is invoked again, this time with the output buffer.
+ with the output buffer as @ref SizeCalculateUsefulBuf to compute the
+ length of the needed output buffer. The correct sized output buffer
+ is allocated. The encoder is invoked a second time with the allocated
+ output buffer.
 
  The double invocation is not required if the maximum output buffer
  size can be predicted. This is usually possible for simple CBOR
- structures.  If the double invocation is implemented, it can be in a
- loop or function as in the example code so that the code doesn't have
- to actually be written twice, saving code size.
+ structures.
 
  If a buffer too small to hold the encoded output is given, the error
  @ref QCBOR_ERR_BUFFER_TOO_SMALL will be returned. Data will never be
@@ -444,22 +443,22 @@
  that will ever be needed and hard code a buffer of that size.
 
  Another way to do it is to have QCBOR calculate it for you. To do
- this set @c Storage.ptr to @c NULL and @c Storage.len to @c
- UINT32_MAX. Then call all the functions to add the CBOR exactly as if
- encoding for real. Then call QCBOREncode_Finish(). The pointer
- returned will be @c NULL, but the length returned is that of what would
- be encoded. Once the length is obtained, allocate a buffer of that
+ this, pass @ref SizeCalculateUsefulBuf for @c Storage.
+ Then call all the functions to add the CBOR exactly as if
+ encoding for real. Finally, call QCBOREncode_FinishGetSize().
+ Once the length is obtained, allocate a buffer of that
  size, call QCBOREncode_Init() again with the real buffer. Call all
  the add functions again and finally, QCBOREncode_Finish() to obtain
- the final result. This uses almost twice the CPU time, but that is
+ the final result. This uses twice the CPU time, but that is
  usually not an issue.
 
  See QCBOREncode_Finish() for how the pointer and length for the
  encoded CBOR is returned.
 
- The maximum output buffer size allowed is @c UINT32_MAX (4GB). The
- error @ref QCBOR_ERR_BUFFER_TOO_LARGE will be returned by
- QCBOREncode_Finish() if a larger buffer length is passed in.
+ For practical purposes QCBOR can't output encoded CBOR larger than
+ @c UINT32_MAX (4GB) even on 64-bit CPUs because the internal offsets
+ used to track the start of an array/map are 32 bits to reduce the
+ size of the encoding context.
 
  A @ref QCBOREncodeContext can be reused over and over as long as
  QCBOREncode_Init() is called before each use.
diff --git a/test/UsefulBuf_Tests.c b/test/UsefulBuf_Tests.c
index b6914d2..17ee051 100644
--- a/test/UsefulBuf_Tests.c
+++ b/test/UsefulBuf_Tests.c
@@ -34,64 +34,97 @@
 #include "UsefulBuf.h"
 
 
-/* Basic exercise...
+/* This calls the main methods to add stuff to a UsefulOutBuf.
+ * The result in the UsefulOutBuf is "heffalump unbounce bluster hunny"
+ */
+const char *AddStuffToUOB(UsefulOutBuf *pUOB)
+{
+   const char *szReturn = NULL;
 
-   Call all the main public functions.
+   if(!UsefulOutBuf_AtStart(pUOB)) {
+      szReturn = "Not at start";
+      goto Done;
+   }
 
-   Binary compare the result to the expected.
+   /* Put 7 bytes at beginning of buf */
+   UsefulOutBuf_AppendData(pUOB, "bluster", 7);
 
-   There is nothing adversarial in this test
+   if(UsefulOutBuf_AtStart(pUOB)) {
+      szReturn = "At start";
+      goto Done;
+   }
+
+   /* add a space to end */
+   UsefulOutBuf_AppendByte(pUOB, ' ');
+
+   /* Add 5 bytes to the end */
+   UsefulBufC UBC = {"hunny", 5};
+   UsefulOutBuf_AppendUsefulBuf(pUOB, UBC);
+
+   /* Insert 9 bytes at the beginning, slide the previous stuff right */
+   UsefulOutBuf_InsertData(pUOB, "heffalump", 9, 0);
+   UsefulOutBuf_InsertByte(pUOB, ' ', 9);
+
+   /* Put 9 bytes in at position 10 -- just after "heffalump " */
+   UsefulBufC UBC2 = {"unbounce ", 9};
+   UsefulOutBuf_InsertUsefulBuf(pUOB, UBC2, 10);
+
+Done:
+   return szReturn;
+}
+
+
+/* Basic exercise of a UsefulOutBuf
+ *
+ *  Call all the main public functions.
+ *
+ *  Binary compare the result to the expected.
+ *
+ * There is nothing adversarial in this test
  */
 const char * UOBTest_NonAdversarial(void)
 {
    const char *szReturn = NULL;
 
-   UsefulBuf_MAKE_STACK_UB(outbuf,50);
+   UsefulBuf_MAKE_STACK_UB(outbuf, 50);
 
    UsefulOutBuf UOB;
 
    UsefulOutBuf_Init(&UOB, outbuf);
 
-   if(!UsefulOutBuf_AtStart(&UOB)) {
-      szReturn = "Not at start";
+   szReturn = AddStuffToUOB(&UOB);
+   if(szReturn) {
       goto Done;
    }
 
-   // Put 7 bytes at beginning of buf
-   UsefulOutBuf_AppendData(&UOB, "bluster", 7);
-
-   if(UsefulOutBuf_AtStart(&UOB)) {
-      szReturn = "At start";
-      goto Done;
-   }
-
-   // add a space to end
-   UsefulOutBuf_AppendByte(&UOB, ' ');
-
-   // Add 5 bytes to the end
-   UsefulBufC UBC = {"hunny", 5};
-   UsefulOutBuf_AppendUsefulBuf(&UOB, UBC);
-
-   // Insert 9 bytes at the beginning, slide the previous stuff right
-   UsefulOutBuf_InsertData(&UOB, "heffalump", 9, 0);
-   UsefulOutBuf_InsertByte(&UOB, ' ', 9);
-
-   // Put 9 bytes in at position 10 -- just after "heffalump "
-   UsefulBufC UBC2 = {"unbounce ", 9};
-   UsefulOutBuf_InsertUsefulBuf(&UOB, UBC2, 10);
-
-
    const UsefulBufC Expected = UsefulBuf_FROM_SZ_LITERAL("heffalump unbounce bluster hunny");
 
    UsefulBufC U = UsefulOutBuf_OutUBuf(&UOB);
-   if(UsefulBuf_IsNULLC(U) || UsefulBuf_Compare(Expected, U) || UsefulOutBuf_GetError(&UOB)) {
+   if(UsefulBuf_IsNULLC(U) ||
+      UsefulBuf_Compare(Expected, U) ||
+      UsefulOutBuf_GetError(&UOB)) {
       szReturn = "OutUBuf";
+      goto Done;
    }
 
    UsefulBuf_MAKE_STACK_UB(buf, 50);
-   UsefulBufC Out =  UsefulOutBuf_CopyOut(&UOB, buf);
+   UsefulBufC Out = UsefulOutBuf_CopyOut(&UOB, buf);
    if(UsefulBuf_IsNULLC(Out) || UsefulBuf_Compare(Expected, Out)) {
       szReturn = "CopyOut";
+      goto Done;
+   }
+
+   /* Now test the size calculation mode */
+   UsefulOutBuf_Init(&UOB, SizeCalculateUsefulBuf);
+
+   szReturn = AddStuffToUOB(&UOB);
+   if(szReturn) {
+      goto Done;
+   }
+
+   U = UsefulOutBuf_OutUBuf(&UOB);
+   if(U.len != Expected.len || U.ptr != NULL) {
+      szReturn = "size calculation failed";
    }
 
 Done:
@@ -163,7 +196,7 @@
 
 const char *UOBTest_BoundaryConditionsTest(void)
 {
-   UsefulBuf_MAKE_STACK_UB(outbuf,2);
+   UsefulBuf_MAKE_STACK_UB(outbuf, 2);
 
    UsefulOutBuf UOB;
 
@@ -275,8 +308,9 @@
 
    UsefulOutBuf_AppendData(&UOB, (const uint8_t *)"bluster", 7);
 
-   if(!UsefulOutBuf_GetError(&UOB))
+   if(!UsefulOutBuf_GetError(&UOB)) {
       return "magic corruption check failed";
+   }
 
 
 
diff --git a/test/qcbor_encode_tests.c b/test/qcbor_encode_tests.c
index 8b41af3..d3ada13 100644
--- a/test/qcbor_encode_tests.c
+++ b/test/qcbor_encode_tests.c
@@ -512,200 +512,222 @@
 --XXXXboundary text--";
 
 
+static void AddAll(QCBOREncodeContext *pECtx)
+{
+   QCBOREncode_OpenArray(pECtx);
+
+   /* Some ints that are tagged and have strings preceeding them
+    * (not labels becase it is not a map) */
+   QCBOREncode_AddSZString(pECtx, "UINT62");
+   QCBOREncode_AddTag(pECtx, 100);
+   QCBOREncode_AddUInt64(pECtx, 89989909);
+   QCBOREncode_AddSZString(pECtx, "INT64");
+   QCBOREncode_AddTag(pECtx, 76);
+   QCBOREncode_AddInt64(pECtx, 77689989909);
+   QCBOREncode_AddUInt64(pECtx,0);
+   QCBOREncode_AddInt64(pECtx, -44);
+
+   /* ints that go in maps */
+   QCBOREncode_OpenMap(pECtx);
+   QCBOREncode_AddUInt64ToMap(pECtx, "LBL", 77);
+   QCBOREncode_AddUInt64ToMapN(pECtx, -4, 88);
+   QCBOREncode_AddInt64ToMap(pECtx, "NEGLBLTHAT IS KIND OF LONG", -2394893489238);
+   QCBOREncode_AddInt64ToMapN(pECtx, -100000000, -800000000);
+   QCBOREncode_CloseMap(pECtx);
+
+   /* Epoch Date */
+   QCBOREncode_AddDateEpoch(pECtx, 2383748234);
+
+   /* Epoch date with labels */
+   QCBOREncode_OpenMap(pECtx);
+   QCBOREncode_AddDateEpochToMap(pECtx, "LongLiveDenisRitchie", 1400000000);
+   QCBOREncode_AddDateEpochToMap(pECtx, "time()", 1477263730);
+   QCBOREncode_AddDateEpochToMapN(pECtx, -1969, 1477263222);
+   QCBOREncode_CloseMap(pECtx);
+
+   /* Binary blobs */
+   QCBOREncode_AddBytes(pECtx, ((UsefulBufC) {(uint8_t []){0xff, 0x00}, 2}));
+
+   /* binary blobs in maps */
+   QCBOREncode_OpenMap(pECtx);
+   QCBOREncode_AddSZString(pECtx, "binbin");
+   QCBOREncode_AddTag(pECtx, 100000);
+   QCBOREncode_AddBytes(pECtx, ((UsefulBufC) {(uint8_t []){0x00}, 1}));
+   QCBOREncode_AddBytesToMap(pECtx, "blabel", ((UsefulBufC) {(uint8_t []){0x01, 0x02, 0x03}, 3}));
+   QCBOREncode_AddBytesToMapN(pECtx, 0, ((UsefulBufC){(uint8_t []){0x04, 0x02, 0x03, 0xfe}, 4}));
+   QCBOREncode_CloseMap(pECtx);
+
+   /* text blobs */
+   QCBOREncode_AddText(pECtx, UsefulBuf_FROM_SZ_LITERAL("bar bar foo bar"));
+   QCBOREncode_AddSZString(pECtx, "oof\n");
+   const char *szURL =
+    "http://stackoverflow.com/questions/28059697/how-do-i-toggle-between-debug-and-release-builds-in-xcode-6-7-8";
+   QCBOREncode_AddURI(pECtx, UsefulBuf_FromSZ(szURL));
+   QCBOREncode_AddB64Text(pECtx, UsefulBuf_FROM_SZ_LITERAL("YW55IGNhcm5hbCBwbGVhc3VyZQ=="));
+   QCBOREncode_AddRegex(pECtx, UsefulBuf_FROM_SZ_LITERAL("[^abc]+"));
+   QCBOREncode_AddMIMEData(pECtx, UsefulBuf_FromSZ(szMIME));
+
+   /* text blobs in maps */
+   QCBOREncode_OpenMap(pECtx);
+   QCBOREncode_AddTextToMap(pECtx, "#####", UsefulBuf_FROM_SZ_LITERAL("foo bar foo foo"));
+   QCBOREncode_AddTextToMap(pECtx, "____", UsefulBuf_FROM_SZ_LITERAL("foo bar"));
+   QCBOREncode_AddSZString(pECtx, "()()()");
+   QCBOREncode_AddTag(pECtx, 1000);
+   QCBOREncode_AddSZString(pECtx, "rab rab oof");
+   QCBOREncode_AddTextToMapN(pECtx,22, UsefulBuf_FROM_SZ_LITERAL("foo foo foo foo"));
+   QCBOREncode_AddSZStringToMap(pECtx, "^^", "oooooooof");
+   QCBOREncode_AddSZStringToMapN(pECtx, 99, "ffffoooooooof");
+   QCBOREncode_AddURIToMap(pECtx,
+                           "RFC",
+                           UsefulBuf_FROM_SZ_LITERAL("https://tools.ietf.org/html/rfc7049#section-2.4.5"));
+   QCBOREncode_AddURIToMapN(pECtx, 0x89, UsefulBuf_FROM_SZ_LITERAL("http://cbor.me/"));
+   QCBOREncode_AddB64TextToMap(pECtx, "whenim64", UsefulBuf_FROM_SZ_LITERAL("cGxlYXN1cmUu"));
+   QCBOREncode_AddB64TextToMapN(pECtx, 64, UsefulBuf_FROM_SZ_LITERAL("c3VyZS4="));
+   QCBOREncode_AddRegexToMap(pECtx, "popo", UsefulBuf_FROM_SZ_LITERAL("100\\s*mk")); //   x code string literal bug
+   QCBOREncode_AddRegexToMapN(pECtx, -51, UsefulBuf_FROM_SZ_LITERAL("perl\\B"));  //   x code string literal bug
+   QCBOREncode_AddMIMEDataToMap(pECtx, "Ned", UsefulBuf_FromSZ(szMIME));
+   QCBOREncode_AddMIMEDataToMapN(pECtx, 10, UsefulBuf_FromSZ(szMIME));
+   QCBOREncode_CloseMap(pECtx);
+
+   /* Date strings */
+   QCBOREncode_AddDateString(pECtx, "2003-12-13T18:30:02Z");
+   QCBOREncode_OpenMap(pECtx);
+   QCBOREncode_AddDateStringToMap(pECtx, "Bed time", "2003-12-13T18:30:02.25+01:00");
+   QCBOREncode_AddDateStringToMapN(pECtx, 88, "2003-12-13T18:30:02.25+01:00");
+   QCBOREncode_CloseMap(pECtx);
+
+   /* true / false ... */
+   QCBOREncode_AddSimple(pECtx, CBOR_SIMPLEV_UNDEF);
+   QCBOREncode_OpenMap(pECtx);
+   QCBOREncode_AddSZString(pECtx, "dare");
+   QCBOREncode_AddTag(pECtx, 66);
+   QCBOREncode_AddBool(pECtx, true);
+   QCBOREncode_AddBoolToMap(pECtx, "uu", false);
+   QCBOREncode_AddSimpleToMapN(pECtx, 737634, CBOR_SIMPLEV_NULL);
+   QCBOREncode_CloseMap(pECtx);
+
+   /* opening an array */
+   QCBOREncode_OpenArray(pECtx);
+   QCBOREncode_CloseArray(pECtx);
+
+   /* opening arrays in a map */
+   QCBOREncode_OpenMap(pECtx);
+   QCBOREncode_AddSZString(pECtx, "label and tagged empty array");
+   QCBOREncode_AddTag(pECtx, 1093);
+   QCBOREncode_OpenArray(pECtx);
+   QCBOREncode_CloseArray(pECtx);
+   QCBOREncode_OpenArrayInMap(pECtx, "alabl");
+   QCBOREncode_CloseArray(pECtx);
+   QCBOREncode_OpenArrayInMapN(pECtx, 42);
+   QCBOREncode_CloseArray(pECtx);
+   QCBOREncode_CloseMap(pECtx);
+
+   /* opening maps with labels and tagging */
+   QCBOREncode_OpenMap(pECtx);
+   QCBOREncode_OpenMapInMap(pECtx, "in a map");
+   QCBOREncode_OpenMapInMapN(pECtx, 5556);
+   QCBOREncode_AddSZString(pECtx, "in a in a in a");
+   QCBOREncode_AddTag(pECtx, 9087);
+   QCBOREncode_OpenMap(pECtx);
+   QCBOREncode_CloseMap(pECtx);
+   QCBOREncode_CloseMap(pECtx);
+   QCBOREncode_CloseMap(pECtx);
+   QCBOREncode_CloseMap(pECtx);
+
+   /* Extended simple values (these are not standard...) */
+   QCBOREncode_OpenMap(pECtx);
+   QCBOREncode_AddSZString(pECtx, "s1");
+   QCBOREncode_AddTag(pECtx, 88);
+   QCBOREncode_AddSimple(pECtx, 255);
+   QCBOREncode_AddSimpleToMap(pECtx, "s2", 0);
+   QCBOREncode_AddSZString(pECtx, "s3");
+   QCBOREncode_AddTag(pECtx, 88);
+   QCBOREncode_AddSimple(pECtx, 33);
+   QCBOREncode_AddInt64(pECtx, 88378374); // label before tag
+   QCBOREncode_AddTag(pECtx, 88);
+   QCBOREncode_AddSimple(pECtx, 255);
+   QCBOREncode_AddInt64(pECtx, 89); // label before tag
+   QCBOREncode_AddTag(pECtx, 88);
+   QCBOREncode_AddSimple(pECtx, 19);
+   QCBOREncode_CloseMap(pECtx);
+
+   /* UUIDs */
+   static const uint8_t ppppUUID[] = {0x53, 0x4D, 0x41, 0x52, 0x54, 0x43,
+                                      0x53, 0x4C, 0x54, 0x54, 0x43, 0x46,
+                                      0x49, 0x43, 0x41, 0x32};
+   const UsefulBufC XXUUID = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(ppppUUID);
+   QCBOREncode_AddBinaryUUID(pECtx, XXUUID);
+   QCBOREncode_OpenMap(pECtx);
+   QCBOREncode_AddBinaryUUIDToMap(pECtx, "UUUU", XXUUID);
+   QCBOREncode_AddBinaryUUIDToMapN(pECtx, 99, XXUUID);
+   QCBOREncode_CloseMap(pECtx);
+
+   /* Bool */
+   QCBOREncode_AddBool(pECtx, true);
+   QCBOREncode_AddBool(pECtx, false);
+   QCBOREncode_OpenMap(pECtx);
+   QCBOREncode_AddBoolToMap(pECtx, "George is the man", true);
+   QCBOREncode_AddBoolToMapN(pECtx, 010101, true);
+   QCBOREncode_CloseMap(pECtx);
+
+   /* Big numbers */
+   static const uint8_t pBignum[] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+   const UsefulBufC BIGNUM = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pBignum);
+   QCBOREncode_AddPositiveBignum(pECtx, BIGNUM);
+   QCBOREncode_AddNegativeBignum(pECtx, BIGNUM);
+   QCBOREncode_OpenMap(pECtx);
+   QCBOREncode_AddPositiveBignumToMap(pECtx, "BN+", BIGNUM);
+   QCBOREncode_AddPositiveBignumToMapN(pECtx, 64, BIGNUM);
+   QCBOREncode_AddNegativeBignumToMap(pECtx, "BN-", BIGNUM);
+   QCBOREncode_AddNegativeBignumToMapN(pECtx, -64, BIGNUM);
+   QCBOREncode_CloseMap(pECtx);
+
+   QCBOREncode_CloseArray(pECtx);
+}
+
+
 int32_t AllAddMethodsTest()
 {
-   // Improvement: this test should be broken down into several so it is more
-   // managable. Tags and labels could be more sensible
+   /* Improvement: this test should be broken down into several so it is more
+    * managable. Tags and labels could be more sensible */
    QCBOREncodeContext ECtx;
    int nReturn = 0;
 
    QCBOREncode_Init(&ECtx, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
 
-   QCBOREncode_OpenArray(&ECtx);
-
-   // Some ints that are tagged and have strings preceeding them
-   // (not labels becase it is not a map)
-   QCBOREncode_AddSZString(&ECtx, "UINT62");
-   QCBOREncode_AddTag(&ECtx, 100);
-   QCBOREncode_AddUInt64(&ECtx, 89989909);
-   QCBOREncode_AddSZString(&ECtx, "INT64");
-   QCBOREncode_AddTag(&ECtx, 76);
-   QCBOREncode_AddInt64(&ECtx, 77689989909);
-   QCBOREncode_AddUInt64(&ECtx,0);
-   QCBOREncode_AddInt64(&ECtx, -44);
-
-   // ints that go in maps
-   QCBOREncode_OpenMap(&ECtx);
-   QCBOREncode_AddUInt64ToMap(&ECtx, "LBL", 77);
-   QCBOREncode_AddUInt64ToMapN(&ECtx, -4, 88);
-   QCBOREncode_AddInt64ToMap(&ECtx, "NEGLBLTHAT IS KIND OF LONG", -2394893489238);
-   QCBOREncode_AddInt64ToMapN(&ECtx, -100000000, -800000000);
-   QCBOREncode_CloseMap(&ECtx);
-
-   // Epoch Date
-   QCBOREncode_AddDateEpoch(&ECtx, 2383748234);
-
-   // Epoch date with labels
-   QCBOREncode_OpenMap(&ECtx);
-   QCBOREncode_AddDateEpochToMap(&ECtx, "LongLiveDenisRitchie", 1400000000);
-   QCBOREncode_AddDateEpochToMap(&ECtx, "time()", 1477263730);
-   QCBOREncode_AddDateEpochToMapN(&ECtx, -1969, 1477263222);
-   QCBOREncode_CloseMap(&ECtx);
-
-   // Binary blobs
-   QCBOREncode_AddBytes(&ECtx, ((UsefulBufC) {(uint8_t []){0xff, 0x00}, 2}));
-
-   // binary blobs in maps
-   QCBOREncode_OpenMap(&ECtx);
-   QCBOREncode_AddSZString(&ECtx, "binbin");
-   QCBOREncode_AddTag(&ECtx, 100000);
-   QCBOREncode_AddBytes(&ECtx, ((UsefulBufC) {(uint8_t []){0x00}, 1}));
-   QCBOREncode_AddBytesToMap(&ECtx, "blabel", ((UsefulBufC) {(uint8_t []){0x01, 0x02, 0x03}, 3}));
-   QCBOREncode_AddBytesToMapN(&ECtx, 0, ((UsefulBufC){(uint8_t []){0x04, 0x02, 0x03, 0xfe}, 4}));
-   QCBOREncode_CloseMap(&ECtx);
-
-   // text blobs
-   QCBOREncode_AddText(&ECtx, UsefulBuf_FROM_SZ_LITERAL("bar bar foo bar"));
-   QCBOREncode_AddSZString(&ECtx, "oof\n");
-   const char *szURL =
-      "http://stackoverflow.com/questions/28059697/how-do-i-toggle-between-debug-and-release-builds-in-xcode-6-7-8";
-   QCBOREncode_AddURI(&ECtx, UsefulBuf_FromSZ(szURL));
-   QCBOREncode_AddB64Text(&ECtx, UsefulBuf_FROM_SZ_LITERAL("YW55IGNhcm5hbCBwbGVhc3VyZQ=="));
-   QCBOREncode_AddRegex(&ECtx, UsefulBuf_FROM_SZ_LITERAL("[^abc]+"));
-   QCBOREncode_AddMIMEData(&ECtx, UsefulBuf_FromSZ(szMIME));
-
-   // text blobs in maps
-   QCBOREncode_OpenMap(&ECtx);
-   QCBOREncode_AddTextToMap(&ECtx, "#####", UsefulBuf_FROM_SZ_LITERAL("foo bar foo foo"));
-   QCBOREncode_AddTextToMap(&ECtx, "____", UsefulBuf_FROM_SZ_LITERAL("foo bar"));
-   QCBOREncode_AddSZString(&ECtx, "()()()");
-   QCBOREncode_AddTag(&ECtx, 1000);
-   QCBOREncode_AddSZString(&ECtx, "rab rab oof");
-   QCBOREncode_AddTextToMapN(&ECtx,22, UsefulBuf_FROM_SZ_LITERAL("foo foo foo foo"));
-   QCBOREncode_AddSZStringToMap(&ECtx, "^^", "oooooooof");
-   QCBOREncode_AddSZStringToMapN(&ECtx, 99, "ffffoooooooof");
-   QCBOREncode_AddURIToMap(&ECtx,
-                           "RFC",
-                           UsefulBuf_FROM_SZ_LITERAL("https://tools.ietf.org/html/rfc7049#section-2.4.5"));
-   QCBOREncode_AddURIToMapN(&ECtx, 0x89, UsefulBuf_FROM_SZ_LITERAL("http://cbor.me/"));
-   QCBOREncode_AddB64TextToMap(&ECtx, "whenim64", UsefulBuf_FROM_SZ_LITERAL("cGxlYXN1cmUu"));
-   QCBOREncode_AddB64TextToMapN(&ECtx, 64, UsefulBuf_FROM_SZ_LITERAL("c3VyZS4="));
-   QCBOREncode_AddRegexToMap(&ECtx, "popo", UsefulBuf_FROM_SZ_LITERAL("100\\s*mk")); //   x code string literal bug
-   QCBOREncode_AddRegexToMapN(&ECtx, -51, UsefulBuf_FROM_SZ_LITERAL("perl\\B"));  //   x code string literal bug
-   QCBOREncode_AddMIMEDataToMap(&ECtx, "Ned", UsefulBuf_FromSZ(szMIME));
-   QCBOREncode_AddMIMEDataToMapN(&ECtx, 10, UsefulBuf_FromSZ(szMIME));
-   QCBOREncode_CloseMap(&ECtx);
-
-   // Date strings
-   QCBOREncode_AddDateString(&ECtx, "2003-12-13T18:30:02Z");
-   QCBOREncode_OpenMap(&ECtx);
-   QCBOREncode_AddDateStringToMap(&ECtx, "Bed time", "2003-12-13T18:30:02.25+01:00");
-   QCBOREncode_AddDateStringToMapN(&ECtx, 88, "2003-12-13T18:30:02.25+01:00");
-   QCBOREncode_CloseMap(&ECtx);
-
-   // true / false ...
-   QCBOREncode_AddSimple(&ECtx, CBOR_SIMPLEV_UNDEF);
-   QCBOREncode_OpenMap(&ECtx);
-   QCBOREncode_AddSZString(&ECtx, "dare");
-   QCBOREncode_AddTag(&ECtx, 66);
-   QCBOREncode_AddBool(&ECtx, true);
-   QCBOREncode_AddBoolToMap(&ECtx, "uu", false);
-   QCBOREncode_AddSimpleToMapN(&ECtx, 737634, CBOR_SIMPLEV_NULL);
-   QCBOREncode_CloseMap(&ECtx);
-
-   // opening an array
-   QCBOREncode_OpenArray(&ECtx);
-   QCBOREncode_CloseArray(&ECtx);
-
-   // opening arrays in a map
-   QCBOREncode_OpenMap(&ECtx);
-   QCBOREncode_AddSZString(&ECtx, "label and tagged empty array");
-   QCBOREncode_AddTag(&ECtx, 1093);
-   QCBOREncode_OpenArray(&ECtx);
-   QCBOREncode_CloseArray(&ECtx);
-   QCBOREncode_OpenArrayInMap(&ECtx, "alabl");
-   QCBOREncode_CloseArray(&ECtx);
-   QCBOREncode_OpenArrayInMapN(&ECtx, 42);
-   QCBOREncode_CloseArray(&ECtx);
-   QCBOREncode_CloseMap(&ECtx);
-
-   // opening maps with labels and tagging
-   QCBOREncode_OpenMap(&ECtx);
-   QCBOREncode_OpenMapInMap(&ECtx, "in a map");
-   QCBOREncode_OpenMapInMapN(&ECtx, 5556);
-   QCBOREncode_AddSZString(&ECtx, "in a in a in a");
-   QCBOREncode_AddTag(&ECtx, 9087);
-   QCBOREncode_OpenMap(&ECtx);
-   QCBOREncode_CloseMap(&ECtx);
-   QCBOREncode_CloseMap(&ECtx);
-   QCBOREncode_CloseMap(&ECtx);
-   QCBOREncode_CloseMap(&ECtx);
-
-
-   // Extended simple values (these are not standard...)
-   QCBOREncode_OpenMap(&ECtx);
-   QCBOREncode_AddSZString(&ECtx, "s1");
-   QCBOREncode_AddTag(&ECtx, 88);
-   QCBOREncode_AddSimple(&ECtx, 255);
-   QCBOREncode_AddSimpleToMap(&ECtx, "s2", 0);
-   QCBOREncode_AddSZString(&ECtx, "s3");
-   QCBOREncode_AddTag(&ECtx, 88);
-   QCBOREncode_AddSimple(&ECtx, 33);
-   QCBOREncode_AddInt64(&ECtx, 88378374); // label before tag
-   QCBOREncode_AddTag(&ECtx, 88);
-   QCBOREncode_AddSimple(&ECtx, 255);
-   QCBOREncode_AddInt64(&ECtx, 89); // label before tag
-   QCBOREncode_AddTag(&ECtx, 88);
-   QCBOREncode_AddSimple(&ECtx, 19);
-   QCBOREncode_CloseMap(&ECtx);
-
-   // UUIDs
-   static const uint8_t ppppUUID[] = {0x53, 0x4D, 0x41, 0x52, 0x54, 0x43,
-                                      0x53, 0x4C, 0x54, 0x54, 0x43, 0x46,
-                                      0x49, 0x43, 0x41, 0x32};
-   const UsefulBufC XXUUID = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(ppppUUID);
-   QCBOREncode_AddBinaryUUID(&ECtx, XXUUID);
-   QCBOREncode_OpenMap(&ECtx);
-   QCBOREncode_AddBinaryUUIDToMap(&ECtx, "UUUU", XXUUID);
-   QCBOREncode_AddBinaryUUIDToMapN(&ECtx, 99, XXUUID);
-   QCBOREncode_CloseMap(&ECtx);
-
-   // Bool
-   QCBOREncode_AddBool(&ECtx, true);
-   QCBOREncode_AddBool(&ECtx, false);
-   QCBOREncode_OpenMap(&ECtx);
-   QCBOREncode_AddBoolToMap(&ECtx, "George is the man", true);
-   QCBOREncode_AddBoolToMapN(&ECtx, 010101, true);
-   QCBOREncode_CloseMap(&ECtx);
-
-
-   static const uint8_t pBignum[] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
-   const UsefulBufC BIGNUM = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pBignum);
-   QCBOREncode_AddPositiveBignum(&ECtx, BIGNUM);
-   QCBOREncode_AddNegativeBignum(&ECtx, BIGNUM);
-   QCBOREncode_OpenMap(&ECtx);
-   QCBOREncode_AddPositiveBignumToMap(&ECtx, "BN+", BIGNUM);
-   QCBOREncode_AddPositiveBignumToMapN(&ECtx, 64, BIGNUM);
-   QCBOREncode_AddNegativeBignumToMap(&ECtx, "BN-", BIGNUM);
-   QCBOREncode_AddNegativeBignumToMapN(&ECtx, -64, BIGNUM);
-   QCBOREncode_CloseMap(&ECtx);
-
-   QCBOREncode_CloseArray(&ECtx);
+   AddAll (&ECtx);
 
    UsefulBufC Enc;
-
    if(QCBOREncode_Finish(&ECtx, &Enc)) {
       nReturn = -1;
       goto Done;
    }
 
-   if(CheckResults(Enc, spExpectedEncodedAll))
+   if(CheckResults(Enc, spExpectedEncodedAll)) {
       nReturn = -2;
+   }
+
+
+   /* Also test size calculation */
+   QCBOREncode_Init(&ECtx, SizeCalculateUsefulBuf);
+
+   AddAll (&ECtx);
+
+   size_t size;
+   if(QCBOREncode_FinishGetSize(&ECtx, &size)) {
+      nReturn = -10;
+      goto Done;
+   }
+
+   if(size != sizeof(spExpectedEncodedAll)) {
+      nReturn = -11;
+   }
 
 Done:
    return nReturn;
 }
 
+
 /*
  98 30                  # array(48)
    3B 7FFFFFFFFFFFFFFF # negative(9223372036854775807)