Add GetSimple, update Simple Value documentation (#236)

Add the method QCBORDecode_GetSimple().  Update testing and documentation for simple values.


* Add GetSimple

* Finish off GetSimple and friends

* Fix tests when tags are disabled

---------

Co-authored-by: Laurence Lundblade <lgl@securitytheory.com>
diff --git a/inc/qcbor/qcbor_decode.h b/inc/qcbor/qcbor_decode.h
index 14b8f15..918d162 100644
--- a/inc/qcbor/qcbor_decode.h
+++ b/inc/qcbor/qcbor_decode.h
@@ -1101,7 +1101,7 @@
  * in the QCBORItem.
  *
  * Tags nest. Here the tag with index 0 has the data item as its content. The
- *  tag with index 1 has the tag at index 0 has its content and so forth.
+ * tag with index 1 has the tag at index 0 has its content and so forth.
  *
  * Deep tag nesting is rare so this implementation imposes a limit of
  * @ref QCBOR_MAX_TAGS_PER_ITEM on nesting and returns @ref
diff --git a/inc/qcbor/qcbor_encode.h b/inc/qcbor/qcbor_encode.h
index daea469..83c167e 100644
--- a/inc/qcbor/qcbor_encode.h
+++ b/inc/qcbor/qcbor_encode.h
@@ -1800,15 +1800,20 @@
  * @param[in] pMe    The encode context.
  * @param[in] uNum   The simple value.
  *
- * Use QCBOREncode_AddBool(), QCBOREncode_AddUndef()... instead of this.
+ * QCBOREncode_AddBool(), QCBOREncode_AddUndef() and
+ * QCBOREncode_AddNull() are preferred to this for the simple values
+ * defined in RFC 8949, but this can be used for them too.
  *
- * Use this to add simple values beyond those in defined RFC
- * 8949. Simple values must be registered with IANA. There is no range
- * of values for proprietary use.
+ * The main purpose of this is to add simple values beyond those in
+ * defined RFC 8949. Note that simple values must be registered with
+ * IANA. Those in the range of 0 to 19 must be standardized.  Those in
+ * the range of 32 to 255 do not require a standard, but must be
+ * publically specified. There is no range of values for proprietary
+ * use. See
  * https://www.iana.org/assignments/cbor-simple-values/cbor-simple-values.xhtml
  */
 static void
-QCBOREncode_AddSimple(QCBOREncodeContext *pMe, const uint64_t uNum);
+QCBOREncode_AddSimple(QCBOREncodeContext *pMe, const uint8_t uNum);
 
 static void
 QCBOREncode_AddSimpleToMap(QCBOREncodeContext *pMe,
@@ -3751,7 +3756,7 @@
 
 
 static inline void
-QCBOREncode_AddSimple(QCBOREncodeContext *pMe, const uint64_t uNum)
+QCBOREncode_AddSimple(QCBOREncodeContext *pMe, const uint8_t uNum)
 {
    /* This check often is optimized out because uNum is known at compile time. */
 #ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
@@ -3766,8 +3771,8 @@
 
 static inline void
 QCBOREncode_AddSimpleToMap(QCBOREncodeContext *pMe,
-                                   const char         *szLabel,
-                                   const uint8_t       uSimple)
+                           const char         *szLabel,
+                           const uint8_t       uSimple)
 {
    QCBOREncode_AddSZString(pMe, szLabel);
    QCBOREncode_AddSimple(pMe, uSimple);
@@ -3775,8 +3780,8 @@
 
 static inline void
 QCBOREncode_AddSimpleToMapN(QCBOREncodeContext *pMe,
-                                    const int64_t       nLabel,
-                                    const uint8_t       uSimple)
+                            const int64_t       nLabel,
+                            const uint8_t       uSimple)
 {
    QCBOREncode_AddInt64(pMe, nLabel);
    QCBOREncode_AddSimple(pMe, uSimple);
diff --git a/inc/qcbor/qcbor_spiffy_decode.h b/inc/qcbor/qcbor_spiffy_decode.h
index eb46210..9e0cd07 100644
--- a/inc/qcbor/qcbor_spiffy_decode.h
+++ b/inc/qcbor/qcbor_spiffy_decode.h
@@ -1170,6 +1170,37 @@
 
 
 /**
+ * @brief Decode the next item as a CBOR simple value.
+ *
+ * @param[in] pCtx            The decode context.
+ * @param[out] puSimpleValue  The simplle value returned.
+ *
+ * The purpose of this is to get a CBOR simple value other than a
+ * Boolean, NULL or "undefined", but this works on all simple
+ * values. See QCBOREncode_AddSimple() for more details on simple
+ * values in general.
+ *
+ * See QCBORDecode_GetBool(), QCBORDecode_GetNull(),
+ * QCBORDecode_GetUndefined() for the preferred way of getting those
+ * simple values.
+ */
+void
+QCBORDecode_GetSimple(QCBORDecodeContext *pCtx, uint8_t *puSimpleValue);
+
+void
+QCBORDecode_GetSimpleInMapN(QCBORDecodeContext *pCtx,
+                            int64_t             nLabel,
+                            uint8_t            *puSimpleValue);
+
+void
+QCBORDecode_GetSimpleInMapSZ(QCBORDecodeContext *pCtx,
+                             const char         *szLabel,
+                             uint8_t            *puSimpleValue);
+
+
+
+
+/**
  * @brief Decode the next item as a date string.
  *
  * @param[in] pCtx             The decode context.
@@ -2586,6 +2617,7 @@
 }
 
 
+
 static inline void
 QCBORDecode_GetDateString(QCBORDecodeContext *pMe,
                           const uint8_t       uTagRequirement,
diff --git a/src/qcbor_decode.c b/src/qcbor_decode.c
index ee56a5f..3ed4d0f 100644
--- a/src/qcbor_decode.c
+++ b/src/qcbor_decode.c
@@ -4584,6 +4584,97 @@
 }
 
 
+/**
+ * @brief Process simple values.
+ *
+ * @param[in] pMe     The decode context.
+ * @param[in] pItem   The item with the simple value.
+ * @param[out] puSimple  The simple value output.
+ *
+ * Sets the internal error if the item isn't a true or a false. Also
+ * records any tag numbers as the tag numbers of the last item.
+ */
+static void
+QCBORDecode_Private_ProcessSimple(QCBORDecodeContext *pMe,
+                                  const QCBORItem    *pItem,
+                                  uint8_t            *puSimple)
+{
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   /* It's kind of lame to remap true...undef back to simple values, but
+    * this function isn't used much and to not do it would require
+    * changing GetNext() behavior in an incompatible way.
+    */
+   switch(pItem->uDataType) {
+      case QCBOR_TYPE_UKNOWN_SIMPLE:
+         *puSimple = pItem->val.uSimple;
+         break;
+
+      case QCBOR_TYPE_TRUE:
+         *puSimple = CBOR_SIMPLEV_TRUE;
+         break;
+
+      case QCBOR_TYPE_FALSE:
+         *puSimple = CBOR_SIMPLEV_FALSE;
+         break;
+
+      case QCBOR_TYPE_NULL:
+         *puSimple = CBOR_SIMPLEV_NULL;
+         break;
+
+      case QCBOR_TYPE_UNDEF:
+         *puSimple = CBOR_SIMPLEV_UNDEF;
+         break;
+
+      default:
+         pMe->uLastError = QCBOR_ERR_UNEXPECTED_TYPE;
+         return;
+   }
+   QCBORDecode_Private_CopyTags(pMe, pItem);
+}
+
+/*
+ * Public function, see header qcbor/qcbor_decode.h file
+ */
+void
+QCBORDecode_GetSimple(QCBORDecodeContext *pMe, uint8_t *puSimple)
+{
+   QCBORItem Item;
+
+   QCBORDecode_VGetNext(pMe, &Item);
+   QCBORDecode_Private_ProcessSimple(pMe, &Item, puSimple);
+}
+
+/*
+ * Public function, see header qcbor/qcbor_decode.h file
+ */
+void
+QCBORDecode_GetSimpleInMapN(QCBORDecodeContext *pMe,
+                            int64_t             nLabel,
+                            uint8_t            *puSimpleValue)
+{
+   QCBORItem Item;
+   QCBORDecode_GetItemInMapN(pMe, nLabel, QCBOR_TYPE_ANY, &Item);
+
+   QCBORDecode_Private_ProcessSimple(pMe, &Item, puSimpleValue);
+}
+
+/*
+ * Public function, see header qcbor/qcbor_decode.h file
+ */
+void
+QCBORDecode_GetSimpleInMapSZ(QCBORDecodeContext *pMe,
+                             const char         *szLabel,
+                             uint8_t            *puSimpleValue)
+{
+   QCBORItem Item;
+   QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item);
+
+   QCBORDecode_Private_ProcessSimple(pMe, &Item, puSimpleValue);
+}
+
 
 
 /**
diff --git a/test/qcbor_decode_tests.c b/test/qcbor_decode_tests.c
index aae1f80..0f48613 100644
--- a/test/qcbor_decode_tests.c
+++ b/test/qcbor_decode_tests.c
@@ -2334,79 +2334,156 @@
    0xf8, 0x00, 0xf8, 0x13, 0xf8, 0x1f, 0xf8, 0x20,
    0xf8, 0xff};
 
-int32_t ParseSimpleTest(void)
+/* A map of good simple values, plus one well-formed integer */
+static const uint8_t spGoodSimpleValues[] = {
+   0xa9, 0x01, 0xf4, 0x02, 0xf5, 0x03, 0xf6, 0x04, 0xf7,
+   0x05, 0xe0, 0x06, 0xf3, 0x07, 0xf8, 0x20, 0x61, 0x40,
+   0xf8, 0xff, 0x0f, 0x0f};
+
+int32_t SimpleValueDecodeTests(void)
 {
    QCBORDecodeContext DCtx;
-   QCBORItem Item;
-   QCBORError nCBORError;
-
+   QCBORItem          Item;
+   QCBORError         uErr;
 
    QCBORDecode_Init(&DCtx,
                     UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spSimpleValues),
                     QCBOR_DECODE_MODE_NORMAL);
 
 
-   if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
-      return (int32_t)nCBORError;
+   if((uErr = QCBORDecode_GetNext(&DCtx, &Item)))
+      return (int32_t)uErr;
    if(Item.uDataType != QCBOR_TYPE_ARRAY ||
       Item.val.uCount != 10)
-      return -1;
+      return 1;
 
-   if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
-      return (int32_t)nCBORError;
+   if((uErr = QCBORDecode_GetNext(&DCtx, &Item)))
+      return (int32_t)uErr;
    if(Item.uDataType != QCBOR_TYPE_FALSE)
-      return -1;
+      return 2;
 
-   if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
-      return (int32_t)nCBORError;
+   if((uErr = QCBORDecode_GetNext(&DCtx, &Item)))
+      return (int32_t)uErr;
    if(Item.uDataType != QCBOR_TYPE_TRUE)
-      return -1;
+      return 3;
 
-   if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
-      return (int32_t)nCBORError;
+   if((uErr = QCBORDecode_GetNext(&DCtx, &Item)))
+      return (int32_t)uErr;
    if(Item.uDataType != QCBOR_TYPE_NULL)
-      return -1;
+      return 4;
 
-   if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
-      return (int32_t)nCBORError;
+   if((uErr = QCBORDecode_GetNext(&DCtx, &Item)))
+      return (int32_t)uErr;
    if(Item.uDataType != QCBOR_TYPE_UNDEF)
-      return -1;
+      return 5;
 
    // A break
    if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_BAD_BREAK)
-      return -1;
+      return 6;
 
-   if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
-      return (int32_t)nCBORError;
+   if((uErr = QCBORDecode_GetNext(&DCtx, &Item)))
+      return (int32_t)uErr;
    if(Item.uDataType != QCBOR_TYPE_UKNOWN_SIMPLE || Item.val.uSimple != 0)
-      return -1;
+      return 7;
 
-   if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
-      return (int32_t)nCBORError;
+   if((uErr = QCBORDecode_GetNext(&DCtx, &Item)))
+      return (int32_t)uErr;
    if(Item.uDataType != QCBOR_TYPE_UKNOWN_SIMPLE || Item.val.uSimple != 19)
-      return -1;
+      return 8;
 
    if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_BAD_TYPE_7)
-      return -1;
+      return 9;
 
    if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_BAD_TYPE_7)
-      return -1;
+      return 10;
 
    if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_BAD_TYPE_7)
-      return -1;
+      return 11;
 
-   if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
-      return (int32_t)nCBORError;
+   if((uErr = QCBORDecode_GetNext(&DCtx, &Item)))
+      return (int32_t)uErr;
    if(Item.uDataType != QCBOR_TYPE_UKNOWN_SIMPLE || Item.val.uSimple != 32)
-      return -1;
+      return 12;
 
-   if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
-      return (int32_t)nCBORError;
+   if((uErr = QCBORDecode_GetNext(&DCtx, &Item)))
+      return (int32_t)uErr;
    if(Item.uDataType != QCBOR_TYPE_UKNOWN_SIMPLE || Item.val.uSimple != 255)
-      return -1;
+      return 13;
+
+
+   QCBORDecode_Init(&DCtx,
+                    UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spGoodSimpleValues),
+                    QCBOR_DECODE_MODE_NORMAL);
+
+   uint8_t uSimple;
+
+   QCBORDecode_EnterMap(&DCtx, &Item);
+   QCBORDecode_GetSimple(&DCtx, &uSimple);
+   if(QCBORDecode_GetError(&DCtx) || uSimple != CBOR_SIMPLEV_FALSE) {
+      return 20;
+   }
+   QCBORDecode_GetSimple(&DCtx, &uSimple);
+   if(QCBORDecode_GetError(&DCtx) || uSimple != CBOR_SIMPLEV_TRUE) {
+      return 21;
+   }
+   QCBORDecode_GetSimple(&DCtx, &uSimple);
+   if(QCBORDecode_GetError(&DCtx) || uSimple != CBOR_SIMPLEV_NULL) {
+      return 22;
+   }
+   QCBORDecode_GetSimple(&DCtx, &uSimple);
+   if(QCBORDecode_GetError(&DCtx) || uSimple != CBOR_SIMPLEV_UNDEF) {
+      return 23;
+   }
+   QCBORDecode_GetSimple(&DCtx, &uSimple);
+   if(QCBORDecode_GetError(&DCtx) || uSimple != 0) {
+      return 24;
+   }
+   QCBORDecode_GetSimple(&DCtx, &uSimple);
+   if(QCBORDecode_GetError(&DCtx) || uSimple != 19) {
+      return 25;
+   }
+   QCBORDecode_GetSimple(&DCtx, &uSimple);
+   if(QCBORDecode_GetError(&DCtx) || uSimple != 32) {
+      return 26;
+   }
+   QCBORDecode_GetSimple(&DCtx, &uSimple);
+   if(QCBORDecode_GetError(&DCtx) || uSimple != 255) {
+      return 27;
+   }
+   QCBORDecode_VGetNext(&DCtx, &Item);
+   QCBORDecode_GetSimple(&DCtx, &uSimple);
+   if(QCBORDecode_GetError(&DCtx) != QCBOR_ERR_NO_MORE_ITEMS) {
+      return 28;
+   }
+
+   QCBORDecode_Rewind(&DCtx);
+
+   QCBORDecode_GetSimpleInMapN(&DCtx, 6, &uSimple);
+   if(QCBORDecode_GetError(&DCtx) || uSimple != 19) {
+      return 30;
+   }
+
+   QCBORDecode_GetSimpleInMapSZ(&DCtx, "@", &uSimple);
+   if(QCBORDecode_GetError(&DCtx) || uSimple != 255) {
+      return 31;
+   }
+
+   QCBORDecode_GetSimpleInMapN(&DCtx, 99, &uSimple);
+   if(QCBORDecode_GetAndResetError(&DCtx) != QCBOR_ERR_LABEL_NOT_FOUND) {
+      return 32;
+   }
+
+   QCBORDecode_GetSimpleInMapSZ(&DCtx, "xx", &uSimple);
+   if(QCBORDecode_GetAndResetError(&DCtx) != QCBOR_ERR_LABEL_NOT_FOUND) {
+      return 33;
+   }
+
+   QCBORDecode_GetSimpleInMapN(&DCtx, 15, &uSimple);
+   if(QCBORDecode_GetAndResetError(&DCtx) != QCBOR_ERR_UNEXPECTED_TYPE) {
+      return 34;
+   }
 
    return 0;
-
 }
 
 
diff --git a/test/qcbor_decode_tests.h b/test/qcbor_decode_tests.h
index b23149a..a08a3af 100644
--- a/test/qcbor_decode_tests.h
+++ b/test/qcbor_decode_tests.h
@@ -126,7 +126,7 @@
 /*
  Test parsing of some simple values like true, false, null...
  */
-int32_t ParseSimpleTest(void);
+int32_t SimpleValueDecodeTests(void);
 
 
 /*
diff --git a/test/run_tests.c b/test/run_tests.c
index a600731..d1d49ed 100644
--- a/test/run_tests.c
+++ b/test/run_tests.c
@@ -85,7 +85,7 @@
     TEST_ENTRY(IndefiniteLengthArrayMapTest),
     TEST_ENTRY(NestedMapTestIndefLen),
 #endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS */
-    TEST_ENTRY(ParseSimpleTest),
+    TEST_ENTRY(SimpleValueDecodeTests),
     TEST_ENTRY(DecodeFailureTests),
     TEST_ENTRY(EncodeRawTest),
     TEST_ENTRY(RTICResultsTest),