Merge branch 'AdvancedDecode' of https://github.com/laurencelundblade/QCBOR into AdvancedDecode
diff --git a/QCBOR.xcodeproj/project.pbxproj b/QCBOR.xcodeproj/project.pbxproj
index a720857..ad20feb 100644
--- a/QCBOR.xcodeproj/project.pbxproj
+++ b/QCBOR.xcodeproj/project.pbxproj
@@ -69,6 +69,7 @@
E73B57632161F8F70080D658 /* run_tests.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.c; name = run_tests.c; path = test/run_tests.c; sourceTree = "<group>"; tabWidth = 3; };
E73B57642161F8F80080D658 /* run_tests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = run_tests.h; path = test/run_tests.h; sourceTree = "<group>"; };
E74BF411245D6713002CE8E8 /* UsefulBuf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UsefulBuf.h; path = inc/qcbor/UsefulBuf.h; sourceTree = "<group>"; };
+ E74FA9FE247D2F2C003F8ECE /* Tagging.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = Tagging.md; sourceTree = "<group>"; };
E772022723B52C02006E966E /* QCBOR_Disable_Exp_Mantissa */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = QCBOR_Disable_Exp_Mantissa; sourceTree = BUILT_PRODUCTS_DIR; };
E776E07C214ADF7F00E67947 /* QCBOR */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = QCBOR; sourceTree = BUILT_PRODUCTS_DIR; };
E776E08C214AE07400E67947 /* qcbor_encode.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.c; name = qcbor_encode.c; path = src/qcbor_encode.c; sourceTree = "<group>"; tabWidth = 3; };
@@ -106,6 +107,7 @@
isa = PBXGroup;
children = (
E776E161214EE19C00E67947 /* README.md */,
+ E74FA9FE247D2F2C003F8ECE /* Tagging.md */,
E776E096214AE0C700E67947 /* cmd_line_main.c */,
E776E092214AE07C00E67947 /* inc */,
E776E08B214AE06600E67947 /* src */,
diff --git a/README.md b/README.md
index 1f8a646..334dff3 100644
--- a/README.md
+++ b/README.md
@@ -121,7 +121,7 @@
## Code Size
-These are approximate sizes on 64-bit x86 with the -Os optimization.
+These are approximate sizes on a 64-bit x86 CPU with the -Os optimization.
| | smallest | largest |
|---------------|----------|---------|
diff --git a/Tagging.md b/Tagging.md
new file mode 100644
index 0000000..c3481f5
--- /dev/null
+++ b/Tagging.md
@@ -0,0 +1,102 @@
+# Types and Tagging in CBOR
+
+## New Types
+
+CBOR provides a means for defining new data types that are either
+aggregates of the primitive types or the association of further sematics
+to a primitive type.
+
+An aggregate is similar to a C structure. A bigfloat is an example. It
+is an array of two data items, an exponent and a mantissa.
+
+An example of association of further semantics to a primitive type
+is an epoch date, where the new data type is
+a primitive integer that is to be interpreted as a date.
+
+## Explicit Tags
+
+These new types can be explicitly tagged by preceding them
+with a CBOR Item of major type 6. The tag data item is a positive
+integer.
+
+For example the epoch date looks lie this:
+
+
+A big float looks like this:
+
+
+The data item tagged is known as the tag content. Most tags
+require the content to be of a specific type or types. A few work
+with content of any type.
+
+There may be more than one explicit tag for a single tag content. When
+this is done, they nest. The order of the explicit tags is significant. The explicit
+tag closes to the content is applied first. That then becomes the
+content for the next closest tag.
+
+If the content for a specific tag is not of the right type then
+the encoded CBOR is invalid.
+
+The explicit tag data item is not always required when the data type is used. In some situations
+in some CBOR protocols, they may actually be prohibited.
+
+## Standard Tags and the Tags Registry
+
+Tags used in CBOR protocols should at least be registered in
+the IANA CBOR Tags Registry. A small number of tags (0-23),
+are full IETF standards. Further, tags 24-255 require published
+documentation, but are not full IETF standards. Beyond
+tag 255, the tags are first come first served.
+
+There is no range for private use, so any tag used in a
+CBOR protocol should be registered. The range of tag
+values is very large to accommodate this.
+
+It is common to use data types from the registry in a CBOR protocol
+without the explicit tag, so in a way the registry is a registry
+of data types.
+
+## When Explicit Tags are Required
+
+In many CBOR protocols, the new type of a data item
+can be known implicitly without any explicit type. In that
+case the explicit tag is redundant. For example,
+if a data item in a map is labled the "expiration date",
+it can be inferred that the type is a date.
+
+All CBOR protocols that use registered data types
+should explicitly say for each occurance whether
+the explicit tag is required or not. If they say it is required,
+it must always be present and it is a protocol decoding
+error if not. Usually the tag is explicitly required because
+it is not possible to infer the type from the context
+of the protocol.
+
+If the protocol says the explicit tag is not required, it
+is a decoding error if it is present.
+
+That is tags are not optional in a protocol (even though they
+were called "optional tags" in RFC 7049).
+
+Part of the result of this is that unknown tags generally
+can't be ignored during decoding. They are not like
+email or HTTP headers.
+
+The QCBOR encoding API for standard registered types
+has an option to include the tag or not. Setting this
+flag depends on the protocol definition and should only
+be true if the protocol requires explicit tagging.
+
+The QCBOR decoding APIs for standard registered types
+has a tag requirements flag. If true it requires the tag
+to be present and sets an error if it is absent. If false
+an error is set if it is present.
+
+During decoding, it will sometimes be necessary to
+peek-decode the data item with the generic PeekNext()
+first to know its type, then call the appropriate GetXxxx(0
+to actually dcode and consume it. When this is necessary
+depends on the design and flow of the protocol.
+
+
+
diff --git a/inc/qcbor/qcbor_decode.h b/inc/qcbor/qcbor_decode.h
index 87b0fb6..a4a96cc 100644
--- a/inc/qcbor/qcbor_decode.h
+++ b/inc/qcbor/qcbor_decode.h
@@ -357,6 +357,41 @@
uint16_t uTags[QCBOR_MAX_TAGS_PER_ITEM];
+ /*
+
+ 0-127, the tag value itself
+ 128-255, index into tag map
+
+ The decode context stores 4 tags (32 bytes)
+
+ Could
+ - allocate space in decode context for new tags encountered, say 32 bytes worth for
+ four extra tags.
+ - require caller to pass in list of tags they are interested in, up to 128 even
+ This already exists. It is an error when an unknown tag is enountered.
+ - allow caller to give space to store tags if using tags > 128 in value
+ It is only an error if more distinct tag values are encountered
+ than there is space to remember them.
+ - Use storage alloator to expand space needed.
+
+
+ */
+ uint8_t auTags[4]; // 4 bytes
+
+ /*
+ Or use the existing tag mapping strategy, and
+ store the offset in 4-bits accommomdating use
+ of 64 tag values in a decode session and using
+ only 2 bytes to store the tag list.
+
+ Can elimiate getNextWithTags.
+
+ Add new function to get the tag value.
+
+ Is annoying to find tag value on error with
+ existing scheme.
+ */
+
} QCBORItem;
@@ -1286,6 +1321,11 @@
static void QCBORDecode_ExitArray(QCBORDecodeContext *pCtx);
+void QCBORDecode_EnterBstrWrapped(QCBORDecodeContext *pCtx, uint8_t uTagRequirement);
+void QCBORDecode_EnterBstrWrappedFromMapN(QCBORDecodeContext *pCtx, uint8_t uTagRequirement, int64_t uLabel);
+void QCBORDecode_EnterBstrWrappedFromMapSZ(QCBORDecodeContext *pCtx, uint8_t uTagRequirement, const char *szLabel);
+void QCBORDecode_ExitBstrWrapped(QCBORDecodeContext *pCtx);
+
/*
Restarts fetching of items in a map to the start of the
@@ -1302,6 +1342,28 @@
/*
+ @brief Get an item in map by label.
+
+ @param[in] pCtx The decode context.
+ @param[in] nLabel The integer label.
+ @param[in] uQcborType The QCBOR type. One of QCBOR_TYPE_XXX.
+ @param[out] pItem The returned item
+
+ A map must have been entered to use this. If not \ref xxx is set.
+
+ The map is searched for an item of the requested label and type.
+ QCBOR_TYPE_ANY can be given to search for the label without
+ matching the type.
+
+ This will always search the entire map. This will always perform
+ duplicate label detection, setting \ref QCBOR_ERR_DUPLICATE_LABEL if there is more than
+ one occurance of the label being searched for.
+
+ This performs a full decode of every item in the map
+ being searched, which involves a full pre-order traversal
+ of every item. For maps with little nesting, this
+ is of little consequence, but
+
Get an item out of a map.
Decoding must be in map mode for this to work.
@@ -1320,35 +1382,41 @@
*/
void QCBORDecode_GetItemInMapN(QCBORDecodeContext *pCtx,
int64_t nLabel,
- uint8_t qcbor_type,
+ uint8_t uQcborType,
QCBORItem *pItem);
-
-QCBORError QCBORDecode_GetItemInMapSZ(QCBORDecodeContext *pCtx,
- const char *szLabel,
- uint8_t qcbor_type,
- QCBORItem *pItem);
+void QCBORDecode_GetItemInMapSZ(QCBORDecodeContext *pCtx,
+ const char *szLabel,
+ uint8_t qcbor_type,
+ QCBORItem *pItem);
/*
+
+ @param[in] pCtx The decode context.
+ @param[in,out] pItemList On input the items to search for. On output the returned items.
+
This gets several labeled items out of a map.
- pItemArray is an array of items terminated by an item
+ pItemList is an array of items terminated by an item
with uLabelType QCBOR_TYPE_NONE.
-
- On input the the array of items is the list of labels to fetch
- items for.
-
- On output the array is the data items found. If the label
- wasn't found, uDataType is QCBOR_TYPE_NONE.
-
+
+ On input the labels to search for are in the uLabelType and
+ label fields in the items in pItemList.
+
+ Also on input are the requested QCBOR types in the field uDataType.
+ To match any type, searching just by lable, uDataType
+ can be QCBOR_TYPE_ANY.
+
This is a CPU-efficient way to decode a bunch of items in a map. It
is more efficient than scanning each individually because the map
only needs to be traversed once.
- If any duplicate labels are detected, this returns an error.
+ If any duplicate labels are detected, this returns \ref QCBOR_ERR_DUPLICATE_LABEL.
This will return maps and arrays that are in the map, but
- provides no way to descend into and decode them.
+ provides no way to descend into and decode them. Use
+ QCBORDecode_EnterMapinMapN(), QCBORDecode_EnterArrayInMapN()
+ and such to decsend into and process maps and arrays.
*/
QCBORError QCBORDecode_GetItemsInMap(QCBORDecodeContext *pCtx, QCBORItem *pItemList);
diff --git a/src/qcbor_decode.c b/src/qcbor_decode.c
index 749327b..3d7fd4b 100644
--- a/src/qcbor_decode.c
+++ b/src/qcbor_decode.c
@@ -2124,11 +2124,16 @@
}
-QCBORError QCBORDecode_GetItemInMapSZ(QCBORDecodeContext *pMe,
- const char *szLabel,
- uint8_t uQcborType,
- QCBORItem *pItem)
+void QCBORDecode_GetItemInMapSZ(QCBORDecodeContext *pMe,
+ const char *szLabel,
+ uint8_t uQcborType,
+ QCBORItem *pItem)
{
+ if(pMe->uLastError != QCBOR_SUCCESS) {
+ return;
+ }
+
+
QCBORItem OneItemSeach[2];
OneItemSeach[0].uLabelType = QCBOR_TYPE_TEXT_STRING;
@@ -2138,16 +2143,15 @@
QCBORError nReturn = MapSearch(pMe, OneItemSeach, NULL, NULL);
if(nReturn) {
- return nReturn;
+ pMe->uLastError = (uint8_t)nReturn;
}
+
if(OneItemSeach[0].uDataType == QCBOR_TYPE_NONE) {
- return QCBOR_ERR_NOT_FOUND;
+ pMe->uLastError = QCBOR_ERR_NOT_FOUND;
}
*pItem = OneItemSeach[0];
-
- return QCBOR_SUCCESS;
}
@@ -2696,12 +2700,22 @@
static inline QCBORError ConvertNegativeBigNumToSigned(const UsefulBufC BigNum, int64_t *pResult)
{
uint64_t uResult;
- QCBORError uError = ConvertBigNumToUnsigned(BigNum, INT64_MAX-1, &uResult);
+ /* negaative int furthest from zero is INT64_MIN
+ which is expressed as -INT64_MAX-1. The value of
+ a negative bignum is -n-1, one further from zero
+ than the positive bignum */
+
+ /* say INT64_MIN is -2; then INT64_MAX is 1.
+ Then -n-1 <= INT64_MIN.
+ Then -n -1 <= -INT64_MAX - 1
+ THen n <= INT64_MAX. */
+ QCBORError uError = ConvertBigNumToUnsigned(BigNum, INT64_MAX, &uResult);
if(uError) {
return uError;
}
/* Cast is safe because ConvertBigNum is told to limit to INT64_MAX */
// TODO: this code is incorrect. See RFC 7049
+ uResult++; // this is the -1 in -n-1
*pResult = -(int64_t)uResult;
return QCBOR_SUCCESS;
}
@@ -3444,7 +3458,7 @@
case QCBOR_TYPE_NEGBIGNUM:
if(uOptions & QCBOR_CONVERT_TYPE_BIG_NUM) {
- *pdValue = -ConvertBigNumToDouble(pItem->val.bigNum);
+ *pdValue = -1-ConvertBigNumToDouble(pItem->val.bigNum);
} else {
return QCBOR_ERR_CONVERSION_NOT_REQUESTED;
}
@@ -3479,7 +3493,7 @@
case QCBOR_TYPE_BIGFLOAT_NEG_BIGNUM:
if(uOptions & QCBOR_CONVERT_TYPE_BIGFLOAT) {
- double dMantissa = -ConvertBigNumToDouble(pItem->val.expAndMantissa.Mantissa.bigNum);
+ double dMantissa = -1-ConvertBigNumToDouble(pItem->val.expAndMantissa.Mantissa.bigNum);
*pdValue = dMantissa * exp2((double)pItem->val.expAndMantissa.nExponent);
} else {
return QCBOR_ERR_CONVERSION_NOT_REQUESTED;
diff --git a/test/qcbor_decode_tests.c b/test/qcbor_decode_tests.c
index fca76e9..11e47f2 100644
--- a/test/qcbor_decode_tests.c
+++ b/test/qcbor_decode_tests.c
@@ -4011,6 +4011,16 @@
static const struct NumberConversion NumberConversions[] = {
{
+ "negative bignum -1",
+ {(uint8_t[]){0xc3, 0x41, 0x00}, 3},
+ -1,
+ QCBOR_SUCCESS,
+ 0,
+ QCBOR_ERR_NUMBER_SIGN_CONVERSION,
+ -1.0,
+ QCBOR_SUCCESS
+ },
+ {
"Decimal Fraction with positive bignum 257 * 10e3",
{(uint8_t[]){0xC4, 0x82, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
0xC2, 0x42, 0x01, 0x01}, 15},
@@ -4022,14 +4032,14 @@
QCBOR_SUCCESS
},
{
- "bigfloat with negative bignum -257 * 2e3",
+ "bigfloat with negative bignum -258 * 2e3",
{(uint8_t[]){0xC5, 0x82, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
0xC3, 0x42, 0x01, 0x01}, 15},
- -2056,
+ -2064,
QCBOR_SUCCESS,
0,
QCBOR_ERR_NUMBER_SIGN_CONVERSION,
- -2056.0,
+ -2064.0,
QCBOR_SUCCESS
},
{
@@ -4044,7 +4054,7 @@
QCBOR_SUCCESS
},
{
- "negative bignum 0xc349010000000000000000",
+ "negative bignum 0xc349010000000000000000 -18446744073709551617",
{(uint8_t[]){0xc3, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 11},
0,
QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW,