Allow partial decode of CBOR sequences (#77)

Add QCBORDecode_PartialFinish() to get the offset to which decoding has progressed. Also reports error status of partial decoding.

Co-authored-by: Laurence Lundblade <lgl@securitytheory.com>
diff --git a/inc/qcbor/qcbor_decode.h b/inc/qcbor/qcbor_decode.h
index ad8c354..63e94cb 100644
--- a/inc/qcbor/qcbor_decode.h
+++ b/inc/qcbor/qcbor_decode.h
@@ -902,6 +902,7 @@
 QCBORDecode_PeekNext(QCBORDecodeContext *pCtx, QCBORItem *pDecodedItem);
 
 
+
 /**
  @brief Gets the next item including full list of tags for item.
 
@@ -1046,10 +1047,39 @@
  input was well-formed and there are extra bytes at the end @ref
  QCBOR_ERR_EXTRA_BYTES will be returned.  This can be considered a
  successful decode.
+
+ See also QCBORDecode_PartialFinish().
  */
 QCBORError QCBORDecode_Finish(QCBORDecodeContext *pCtx);
 
 
+/**
+ @brief Return number of bytes consumed so far.
+
+ @param[in]  pCtx        The context to check.
+ @param[out] puConsumed  The number of bytes consumed so far. May be @c NULL.
+
+ @returns The same as QCBORDecode_Finish();
+
+ This is primarily for partially decoding CBOR sequences. It is the
+ same as QCBORDecode_Finish() except it returns the number of bytes
+ consumed and doesn't call the destructor for the string allocator
+ (See @ref and QCBORDecode_SetMemPool()).
+
+ When this is called before all input bytes are consumed, @ref
+ QCBOR_ERR_EXTRA_BYTES will be returned as QCBORDecode_Finish()
+ does. For typical use of this, that particular error is disregarded.
+
+ Decoding with the same @ref QCBORDecodeContext can continue after
+ calling this and this may be called many times.
+
+ Another way to resume decoding is to call QCBORDecode_Init() on the
+ bytes not decoded, but this only works on CBOR sequences when the
+ decoding stopped with no open arrays, maps or byte strings.
+ */
+QCBORError
+QCBORDecode_PartialFinish(QCBORDecodeContext *pCtx, size_t *puConsumed);
+
 
 /**
  @brief Get the decoding error.
diff --git a/src/qcbor_decode.c b/src/qcbor_decode.c
index 8989211..b46908d 100644
--- a/src/qcbor_decode.c
+++ b/src/qcbor_decode.c
@@ -2391,8 +2391,12 @@
 /*
  * Public function, see header qcbor/qcbor_decode.h file
  */
-QCBORError QCBORDecode_Finish(QCBORDecodeContext *pMe)
+QCBORError QCBORDecode_PartialFinish(QCBORDecodeContext *pMe, size_t *puConsumed)
 {
+   if(puConsumed != NULL) {
+      *puConsumed = pMe->InBuf.cursor;
+   }
+
    QCBORError uReturn = pMe->uLastError;
 
    if(uReturn != QCBOR_SUCCESS) {
@@ -2411,6 +2415,15 @@
    }
 
 Done:
+   return uReturn;
+}
+
+
+/*
+ * Public function, see header qcbor/qcbor_decode.h file
+ */
+QCBORError QCBORDecode_Finish(QCBORDecodeContext *pMe)
+{
 #ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS
    /* Call the destructor for the string allocator if there is one.
     * Always called, even if there are errors; always have to clean up.
@@ -2418,7 +2431,7 @@
    StringAllocator_Destruct(&(pMe->StringAllocator));
 #endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS */
 
-   return uReturn;
+   return QCBORDecode_PartialFinish(pMe, NULL);
 }
 
 
diff --git a/test/qcbor_decode_tests.c b/test/qcbor_decode_tests.c
index a7ef065..567f654 100644
--- a/test/qcbor_decode_tests.c
+++ b/test/qcbor_decode_tests.c
@@ -6138,8 +6138,9 @@
 int32_t CBORSequenceDecodeTests(void)
 {
    QCBORDecodeContext DCtx;
-   QCBORItem Item;
-   QCBORError uCBORError;
+   QCBORItem          Item;
+   QCBORError         uCBORError;
+   size_t             uConsumed;
 
    // --- Test a sequence with extra bytes ---
 
@@ -6159,12 +6160,24 @@
       return 2;
    }
 
+   uCBORError = QCBORDecode_PartialFinish(&DCtx, &uConsumed);
+   if(uCBORError != QCBOR_ERR_EXTRA_BYTES ||
+      uConsumed != 12) {
+      return 102;
+   }
+
    // Get a second item
    uCBORError = QCBORDecode_GetNext(&DCtx, &Item);
    if(uCBORError != QCBOR_ERR_BAD_OPT_TAG) {
       return 66;
    }
 
+   uCBORError = QCBORDecode_PartialFinish(&DCtx, &uConsumed);
+   if(uCBORError != QCBOR_ERR_EXTRA_BYTES ||
+      uConsumed != 14) {
+      return 102;
+   }
+
    // Get a third item
    uCBORError = QCBORDecode_GetNext(&DCtx, &Item);
    if(uCBORError != QCBOR_SUCCESS) {