Add EndCheck(); change QCBORDecode_Tell() behavior (#231)

QCBORDecode_Tell() returns the offset when at the end of the input rather than UINT32_MAX. This is a non-compatible change, but QCBORDecode_Tell() was very recently introduced and is not present in any official releases.

QCBORDecode_EndCheck() is added to check to see if the cursor is at the end of the input.

Addresses #230


* Add EndCheck(); change Tell() behavior

* Minor corrections

* Minor doc update

---------

Co-authored-by: Laurence Lundblade <lgl@securitytheory.com>
diff --git a/inc/qcbor/qcbor_decode.h b/inc/qcbor/qcbor_decode.h
index 918d162..387d3af 100644
--- a/inc/qcbor/qcbor_decode.h
+++ b/inc/qcbor/qcbor_decode.h
@@ -1053,9 +1053,13 @@
  * @returns The traversal cursor offset or @c UINT32_MAX.
 
  * The position returned is always the start of the next item that
- * would be next decoded with QCBORDecode_VGetNext().  If the cursor
- * is at the end of the input or in the error state, @c UINT32_MAX is
- * returned.
+ * would be next decoded with QCBORDecode_VGetNext(). The cursor
+ * returned may be at the end of the input in which case the next call
+ * to QCBORDecode_VGetNext() will result in the @ref
+ * QCBOR_ERR_NO_MORE_ITEMS. See also QCBORDecode_AtEnd().
+ *
+ * If the decoder is in error state from previous decoding,
+ * @c UINT32_MAX is returned.
  *
  * When decoding map items, the position returned is always of the
  * label, never the value.
@@ -1083,11 +1087,27 @@
  * There is no corresponding seek method because it is too complicated
  * to restore the internal decoder state that tracks nesting.
  */
-uint32_t
+static uint32_t
 QCBORDecode_Tell(QCBORDecodeContext *pCtx);
 
 
 /**
+ * @brief Tell whether cursor is at end of the input.
+ *
+ * @param[in] pCtx   The decoder context.
+ *
+ * @returns Error code possibly indicating end of input.
+ *
+ * This returns the same as QCBORDecode_GetError() except that @ref
+ * QCBOR_ERR_NO_MORE_ITEMS is returned if the travseral cursor is at
+ * the end of the CBOR input bytes (not the end of an entered array or
+ * map).
+ */
+QCBORError
+QCBORDecode_EndCheck(QCBORDecodeContext *pCtx);
+
+
+/**
  * @brief Returns the tag numbers for an item.
  *
  * @param[in] pCtx    The decoder context.
@@ -1567,6 +1587,17 @@
  * Inline implementations of public functions defined above.
  * ---- */
 
+static inline uint32_t
+QCBORDecode_Tell(QCBORDecodeContext *pMe)
+{
+   if(pMe->uLastError) {
+      return UINT32_MAX;
+   }
+
+   /* Cast is safe because decoder input size is restricted. */
+   return (uint32_t)UsefulInputBuf_Tell(&(pMe->InBuf));
+}
+
 static inline QCBORError
 QCBORDecode_GetError(QCBORDecodeContext *pMe)
 {
diff --git a/src/qcbor_decode.c b/src/qcbor_decode.c
index 3ed4d0f..9bd518e 100644
--- a/src/qcbor_decode.c
+++ b/src/qcbor_decode.c
@@ -3219,23 +3219,24 @@
 /*
  * Public function, see header qcbor/qcbor_decode.h file
  */
-uint32_t
-QCBORDecode_Tell(QCBORDecodeContext *pMe)
+QCBORError
+QCBORDecode_EndCheck(QCBORDecodeContext *pMe)
 {
-   size_t uCursorOffset;
+   size_t     uCursorOffset;
+   QCBORError uErr;
 
-   if(pMe->uLastError != QCBOR_SUCCESS) {
-      return UINT32_MAX;
+   uErr = QCBORDecode_GetError(pMe);
+   if(uErr != QCBOR_SUCCESS) {
+      return uErr;
    }
 
    uCursorOffset = UsefulInputBuf_Tell(&(pMe->InBuf));
 
    if(uCursorOffset == UsefulInputBuf_GetBufferLength(&(pMe->InBuf))) {
-      return UINT32_MAX;
-   } else {
-      /* Cast is safe because decoder input size is restricted. */
-      return (uint32_t)uCursorOffset;
+      return QCBOR_ERR_NO_MORE_ITEMS;
    }
+
+   return QCBOR_SUCCESS;
 }
 
 
diff --git a/test/qcbor_decode_tests.c b/test/qcbor_decode_tests.c
index 0f48613..b79772d 100644
--- a/test/qcbor_decode_tests.c
+++ b/test/qcbor_decode_tests.c
@@ -10057,7 +10057,7 @@
 
    // Improvement: rewrite so this can run with only integer labels
    static const uint32_t aPos[] =
-       {0, 1, 17, 42, 50, 58, 72, 85, 98, 112, UINT32_MAX};
+       {0, 1, 17, 42, 50, 58, 72, 85, 98, 112, 151};
    QCBORDecode_Init(&DCtx,
                     UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pValidMapEncoded),
                     0);
@@ -10067,7 +10067,7 @@
          return nIndex;
       }
 
-      if(uPosition == UINT32_MAX) {
+      if(QCBORDecode_EndCheck(&DCtx)) {
          break;
       }
 
@@ -10076,7 +10076,7 @@
 
 #ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS
    static const uint32_t aPosIndef[] =
-       {0, 1, 17, 42, 50, 59, 73, 86, 99, 113, UINT32_MAX};
+       {0, 1, 17, 42, 50, 59, 73, 86, 99, 113, 154};
    QCBORDecode_Init(&DCtx,
                     UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pValidMapIndefEncoded),
                     0);
@@ -10087,7 +10087,7 @@
          return nIndex + 100;
       }
 
-      if(uPosition == UINT32_MAX) {
+      if(QCBORDecode_EndCheck(&DCtx)) {
          break;
       }
 
@@ -10134,7 +10134,7 @@
    }
 
    QCBORDecode_ExitMap(&DCtx);
-   if(QCBORDecode_Tell(&DCtx) != UINT32_MAX) {
+   if(QCBORDecode_Tell(&DCtx) != 151) {
       return 1009;
    }
    if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_NO_MORE_ITEMS) {
@@ -10181,7 +10181,7 @@
    }
 
    QCBORDecode_ExitMap(&DCtx);
-   if(QCBORDecode_Tell(&DCtx) != UINT32_MAX) {
+   if(QCBORDecode_Tell(&DCtx) != 154) {
       return 2008;
    }
    if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_NO_MORE_ITEMS) {
@@ -10200,6 +10200,10 @@
    if(QCBORDecode_Tell(&DCtx) != UINT32_MAX) {
       return 3000;
    }
+   if(QCBORDecode_EndCheck(&DCtx) != QCBOR_ERR_MAP_NOT_ENTERED) {
+      return 3001;
+   }
+
 
    /* Empties tests */
    const uint8_t pMinimalCBOR[] = {0xa0}; // One empty map
@@ -10207,19 +10211,25 @@
    if(QCBORDecode_Tell(&DCtx) != 0) {
       return 4000;
    }
+   if(QCBORDecode_EndCheck(&DCtx) != QCBOR_SUCCESS) {
+      return 4008;
+   }
    QCBORDecode_EnterMap(&DCtx, &Item);
    if(QCBORDecode_GetError(&DCtx) != QCBOR_SUCCESS) {
       return 4001;
    }
-   if(QCBORDecode_Tell(&DCtx) != UINT32_MAX) {
+   if(QCBORDecode_Tell(&DCtx) != 1) {
       return 4002;
    }
    QCBORDecode_ExitMap(&DCtx);
    if(QCBORDecode_GetError(&DCtx) != QCBOR_SUCCESS) {
-      return 4001;
+      return 4003;
    }
-   if(QCBORDecode_Tell(&DCtx) != UINT32_MAX) {
-      return 4002;
+   if(QCBORDecode_Tell(&DCtx) != 1) {
+      return 4004;
+   }
+   if(QCBORDecode_EndCheck(&DCtx) != QCBOR_ERR_NO_MORE_ITEMS) {
+      return 4005;
    }
    if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_NO_MORE_ITEMS) {
       return 4010;
@@ -10235,15 +10245,18 @@
    if(QCBORDecode_GetError(&DCtx) != QCBOR_SUCCESS) {
       return 4101;
    }
-   if(QCBORDecode_Tell(&DCtx) != UINT32_MAX) {
+   if(QCBORDecode_Tell(&DCtx) != 2) {
       return 4102;
    }
    QCBORDecode_ExitMap(&DCtx);
    if(QCBORDecode_GetError(&DCtx) != QCBOR_SUCCESS) {
-      return 4101;
+      return 4103;
    }
-   if(QCBORDecode_Tell(&DCtx) != UINT32_MAX) {
-      return 4102;
+   if(QCBORDecode_Tell(&DCtx) != 2) {
+      return 4104;
+   }
+   if(QCBORDecode_EndCheck(&DCtx) != QCBOR_ERR_NO_MORE_ITEMS) {
+      return 4005;
    }
    if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_NO_MORE_ITEMS) {
       return 4110;
@@ -10280,7 +10293,7 @@
    if(QCBORDecode_GetError(&DCtx) != QCBOR_SUCCESS) {
       return 5007;
    }
-   if(QCBORDecode_Tell(&DCtx) != UINT32_MAX) {
+   if(QCBORDecode_Tell(&DCtx) != 20) {
       return 5008;
    }
    if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_NO_MORE_ITEMS) {
@@ -10315,7 +10328,7 @@
    }
 
    static const uint32_t aEmptiesPos[] =
-       {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, UINT32_MAX};
+       {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 15};
    QCBORDecode_Init(&DCtx,
                     UsefulBuf_FROM_BYTE_ARRAY_LITERAL(sEmpties),
                     0);
@@ -10325,7 +10338,7 @@
          return nIndex + 200;
       }
 
-      if(uPosition == UINT32_MAX) {
+      if(QCBORDecode_EndCheck(&DCtx)) {
          break;
       }
 
@@ -10334,7 +10347,7 @@
 
 #ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS
    static const uint32_t aIndefEmptiesPos[] =
-       {0, 1, 2, 4, 5, 7, 8, 10, 12, 13, 16, 19, UINT32_MAX};
+       {0, 1, 2, 4, 5, 7, 8, 10, 12, 13, 16, 19, 25};
    QCBORDecode_Init(&DCtx,
                     UsefulBuf_FROM_BYTE_ARRAY_LITERAL(sEmptiesIndef),
                     0);
@@ -10344,7 +10357,7 @@
          return nIndex + 300;
       }
 
-      if(uPosition == UINT32_MAX) {
+      if(QCBORDecode_EndCheck(&DCtx)) {
          break;
       }