GetNext error handling and a few other fixes
diff --git a/inc/qcbor/qcbor_common.h b/inc/qcbor/qcbor_common.h
index 7a7a132..20dff0e 100644
--- a/inc/qcbor/qcbor_common.h
+++ b/inc/qcbor/qcbor_common.h
@@ -34,6 +34,21 @@
#ifndef qcbor_common_h
#define qcbor_common_h
+
+/**
+ This define indicates a version of QCBOR that supports spiffy decode,
+ the decode functions found in qcbor_spiffy_decode.h.
+
+ Versions of QCBOR that support spiffy decode are backwards compatible
+ with previous versions, but there are a few minor exceptions such as
+ some aspects of tag handling that are different. This define can be
+ used handle these variances.
+*/
+#define QCBOR_SPIFFY_DECODE
+
+
+
+
/* Standard CBOR Major type for positive integers of various lengths */
#define CBOR_MAJOR_TYPE_POSITIVE_INT 0
@@ -408,6 +423,9 @@
/**
+ This is deprecated. See QCBORDecode_GetNthTag() and QCBORDecode_GetNthTagOfLast()
+ for tag handling.
+
The maximum number of tags that can be in @ref QCBORTagListIn and passed to
QCBORDecode_SetCallerConfiguredTagList()
*/
diff --git a/inc/qcbor/qcbor_decode.h b/inc/qcbor/qcbor_decode.h
index c440cd2..be6697a 100644
--- a/inc/qcbor/qcbor_decode.h
+++ b/inc/qcbor/qcbor_decode.h
@@ -829,7 +829,7 @@
map or array has been encountered. This works the same for both
definite and indefinite-length arrays.
- TODO: revise this documentation
+ TODO: revise this documentation on tags. It is wrong.
This decoder support CBOR type 6 tagging. The decoding of particular
given tag value may be supported in one of three different ways.
@@ -893,10 +893,26 @@
- There are a few CBOR constructs that are not handled without some
extra configuration. These are indefinite length strings and maps
with labels that are not strings or integers. See QCBORDecode_Init().
+
+ This does not set the internal error code or cease to function when
+ it is set. The error returned must always be checked. See also
+ QCBORDecode_VGetNext().
*/
QCBORError QCBORDecode_GetNext(QCBORDecodeContext *pCtx, QCBORItem *pDecodedItem);
+/**
+ @brief QCBORDecode_GetNext() using internal error state error handling.
+
+ @param[in] pCtx The decoder context.
+ @param[out] pDecodedItem Holds the CBOR item just decoded.
+
+ This is the same as QCBORDecode_GetNext() but uses the error handling
+ method of spiffy decode where an internal error is set instead
+ of returning an error. If the internal error is set, this doesn't
+ do anything.
+*/
+void QCBORDecode_VGetNext(QCBORDecodeContext *pCtx, QCBORItem *pDecodedItem);
/**
@@ -1037,16 +1053,24 @@
@brief Get the decoding error.
@param[in] pCtx The decoder context.
- @returns The decoding error.
+ @return The decoding error.
- All decoding functions except GetNext() do not return an error.
- Instead they set an internal error state. Once an error has
- occured, no further decoding will be performed even if further
- decoding functions are called.
+ All decoding functions set a saved internal error when they fail.
+ Most decoding functions do not return an error. To know the error,
+ this function must be called.
- The error will be returned when QCBORDecode_Finish() finish is
- called. This can make call sequence for decoding a given
- CBOR protocol very clean and simple in many cases.
+ The intended use is that decoding functions can be called one
+ after another with no regard to error until either the whole
+ decode is finished or some data returned from the decode must
+ be referenced. This makes implementations of protocols much
+ cleaner and prettier. For simple protocols the only error check
+ may be the return code from QCBORDecode_Finish().
+
+ Once a decoding error has occured most decode functions will do
+ nothing if called. QCBORDecode_GetNext() is an exception.
+
+ The implementation of this is just an inline accessor function
+ so its use adds very little object code.
Note that no reference to the decoded data should be made until
after QCBORDecode_Finish() is called as it will not be valid
@@ -1066,6 +1090,7 @@
*/
static QCBORError QCBORDecode_GetError(QCBORDecodeContext *pCtx);
+
/**
@brief Get and reset the decoding error.
diff --git a/src/qcbor_decode.c b/src/qcbor_decode.c
index 6450203..6c66f1e 100644
--- a/src/qcbor_decode.c
+++ b/src/qcbor_decode.c
@@ -1879,7 +1879,7 @@
nReturn = QCBOR_ERR_BAD_EXP_AND_MANTISSA;
goto Done;
}
- pDecodedItem->uNextNestLevel = mantissaItem.uNextNestLevel; // TODO: make sure this is right
+ pDecodedItem->uNextNestLevel = mantissaItem.uNextNestLevel;
Done:
return nReturn;
@@ -2087,6 +2087,19 @@
/*
Public function, see header qcbor/qcbor_decode.h file
*/
+void QCBORDecode_VGetNext(QCBORDecodeContext *pMe, QCBORItem *pDecodedItem)
+{
+ if(pMe->uLastError != QCBOR_SUCCESS) {
+ return;
+ }
+
+ pMe->uLastError = (uint8_t)QCBORDecode_GetNext(pMe, pDecodedItem);
+}
+
+
+/*
+ Public function, see header qcbor/qcbor_decode.h file
+ */
QCBORError
QCBORDecode_GetNextWithTags(QCBORDecodeContext *me,
QCBORItem *pDecodedItem,
@@ -2814,40 +2827,14 @@
/**
@param[in] TagSpec Specification for matching tags.
- @param[in] uDataType A QCBOR data type
+ @param[in] pItem The item to check.
@retval QCBOR_SUCCESS \c uDataType is allowed by @c TagSpec
@retval QCBOR_ERR_UNEXPECTED_TYPE \c uDataType is not allowed by @c TagSpec
The data type must be one of the QCBOR_TYPEs, not the IETF CBOR Registered tag value.
*/
-static QCBORError CheckTagRequirement(const TagSpecification TagSpec, uint8_t uDataType)
-{
- if((TagSpec.uTagRequirement & (~QCBOR_TAG_REQUIREMENT_ALLOW_ADDITIONAL_TAGS)) == QCBOR_TAG_REQUIREMENT_TAG) {
- // Must match the tag and only the tag
- return CheckTypeList(uDataType, TagSpec.uTaggedTypes);
- }
-
- QCBORError uReturn = CheckTypeList(uDataType, TagSpec.uAllowedContentTypes);
- if(uReturn == QCBOR_SUCCESS) {
- return QCBOR_SUCCESS;
- }
-
- if((TagSpec.uTagRequirement & (~QCBOR_TAG_REQUIREMENT_ALLOW_ADDITIONAL_TAGS)) == QCBOR_TAG_REQUIREMENT_NOT_A_TAG) {
- /* Must match the content type and only the content type.
- There was no match just above so it is a fail. */
- return QCBOR_ERR_UNEXPECTED_TYPE;
- }
-
- /* If here it can match either the tag or the content
- and it hasn't matched the content, so the end
- result is whether it matches the tag. This is
- also the case that the CBOR standard discourages. */
-
- return CheckTypeList(uDataType, TagSpec.uTaggedTypes);
-}
-
-static QCBORError CheckTagRequirement2(const TagSpecification TagSpec, const QCBORItem *pItem)
+static QCBORError CheckTagRequirement(const TagSpecification TagSpec, const QCBORItem *pItem)
{
if(!(TagSpec.uTagRequirement & QCBOR_TAG_REQUIREMENT_ALLOW_ADDITIONAL_TAGS) &&
pItem->uTags[0] != CBOR_TAG_INVALID16) {
@@ -2883,136 +2870,7 @@
return CheckTypeList(nItemType, TagSpec.uTaggedTypes);
}
-#if 0
-/**
- @param[in] TagSpec Specification for matching tags.
- @param[in] uDataType A QCBOR data type
- @retval QCBOR_SUCCESS \c uDataType is allowed by @c TagSpec
- @retval QCBOR_ERR_UNEXPECTED_TYPE \c uDataType is not allowed by @c TagSpec
-
- The data type must be one of the QCBOR_TYPEs, not the IETF CBOR Registered tag value.
- */
-static QCBORError CheckTagRequirement2(const TagSpecification TagSpec, uint8_t uDataType)
-{
- const uint16_t *pTags;
-/*
-For all the tag-specific accessor methods supported, GetNext will
- process then automatically when encountered during decoding.
- The will have a QCBOR_TYPE and a representation in QCBORItem.
- There are no expections to this (so far).
-
- That means the tag list in the QCBORItem will never have
- these tags in it. The tags in that list never need to
- be examined here.
-
-
-
-1 Tag list must be empty
-
-2 and 5, first tag must be in expected list
-
-4
-
-3 tag list must be empty or one in the expected list
-
-6, if first tag is expected, consume it, pass the rest on
-
-
- */
-
- /*
- First thing to understand is that GetNext will have processed
- the tags this code knows about. They will not be in the unprocessed
- tags list and the dataType will be of the processed CBOR.
- */
-
- const bool bUnprocessedTagsEmpty = pTags[0] != CBOR_TAG_INVALID16;
-
- const bool bDataTypeMatchesRequestedTaggedType = !CheckTypeList(uDataType, TagSpec.uTaggedTypes);
-
- const bool bDataTypeMatchesRequestedContentType = !CheckTypeList(uDataType, TagSpec.uAllowedContentTypes);
-
-
- if(TagSpec.uTagRequirement == 1) {
- /* There should be no tags at all, so the unprocessed tag
- list should be empty.
-
- The content has to match the expected content. If
- there was a tag that was interpreted, the content
- wouldn't match.
-
- */
- if(!bUnprocessedTagsEmpty) {
- return QCBOR_ERR_UNEXPECTED_TYPE;
- } else {
- if(!bDataTypeMatchesRequestedContentType) {
- return QCBOR_ERR_UNEXPECTED_TYPE;
- } else {
- return QCBOR_SUCCESS;
- }
-
- }
-
-
-
- } else if(TagSpec.uTagRequirement == 2) {
- /* The tag was present so GetNext will have interpreted it by now.
- The data type should be one of the requested types
- and there should be no other tags. */
-
- if(!bUnprocessedTagsEmpty) {
- return QCBOR_ERR_UNEXPECTED_TYPE;
- } else {
- if(!bDataTypeMatchesRequestedTaggedType) {
- return QCBOR_ERR_UNEXPECTED_TYPE;
- } else {
- return QCBOR_SUCCESS;
- }
- }
- } else if(TagSpec.uTagRequirement == 3) {
- if(!bUnprocessedTagsEmpty) {
- return QCBOR_ERR_UNEXPECTED_TYPE;
- } else {
- if(bDataTypeMatchesRequestedTaggedType || bDataTypeMatchesRequestedContentType) {
- return true;
- } else {
- return false;
- }
- }
- } else if(TagSpec.uTagRequirement == 4) {
-
-
-
-
-
-
-
- if(TagSpec.uTagRequirement == QCBOR_TAG_REQUIREMENT_TAG) {
- // Must match the tag and only the tag
- return CheckTypeList(uDataType, TagSpec.uTaggedTypes);
- }
-
- QCBORError uReturn = CheckTypeList(uDataType, TagSpec.uAllowedContentTypes);
- if(uReturn == QCBOR_SUCCESS) {
- return QCBOR_SUCCESS;
- }
-
- if(TagSpec.uTagRequirement == QCBOR_TAG_REQUIREMENT_NOT_A_TAG) {
- /* Must match the content type and only the content type.
- There was no match just above so it is a fail. */
- return QCBOR_ERR_UNEXPECTED_TYPE;
- }
-
- /* If here it can match either the tag or the content
- and it hasn't matched the content, so the end
- result is whether it matches the tag. This is
- also the case that the CBOR standard discourages. */
-
- return CheckTypeList(uDataType, TagSpec.uTaggedTypes);
-}
-
-#endif
// Semi-private
@@ -3027,7 +2885,7 @@
return;
}
- pMe->uLastError = (uint8_t)CheckTagRequirement2(TagSpec, pItem);
+ pMe->uLastError = (uint8_t)CheckTagRequirement(TagSpec, pItem);
}
// Semi-private
@@ -3041,7 +2899,7 @@
return;
}
- pMe->uLastError = (uint8_t)CheckTagRequirement(TagSpec, pItem->uDataType);
+ pMe->uLastError = (uint8_t)CheckTagRequirement(TagSpec, pItem);
}
// Semi-private
@@ -3359,7 +3217,7 @@
{QCBOR_TYPE_BYTE_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
};
- uError = CheckTagRequirement(TagSpec, pItem->uDataType);
+ uError = CheckTagRequirement(TagSpec, pItem);
if(uError != QCBOR_SUCCESS) {
goto Done;
}
@@ -3652,7 +3510,7 @@
// TODO: this will give an unexpected type error instead of
// overflow error for QCBOR_TYPE_UINT64 because TagSpec
// only has three target types.
- uErr = CheckTagRequirement2(TagSpec, pItem);
+ uErr = CheckTagRequirement(TagSpec, pItem);
if(uErr != QCBOR_SUCCESS) {
goto Done;
}
@@ -3735,7 +3593,7 @@
return;
}
- pMe->uLastError = (uint8_t)CheckTagRequirement(TagSpec, Item.uDataType);
+ pMe->uLastError = (uint8_t)CheckTagRequirement(TagSpec, &Item);
if(pMe->uLastError == QCBOR_SUCCESS) {
*pBstr = Item.val.string;
@@ -3759,7 +3617,7 @@
{QCBOR_TYPE_BYTE_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
};
- QCBORError uErr = CheckTagRequirement(TagSpec, pItem->uDataType);
+ QCBORError uErr = CheckTagRequirement(TagSpec, pItem);
if(uErr != QCBOR_SUCCESS) {
return uErr;
}
@@ -3861,13 +3719,13 @@
QCBORError uReturn;
- if(CheckTagRequirement(TagSpecText, pItem->uDataType) == QCBOR_SUCCESS) {
+ if(CheckTagRequirement(TagSpecText, pItem) == QCBOR_SUCCESS) {
*pMessage = pItem->val.string;
if(pbIsNot7Bit != NULL) {
*pbIsNot7Bit = false;
}
uReturn = QCBOR_SUCCESS;
- } else if(CheckTagRequirement(TagSpecBinary, pItem->uDataType) == QCBOR_SUCCESS) {
+ } else if(CheckTagRequirement(TagSpecBinary, pItem) == QCBOR_SUCCESS) {
*pMessage = pItem->val.string;
if(pbIsNot7Bit != NULL) {
*pbIsNot7Bit = true;
@@ -5076,7 +4934,7 @@
QCBORError uErr;
// Loops runs at most 1.5 times. Making it a loop saves object code.
while(1) {
- uErr = CheckTagRequirement(TagSpec, pItem->uDataType);
+ uErr = CheckTagRequirement(TagSpec, pItem);
if(uErr != QCBOR_SUCCESS) {
goto Done;
}
diff --git a/test/qcbor_decode_tests.c b/test/qcbor_decode_tests.c
index bd10acc..a3798fe 100644
--- a/test/qcbor_decode_tests.c
+++ b/test/qcbor_decode_tests.c
@@ -4864,8 +4864,8 @@
QCBORDecode_Init(&DCtx, ValidEncodedMap, 0);
- QCBORDecode_GetNext(&DCtx, &Item1);
- QCBORDecode_GetNext(&DCtx, &Item1);
+ QCBORDecode_VGetNext(&DCtx, &Item1);
+ QCBORDecode_VGetNext(&DCtx, &Item1);
QCBORDecode_EnterArray(&DCtx);
QCBORDecode_GetNext(&DCtx, &Item1);
if(Item1.uDataType != QCBOR_TYPE_TEXT_STRING) {