Add QCBOREncode_Tell and QCBOREncode_SubString (#251)

* QCBOREncode_Tell and QCBOREncode_SubString

* Test and documentation improvements

* test, doc, back out error checks that didn't work

* more tests; tidiness

* Small documentation improvement

* Add RetreiveUndecodedInput

* Proper factoring for UsefulOutBuf Storage

* nits

---------

Co-authored-by: Laurence Lundblade <lgl@securitytheory.com>
diff --git a/test/UsefulBuf_Tests.c b/test/UsefulBuf_Tests.c
index 6d2b5a4..83e9a68 100644
--- a/test/UsefulBuf_Tests.c
+++ b/test/UsefulBuf_Tests.c
@@ -127,6 +127,30 @@
       goto Done;
    }
 
+   Out = UsefulOutBuf_SubString(&UOB, 10, 8);
+   if(UsefulBuf_IsNULLC(Out) ||
+      UsefulBuf_Compare(UsefulBuf_FROM_SZ_LITERAL("unbounce"), Out) ||
+      UsefulOutBuf_GetError(&UOB)) {
+      szReturn = "SubString substring";
+      goto Done;
+   }
+
+   Out = UsefulOutBuf_SubString(&UOB, 0, Expected.len);
+   if(UsefulBuf_IsNULLC(Out) ||
+      UsefulBuf_Compare(Expected, Out) ||
+      UsefulOutBuf_GetError(&UOB)) {
+      szReturn = "SubString all";
+      goto Done;
+   }
+
+   Out = UsefulOutBuf_SubString(&UOB, Expected.len, 0);
+   if(UsefulBuf_IsNULLC(Out) ||
+      UsefulBuf_Compare(UsefulBuf_FROM_SZ_LITERAL(""), Out) ||
+      UsefulOutBuf_GetError(&UOB)) {
+      szReturn = "SubString empty";
+      goto Done;
+   }
+
    /* Now test the size calculation mode */
    UsefulOutBuf_Init(&UOB, SizeCalculateUsefulBuf);
 
@@ -246,7 +270,7 @@
       return "Bad insertion point not caught";
 
 
-   UsefulBuf_MAKE_STACK_UB(outBuf2,10);
+   UsefulBuf_MAKE_STACK_UB(outBuf2, 10);
 
    UsefulOutBuf_Init(&UOB, outBuf2);
 
@@ -260,6 +284,29 @@
       return "insert with data should have failed";
    }
 
+   UsefulOutBuf_Init(&UOB, outBuf2);
+   UsefulOutBuf_AppendString(&UOB, "abc123");
+
+   UsefulBufC Out = UsefulOutBuf_SubString(&UOB, 7, 1);
+   if(!UsefulBuf_IsNULLC(Out)) {
+      return "SubString start should fail off end 1";
+   }
+   Out = UsefulOutBuf_SubString(&UOB, 5, 3);
+   if(!UsefulBuf_IsNULLC(Out)) {
+      return "SubString len should fail off end 2";
+   }
+   Out = UsefulOutBuf_SubString(&UOB, 0, 7);
+   if(!UsefulBuf_IsNULLC(Out)) {
+      return "SubString len should fail off end 3";
+   }
+   Out = UsefulOutBuf_SubString(&UOB, 7, 0);
+   if(!UsefulBuf_IsNULLC(Out)) {
+      return "SubString len should fail off end 4";
+   }
+   Out = UsefulOutBuf_SubString(&UOB, 6, 1);
+   if(!UsefulBuf_IsNULLC(Out)) {
+      return "SubString len should fail off end 5";
+   }
 
    UsefulOutBuf_Init(&UOB, (UsefulBuf){NULL, SIZE_MAX - 5});
    UsefulOutBuf_AppendData(&UOB, "123456789", SIZE_MAX -6);
diff --git a/test/qcbor_decode_tests.c b/test/qcbor_decode_tests.c
index 25c9a85..7434f0d 100644
--- a/test/qcbor_decode_tests.c
+++ b/test/qcbor_decode_tests.c
@@ -10165,6 +10165,18 @@
       return -107;
    }
 
+   QCBORDecode_Init(&DCtx,
+                    UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pValidMapEncoded),
+                    QCBOR_DECODE_MODE_NORMAL);
+
+   UsefulBufC Xx = QCBORDecode_RetrieveUndecodedInput(&DCtx);
+   if(Xx.ptr != pValidMapEncoded) {
+      return -200;
+   }
+   if(Xx.len != sizeof(pValidMapEncoded)) {
+      return -201;
+   }
+
    return 0;
 }
 
diff --git a/test/qcbor_encode_tests.c b/test/qcbor_encode_tests.c
index be5548c..44f97ab 100644
--- a/test/qcbor_encode_tests.c
+++ b/test/qcbor_encode_tests.c
@@ -180,6 +180,11 @@
    }
 
 
+   UsefulBuf Tmp = QCBOREncode_RetrieveOutputStorage(&EC);
+   if(Tmp.ptr != spBigBuf && Tmp.len != sizeof(spBigBuf)) {
+      return -111;
+   }
+
    // Make another encoded message with the CBOR from the previous
    // put into this one
    UsefulBuf_MAKE_STACK_UB(MemoryForEncoded2, 20);
@@ -196,6 +201,8 @@
    if(QCBOREncode_Finish(&EC, &Encoded2)) {
       return -5;
    }
+
+
     /*
      [                // 0    1:3
         451,          // 1    1:2
@@ -2652,6 +2659,12 @@
       return -11;
    }
 
+   UsefulBuf Tmp;
+   Tmp = QCBOREncode_RetrieveOutputStorage(&EC);
+   if(Tmp.ptr != NULL && Tmp.len != UINT32_MAX) {
+      return -111;
+   }
+
    /* ------ QCBOR_ERR_UNSUPPORTED -------- */
    QCBOREncode_Init(&EC, Large);
    QCBOREncode_OpenArray(&EC);
@@ -3109,3 +3122,85 @@
 
    return 0;
 }
+
+
+int32_t SubStringTest(void)
+{
+   QCBOREncodeContext EC;
+   size_t             uStart;
+   size_t             uCurrent;
+   UsefulBufC         SS;
+   UsefulBufC         Encoded;
+   QCBORError         uErr;
+
+   QCBOREncode_Init(&EC, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+   QCBOREncode_OpenArray(&EC);
+   uStart = QCBOREncode_Tell(&EC);
+   QCBOREncode_AddInt64(&EC, 0);
+   SS = QCBOREncode_SubString(&EC, uStart);
+   if(UsefulBuf_Compare(SS, (UsefulBufC){"\x00", 1})) {
+      return 1;
+   }
+
+   QCBOREncode_OpenArray(&EC);
+
+   QCBOREncode_CloseArray(&EC);
+   SS = QCBOREncode_SubString(&EC, uStart);
+   if(UsefulBuf_Compare(SS, (UsefulBufC){"\x00\x80", 2})) {
+      return 3;
+   }
+
+
+   /* Try it on a sequence */
+   QCBOREncode_Init(&EC, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+   uStart = QCBOREncode_Tell(&EC);
+   QCBOREncode_AddInt64(&EC, 1);
+   QCBOREncode_AddInt64(&EC, 1);
+   QCBOREncode_AddInt64(&EC, 1);
+   QCBOREncode_AddInt64(&EC, 1);
+   SS = QCBOREncode_SubString(&EC, uStart);
+   if(UsefulBuf_Compare(SS, (UsefulBufC){"\x01\x01\x01\x01", 4})) {
+      return 10;
+   }
+
+   uCurrent = QCBOREncode_Tell(&EC);
+   if(!UsefulBuf_IsNULLC(QCBOREncode_SubString(&EC, uCurrent+1))) {
+      return 11;
+   }
+
+#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
+   /* Now cause an error */
+   QCBOREncode_OpenMap(&EC);
+   QCBOREncode_CloseArray(&EC);
+   if(!UsefulBuf_IsNULLC(QCBOREncode_SubString(&EC, uStart))) {
+      return 15;
+   }
+#endif /* ! QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
+
+
+   QCBOREncode_Init(&EC, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+   QCBOREncode_AddInt64(&EC, 1);
+   QCBOREncode_AddInt64(&EC, 1);
+   uStart = QCBOREncode_Tell(&EC);
+   QCBOREncode_OpenMap(&EC);
+   QCBOREncode_OpenMapInMapN(&EC, 3);
+   QCBOREncode_OpenArrayInMapN(&EC, 4);
+   QCBOREncode_AddInt64(&EC, 0);
+   QCBOREncode_CloseArray(&EC);
+   QCBOREncode_CloseMap(&EC);
+   QCBOREncode_CloseMap(&EC);
+   SS = QCBOREncode_SubString(&EC, uStart);
+   if(UsefulBuf_Compare(SS, (UsefulBufC){"\xA1\x03\xA1\x04\x81\x00", 6})) {
+      return 20;
+   }
+
+   uErr = QCBOREncode_Finish(&EC, &Encoded);
+   if(uErr) {
+      return 21;
+   }
+   if(UsefulBuf_Compare(Encoded, (UsefulBufC){"\x01\x01\xA1\x03\xA1\x04\x81\x00", 8})) {
+      return 22;
+   }
+
+   return 0;
+}
diff --git a/test/qcbor_encode_tests.h b/test/qcbor_encode_tests.h
index 8452009..43a6290 100644
--- a/test/qcbor_encode_tests.h
+++ b/test/qcbor_encode_tests.h
@@ -191,5 +191,7 @@
 int32_t OpenCloseBytesTest(void);
 
 
+int32_t SubStringTest(void);
+
 
 #endif /* defined(__QCBOR__qcbor_encode_tests__) */
diff --git a/test/run_tests.c b/test/run_tests.c
index d1d49ed..140eb53 100644
--- a/test/run_tests.c
+++ b/test/run_tests.c
@@ -162,6 +162,7 @@
     TEST_ENTRY(ExponentAndMantissaEncodeTests),
 #endif /* QCBOR_DISABLE_EXP_AND_MANTISSA */
     TEST_ENTRY(ParseEmptyMapInMapTest),
+    TEST_ENTRY(SubStringTest),
     TEST_ENTRY(BoolTest)
 };