Add decoding methods GetNull and GetUndefined (#103)

* implementation of GetNull

* add support for undef decoding

Co-authored-by: Laurence Lundblade <lgl@securitytheory.com>
diff --git a/inc/qcbor/qcbor_spiffy_decode.h b/inc/qcbor/qcbor_spiffy_decode.h
index 38e793d..3e69221 100644
--- a/inc/qcbor/qcbor_spiffy_decode.h
+++ b/inc/qcbor/qcbor_spiffy_decode.h
@@ -932,7 +932,7 @@
  The CBOR item to decode must be either the CBOR simple value (CBOR
  type 7) @c true or @c false.
 
- See @ref Decode-Errors for discussion on how error handling works.  It
+ See @ref Decode-Errors for discussion on how error handling works.  If
  the CBOR item to decode is not true or false the @ref
  QCBOR_ERR_UNEXPECTED_TYPE error is set.
 */
@@ -947,8 +947,43 @@
                                 bool               *pbBool);
 
 
+/**
+ @brief Decode the next item as a null.
+
+ @param[in] pCtx   The decode context.
+
+ The CBOR item to decode must be the CBOR simple value (CBOR type 7)
+ @c null. The reason to call this is to see if an error is returned or
+ not indicating whether the item is a CBOR null. If it is not then the
+ @ref QCBOR_ERR_UNEXPECTED_TYPE error is set.
+*/
+static void QCBORDecode_GetNull(QCBORDecodeContext *pCtx);
+
+static void QCBORDecode_GetNullInMapN(QCBORDecodeContext *pCtx,
+                                      int64_t             nLabel);
+
+static void QCBORDecode_GetNullInMapSZ(QCBORDecodeContext *pCtx,
+                                       const char         *szLabel);
 
 
+/**
+ @brief Decode the next item as a CBOR "undefined" item.
+
+ @param[in] pCtx   The decode context.
+
+ The CBOR item to decode must be the CBOR simple value (CBOR type 7)
+ @c undefined. The reason to call this is to see if an error is
+ returned or not indicating whether the item is a CBOR undefed
+ item. If it is not then the @ref QCBOR_ERR_UNEXPECTED_TYPE error is
+ set.
+*/
+static void QCBORDecode_GetUndefined(QCBORDecodeContext *pCtx);
+
+static void QCBORDecode_GetUndefinedInMapN(QCBORDecodeContext *pCtx,
+                                           int64_t             nLabel);
+
+static void QCBORDecode_GetUndefinedInMapSZ(QCBORDecodeContext *pCtx,
+                                            const char         *szLabel);
 
 
 /**
@@ -1947,7 +1982,7 @@
 static inline void
 QCBORDecode_GetByteString(QCBORDecodeContext *pMe,  UsefulBufC *pValue)
 {
-   // Complier should make this just 64-bit integer parameter
+   // Complier should make this just a 64-bit integer parameter
    const TagSpecification TagSpec =
       {
          QCBOR_TAG_REQUIREMENT_NOT_A_TAG,
@@ -2034,6 +2069,60 @@
    QCBORDecode_GetTaggedStringInMapSZ(pMe, szLabel, TagSpec, pText);
 }
 
+static inline void
+QCBORDecode_GetNull(QCBORDecodeContext *pMe)
+{
+   QCBORItem item;
+
+   QCBORDecode_VGetNext(pMe, &item);
+   if(pMe->uLastError == QCBOR_SUCCESS && item.uDataType != QCBOR_TYPE_NULL) {
+      pMe->uLastError = QCBOR_ERR_UNEXPECTED_TYPE;
+   }
+}
+
+static inline void
+QCBORDecode_GetNullInMapN(QCBORDecodeContext *pMe,
+                          int64_t             nLabel)
+{
+   QCBORItem Item;
+   QCBORDecode_GetItemInMapN(pMe, nLabel, QCBOR_TYPE_NULL, &Item);
+}
+
+static inline void
+QCBORDecode_GetNullInMapSZ(QCBORDecodeContext *pMe,
+                                      const char * szLabel)
+{
+   QCBORItem Item;
+   QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_NULL, &Item);
+}
+
+static inline void
+QCBORDecode_GetUndefined(QCBORDecodeContext *pMe)
+{
+   QCBORItem item;
+
+   QCBORDecode_VGetNext(pMe, &item);
+   if(pMe->uLastError == QCBOR_SUCCESS && item.uDataType != QCBOR_TYPE_UNDEF) {
+      pMe->uLastError = QCBOR_ERR_UNEXPECTED_TYPE;
+   }
+}
+
+static inline void
+QCBORDecode_GetUndefinedInMapN(QCBORDecodeContext *pMe,
+                          int64_t             nLabel)
+{
+   QCBORItem Item;
+   QCBORDecode_GetItemInMapN(pMe, nLabel, QCBOR_TYPE_UNDEF, &Item);
+}
+
+static inline void
+QCBORDecode_GetUndefinedInMapSZ(QCBORDecodeContext *pMe,
+                                      const char * szLabel)
+{
+   QCBORItem Item;
+   QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_UNDEF, &Item);
+}
+
 
 static inline void
 QCBORDecode_GetDateString(QCBORDecodeContext *pMe,
diff --git a/test/qcbor_decode_tests.c b/test/qcbor_decode_tests.c
index 5ce62d0..236f59c 100644
--- a/test/qcbor_decode_tests.c
+++ b/test/qcbor_decode_tests.c
@@ -7796,14 +7796,25 @@
    0xa1, 0x08, 0x1a
 };
 
+static const uint8_t spNullInMap[] =
+{
+   0xa1, 0x08, 0xf6
+};
+
+static const uint8_t spUndefinedInMap[] =
+{
+   0xa1, 0x08, 0xf7
+};
+
 
 int32_t BoolTest(void)
 {
    QCBORDecodeContext DCtx;
    bool               b;
 
-   QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spBooleansInMap), 0);
-
+   QCBORDecode_Init(&DCtx,
+                    UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spBooleansInMap),
+                    0);
    QCBORDecode_EnterMap(&DCtx, NULL);
    QCBORDecode_GetBool(&DCtx, &b);
    if(QCBORDecode_GetAndResetError(&DCtx) || !b) {
@@ -7826,21 +7837,114 @@
        return 4;
     }
 
-   QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spBooleansInMapWrongType), 0);
-
+   QCBORDecode_Init(&DCtx,
+                    UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spBooleansInMapWrongType),
+                    0);
    QCBORDecode_EnterMap(&DCtx, NULL);
    QCBORDecode_GetBool(&DCtx, &b);
    if(QCBORDecode_GetAndResetError(&DCtx) != QCBOR_ERR_UNEXPECTED_TYPE) {
       return 5;
    }
 
-   QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spBooleansInMapNWF), 0);
-
+   QCBORDecode_Init(&DCtx,
+                    UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spBooleansInMapNWF),
+                    0);
    QCBORDecode_EnterMap(&DCtx, NULL);
    QCBORDecode_GetBool(&DCtx, &b);
    if(QCBORDecode_GetAndResetError(&DCtx) != QCBOR_ERR_HIT_END) {
       return 6;
    }
 
+
+   QCBORDecode_Init(&DCtx,
+                    UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spNullInMap),
+                    0);
+   QCBORDecode_EnterMap(&DCtx, NULL);
+   QCBORDecode_GetNull(&DCtx);
+   if(QCBORDecode_GetAndResetError(&DCtx)) {
+      return 7;
+   }
+
+   QCBORDecode_Init(&DCtx,
+                    UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spBooleansInMap),
+                    0);
+   QCBORDecode_EnterMap(&DCtx, NULL);
+   QCBORDecode_GetNull(&DCtx);
+   if(QCBORDecode_GetAndResetError(&DCtx) != QCBOR_ERR_UNEXPECTED_TYPE) {
+      return 8;
+   }
+
+   QCBORDecode_Init(&DCtx,
+                    UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spNullInMap),
+                    0);
+   QCBORDecode_EnterMap(&DCtx, NULL);
+   QCBORDecode_GetNullInMapN(&DCtx, 8);
+   if(QCBORDecode_GetAndResetError(&DCtx)) {
+      return 9;
+   }
+
+   QCBORDecode_Init(&DCtx,
+                    UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spBooleansInMap),
+                    0);
+   QCBORDecode_EnterMap(&DCtx, NULL);
+   QCBORDecode_GetNullInMapN(&DCtx, 8);
+   if(QCBORDecode_GetAndResetError(&DCtx) != QCBOR_ERR_UNEXPECTED_TYPE) {
+      return 10;
+   }
+
+   QCBORDecode_Init(&DCtx,
+                    UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spBooleansInMapNWF),
+                    0);
+   QCBORDecode_EnterMap(&DCtx, NULL);
+   QCBORDecode_GetUndefined(&DCtx);
+   if(QCBORDecode_GetAndResetError(&DCtx) != QCBOR_ERR_HIT_END) {
+      return 11;
+   }
+
+   QCBORDecode_Init(&DCtx,
+                    UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spUndefinedInMap),
+                    0);
+   QCBORDecode_EnterMap(&DCtx, NULL);
+   QCBORDecode_GetUndefined(&DCtx);
+   if(QCBORDecode_GetAndResetError(&DCtx)) {
+      return 12;
+   }
+
+   QCBORDecode_Init(&DCtx,
+                    UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spBooleansInMap),
+                    0);
+   QCBORDecode_EnterMap(&DCtx, NULL);
+   QCBORDecode_GetUndefined(&DCtx);
+   if(QCBORDecode_GetAndResetError(&DCtx) != QCBOR_ERR_UNEXPECTED_TYPE) {
+      return 13;
+   }
+
+   QCBORDecode_Init(&DCtx,
+                    UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spUndefinedInMap),
+                    0);
+   QCBORDecode_EnterMap(&DCtx, NULL);
+   QCBORDecode_GetUndefinedInMapN(&DCtx, 8);
+   if(QCBORDecode_GetAndResetError(&DCtx)) {
+      return 14;
+   }
+
+   QCBORDecode_Init(&DCtx,
+                    UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spBooleansInMap),
+                    0);
+   QCBORDecode_EnterMap(&DCtx, NULL);
+   QCBORDecode_GetUndefinedInMapN(&DCtx, 8);
+   if(QCBORDecode_GetAndResetError(&DCtx) != QCBOR_ERR_UNEXPECTED_TYPE) {
+      return 15;
+   }
+
+   QCBORDecode_Init(&DCtx,
+                    UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spBooleansInMapNWF),
+                    0);
+   QCBORDecode_EnterMap(&DCtx, NULL);
+   QCBORDecode_GetUndefined(&DCtx);
+   if(QCBORDecode_GetAndResetError(&DCtx) != QCBOR_ERR_HIT_END) {
+      return 15;
+   }
+
    return 0;
 }