Merge branch 'master' into AdvancedDecode
diff --git a/QCBOR.xcodeproj/project.pbxproj b/QCBOR.xcodeproj/project.pbxproj
index baed191..a193355 100644
--- a/QCBOR.xcodeproj/project.pbxproj
+++ b/QCBOR.xcodeproj/project.pbxproj
@@ -74,13 +74,13 @@
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; };
E776E08D214AE07500E67947 /* UsefulBuf.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.c; name = UsefulBuf.c; path = src/UsefulBuf.c; sourceTree = "<group>"; tabWidth = 3; };
E776E08E214AE07500E67947 /* qcbor_decode.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.c; name = qcbor_decode.c; path = src/qcbor_decode.c; sourceTree = "<group>"; tabWidth = 3; };
- E776E093214AE08B00E67947 /* qcbor.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.h; name = qcbor.h; path = inc/qcbor.h; sourceTree = "<group>"; tabWidth = 3; };
E776E094214AE09700E67947 /* UsefulBuf.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.h; name = UsefulBuf.h; path = inc/UsefulBuf.h; sourceTree = "<group>"; tabWidth = 3; };
E776E096214AE0C700E67947 /* cmd_line_main.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_line_main.c; sourceTree = "<group>"; };
E776E161214EE19C00E67947 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
+ E788315B243BFDF0001893CD /* qcbor_decode_map.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = qcbor_decode_map.h; path = inc/qcbor/qcbor_decode_map.h; sourceTree = "<group>"; };
E78C91DE240C90C100F4CECE /* qcbor_decode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = qcbor_decode.h; path = inc/qcbor/qcbor_decode.h; sourceTree = "<group>"; };
E78C91DF240C90C100F4CECE /* qcbor_common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = qcbor_common.h; path = inc/qcbor/qcbor_common.h; sourceTree = "<group>"; };
- E78C91E0240C90C100F4CECE /* qcbor_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = qcbor_private.h; path = inc/qcbor/qcbor_private.h; sourceTree = "<group>"; };
+ E78C91E0240C90C100F4CECE /* qcbor_private.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.h; name = qcbor_private.h; path = inc/qcbor/qcbor_private.h; sourceTree = "<group>"; tabWidth = 3; };
E78C91E1240C90C100F4CECE /* qcbor_encode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = qcbor_encode.h; path = inc/qcbor/qcbor_encode.h; sourceTree = "<group>"; };
/* End PBXFileReference section */
@@ -143,8 +143,8 @@
E78C91DE240C90C100F4CECE /* qcbor_decode.h */,
E78C91E1240C90C100F4CECE /* qcbor_encode.h */,
E78C91E0240C90C100F4CECE /* qcbor_private.h */,
- E776E093214AE08B00E67947 /* qcbor.h */,
E776E094214AE09700E67947 /* UsefulBuf.h */,
+ E788315B243BFDF0001893CD /* qcbor_decode_map.h */,
);
name = inc;
sourceTree = "<group>";
diff --git a/inc/qcbor.h b/inc/qcbor.h
deleted file mode 100644
index 377bade..0000000
--- a/inc/qcbor.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*==============================================================================
- Copyright (c) 2016-2018, The Linux Foundation.
- Copyright (c) 2018-2020, Laurence Lundblade.
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are
- met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials provided
- with the distribution.
- * Neither the name of The Linux Foundation nor the names of its
- contributors, nor the name "Laurence Lundblade" may be used to
- endorse or promote products derived from this software without
- specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
- WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
- BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
- OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
- IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- =============================================================================*/
-
-/**
- * @file qcbor.h
- *
- * Backwards compatibility for includers of qcbor.h (which has been split
- * into four include files).
- */
-
-#include "qcbor/qcbor_encode.h"
-#include "qcbor/qcbor_decode.h"
diff --git a/inc/qcbor/qcbor_common.h b/inc/qcbor/qcbor_common.h
index aaea610..2cb2642 100644
--- a/inc/qcbor/qcbor_common.h
+++ b/inc/qcbor/qcbor_common.h
@@ -319,8 +319,34 @@
should be treated as such. The strange situation is a CPU with a very
small size_t (e.g., a 16-bit CPU) and a large string (e.g., > 65KB).
*/
- QCBOR_ERR_STRING_TOO_LONG = 24
+ QCBOR_ERR_STRING_TOO_LONG = 24,
+
+ /** When decodeing for a specific type, the type was not was expected.
+ See also \ref QCBOR_ERR_CONVERSION_NOT_REQUESTED which in many cases
+ is effectively the same error */
+ QCBOR_ERR_UNEXPECTED_TYPE = 25,
+
+ /** Duplicate label in map detected */
+ QCBOR_ERR_DUPLICATE_LABEL = 26,
+
+ /** Item with requested label is not found */
+ QCBOR_ERR_NOT_FOUND = 27,
+ /** Number conversion failed because of sign. For example a negative
+ int64_t can't be converted to a uint64_t */
+ QCBOR_ERR_NUMBER_SIGN_CONVERSION = 28,
+
+ /** A conversion is possible, but the option for it was not set. For
+ example conversion from a float to an int64_t without the XXX option. TODO: */
+ QCBOR_ERR_CONVERSION_NOT_REQUESTED = 29,
+
+ /** When converting a decoded number, the value is too large or to small
+ for the conversion target */
+ QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW = 30,
+
+ /** Trying to get an item by label when a map has not been entered. */
+ QCBOR_ERR_NOT_ENTERED = 31,
+
/* This is stored in uint8_t in places; never add values > 255 */
} QCBORError;
diff --git a/inc/qcbor/qcbor_decode.h b/inc/qcbor/qcbor_decode.h
index f799517..af1354e 100644
--- a/inc/qcbor/qcbor_decode.h
+++ b/inc/qcbor/qcbor_decode.h
@@ -49,9 +49,87 @@
/**
- @file qcbor_decode.h
+@file qcbor_decode.h
- This describes CBOR decoding.
+Q C B O R D e c o d e
+
+ This section just discusses decoding assuming familiarity with the general
+ description of this encoder / decoder in section XXX.
+
+ Encoded CBOR can be viewed to have a tree structure
+ where the lead nodes are non-aggregate types like
+ integers and strings and the intermediate nodes are
+ either arrays or maps. Fundamentally, all decoding
+ is a pre-order traversal of the tree. Calling
+ GetNext() repeatedly will perform this.
+
+ This pre-order traversal gives natural decoding of
+ arrays where the array members are taken
+ in order, but does not give natural decoding of
+ maps where access by label is usually preferred.
+ Using the EnterMap and GetByLabel methods,
+ map items can be accessed by label. EnterMap
+narrows decoding to a particular map. GetByLabel
+ allows decoding the item of a particular label in
+ the particular map. This can be used with nested
+ maps by calling EnterMapByLabel.
+
+ When EnterMap is called, pre-order traversal
+ continues to work. There is a cursor that is run
+ over the tree with calls to GetNext. This can be
+ intermixed with calls to GetByLabel. The pre-order
+ traversal is limited just to the map entered. Attempts
+ to GetNext beyond the end of the map will give
+ the HIT END error.
+
+ There is also EnterArray to decode arrays. It will
+ narrow the traversal to the extent of the array
+ entered.
+
+ GetByLabel supports duplicate label detection
+ and will result in an error if the map has
+ duplicate labels.
+
+ GetByLabel is implemented by performing the
+ pre-order traversal of the map to find the labeled
+ item everytime it is called. It doesn't build up
+ a hash table, a binary search tree or some other
+ efficiently searchable structure internally. For simple
+ trees this is fine and for high-speed CPUs this is
+ fine, but for complex trees on slow CPUs,
+ it may have performance issues (these have
+ not be quantified yet). One way ease this is
+ to use GetItems which allows decoding of
+ a list of items expected in an map in one
+ traveral.
+
+ Like encoding, decoding maintains an
+ internal error state. Once a call to the
+ decoder returns an error, this error state
+ is entered and subsequent decoder calls
+ do nothing. This allows for prettier and cleaner
+ decoding code. The only error check needed
+ is in the Finish call.
+
+ An easy and clean way to use this decoder
+ is to always use EnterMap and EnterArray
+ for each array or map. They will error
+ if the input CBOR is not the expected
+ array or map. Then use GetInt, GetString
+ to get the individual items of of the
+ maps and arrays making use of the
+ internal error tracking provided by this
+ decoder. The only error check needed
+ is the call to Finish.
+
+ In some CBOR protocols, the type of
+ a data item may be variable. Maybe even
+ the type of one data item is dependent
+ on another. In such designs, GetNext has
+ to be used and the internal error checking
+ can't be relied upon.
+
+
*/
/**
@@ -74,6 +152,9 @@
/* Do not renumber these. Code depends on some of these values. */
/** The data type is unknown, unset or invalid. */
#define QCBOR_TYPE_NONE 0
+// TODO: comment
+#define QCBOR_TYPE_ANY 1
+
/** Type for an integer that decoded either between @c INT64_MIN and
@c INT32_MIN or @c INT32_MAX and @c INT64_MAX. Data is in member
@c val.int64. */
@@ -176,8 +257,8 @@
/** Tells what element of the @c val union to use. One of @c
QCBOR_TYPE_XXXX */
uint8_t uDataType;
- /** How deep the nesting from arrays and maps are. 0 is the top
- level with no arrays or maps entered. */
+ /** How deep the nesting from arrays and maps is. 0 is the top
+ level with no arrays or maps entered. TODO: udpate this comment*/
uint8_t uNestingLevel;
/** Tells what element of the label union to use. */
uint8_t uLabelType;
@@ -186,8 +267,9 @@
uint8_t uDataAlloc;
/** Like @c uDataAlloc, but for label. */
uint8_t uLabelAlloc;
- /** If not equal to @c uNestingLevel, this item closed out at least
- one map/array */
+ /** If less than @c uNestingLevel, this item was the last one
+ in an arry or map and closed out at least
+ one nesting level */
uint8_t uNextNestLevel;
/** The union holding the item's value. Select union member based
@@ -273,6 +355,14 @@
+#define QCBOR_CONVERT_TYPE_INT64 0x01
+#define QCBOR_CONVERT_TYPE_UINT64 0x02
+#define QCBOR_CONVERT_TYPE_FLOAT 0x04
+#define QCBOR_CONVERT_TYPE_BIGFLOAT 0x08
+#define QCBOR_CONVERT_TYPE_DECIMAL_FRACTION 0x10
+#define QCBOR_CONVERT_TYPE_BIG_NUM 0x20
+
+
/**
@brief The type defining what a string allocator function must do.
@@ -851,6 +941,224 @@
QCBORError QCBORDecode_Finish(QCBORDecodeContext *pCtx);
+static QCBORError QCBORDecode_GetLastError(QCBORDecodeContext *pCtx);
+
+
+void QCBORDecode_GetInt64Convert(QCBORDecodeContext *pMe, uint32_t uOptions, int64_t *pValue);
+
+
+
+
+/*
+ Get the next item as an int64_t. The CBOR type can be unsigned, negative, float
+ a big float, a decimal fraction or a big num. Conversion will be dones as
+ expected. Some cases will error out with under or over flow.
+ */
+void QCBORDecode_GetInt64ConvertAll(QCBORDecodeContext *pMe, uint32_t uOptions, int64_t *pValue);
+
+
+
+void QCBORDecode_GetBytes(QCBORDecodeContext *pCtx, UsefulBufC *pValue);
+
+void QCBORDecode_GetText(QCBORDecodeContext *pCtx, UsefulBufC *pValue);
+
+void QCBORDecode_GetPosBignum(QCBORDecodeContext *pCtx, UsefulBufC *pValue);
+
+void QCBORDecode_GetNegBignum(QCBORDecodeContext *pCtx, UsefulBufC *pValue);
+
+
+
+
+/* Next item must be map or this generates an error.
+
+
+This puts the decoder in map mode which narrows
+decoding to the map entered and enables use of
+getting items by label.
+
+ Nested maps can be decoded like this by entering
+ each map in turn.
+
+ Call QCBORDecode_ExitMap() to exit the current map
+ decoding level. When all map decoding layers are exited
+ then map mode is fully exited.
+
+ While in map mode, GetNext works as usual on the
+ map and the standard in-order traversal cursor
+ is maintained. Attempts to get items off the end of the
+ map will give error XXX (rather going to the next
+ item after the map as it would when not in map
+ mode).
+
+ You can rewind the inorder traversal cursor to the
+ beginning of the map with RewindMap().
+
+ Exiting leaves the cursor at the
+ data item following the last entry in the map.
+
+ Entering and Exiting map mode consumes the whole
+ map and its contents as a GetNext after exiting
+ will return the item after the map. */
+QCBORError QCBORDecode_EnterMap(QCBORDecodeContext *pCtx);
+
+
+void QCBORDecode_ExitMap(QCBORDecodeContext *pCtx);
+
+/*
+ Indicate if decoding is in map mode more not.
+ */
+bool QCBORDecode_InMapMode(QCBORDecodeContext *pCtxt);
+
+
+/*
+ Restarts fetching of items in a map to the start of the
+ map. This is for GetNext. It has no effect on
+ GetByLabel (which always searches from the start).
+ */
+void QCBORDecode_RewindMap(QCBORDecodeContext *pCtxt);
+
+
+QCBORError QCBORDecode_EnterArray(QCBORDecodeContext *pCtx);
+
+
+void QCBORDecode_ExitArray(QCBORDecodeContext *pCtx);
+
+QCBORError QCBORDecode_EnterArrayFromMapN(QCBORDecodeContext *pMe, int64_t uLabel);
+
+
+QCBORError QCBORDecode_EnterArrayFromMapSZ(QCBORDecodeContext *pMe, const char *szLabel);
+
+
+//QCBORError QCBORDecode_EnterMapX(QCBORDecodeContext *pCtx, MapDecode *pMap);
+
+
+
+
+/*
+ Get an item out of a map.
+
+ Decoding must be in map mode for this to work.
+
+
+
+Seek to the beginning of the map.
+Consume items looking for the nLabel.
+Always go through the whole map and always look for duplicates.
+Return the item found, if no errors.
+
+Allow specification of type required.
+
+
+
+*/
+QCBORError QCBORDecode_GetItemInMap(QCBORDecodeContext *pCtx,
+ int64_t nLabel,
+ uint8_t qcbor_type,
+ QCBORItem *pItem);
+
+
+QCBORError QCBORDecode_GetItemInMapSZ(QCBORDecodeContext *pCtx,
+const char *szLabel,
+uint8_t qcbor_type,
+QCBORItem *pItem);
+
+/*
+ This gets several labeled items out of a map.
+
+ pItemArray 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.
+
+ 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.
+
+ This will return maps and arrays that are in the map, but
+ provides no way to descend into and decode them.
+
+ */
+QCBORError QCBORDecode_GetItemsInMap(QCBORDecodeContext *pCtx, QCBORItem *pItemList);
+
+
+
+QCBORError QCBORDecode_GetIntInMap(QCBORDecodeContext *pCtx, int64_t nLabel, int64_t *pInt);
+
+void QCBORDecode_GetIntInMapSZ(QCBORDecodeContext *pCtx, const char *szLabel, int64_t *pInt);
+
+
+void QCBORDecode_GetBstrInMapSZ(QCBORDecodeContext *pCtx, const char *szLabel, UsefulBufC *pBstr);
+
+void QCBORDecode_GetTextInMapSZ(QCBORDecodeContext *pCtx, const char *szLabel, UsefulBufC *pBstr);
+
+
+/*
+ Find a map in a map by integer label and enter it.
+
+ This will do duplicate detection on the particular label.
+
+ Call QCBORDecode_ExitMap() to return to the mode / level
+ from before this was called.
+
+ Seek to to the beginning of the map.
+ Consume items looking for nLabel
+ */
+QCBORError QCBORDecode_EnterMapFromMap(QCBORDecodeContext *pCtx, int64_t nLabel);
+
+QCBORError QCBORDecode_EnterMapFromMapSZ(QCBORDecodeContext *pCtx, const char *szLabel);
+
+
+
+
+/*
+ Normally decoding is just in-order traversal. You can get next
+ of any type, get next of a particular type including conversions.
+
+ If the cursor is at a map and you enter it, then you can use
+ methods that Get things by label, either numeric or string.
+
+ These methods work only at the particular level in the map.
+ To go into a map nested in a map call the special method
+ to enter a map by label.
+
+ When in a map, the GetNext methods work too, but only
+ to the end of the map. You can't traverse off the end of the
+ map.
+
+ You can rewind to the start of the map and traverse it again
+ with the MapRestart method.
+
+ The exit map method will leave the traversal cursor at the first itme after
+ the map.
+
+
+ The beginning of each map must be recorded so the scan can be done
+ through the whole map.
+
+ A bit per level to indicate in map mode for that level so
+ it is clear what GetNext at end does and what happens on MapExit
+ and where to set the cursor.
+
+
+
+
+
+
+
+
+
+
+
+ */
+
+
+
/**
@@ -966,6 +1274,16 @@
return 0;
}
+
+
+static inline QCBORError QCBORDecode_GetLastError(QCBORDecodeContext *pMe)
+{
+ return pMe->uLastError;
+}
+
+
+
+
#ifdef __cplusplus
}
#endif
diff --git a/inc/qcbor/qcbor_decode_map.h b/inc/qcbor/qcbor_decode_map.h
new file mode 100644
index 0000000..ec6c43b
--- /dev/null
+++ b/inc/qcbor/qcbor_decode_map.h
@@ -0,0 +1,209 @@
+#if 0
+//
+// qcbor_decode_map.h
+// QCBOR
+//
+// Created by Laurence Lundblade on 4/6/20.
+// Copyright © 2020 Laurence Lundblade. All rights reserved.
+//
+
+#ifndef qcbor_decode_map_h
+#define qcbor_decode_map_h
+
+
+#include "qcbor_decode.h"
+
+
+
+
+/* Next item must be map or this generates an error.
+
+
+This puts the decoder in map mode which narrows
+decoding to the map entered and enables use of
+getting items by label.
+
+ Nested maps can be decoded like this by entering
+ each map in turn.
+
+ Call QCBORDecode_ExitMap() to exit the current map
+ decoding level. When all map decoding layers are exited
+ then map mode is fully exited.
+
+ While in map mode, GetNext works as usual on the
+ map and the standard in-order traversal cursor
+ is maintained. Attempts to get items off the end of the
+ map will give error XXX (rather going to the next
+ item after the map as it would when not in map
+ mode).
+
+ You can rewind the inorder traversal cursor to the
+ beginning of the map with RewindMap().
+
+ Exiting leaves the cursor at the
+ data item following the last entry in the map.
+
+ Entering and Exiting map mode consumes the whole
+ map and its contents as a GetNext after exiting
+ will return the item after the map. */
+QCBORError QCBORDecode_EnterMap(QCBORDecodeContext *pCtx);
+
+
+void QCBORDecode_ExitMap(QCBORDecodeContext *pCtx);
+
+/*
+ Indicate if decoding is in map mode more not.
+ */
+bool QCBORDecode_InMapMode(QCBORDecodeContext *pCtxt);
+
+
+/*
+ Restarts fetching of items in a map to the start of the
+ map. This is for GetNext. It has no effect on
+ GetByLabel (which always searches from the start).
+ */
+void QCBORDecode_RewindMap(QCBORDecodeContext *pCtxt);
+
+
+QCBORError QCBORDecode_EnterArray(QCBORDecodeContext *pCtx);
+
+
+void QCBORDecode_ExitArray(QCBORDecodeContext *pCtx);
+
+QCBORError QCBORDecode_EnterArrayFromMapSZ(QCBORDecodeContext *pMe, const char *szLabel);
+
+
+//QCBORError QCBORDecode_EnterMapX(QCBORDecodeContext *pCtx, MapDecode *pMap);
+
+
+
+
+/*
+ Get an item out of a map.
+
+ Decoding must be in map mode for this to work.
+
+
+
+Seek to the beginning of the map.
+Consume items looking for the nLabel.
+Always go through the whole map and always look for duplicates.
+Return the item found, if no errors.
+
+Allow specification of type required.
+
+
+
+*/
+QCBORError QCBORDecode_GetItemInMap(QCBORDecodeContext *pCtx,
+ int64_t nLabel,
+ uint8_t qcbor_type,
+ QCBORItem *pItem);
+
+
+QCBORError QCBORDecode_GetItemInMapSZ(QCBORDecodeContext *pCtx,
+const char *szLabel,
+uint8_t qcbor_type,
+QCBORItem *pItem);
+
+/*
+ This gets several labeled items out of a map.
+
+ pItemArray 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.
+
+ 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.
+
+ This will return maps and arrays that are in the map, but
+ provides no way to descend into and decode them.
+
+ */
+QCBORError QCBORDecode_GetItemsInMap(QCBORDecodeContext *pCtx, QCBORItem *pItemList);
+
+
+
+QCBORError QCBORDecode_GetIntInMap(QCBORDecodeContext *pCtx, int64_t nLabel, int64_t *pInt);
+
+QCBORError QCBORDecode_GetIntInMapSZ(QCBORDecodeContext *pCtx, const char *szLabel, int64_t *pInt);
+
+
+void QCBORDecode_GetBstrInMapSZ(QCBORDecodeContext *pCtx, const char *szLabel, UsefulBufC *pBstr);
+
+void QCBORDecode_GetTextInMapSZ(QCBORDecodeContext *pCtx, const char *szLabel, UsefulBufC *pBstr);
+
+
+/*
+ Find a map in a map by integer label and enter it.
+
+ This will do duplicate detection on the particular label.
+
+ Call QCBORDecode_ExitMap() to return to the mode / level
+ from before this was called.
+
+ Seek to to the beginning of the map.
+ Consume items looking for nLabel
+ */
+QCBORError QCBORDecode_EnterMapFromMap(QCBORDecodeContext *pCtx, int64_t nLabel);
+
+QCBORError QCBORDecode_EnterMapFromMapSZ(QCBORDecodeContext *pCtx, const char *szLabel);
+
+
+
+
+/*
+ Normally decoding is just in-order traversal. You can get next
+ of any type, get next of a particular type including conversions.
+
+ If the cursor is at a map and you enter it, then you can use
+ methods that Get things by label, either numeric or string.
+
+ These methods work only at the particular level in the map.
+ To go into a map nested in a map call the special method
+ to enter a map by label.
+
+ When in a map, the GetNext methods work too, but only
+ to the end of the map. You can't traverse off the end of the
+ map.
+
+ You can rewind to the start of the map and traverse it again
+ with the MapRestart method.
+
+ The exit map method will leave the traversal cursor at the first itme after
+ the map.
+
+
+ The beginning of each map must be recorded so the scan can be done
+ through the whole map.
+
+ A bit per level to indicate in map mode for that level so
+ it is clear what GetNext at end does and what happens on MapExit
+ and where to set the cursor.
+
+
+
+
+
+
+
+
+
+
+
+ */
+
+
+
+
+
+#endif /* qcbor_decode_map_h */
+#endif
diff --git a/inc/qcbor/qcbor_private.h b/inc/qcbor/qcbor_private.h
index a7cb440..17ee20e 100644
--- a/inc/qcbor/qcbor_private.h
+++ b/inc/qcbor/qcbor_private.h
@@ -123,9 +123,12 @@
*/
typedef struct __QCBORDecodeNesting {
// PRIVATE DATA STRUCTURE
- struct {
- uint16_t uCount;
- uint8_t uMajorType;
+ struct nesting_decode_level {
+ uint32_t uOffset;
+ uint16_t uCount; // Cursor
+ uint8_t uMajorType; // TODO: one bit?
+ uint8_t uMapMode; // Used by map mode TODO: one bit?
+ uint16_t uSaveCount; // Used by map mode
} pMapsAndArrays[QCBOR_MAX_ARRAY_NESTING1+1],
*pCurrent;
} QCBORDecodeNesting;
@@ -154,8 +157,13 @@
uint8_t uDecodeMode;
uint8_t bStringAllocateAll;
+ uint8_t uLastError; // QCBORError stuffed into a uint8_t
QCBORDecodeNesting nesting;
+
+ // A cached offset to the end of the current map
+ // 0 if no value is cached.
+ uint32_t uMapEndOffset;
// If a string allocator is configured for indefinite-length
// strings, it is configured here.
diff --git a/src/qcbor_decode.c b/src/qcbor_decode.c
index d3d1ace..7cb7453 100644
--- a/src/qcbor_decode.c
+++ b/src/qcbor_decode.c
@@ -50,36 +50,137 @@
used here: QCBORDecodeNesting
===========================================================================*/
-inline static int
+
+
+/*
+The main mode of decoding is a pre-order travesal of the tree of leaves (numbers, strings...)
+formed by intermediate nodes (arrays and maps). The cursor for the traversal
+ is the byte offset in the encoded input and a leaf counter for definite
+ length maps and arrays. Indefinite length maps and arrays are handled
+ by look ahead for the break.
+
+ The view presented to the caller has tags, labels and the chunks of
+ indefinite length strings aggregated into one decorated data item.
+
+The caller understands the nesting level in pre-order traversal by
+ the fact that a data item that is a map or array is presented to
+ the caller when it is first encountered in the pre-order traversal and that all data items are presented with its nesting level
+ and the nesting level of the next item.
+
+ The caller traverse maps and arrays in a special mode that often more convenient
+ that tracking by nesting level. When an array or map is expected or encountered
+ the EnterMap or EnteryArray can be called.
+
+ When entering a map or array like this, the cursor points to the first
+ item in the map or array. When exiting, it points to the item after
+ the map or array, regardless of whether the items in the map or array were
+ all traversed.
+
+ When in a map or array, the cursor functions as normal, but traversal
+ cannot go past the end of the map or array that was entered. If this
+ is attempted the QCBOR_ERR_NO_MORE_ITEMS error is returned. To
+ go past the end of the map or array ExitMap() or ExitArray() must
+ be called. It can be called any time regardless of the position
+ of the cursor.
+
+ When a map is entered, a special function allows fetching data items
+ by label. This call will traversal the whole map looking for the
+ labeled item. The whole map is traversed so as to detect duplicates.
+ This type of fetching items does not affect the normal traversal
+ cursor.
+
+
+
+
+
+
+
+
+
+
+When a data item is presented to the caller, the nesting level of the data
+ item is presented along with the nesting level of the item that would be
+ next consumed.
+
+
+
+
+
+
+
+
+
+ */
+
+inline static bool
+// TODO: test Map as array better?
IsMapOrArray(uint8_t uDataType)
{
- return uDataType == QCBOR_TYPE_MAP || uDataType == QCBOR_TYPE_ARRAY;
+ return uDataType == QCBOR_TYPE_MAP ||
+ uDataType == QCBOR_TYPE_ARRAY ||
+ uDataType == QCBOR_TYPE_MAP_AS_ARRAY;
}
-inline static int
-DecodeNesting_IsNested(const QCBORDecodeNesting *pNesting)
+inline static bool
+DecodeNesting_IsAtTop(const QCBORDecodeNesting *pNesting)
{
- return pNesting->pCurrent != &(pNesting->pMapsAndArrays[0]);
+ if(pNesting->pCurrent == &(pNesting->pMapsAndArrays[0])) {
+ return true;
+ } else {
+ return false;
+ }
}
+// Determine if at the end of a map or array, taking into
+// account map mode. If this returns true, it is OK
+// to get another item.
+inline static bool
+DecodeNesting_AtEnd(const QCBORDecodeNesting *pNesting)
+{
+ if(DecodeNesting_IsAtTop(pNesting)){
+ // Always at end if at the top level of nesting
+ return true;
+ }
+
+ if(pNesting->pCurrent->uMapMode) {
+ if(pNesting->pCurrent->uCount == 0) {
+ // In map mode and consumed all items, so it is the end
+ return true;
+ } else {
+ // In map mode, all items not consumed, so it is NOT the end
+ return false;
+ }
+ } else {
+ // Not in map mode, and not at top level so it NOT the end.
+ return false;
+ }
+}
+
+
inline static int
DecodeNesting_IsIndefiniteLength(const QCBORDecodeNesting *pNesting)
{
return pNesting->pCurrent->uCount == UINT16_MAX;
}
+inline static int
+DecodeNesting_InMapMode(const QCBORDecodeNesting *pNesting)
+{
+ return (bool)pNesting->pCurrent->uMapMode;
+}
+
inline static uint8_t
DecodeNesting_GetLevel(QCBORDecodeNesting *pNesting)
{
// Check in DecodeNesting_Descend and never having
- // QCBOR_MAX_ARRAY_NESTING > 255 gaurantee cast is safe
+ // QCBOR_MAX_ARRAY_NESTING > 255 gaurantees cast is safe
return (uint8_t)(pNesting->pCurrent - &(pNesting->pMapsAndArrays[0]));
}
inline static int
DecodeNesting_TypeIsMap(const QCBORDecodeNesting *pNesting)
{
- if(!DecodeNesting_IsNested(pNesting)) {
+ if(DecodeNesting_IsAtTop(pNesting)) {
return 0;
}
@@ -91,7 +192,7 @@
DecodeNesting_BreakAscend(QCBORDecodeNesting *pNesting)
{
// breaks must always occur when there is nesting
- if(!DecodeNesting_IsNested(pNesting)) {
+ if(DecodeNesting_IsAtTop(pNesting)) {
return QCBOR_ERR_BAD_BREAK;
}
@@ -106,11 +207,15 @@
return QCBOR_SUCCESS;
}
-// Called on every single item except breaks including open of a map/array
+// Called on every single item except breaks including decode of a map/array
+/* Decrements the map/array counter if possible. If decrement
+ closed out a map or array, then level up in nesting and decrement
+ again, until, the top is reached or the end of a map mode is reached
+ */
inline static void
DecodeNesting_DecrementCount(QCBORDecodeNesting *pNesting)
{
- while(DecodeNesting_IsNested(pNesting)) {
+ while(!DecodeNesting_IsAtTop(pNesting)) {
// Not at the top level, so there is decrementing to be done.
if(!DecodeNesting_IsIndefiniteLength(pNesting)) {
@@ -122,6 +227,11 @@
// Did not close out an array or map, so nothing further
break;
}
+
+ if(pNesting->pCurrent->uMapMode) {
+ // In map mode the level-up must be done explicitly
+ break;
+ }
// Closed out an array or map so level up
pNesting->pCurrent--;
@@ -130,6 +240,23 @@
}
}
+inline static void
+DecodeNesting_EnterMapMode(QCBORDecodeNesting *pNesting, size_t uOffset)
+{
+ pNesting->pCurrent->uMapMode = 1;
+ // Cast to uint32_t is safe because QCBOR onl works on data < UINT32_MAX
+ pNesting->pCurrent->uOffset = (uint32_t)uOffset;
+}
+
+inline static void
+DecodeNesting_Exit(QCBORDecodeNesting *pNesting)
+{
+ pNesting->pCurrent->uMapMode = 0;
+ pNesting->pCurrent--;
+
+ DecodeNesting_DecrementCount(pNesting);
+}
+
// Called on every map/array
inline static QCBORError
DecodeNesting_Descend(QCBORDecodeNesting *pNesting, QCBORItem *pItem)
@@ -161,6 +288,8 @@
// Record a few details for this nesting level
pNesting->pCurrent->uMajorType = pItem->uDataType;
pNesting->pCurrent->uCount = pItem->val.uCount;
+ pNesting->pCurrent->uSaveCount = pItem->val.uCount;
+ pNesting->pCurrent->uMapMode = 0;
Done:
return nReturn;;
@@ -1034,6 +1163,7 @@
/*
Public function, see header qcbor/qcbor_decode.h file
+ TODO: correct this comment
*/
QCBORError QCBORDecode_GetNextMapOrArray(QCBORDecodeContext *me,
QCBORItem *pDecodedItem,
@@ -1041,12 +1171,16 @@
{
// Stack ptr/int: 2, QCBORItem : 64
- // The public entry point for fetching and parsing the next QCBORItem.
- // All the CBOR parsing work is here and in subordinate calls.
QCBORError nReturn;
- // Check if there are an
- if(UsefulInputBuf_BytesUnconsumed(&(me->InBuf)) == 0 && !DecodeNesting_IsNested(&(me->nesting))) {
+ // Check if there are an TODO: incomplete comment
+ if(UsefulInputBuf_BytesUnconsumed(&(me->InBuf)) == 0 && DecodeNesting_IsAtTop(&(me->nesting))) {
+ nReturn = QCBOR_ERR_NO_MORE_ITEMS;
+ goto Done;
+ }
+
+ // This is to handle map and array mode
+ if(UsefulInputBuf_Tell(&(me->InBuf)) != 0 && DecodeNesting_AtEnd(&(me->nesting))) {
nReturn = QCBOR_ERR_NO_MORE_ITEMS;
goto Done;
}
@@ -1094,7 +1228,7 @@
// all breaks that might terminate them. The equivalent
// for definite length maps/arrays happens in
// DecodeNesting_DecrementCount().
- if(DecodeNesting_IsNested(&(me->nesting)) && DecodeNesting_IsIndefiniteLength(&(me->nesting))) {
+ if(!DecodeNesting_IsAtTop(&(me->nesting)) && DecodeNesting_IsIndefiniteLength(&(me->nesting))) {
while(UsefulInputBuf_BytesUnconsumed(&(me->InBuf))) {
// Peek forward one item to see if it is a break.
QCBORItem Peek;
@@ -1121,7 +1255,13 @@
// Tell the caller what level is next. This tells them what maps/arrays
// were closed out and makes it possible for them to reconstruct
// the tree with just the information returned by GetNext
- pDecodedItem->uNextNestLevel = DecodeNesting_GetLevel(&(me->nesting));
+ if(me->nesting.pCurrent->uMapMode && me->nesting.pCurrent->uCount == 0) {
+ // At end of a map / array in map mode, so next nest is 0 to
+ // indicate this end.
+ pDecodedItem->uNextNestLevel = 0;
+ } else {
+ pDecodedItem->uNextNestLevel = DecodeNesting_GetLevel(&(me->nesting));
+ }
Done:
if(nReturn != QCBOR_SUCCESS) {
@@ -1427,7 +1567,7 @@
- QCBORDecode_GetNextMapOrArray - This manages the beginnings and
ends of maps and arrays. It tracks descending into and ascending
out of maps/arrays. It processes all breaks that terminate
- maps and arrays.
+ indefinite length maps and arrays.
- GetNext_MapEntry -- This handles the combining of two
items, the label and the data, that make up a map entry.
@@ -1485,7 +1625,7 @@
QCBORError nReturn = QCBOR_SUCCESS;
// Error out if all the maps/arrays are not closed out
- if(DecodeNesting_IsNested(&(me->nesting))) {
+ if(!DecodeNesting_IsAtTop(&(me->nesting))) {
nReturn = QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN;
goto Done;
}
@@ -1718,3 +1858,1493 @@
return QCBOR_SUCCESS;
}
+
+#include <stdio.h>
+void printdecode(QCBORDecodeContext *pMe, const char *szName)
+{
+ printf("---%s--%d--%d--\nLevel Count Type Offset SaveCount MapMode\n",
+ szName,
+ (uint32_t)pMe->InBuf.cursor,
+ (uint32_t)pMe->InBuf.UB.len);
+/* for(int i = 0; i < QCBOR_MAX_ARRAY_NESTING; i++) {
+ if(&(pMe->nesting.pMapsAndArrays[i]) > pMe->nesting.pCurrent) {
+ break;
+ }
+ printf(" %2d %5d %s %6u %2d %d\n",
+ i,
+ pMe->nesting.pMapsAndArrays[i].uCount,
+ pMe->nesting.pMapsAndArrays[i].uMajorType == QCBOR_TYPE_MAP ? " map" :
+ (pMe->nesting.pMapsAndArrays[i].uMajorType == QCBOR_TYPE_ARRAY ? "array" :
+ (pMe->nesting.pMapsAndArrays[i].uMajorType == QCBOR_TYPE_NONE ? " none" : "?????")),
+ pMe->nesting.pMapsAndArrays[i].uOffset,
+ pMe->nesting.pMapsAndArrays[i].uSaveCount,
+ pMe->nesting.pMapsAndArrays[i].uMapMode
+ );
+
+ }
+ printf("\n"); */
+}
+
+
+/*
+ *
+ */
+static inline QCBORError
+ConsumeItem(QCBORDecodeContext *pMe,
+ const QCBORItem *pItemToConsume,
+ uint_fast8_t *puNextNestLevel)
+{
+ QCBORError nReturn;
+ QCBORItem Item;
+
+ printdecode(pMe, "ConsumeItem");
+
+ if(IsMapOrArray(pItemToConsume->uDataType)) {
+ /* There is only real work to do for maps and arrays */
+
+ /* This works for definite and indefinite length
+ * maps and arrays by using the nesting level
+ */
+ do {
+ nReturn = QCBORDecode_GetNext(pMe, &Item);
+ if(nReturn != QCBOR_SUCCESS) {
+ goto Done;
+ }
+ } while(Item.uNextNestLevel >= pItemToConsume->uNextNestLevel);
+
+ if(puNextNestLevel != NULL) {
+ *puNextNestLevel = Item.uNextNestLevel;
+ }
+ nReturn = QCBOR_SUCCESS;
+
+ } else {
+ /* item_to_consume is not a map or array */
+ if(puNextNestLevel != NULL) {
+ /* Just pass the nesting level through */
+ *puNextNestLevel = pItemToConsume->uNextNestLevel;
+ }
+ nReturn = QCBOR_SUCCESS;
+ }
+
+Done:
+ return nReturn;
+}
+
+
+/* Return true if the labels in Item1 and Item2 are the same.
+ Works only for integer and string labels. Returns false
+ for any other type. */
+static inline bool
+MatchLabel(QCBORItem Item1, QCBORItem Item2)
+{
+ if(Item1.uLabelType == QCBOR_TYPE_INT64) {
+ if(Item2.uLabelType == QCBOR_TYPE_INT64 && Item1.label.int64 == Item2.label.int64) {
+ return true;
+ }
+ } else if(Item1.uLabelType == QCBOR_TYPE_TEXT_STRING) {
+ if(Item2.uLabelType == QCBOR_TYPE_TEXT_STRING && !UsefulBuf_Compare(Item1.label.string, Item2.label.string)) {
+ return true;
+ }
+ } else if(Item1.uLabelType == QCBOR_TYPE_BYTE_STRING) {
+ if(Item2.uLabelType == QCBOR_TYPE_BYTE_STRING && !UsefulBuf_Compare(Item1.label.string, Item2.label.string)) {
+ return true;
+ }
+ } else if(Item1.uLabelType == QCBOR_TYPE_UINT64) {
+ if(Item2.uLabelType == QCBOR_TYPE_UINT64 && Item1.label.uint64 == Item2.label.uint64) {
+ return true;
+ }
+ }
+
+ /* Other label types are never matched */
+ return false;
+}
+
+static inline bool
+MatchType(QCBORItem Item1, QCBORItem Item2)
+{
+ if(Item1.uDataType == Item2.uDataType) {
+ return true;
+ } else if(Item1.uLabelType == QCBOR_TYPE_ANY) {
+ return true;
+ } else if(Item2.uLabelType == QCBOR_TYPE_ANY) {
+ return true;
+ }
+ return false;
+}
+
+
+/*
+ On input pItemArray contains a list of labels and data types
+ of items to be found.
+
+ On output the fully retrieved items are filled in with
+ values and such. The label was matched, so it never changes.
+
+ If an item was not found, its data type is set to none.
+
+ */
+QCBORError
+GetItemsInMap(QCBORDecodeContext *pMe, QCBORItem *pItemArray, size_t *puOffset, size_t *puEndOffset)
+{
+ QCBORError nReturn;
+
+ // TODO: what if pre-order cursor is not at the same level as map? This should be OK.
+ if(!DecodeNesting_InMapMode(&(pMe->nesting))) {
+ return QCBOR_ERR_NOT_ENTERED;
+ }
+
+ QCBORDecodeNesting N = pMe->nesting;
+
+ if(pMe->nesting.pCurrent->uCount != UINT16_MAX) {
+ pMe->nesting.pCurrent->uCount = pMe->nesting.pCurrent->uSaveCount;
+ }
+
+ UsefulInputBuf_Seek(&(pMe->InBuf), pMe->nesting.pCurrent->uOffset);
+
+ /* Loop over all the items in the map. They could be
+ * deeply nested and this should handle both definite
+ * and indefinite length maps and arrays, so this
+ * adds some complexity. */
+ const uint8_t uMapNestLevel = DecodeNesting_GetLevel(&(pMe->nesting));
+
+ uint_fast8_t uNextNestLevel;
+
+ uint64_t uFound = 0;
+
+ do {
+ /* Remember offset because sometims we have to return it */
+ const size_t uOffset = UsefulInputBuf_Tell(&(pMe->InBuf));
+
+ /* Get the item */
+ QCBORItem Item;
+ nReturn = QCBORDecode_GetNext(pMe, &Item);
+ if(nReturn != QCBOR_SUCCESS) {
+ /* Got non-well-formed CBOR */
+ goto Done;
+ }
+
+ /* See if item has one of the labels that are of interest */
+ int i;
+ QCBORItem *pIterator;
+ for(pIterator = pItemArray, i = 0; pIterator->uLabelType != 0; pIterator++, i++) {
+ if(MatchLabel(Item, *pIterator)) {
+ // A label match has been found
+ if(uFound & (0x01ULL << i)) {
+ nReturn = QCBOR_ERR_DUPLICATE_LABEL;
+ goto Done;
+ }
+ if(!MatchType(Item, *pIterator)) {
+ nReturn = QCBOR_ERR_UNEXPECTED_TYPE;
+ goto Done;
+ }
+
+ /* Successful match. Return the item. */
+ *pIterator = Item;
+ uFound |= 0x01ULL << i;
+ if(puOffset) {
+ *puOffset = uOffset;
+ }
+ }
+ }
+
+ /* Consume the item whether matched or not. This
+ does th work of traversing maps and array and
+ everything in them. In this loop only the
+ items at the current nesting level are examined
+ to match the labels. */
+ nReturn = ConsumeItem(pMe, &Item, &uNextNestLevel);
+ if(nReturn) {
+ goto Done;
+ }
+
+ } while (uNextNestLevel >= uMapNestLevel);
+
+
+ nReturn = QCBOR_SUCCESS;
+
+ const size_t uEndOffset = UsefulInputBuf_Tell(&(pMe->InBuf));
+ // Cast OK because encoded CBOR is limited to UINT32_MAX
+ pMe->uMapEndOffset = (uint32_t)uEndOffset;
+ // TODO: is zero *puOffset OK?
+ if(puEndOffset) {
+ *puEndOffset = uEndOffset;
+ }
+
+ /* For all items not found, set the data type to QCBOR_TYPE_NONE */
+ int i;
+ QCBORItem *pIterator;
+ for(pIterator = pItemArray, i = 0; pIterator->uLabelType != 0; pIterator++, i++) {
+ if(!(uFound & (0x01ULL << i))) {
+ pIterator->uDataType = QCBOR_TYPE_NONE;
+ }
+ }
+
+Done:
+ pMe->nesting = N;
+
+ return nReturn;
+}
+
+
+void QCBORDecode_ExitMap(QCBORDecodeContext *pMe)
+{
+ size_t uEndOffset;
+
+/*
+ if(pMe->uMapEndOffset) {
+ uEndOffset = pMe->uMapEndOffset;
+ // It is only valid once.
+ pMe->uMapEndOffset = 0;
+ } else { */
+ QCBORItem Dummy;
+
+ Dummy.uLabelType = QCBOR_TYPE_NONE;
+
+ QCBORError nReturn = GetItemsInMap(pMe, &Dummy, NULL, &uEndOffset);
+
+ (void)nReturn; // TODO:
+// }
+
+ printdecode(pMe, "start exit");
+ UsefulInputBuf_Seek(&(pMe->InBuf), uEndOffset);
+
+ DecodeNesting_Exit(&(pMe->nesting));
+ printdecode(pMe, "end exit");
+
+}
+
+
+QCBORError QCBORDecode_GetItemInMap(QCBORDecodeContext *pMe,
+ int64_t nLabel,
+ uint8_t uQcborType,
+ QCBORItem *pItem)
+{
+ QCBORItem One[2];
+
+ One[0].uLabelType = QCBOR_TYPE_INT64;
+ One[0].label.int64 = nLabel;
+ One[0].uDataType = uQcborType;
+ One[1].uLabelType = QCBOR_TYPE_NONE; // Indicates end of array
+
+ QCBORError nReturn = GetItemsInMap(pMe, One, NULL, NULL);
+ if(nReturn) {
+ return nReturn;
+ }
+
+ if(One[0].uDataType == QCBOR_TYPE_NONE) {
+ return QCBOR_ERR_NOT_FOUND;
+ }
+
+ *pItem = One[0];
+
+ return QCBOR_SUCCESS;
+}
+
+
+QCBORError QCBORDecode_GetItemInMapSZ(QCBORDecodeContext *pMe,
+ const char *szLabel,
+ uint8_t uQcborType,
+ QCBORItem *pItem)
+{
+ QCBORItem One[2];
+
+ One[0].uLabelType = QCBOR_TYPE_TEXT_STRING;
+ One[0].label.string = UsefulBuf_FromSZ(szLabel);
+ One[0].uDataType = uQcborType;
+ One[1].uLabelType = QCBOR_TYPE_NONE; // Indicates end of array
+
+ QCBORError nReturn = GetItemsInMap(pMe, One, NULL, NULL);
+ if(nReturn) {
+ return nReturn;
+ }
+
+ if(One[0].uDataType == QCBOR_TYPE_NONE) {
+ return QCBOR_ERR_NOT_FOUND;
+ }
+
+ *pItem = One[0];
+
+ return QCBOR_SUCCESS;
+}
+
+
+
+
+static int FinishEnter(QCBORDecodeContext *pMe, size_t uOffset)
+{
+ /* Seek to the data item that is the map or array */
+ UsefulInputBuf_Seek(&(pMe->InBuf), uOffset);
+
+ /* Skip the data item that is the map or array */
+ QCBORItem MapToEnter;
+ // TODO: check error
+ QCBORDecode_GetNext(pMe, &MapToEnter);
+
+ /* Enter map mode with an offset that is the first item
+ in the map or array. */
+ // TODO: what if map or array is empty?
+ DecodeNesting_EnterMapMode(&(pMe->nesting), UsefulInputBuf_Tell(&(pMe->InBuf)));
+
+
+ printdecode(pMe, "Entered Map in Map");
+
+ return 0;
+}
+
+
+QCBORError QCBORDecode_EnterMapInMapN(QCBORDecodeContext *pMe, int64_t nLabel)
+{
+ /* Use GetItemsInMap to find the map by label, including the
+ byte offset of it. */
+ QCBORItem One[2];
+ One[0].uLabelType = QCBOR_TYPE_INT64;
+ One[0].label.int64 = nLabel;
+ One[0].uDataType = QCBOR_TYPE_MAP;
+ One[1].uLabelType = QCBOR_TYPE_NONE;
+
+ size_t uOffset;
+ QCBORError nReturn = GetItemsInMap(pMe, One, &uOffset, NULL);
+ if(nReturn) {
+ return nReturn;
+ }
+
+ /* The map to enter was found, now finish of entering it. */
+ FinishEnter(pMe, uOffset);
+
+ // TODO: error code?
+ return 0;
+}
+
+
+QCBORError QCBORDecode_EnterMapFromMapSZ(QCBORDecodeContext *pMe, const char *szLabel)
+{
+ QCBORItem One[2];
+
+ One[0].uLabelType = QCBOR_TYPE_TEXT_STRING;
+ One[0].label.string = UsefulBuf_FromSZ(szLabel);
+ One[0].uDataType = QCBOR_TYPE_MAP;
+ One[1].uLabelType = QCBOR_TYPE_NONE;
+
+ size_t uOffset;
+
+ QCBORError nReturn = GetItemsInMap(pMe, One, &uOffset, NULL);
+
+ if(nReturn) {
+ return nReturn;
+ }
+
+ FinishEnter(pMe, uOffset);
+
+ return 0;
+}
+
+
+QCBORError QCBORDecode_EnterArrayFromMapN(QCBORDecodeContext *pMe, int64_t nLabel)
+{
+ QCBORItem One[2];
+
+ One[0].uLabelType = QCBOR_TYPE_INT64;
+ One[0].label.int64 = nLabel;
+ One[0].uDataType = QCBOR_TYPE_ARRAY;
+ One[1].uLabelType = QCBOR_TYPE_NONE;
+
+ size_t uOffset;
+
+ QCBORError nReturn = GetItemsInMap(pMe, One, &uOffset, NULL);
+
+ if(nReturn != QCBOR_SUCCESS) {
+ return nReturn;
+ }
+
+ FinishEnter(pMe, uOffset);
+
+ return 0;
+}
+
+
+QCBORError QCBORDecode_EnterArrayFromMapSZ(QCBORDecodeContext *pMe, const char *szLabel)
+{
+ QCBORItem One[2];
+
+ One[0].uLabelType = QCBOR_TYPE_TEXT_STRING;
+ One[0].label.string = UsefulBuf_FromSZ(szLabel);
+ One[0].uDataType = QCBOR_TYPE_ARRAY;
+ One[1].uLabelType = QCBOR_TYPE_NONE;
+
+ size_t uOffset;
+
+ QCBORError nReturn = GetItemsInMap(pMe, One, &uOffset, NULL);
+
+ if(nReturn != QCBOR_SUCCESS) {
+ return nReturn;
+ }
+
+ FinishEnter(pMe, uOffset);
+
+ return 0;
+}
+
+
+
+
+
+/* Next item must be map or this generates an error */
+QCBORError QCBORDecode_EnterMap(QCBORDecodeContext *pMe)
+{
+ QCBORItem Item;
+ QCBORError nReturn;
+
+ /* Get the data item that is the map that is being searched */
+ nReturn = QCBORDecode_GetNext(pMe, &Item);
+ if(nReturn != QCBOR_SUCCESS) {
+ return nReturn;
+ }
+ if(Item.uDataType != QCBOR_TYPE_MAP) {
+ return QCBOR_ERR_UNEXPECTED_TYPE;
+ }
+
+ DecodeNesting_EnterMapMode(&(pMe->nesting), UsefulInputBuf_Tell(&(pMe->InBuf)));
+
+ printdecode(pMe, "EnterMapDone");
+
+ return QCBOR_SUCCESS;
+}
+
+
+
+QCBORError QCBORDecode_GetItemsInMap(QCBORDecodeContext *pCtx, QCBORItem *pItemList)
+{
+ return GetItemsInMap(pCtx, pItemList, NULL, NULL);
+}
+
+
+
+
+
+void QCBORDecode_RewindMap(QCBORDecodeContext *pMe)
+{
+ // TODO: check for map mode
+ pMe->nesting.pCurrent->uCount = pMe->nesting.pCurrent->uSaveCount;
+ UsefulInputBuf_Seek(&(pMe->InBuf), pMe->nesting.pCurrent->uOffset);
+}
+
+
+QCBORError QCBORDecode_EnterArray(QCBORDecodeContext *pMe)
+{
+ QCBORItem Item;
+ QCBORError nReturn;
+
+ /* Get the data item that is the map that is being searched */
+ nReturn = QCBORDecode_GetNext(pMe, &Item);
+ if(nReturn != QCBOR_SUCCESS) {
+ return nReturn;
+ }
+ if(Item.uDataType != QCBOR_TYPE_ARRAY) {
+ return QCBOR_ERR_UNEXPECTED_TYPE;
+ }
+
+ printdecode(pMe, "EnterArray");
+
+ DecodeNesting_EnterMapMode(&(pMe->nesting), UsefulInputBuf_Tell(&(pMe->InBuf)));
+
+ return QCBOR_SUCCESS;
+}
+
+
+void QCBORDecode_ExitArray(QCBORDecodeContext *pMe)
+{
+ // TODO: make sure we have entered an array
+ // TODO: combine with code for map? It is the same so far.
+ size_t uEndOffset;
+
+ /* if(pMe->uMapEndOffset) {
+ uEndOffset = pMe->uMapEndOffset;
+ // It is only valid once.
+ pMe->uMapEndOffset = 0;
+ } else {*/
+ QCBORItem Dummy;
+
+ Dummy.uLabelType = QCBOR_TYPE_NONE;
+
+ QCBORError nReturn = GetItemsInMap(pMe, &Dummy, NULL, &uEndOffset);
+
+ (void)nReturn; // TODO:
+ //}
+
+ printdecode(pMe, "start exit");
+ UsefulInputBuf_Seek(&(pMe->InBuf), uEndOffset);
+
+ DecodeNesting_Exit(&(pMe->nesting));
+ printdecode(pMe, "end exit");
+}
+
+
+void QCBORDecode_GetIntInMapSZ(QCBORDecodeContext *pMe, const char *szLabel, int64_t *pInt)
+{
+ // TODO: error handling
+ QCBORItem Item;
+ QCBORDecode_GetItemInMapSZ(pMe,szLabel, QCBOR_TYPE_INT64, &Item);
+ *pInt = Item.val.int64;
+}
+
+void QCBORDecode_GetBstrInMapN(QCBORDecodeContext *pMe, int64_t nLabel, UsefulBufC *pBstr)
+{
+ // TODO: error handling
+ QCBORItem Item;
+ QCBORDecode_GetItemInMap(pMe, nLabel, QCBOR_TYPE_BYTE_STRING, &Item);
+ *pBstr = Item.val.string;
+}
+
+void QCBORDecode_GetBstrInMapSZ(QCBORDecodeContext *pMe, const char *szLabel, UsefulBufC *pBstr)
+{
+ // TODO: error handling
+ QCBORItem Item;
+ QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_BYTE_STRING, &Item);
+ *pBstr = Item.val.string;
+}
+
+void QCBORDecode_GetTextInMapSZ(QCBORDecodeContext *pMe, const char *szLabel, UsefulBufC *pBstr)
+{
+ // TODO: error handling
+ QCBORItem Item;
+ QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_TEXT_STRING, &Item);
+ *pBstr = Item.val.string;
+}
+
+
+void QCBORDecode_GetBool(QCBORDecodeContext *pMe, bool *pValue)
+{
+ if(pMe->uLastError != QCBOR_SUCCESS) {
+ // Already in error state, do nothing
+ return;
+ }
+
+ QCBORError nError;
+ QCBORItem Item;
+
+ nError = QCBORDecode_GetNext(pMe, &Item);
+ if(nError != QCBOR_SUCCESS) {
+ pMe->uLastError = (uint8_t)nError;
+ return;
+ }
+
+ switch(Item.uDataType) {
+ case QCBOR_TYPE_TRUE:
+ *pValue = true;
+ break;
+
+ case QCBOR_TYPE_FALSE:
+ *pValue = false;
+ break;
+
+ default:
+ pMe->uLastError = QCBOR_ERR_UNEXPECTED_TYPE;
+ break;
+ }
+}
+
+#if 0
+// TODO: fix this
+/* Types of text strings
+ * Plain, b64, b64url, URI, regex, MIME Text
+ * One function for each with options to expect plain?
+ * One function for all so you can say what you want?
+ *
+ * A label is expected if pLabel is not NULL.
+ */
+void QCBORDecode_GetTextFoo(QCBORDecodeContext *pMe, QCBORLabel *pLabel, UsefulBufC *pValue)
+{
+ QCBORItem Item;
+ QCBORError nError;
+
+ nError = QCBORDecode_GetNext(pMe, &Item);
+ if(nError) {
+ pMe->uLastError = nError;
+ return;
+ }
+
+ if(pLabel != NULL) {
+ if(Item.uLabelType == QCBOR_TYPE_NONE) {
+ pMe->uLastError = 9; // TODO: error code
+ return;
+ } else {
+ // TODO: what about label allocation?
+ pLabel->uLabelType = Item.uLabelType;
+ pLabel->label.xx = Item.label.int64; // TOOD: figure out assignment
+ }
+ }
+
+ switch(Item.uDataType) {
+ case QCBOR_TYPE_TEXT_STRING:
+ *pValue = Item.val.string;
+ break;
+
+ default:
+ pMe->uLastError = QCBOR_ERR_UNEXPECTED_TYPE;
+ }
+}
+#endif
+
+
+/*
+ Options for MIME data, CBOR, positive big num, negative big num ??
+ */
+void QCBORDecode_GetStringInternal(QCBORDecodeContext *pMe, UsefulBufC *pValue, uint8_t uType)
+{
+ if(pMe->uLastError != QCBOR_SUCCESS) {
+ // Already in error state, do nothing
+ return;
+ }
+
+ QCBORError nError;
+ QCBORItem Item;
+
+ nError = QCBORDecode_GetNext(pMe, &Item);
+ if(nError != QCBOR_SUCCESS) {
+ pMe->uLastError = (uint8_t)nError;
+ return;
+ }
+
+ if(Item.uDataType == uType) {
+ *pValue = Item.val.string;
+ } else {
+ pMe->uLastError = QCBOR_ERR_UNEXPECTED_TYPE;
+ }
+}
+
+void QCBORDecode_GetBytes(QCBORDecodeContext *pMe, UsefulBufC *pValue)
+{
+ QCBORDecode_GetStringInternal(pMe, pValue, QCBOR_TYPE_BYTE_STRING);
+}
+
+
+void QCBORDecode_GetText(QCBORDecodeContext *pMe, UsefulBufC *pValue)
+{
+ QCBORDecode_GetStringInternal(pMe, pValue, QCBOR_TYPE_TEXT_STRING);
+}
+
+
+void QCBORDecode_GetPosBignum(QCBORDecodeContext *pMe, UsefulBufC *pValue)
+{
+ // TODO: do these have to be tagged?
+ // Probably should allow tagged or untagged, but not wrong-tagged
+ QCBORDecode_GetStringInternal(pMe, pValue, QCBOR_TYPE_POSBIGNUM);
+}
+
+void QCBORDecode_GetNegBignum(QCBORDecodeContext *pMe, UsefulBufC *pValue)
+{
+ QCBORDecode_GetStringInternal(pMe, pValue, QCBOR_TYPE_NEGBIGNUM);
+}
+
+
+
+
+typedef QCBORError (*fExponentiator)(uint64_t uMantissa, int64_t nExponent, uint64_t *puResult);
+
+
+// The main exponentiator that works on only positive numbers
+static QCBORError Exponentitate10UU(uint64_t uMantissa, int64_t nExponent, uint64_t *puResult)
+{
+ uint64_t uResult;
+
+ uResult = uMantissa;
+
+ /* This loop will run a maximum of 19 times because
+ * UINT64_MAX < 10 ^^ 19. More than that will cause
+ * exit with the overflow error
+ */
+ while(nExponent > 0) {
+ if(uResult > UINT64_MAX / 10) {
+ return QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW; // Error overflow
+ }
+ uResult = uResult * 10;
+ nExponent--;
+ }
+
+ while(nExponent < 0 ) {
+ if(uResult == 0) {
+ return QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW; // Underflow error
+ }
+ uResult = uResult / 10;
+ nExponent--;
+ }
+
+ *puResult = uResult;
+
+ return QCBOR_SUCCESS;
+}
+
+
+/* Convert a decimal fraction to an int64_t without using
+ floating point or math libraries. Most decimal fractions
+ will not fit in an int64_t and this will error out with
+ under or overflow
+ */
+static QCBORError Exponentitate2UU(uint64_t nMantissa, int64_t nExponent, uint64_t *pnResult)
+{
+ uint64_t nResult;
+
+ nResult = nMantissa;
+
+ /* This loop will run a maximum of 64 times because
+ * INT64_MAX < 2^31. More than that will cause
+ * exist with the overflow error
+ */
+ while(nExponent > 0) {
+ if(nResult > UINT64_MAX >> 1) {
+ return QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW; // Error overflow
+ }
+ nResult = nResult << 1;
+ nExponent--;
+ }
+
+ while(nExponent < 0 ) {
+ if(nResult == 0) {
+ return QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW; // Underflow error
+ }
+ nResult = nResult >> 1;
+ nExponent--;
+ }
+
+ *pnResult = nResult;
+
+ return QCBOR_SUCCESS;
+}
+
+
+static inline QCBORError ExponentiateNN(int64_t nMantissa, int64_t nExponent, int64_t *pnResult, fExponentiator pfExp)
+{
+ uint64_t uResult;
+
+ // Take the absolute value of the mantissa
+ uint64_t uMantissa = nMantissa > 0 ? (uint64_t)nMantissa : (uint64_t)-nMantissa;
+
+ // Do the exponentiation of the positive mantissa
+ QCBORError uReturn = (*pfExp)(uMantissa, nExponent, &uResult);
+ if(uReturn) {
+ return uReturn;
+ }
+
+ // Error out if too large on the plus side for an int64_t
+ if(uResult > INT64_MAX) {
+ return QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW;
+ }
+
+ // Error out if too large on the negative side for an int64_t
+ if(uResult < (uint64_t)INT64_MAX+1) {
+ /* (uint64_t)INT64_MAX+1 is used to represent the absolute value
+ of INT64_MIN. This assumes two's compliment representation where
+ INT64_MIN is one increment farther from 0 than INT64_MAX.
+ Trying to write -INT64_MIN doesn't work to get this because the
+ compiler tries to work with an int64_t which can't represent
+ -INT64_MIN.
+ */
+ return QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW;
+ }
+
+ // Casts are safe because of checks above
+ *pnResult = nMantissa > 0 ? (int64_t)uResult : -(int64_t)uResult;
+
+ return QCBOR_SUCCESS;
+}
+
+
+static inline QCBORError ExponentitateNU(int64_t nMantissa, int64_t nExponent, uint64_t *puResult, fExponentiator pfExp)
+{
+ if(nMantissa < 0) {
+ return QCBOR_ERR_NUMBER_SIGN_CONVERSION;
+ }
+
+ // Cast to unsigned is OK because of check for negative
+ // Cast to unsigned is OK because UINT64_MAX > INT64_MAX
+ // Exponentiation is straight forward
+ return (*pfExp)((uint64_t)nMantissa, nExponent, puResult);
+}
+
+
+// TODO: use this or get rid of it
+QCBORError ExponentitateUN(uint64_t uMantissa, int64_t nExponent, int64_t *pnResult, fExponentiator pfExp)
+{
+ uint64_t uResult;
+
+ QCBORError uR;
+
+ uR = (*pfExp)(uMantissa, nExponent, &uResult);
+ if(uR) {
+ return uR;
+ }
+
+ if(uResult > INT64_MAX) {
+ return QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW;
+ }
+
+ // Cast is OK because of check above
+ *pnResult = (int64_t)uResult;
+
+ return QCBOR_SUCCESS;
+}
+
+
+
+
+#include <math.h>
+/*
+static inline uint8_t Exponentitate10F(uint64_t uMantissa, int64_t nExponent, double *pfResult)
+{
+ // TODO: checkout exceptions; what is HUGE_VAL?
+ *pfResult = pow((double)10, (double)nExponent) * (double)uMantissa;
+
+ //if(*pfResult == HUGE_VAL)
+ return 0;
+}
+*/
+
+
+
+
+
+
+/*
+ A) bignum is positive
+ A1) output is signed INT64_MAX
+ A2) output is unsigned UINT64_MAX
+ B) bignum is negative
+ B1) output is signed INT64_MAX
+ B2) output is unsigned error
+ */
+static inline QCBORError ConvertBigNum(const UsefulBufC BigNum, uint64_t uMax, uint64_t *pResult)
+{
+ uint64_t uResult;
+
+ uResult = 0;
+ const uint8_t *pByte = BigNum.ptr;
+ size_t uLen = BigNum.len;
+ while(uLen--) {
+ if(uResult > uMax >> 8) {
+ return QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW;
+ }
+ uResult = (uResult << 8) + *pByte;
+ }
+
+ *pResult = uResult;
+ return QCBOR_SUCCESS;
+}
+
+
+#if 0
+static inline QCBORError ConvertBigNumToDouble(const UsefulBufC BigNum, uint64_t uMax, double *pResult)
+{
+ double nResult;
+
+ nResult = 0;
+ const uint8_t *pByte = BigNum.ptr;
+ size_t uLen = BigNum.len;
+ /* This will overflow and become the float value INFINITY if the number
+ is too large to fit. No error will be logged.
+ TODO: should an error be logged? */
+ while(uLen--) {
+ nResult = (nResult * 256) + *pByte;
+ }
+
+ *pResult = nResult;
+ return 0;
+}
+#endif
+
+static inline QCBORError ConvertPositiveBigNumToUnSigned(const UsefulBufC BigNum, uint64_t *pResult)
+{
+ return ConvertBigNum(BigNum, UINT64_MAX, pResult);
+}
+
+static inline QCBORError ConvertPositiveBigNumToSigned(const UsefulBufC BigNum, int64_t *pResult)
+{
+ uint64_t uResult;
+ QCBORError n = ConvertBigNum(BigNum, INT64_MAX, &uResult);
+ if(n) {
+ return n;
+ }
+ /* Cast is safe because ConvertBigNum is told to limit to INT64_MAX */
+ *pResult = (int64_t)uResult;
+ return QCBOR_SUCCESS;
+}
+
+
+static inline QCBORError ConvertNegativeBigNumToSigned(const UsefulBufC BigNum, int64_t *pResult)
+{
+ uint64_t uResult;
+ QCBORError n = ConvertBigNum(BigNum, INT64_MAX-1, &uResult);
+ if(n) {
+ return n;
+ }
+ /* Cast is safe because ConvertBigNum is told to limit to INT64_MAX */
+ *pResult = -(int64_t)uResult;
+ return 0;
+}
+
+// No function to convert a negative bignum to unsigned; it is an error
+
+
+#if 0
+static inline int ConvertXYZ(const UsefulBufC Mantissa, int64_t nExponent, int64_t *pResult)
+{
+ int64_t nMantissa;
+
+ int xx = ConvertPositiveBigNumToSigned(Mantissa, &nMantissa);
+ if(xx) {
+ return xx;
+ }
+
+ return ExponentiateNN(nMantissa, nExponent, pResult, &Exponentitate10UU);
+}
+
+#endif
+
+
+
+
+/*
+ Get the next item as an int64_t. The CBOR type can be unsigned, negative, float
+ a big float, a decimal fraction or a big num. Conversion will be dones as
+ expected. Some cases will error out with under or over flow.
+ */
+void QCBORDecode_GetInt64ConvertInternal(QCBORDecodeContext *pMe, uint32_t uOptions, int64_t *pValue, QCBORItem *pItem)
+{
+ if(pMe->uLastError != QCBOR_SUCCESS) {
+ return;
+ }
+
+ QCBORItem Item;
+ QCBORError nError;
+
+ nError = QCBORDecode_GetNext(pMe, &Item);
+ if(nError) {
+ pMe->uLastError = (uint8_t)nError;
+ return;
+ }
+
+ if(pItem) {
+ *pItem = Item;
+ }
+
+ switch(Item.uDataType) {
+ case QCBOR_TYPE_FLOAT:
+ if(uOptions & QCBOR_CONVERT_TYPE_FLOAT) {
+ // TODO: what about under/overflow here?
+ // Invokes the floating-point HW and/or compiler-added libraries
+ *pValue = (int64_t)Item.val.dfnum;
+ } else {
+ pMe->uLastError = QCBOR_ERR_CONVERSION_NOT_REQUESTED;
+ }
+ break;
+
+ case QCBOR_TYPE_INT64:
+ if(uOptions & QCBOR_CONVERT_TYPE_INT64) {
+ *pValue = Item.val.int64;
+ } else {
+ pMe->uLastError = QCBOR_ERR_CONVERSION_NOT_REQUESTED;
+ }
+ break;
+
+ case QCBOR_TYPE_UINT64:
+ if(uOptions & QCBOR_CONVERT_TYPE_UINT64) {
+ if(Item.val.uint64 < INT64_MAX) {
+ *pValue = Item.val.int64;
+ } else {
+ pMe->uLastError = QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW;
+ }
+ } else {
+ pMe->uLastError = QCBOR_ERR_CONVERSION_NOT_REQUESTED;
+ }
+ break;
+
+ default:
+ pMe->uLastError = QCBOR_ERR_UNEXPECTED_TYPE;
+ }
+}
+
+/* This works for signed, unsigned and float */
+void QCBORDecode_GetInt64Convert(QCBORDecodeContext *pMe, uint32_t uOptions, int64_t *pValue)
+{
+ QCBORItem Item;
+ QCBORDecode_GetInt64ConvertInternal(pMe, uOptions, pValue, &Item);
+}
+
+
+// TODO make this inline
+void QCBORDecode_GetInt64(QCBORDecodeContext *pMe, uint32_t uOptions, int64_t *pValue)
+{
+ QCBORDecode_GetInt64Convert(pMe, QCBOR_TYPE_INT64, pValue);
+}
+
+
+
+/*
+ Get the next item as an int64_t. The CBOR type can be unsigned, negative, float
+ a big float, a decimal fraction or a big num. Conversion will be dones as
+ expected. Some cases will error out with under or over flow.
+ */
+void QCBORDecode_GetInt64ConvertAll(QCBORDecodeContext *pMe, uint32_t uOptions, int64_t *pValue)
+{
+ QCBORItem Item;
+
+ QCBORDecode_GetInt64ConvertInternal(pMe, uOptions, pValue, &Item);
+
+ if(pMe->uLastError == QCBOR_SUCCESS) {
+ // The above conversion succeeded
+ return;
+ }
+
+ if(pMe->uLastError != QCBOR_SUCCESS && pMe->uLastError != QCBOR_ERR_UNEXPECTED_TYPE) {
+ // The above conversion failed in a way that code below can't correct
+ return;
+ }
+
+ switch(Item.uDataType) {
+
+ case QCBOR_TYPE_POSBIGNUM:
+ if(uOptions & QCBOR_CONVERT_TYPE_BIG_NUM) {
+ pMe->uLastError = (uint8_t)ConvertPositiveBigNumToSigned(Item.val.bigNum, pValue);
+ } else {
+ pMe->uLastError = QCBOR_ERR_CONVERSION_NOT_REQUESTED;
+ }
+ break;
+
+ case QCBOR_TYPE_NEGBIGNUM:
+ if(uOptions & QCBOR_CONVERT_TYPE_BIG_NUM) {
+ pMe->uLastError = (uint8_t)ConvertNegativeBigNumToSigned(Item.val.bigNum, pValue);
+ } else {
+ pMe->uLastError = QCBOR_ERR_CONVERSION_NOT_REQUESTED;
+ }
+ break;
+
+#ifndef QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA
+ case QCBOR_TYPE_DECIMAL_FRACTION:
+ if(uOptions & QCBOR_CONVERT_TYPE_DECIMAL_FRACTION) {
+ pMe->uLastError = (uint8_t)ExponentiateNN(Item.val.expAndMantissa.Mantissa.nInt,
+ Item.val.expAndMantissa.nExponent,
+ pValue,
+ &Exponentitate10UU);
+ } else {
+ pMe->uLastError = QCBOR_ERR_CONVERSION_NOT_REQUESTED;
+ }
+ break;
+
+ case QCBOR_TYPE_BIGFLOAT:
+ if(uOptions & QCBOR_CONVERT_TYPE_BIGFLOAT) {
+ pMe->uLastError = (uint8_t)ExponentiateNN(Item.val.expAndMantissa.Mantissa.nInt,
+ Item.val.expAndMantissa.nExponent,
+ pValue,
+ &Exponentitate2UU);
+ } else {
+ pMe->uLastError = QCBOR_ERR_CONVERSION_NOT_REQUESTED;
+ }
+ break;
+
+
+ case QCBOR_TYPE_DECIMAL_FRACTION_POS_BIGNUM:
+ if(uOptions & QCBOR_CONVERT_TYPE_DECIMAL_FRACTION) {
+ int64_t nMantissa;
+ pMe->uLastError = (uint8_t)ConvertPositiveBigNumToSigned(Item.val.expAndMantissa.Mantissa.bigNum, &nMantissa);
+ if(!pMe->uLastError) {
+ pMe->uLastError = (uint8_t)ExponentiateNN(nMantissa,
+ Item.val.expAndMantissa.nExponent,
+ pValue,
+ &Exponentitate10UU);
+ }
+ } else {
+ pMe->uLastError = QCBOR_ERR_CONVERSION_NOT_REQUESTED;
+ }
+ break;
+
+ case QCBOR_TYPE_DECIMAL_FRACTION_NEG_BIGNUM:
+ if(uOptions & QCBOR_CONVERT_TYPE_DECIMAL_FRACTION) {
+ int64_t nMantissa;
+ pMe->uLastError = (uint8_t)ConvertNegativeBigNumToSigned(Item.val.expAndMantissa.Mantissa.bigNum, &nMantissa);
+ if(!pMe->uLastError) {
+ pMe->uLastError = (uint8_t)ExponentiateNN(nMantissa,
+ Item.val.expAndMantissa.nExponent,
+ pValue,
+ Exponentitate10UU);
+ }
+ } else {
+ pMe->uLastError = QCBOR_ERR_CONVERSION_NOT_REQUESTED;
+ }
+ break;
+
+ case QCBOR_TYPE_BIGFLOAT_POS_BIGNUM:
+ if(uOptions & QCBOR_CONVERT_TYPE_DECIMAL_FRACTION) {
+ int64_t nMantissa;
+ pMe->uLastError = (uint8_t)ConvertPositiveBigNumToSigned(Item.val.expAndMantissa.Mantissa.bigNum, &nMantissa);
+ if(!pMe->uLastError) {
+ pMe->uLastError = (uint8_t)ExponentiateNN(nMantissa,
+ Item.val.expAndMantissa.nExponent,
+ pValue,
+ Exponentitate2UU);
+ }
+ } else {
+ pMe->uLastError = QCBOR_ERR_CONVERSION_NOT_REQUESTED;
+ }
+ break;
+
+ case QCBOR_TYPE_BIGFLOAT_NEG_BIGNUM:
+ if(uOptions & QCBOR_CONVERT_TYPE_DECIMAL_FRACTION) {
+ int64_t nMantissa;
+ pMe->uLastError = (uint8_t)ConvertNegativeBigNumToSigned(Item.val.expAndMantissa.Mantissa.bigNum, &nMantissa);
+ if(!pMe->uLastError) {
+ pMe->uLastError = (uint8_t)ExponentiateNN(nMantissa,
+ Item.val.expAndMantissa.nExponent,
+ pValue,
+ Exponentitate2UU);
+ }
+ } else {
+ pMe->uLastError = QCBOR_ERR_CONVERSION_NOT_REQUESTED;
+ }
+ break;
+
+ default:
+ pMe->uLastError = QCBOR_ERR_UNEXPECTED_TYPE;
+#endif
+ }
+}
+
+
+
+void QCBORDecode_GetUInt64ConvertInternal(QCBORDecodeContext *pMe, uint32_t uOptions, uint64_t *pValue, QCBORItem *pItem)
+{
+ if(pMe->uLastError != QCBOR_SUCCESS) {
+ return;
+ }
+
+ QCBORItem Item;
+ QCBORError nError;
+
+ nError = QCBORDecode_GetNext(pMe, &Item);
+ if(nError) {
+ pMe->uLastError = (uint8_t)nError;
+ return;
+ }
+
+ switch(Item.uDataType) {
+ case QCBOR_TYPE_FLOAT:
+ if(uOptions & QCBOR_CONVERT_TYPE_FLOAT) {
+ if(Item.val.dfnum >= 0) {
+ // TODO: over/underflow
+ *pValue = (uint64_t)Item.val.dfnum;
+ } else {
+ pMe->uLastError = QCBOR_ERR_NUMBER_SIGN_CONVERSION;
+ }
+ } else {
+ pMe->uLastError = QCBOR_ERR_CONVERSION_NOT_REQUESTED;
+ }
+ break;
+
+ case QCBOR_TYPE_INT64:
+ if(uOptions & QCBOR_CONVERT_TYPE_INT64) {
+ if(Item.val.int64 >= 0) {
+ *pValue = (uint64_t)Item.val.int64;
+ } else {
+ pMe->uLastError = QCBOR_ERR_NUMBER_SIGN_CONVERSION;
+ }
+ } else {
+ pMe->uLastError = QCBOR_ERR_CONVERSION_NOT_REQUESTED;
+ }
+ break;
+
+ case QCBOR_TYPE_UINT64:
+ if(uOptions & QCBOR_CONVERT_TYPE_UINT64) {
+ *pValue = Item.val.uint64;
+ } else {
+ pMe->uLastError = QCBOR_ERR_CONVERSION_NOT_REQUESTED;
+ }
+ break;
+
+ default:
+ pMe->uLastError = QCBOR_ERR_UNEXPECTED_TYPE;
+ }
+}
+
+
+/* This works for signed, unsigned and float */
+void QCBORDecode_GetUInt64Convert(QCBORDecodeContext *pMe, uint32_t uOptions, uint64_t *pValue)
+{
+ QCBORItem Item;
+ QCBORDecode_GetUInt64ConvertInternal(pMe, uOptions, pValue, &Item);
+}
+
+
+// TODO make this inline
+void QCBORDecode_GetUInt64(QCBORDecodeContext *pMe, uint32_t uOptions, uint64_t *pValue)
+{
+ QCBORDecode_GetUInt64Convert(pMe, QCBOR_TYPE_UINT64, pValue);
+}
+
+
+
+
+void QCBORDecode_GetUInt64ConvertAll(QCBORDecodeContext *pMe, uint32_t uOptions, uint64_t *pValue)
+{
+ QCBORItem Item;
+
+ QCBORDecode_GetUInt64ConvertInternal(pMe, uOptions, pValue, &Item);
+
+ if(pMe->uLastError != QCBOR_SUCCESS && pMe->uLastError != QCBOR_ERR_UNEXPECTED_TYPE) {
+ return;
+ }
+
+ switch(Item.uDataType) {
+
+ case QCBOR_TYPE_POSBIGNUM:
+ if(uOptions & QCBOR_CONVERT_TYPE_BIG_NUM) {
+ pMe->uLastError = (uint8_t)ConvertPositiveBigNumToUnSigned(Item.val.bigNum, pValue);
+ } else {
+ pMe->uLastError = QCBOR_ERR_CONVERSION_NOT_REQUESTED;
+ }
+ break;
+
+ case QCBOR_TYPE_NEGBIGNUM:
+ if(uOptions & QCBOR_CONVERT_TYPE_BIG_NUM) {
+ pMe->uLastError = (uint8_t)ConvertPositiveBigNumToUnSigned(Item.val.bigNum, pValue);
+ } else {
+ pMe->uLastError = QCBOR_ERR_CONVERSION_NOT_REQUESTED;
+ }
+ break;
+
+#ifndef QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA
+
+ case QCBOR_TYPE_DECIMAL_FRACTION:
+ if(uOptions & QCBOR_CONVERT_TYPE_DECIMAL_FRACTION) {
+ pMe->uLastError = (uint8_t)ExponentitateNU(Item.val.expAndMantissa.Mantissa.nInt,
+ Item.val.expAndMantissa.nExponent,
+ pValue,
+ Exponentitate10UU);
+ } else {
+ pMe->uLastError = QCBOR_ERR_CONVERSION_NOT_REQUESTED; // TODO: error code
+ }
+ break;
+
+ case QCBOR_TYPE_BIGFLOAT:
+ if(uOptions & QCBOR_CONVERT_TYPE_BIGFLOAT) {
+ pMe->uLastError = (uint8_t)ExponentitateNU(Item.val.expAndMantissa.Mantissa.nInt,
+ Item.val.expAndMantissa.nExponent,
+ pValue,
+ Exponentitate2UU);
+ } else {
+ pMe->uLastError = QCBOR_ERR_CONVERSION_NOT_REQUESTED; // TODO: error code
+ }
+ break;
+
+
+
+ case QCBOR_TYPE_DECIMAL_FRACTION_POS_BIGNUM:
+ if(uOptions & QCBOR_CONVERT_TYPE_DECIMAL_FRACTION) {
+ int64_t nMantissa;
+ pMe->uLastError = (uint8_t)ConvertPositiveBigNumToSigned(Item.val.expAndMantissa.Mantissa.bigNum, &nMantissa);
+ if(!pMe->uLastError) {
+ pMe->uLastError = (uint8_t)ExponentitateNU(nMantissa,
+ Item.val.expAndMantissa.nExponent,
+ pValue,
+ Exponentitate10UU);
+ }
+ } else {
+ pMe->uLastError = QCBOR_ERR_CONVERSION_NOT_REQUESTED; // TODO: error code
+ }
+ break;
+
+ case QCBOR_TYPE_DECIMAL_FRACTION_NEG_BIGNUM:
+ if(uOptions & QCBOR_CONVERT_TYPE_DECIMAL_FRACTION) {
+ int64_t nMantissa;
+ pMe->uLastError = (uint8_t)ConvertNegativeBigNumToSigned(Item.val.expAndMantissa.Mantissa.bigNum, &nMantissa);
+ if(!pMe->uLastError) {
+ pMe->uLastError = (uint8_t)ExponentitateNU(nMantissa,
+ Item.val.expAndMantissa.nExponent,
+ pValue,
+ Exponentitate10UU);
+ }
+ } else {
+ pMe->uLastError = QCBOR_ERR_CONVERSION_NOT_REQUESTED; // TODO: error code
+ }
+ break;
+
+ case QCBOR_TYPE_BIGFLOAT_POS_BIGNUM:
+ if(uOptions & QCBOR_CONVERT_TYPE_DECIMAL_FRACTION) {
+ int64_t nMantissa;
+ pMe->uLastError = (uint8_t)ConvertPositiveBigNumToSigned(Item.val.expAndMantissa.Mantissa.bigNum, &nMantissa);
+ if(!pMe->uLastError) {
+ pMe->uLastError = (uint8_t)ExponentitateNU(nMantissa,
+ Item.val.expAndMantissa.nExponent,
+ pValue,
+ Exponentitate2UU);
+ }
+ } else {
+ pMe->uLastError = QCBOR_ERR_CONVERSION_NOT_REQUESTED; // TODO: error code
+ }
+ break;
+
+ case QCBOR_TYPE_BIGFLOAT_NEG_BIGNUM:
+ if(uOptions & QCBOR_CONVERT_TYPE_DECIMAL_FRACTION) {
+ int64_t nMantissa;
+ pMe->uLastError = (uint8_t)ConvertNegativeBigNumToSigned(Item.val.expAndMantissa.Mantissa.bigNum, &nMantissa);
+ if(!pMe->uLastError) {
+ pMe->uLastError = (uint8_t)ExponentitateNU(nMantissa,
+ Item.val.expAndMantissa.nExponent,
+ pValue,
+ Exponentitate2UU);
+ }
+ } else {
+ pMe->uLastError = QCBOR_ERR_CONVERSION_NOT_REQUESTED;
+ }
+ break;
+#endif
+ default:
+ pMe->uLastError = QCBOR_ERR_UNEXPECTED_TYPE;
+ }
+}
+
+
+
+#if 0
+/*
+
+ Convert from bignums,
+
+ */
+void QCBORDecode_GetDoubleConvertAll(QCBORDecodeContext *pMe, uint32_t uOptions, double *pValue)
+{
+ /* the same range of conversions */
+
+ /* Conversion from bignums, decimal fractions and such will be interesting */
+
+ QCBORItem Item;
+ QCBORError nError;
+
+ nError = QCBORDecode_GetNext(pMe, &Item);
+ if(nError) {
+ pMe->uLastError = nError;
+ return;
+ }
+
+
+ switch(Item.uDataType) {
+ case QCBOR_TYPE_FLOAT:
+ if(uOptions & QCBOR_DECODE_TYPE_FLOAT) {
+ *pValue = Item.val.dfnum;
+ } else {
+ pMe->uLastError = 99; // TODO: error code
+ }
+ break;
+
+ case QCBOR_TYPE_INT64:
+ if(uOptions & QCBOR_DECODE_TYPE_INT64) {
+ // TODO: how does this work?
+ *pValue = (double)Item.val.int64;
+ } else {
+ pMe->uLastError = 99; // TODO: error code
+ }
+ break;
+
+ case QCBOR_TYPE_UINT64:
+ if(uOptions & QCBOR_DECODE_TYPE_UINT64) {
+ *pValue = (double)Item.val.uint64;
+ } else {
+ pMe->uLastError = 99; // TODO: error code
+ }
+ break;
+
+ case QCBOR_TYPE_DECIMAL_FRACTION:
+ if(uOptions & QCBOR_DECODE_TYPE_DECIMAL_FRACTION) {
+ pMe->uLastError = Exponentitate10(Item.val.expAndMantissa.Mantissa.nInt,
+ Item.val.expAndMantissa.nExponent,
+ pValue);
+ } else {
+ pMe->uLastError = 99; // TODO: error code
+ }
+ break;
+
+ case QCBOR_TYPE_BIGFLOAT:
+ if(uOptions & QCBOR_DECODE_TYPE_BIGFLOAT) {
+ pMe->uLastError = Exponentitate2(Item.val.expAndMantissa.Mantissa.nInt,
+ Item.val.expAndMantissa.nExponent,
+ pValue);
+ } else {
+ pMe->uLastError = 99; // TODO: error code
+ }
+ break;
+
+ case QCBOR_TYPE_POSBIGNUM:
+ if(uOptions & QCBOR_DECODE_TYPE_BIG_NUM) {
+ pMe->uLastError = ConvertPositiveBigNumToSigned(Item.val.bigNum, pValue);
+ } else {
+ pMe->uLastError = 99;
+ }
+ break;
+
+ case QCBOR_TYPE_NEGBIGNUM:
+ if(uOptions & QCBOR_DECODE_TYPE_BIG_NUM) {
+ pMe->uLastError = ConvertNegativeBigNumToSigned(Item.val.bigNum, pValue);
+ } else {
+ pMe->uLastError = 99;
+ }
+ break;
+
+ case QCBOR_TYPE_DECIMAL_FRACTION_POS_BIGNUM:
+ if(uOptions & QCBOR_DECODE_TYPE_DECIMAL_FRACTION) {
+ int64_t nMantissa;
+ pMe->uLastError = ConvertPositiveBigNumToSigned(Item.val.expAndMantissa.Mantissa.bigNum, &nMantissa);
+ if(!pMe->uLastError) {
+ pMe->uLastError = Exponentitate10(nMantissa,
+ Item.val.expAndMantissa.nExponent,
+ pValue);
+ }
+ } else {
+ pMe->uLastError = 99; // TODO: error code
+ }
+ break;
+
+ case QCBOR_TYPE_DECIMAL_FRACTION_NEG_BIGNUM:
+ if(uOptions & QCBOR_DECODE_TYPE_DECIMAL_FRACTION) {
+ int64_t nMantissa;
+ pMe->uLastError = ConvertNegativeBigNumToSigned(Item.val.expAndMantissa.Mantissa.bigNum, &nMantissa);
+ if(!pMe->uLastError) {
+ pMe->uLastError = Exponentitate10(nMantissa,
+ Item.val.expAndMantissa.nExponent,
+ pValue);
+ }
+ } else {
+ pMe->uLastError = 99; // TODO: error code
+ }
+ break;
+
+ case QCBOR_TYPE_BIGFLOAT_POS_BIGNUM:
+ if(uOptions & QCBOR_DECODE_TYPE_DECIMAL_FRACTION) {
+ int64_t nMantissa;
+ pMe->uLastError = ConvertPositiveBigNumToSigned(Item.val.expAndMantissa.Mantissa.bigNum, &nMantissa);
+ if(!pMe->uLastError) {
+ pMe->uLastError = Exponentitate2(nMantissa,
+ Item.val.expAndMantissa.nExponent,
+ pValue);
+ }
+ } else {
+ pMe->uLastError = 99; // TODO: error code
+ }
+ break;
+
+ case QCBOR_TYPE_BIGFLOAT_NEG_BIGNUM:
+ if(uOptions & QCBOR_DECODE_TYPE_DECIMAL_FRACTION) {
+ int64_t nMantissa;
+ pMe->uLastError = ConvertNegativeBigNumToSigned(Item.val.expAndMantissa.Mantissa.bigNum, &nMantissa);
+ if(!pMe->uLastError) {
+ pMe->uLastError = Exponentitate2(nMantissa,
+ Item.val.expAndMantissa.nExponent,
+ pValue);
+ }
+ } else {
+ pMe->uLastError = 99; // TODO: error code
+ }
+ break;
+ }
+
+
+
+}
+
+#endif
diff --git a/src/qcbor_err_to_str.c b/src/qcbor_err_to_str.c
index 3f9db0a..521b8c5 100644
--- a/src/qcbor_err_to_str.c
+++ b/src/qcbor_err_to_str.c
@@ -10,7 +10,7 @@
Created on 3/21/20
=============================================================================*/
-#include "qcbor.h"
+#include "qcbor/qcbor_common.h"
#define _ERR_TO_STR(errpart) case QCBOR_##errpart: return "QCBOR_" #errpart;
diff --git a/test/qcbor_decode_tests.c b/test/qcbor_decode_tests.c
index 2be56d7..b18d40b 100644
--- a/test/qcbor_decode_tests.c
+++ b/test/qcbor_decode_tests.c
@@ -3789,6 +3789,193 @@
#endif /* QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA */
+
+/*
+ Some basic CBOR with map and array used in a lot of tests.
+ The map labels are all strings
+
+ {"first integer": 42,
+ "an array of two strings": [
+ "string1", "string2"
+ ],
+ "map in a map": {
+ "bytes 1": h'78787878',
+ "bytes 2": h'79797979',
+ "another int": 98,
+ "text 2": "lies, damn lies and statistics"
+ }
+ }
+ */
+
+#include "qcbor/qcbor_decode_map.h"
+#include <stdio.h>
+
+static char strbuf[10];
+const char *PrintType(uint8_t type) {
+ switch(type) {
+ case QCBOR_TYPE_INT64: return "INT64";
+ case QCBOR_TYPE_UINT64: return "UINT64";
+ case QCBOR_TYPE_ARRAY: return "ARRAY";
+ case QCBOR_TYPE_MAP: return "MAP";
+ case QCBOR_TYPE_BYTE_STRING: return "BYTE_STRING";
+ case QCBOR_TYPE_TEXT_STRING: return "TEXT_STRING";
+ default:
+ sprintf(strbuf, "%d", type);
+ return strbuf;
+ }
+}
+
+
+void PrintItem(QCBORItem Item)
+{
+ printf("\nData: %s nest: %d,%d %s\n", PrintType(Item.uDataType), Item.uNestingLevel, Item.uNextNestLevel, Item.uDataAlloc ? "Allocated":"");
+ if(Item.uLabelType) {
+ printf("Label: %s ", PrintType(Item.uLabelType));
+ if(Item.uLabelType == QCBOR_TYPE_INT64) {
+ printf("%lld\n", Item.label.int64);
+ } else if(Item.uLabelType == QCBOR_TYPE_TEXT_STRING) {
+ printf("\"%4.4s\"\n", Item.label.string.ptr);
+ }
+ }
+}
+
+int32_t EnterMapTest()
+{
+
+
+ QCBORDecodeContext DCtx;
+ QCBORError nCBORError;
+
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pValidMapEncoded), 0);
+
+
+ /* do {
+ QCBORItem Item;
+
+ nCBORError = QCBORDecode_GetNext(&DCtx, &Item);
+
+ PrintItem(Item);
+ //printf("type: %d, nest %d, next: %d\n", Item.uDataType, Item.uNestingLevel, Item.uNextNestLevel);
+ } while(nCBORError == 0); */
+
+
+
+
+ QCBORDecode_EnterMap(&DCtx);
+
+ int64_t nDecodedInt1, nDecodedInt2;
+ UsefulBufC B1, B2, S1;
+
+ QCBORDecode_GetIntInMapSZ(&DCtx, "first integer", &nDecodedInt1);
+
+ QCBORDecode_EnterMapFromMapSZ(&DCtx, "map in a map");
+
+ QCBORDecode_GetIntInMapSZ(&DCtx, "another int", &nDecodedInt2);
+ QCBORDecode_GetBstrInMapSZ(&DCtx, "bytes 1", &B1);
+ QCBORDecode_GetBstrInMapSZ(&DCtx, "bytes 2", &B2);
+ QCBORDecode_GetTextInMapSZ(&DCtx, "text 2", &S1);
+
+ QCBORDecode_ExitMap(&DCtx);
+
+ QCBORDecode_EnterArrayFromMapSZ(&DCtx, "an array of two strings");
+
+ QCBORItem Item1, Item2, Item3;
+ QCBORDecode_GetNext(&DCtx, &Item1);
+ QCBORDecode_GetNext(&DCtx, &Item2);
+ if(QCBORDecode_GetNext(&DCtx, &Item3) != QCBOR_ERR_NO_MORE_ITEMS) {
+ return -400;
+ }
+
+
+ QCBORDecode_ExitArray(&DCtx);
+
+
+ QCBORDecode_ExitMap(&DCtx);
+
+ nCBORError = QCBORDecode_Finish(&DCtx);
+
+ if(nCBORError) {
+ return (int32_t)nCBORError;
+ }
+
+ if(nDecodedInt1 != 42) {
+ return 1000;
+ }
+
+ if(nDecodedInt2 != 98) {
+ return 2000;
+ }
+
+ return 0;
+}
+
+
+
+
+
+int32_t IntegerConvertTest()
+{
+
+ QCBORDecodeContext DCtx;
+ QCBORError nCBORError;
+
+ /*
+ [
+ 4([-1, 3]),
+ 4([-20, 4759477275222530853136]),
+ 4([9223372036854775807, -4759477275222530853137]),
+ 5([300, 100]),
+ 5([-20, 4759477275222530853136]),
+ 5([-9223372036854775807, -4759477275222530853137])
+ 5([9223372036854775806, -4759477275222530853137])
+ 5([9223372036854775806, 9223372036854775806])]
+ ]
+ */
+
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedExponentsAndMantissas), 0);
+
+ QCBORItem Item;
+ nCBORError = QCBORDecode_GetNext(&DCtx, &Item);
+ if(nCBORError) {
+ return -1;
+ }
+
+ int64_t integer;
+ QCBORDecode_GetInt64ConvertAll(&DCtx, QCBOR_CONVERT_TYPE_DECIMAL_FRACTION, &integer);
+ if(QCBORDecode_GetLastError(&DCtx) != QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW) {
+ return -2;
+ }
+
+ DCtx.uLastError = 0; // TODO: a method for this
+
+ QCBORDecode_GetInt64ConvertAll(&DCtx, QCBOR_CONVERT_TYPE_DECIMAL_FRACTION, &integer);
+ if(QCBORDecode_GetLastError(&DCtx) != QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW) {
+ return -2;
+ }
+ DCtx.uLastError = 0; // TODO: a method for this
+
+ QCBORDecode_GetInt64ConvertAll(&DCtx, QCBOR_CONVERT_TYPE_DECIMAL_FRACTION, &integer);
+ if(QCBORDecode_GetLastError(&DCtx) != QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW) {
+ return -2;
+ }
+ DCtx.uLastError = 0; // TODO: a method for this
+
+ QCBORDecode_GetInt64ConvertAll(&DCtx, QCBOR_CONVERT_TYPE_DECIMAL_FRACTION|QCBOR_CONVERT_TYPE_BIGFLOAT, &integer);
+ if(QCBORDecode_GetLastError(&DCtx) != QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW) {
+ return -2;
+ }
+ DCtx.uLastError = 0; // TODO: a method for this
+
+ QCBORDecode_GetInt64ConvertAll(&DCtx, QCBOR_CONVERT_TYPE_DECIMAL_FRACTION|QCBOR_CONVERT_TYPE_BIGFLOAT, &integer);
+ if(QCBORDecode_GetLastError(&DCtx) != QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW) {
+ return -2;
+ }
+ DCtx.uLastError = 0; // TODO: a method for this
+
+ return 0;
+}
+
+
int32_t CBORSequenceDecodeTests(void)
{
QCBORDecodeContext DCtx;
@@ -3917,4 +4104,3 @@
return 0;
}
-
diff --git a/test/qcbor_decode_tests.h b/test/qcbor_decode_tests.h
index 92e217c..daabeab 100644
--- a/test/qcbor_decode_tests.h
+++ b/test/qcbor_decode_tests.h
@@ -252,6 +252,9 @@
#endif /* QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA */
+int32_t EnterMapTest(void);
+
+int32_t IntegerConvertTest(void);
/*
Tests decoding of CBOR Sequences defined in RFC 8742
*/
diff --git a/test/run_tests.c b/test/run_tests.c
index ac2ab78..bcf3fd1 100644
--- a/test/run_tests.c
+++ b/test/run_tests.c
@@ -56,6 +56,8 @@
static test_entry s_tests[] = {
+ TEST_ENTRY(IntegerConvertTest),
+ TEST_ENTRY(EnterMapTest),
TEST_ENTRY(QCBORHeadTest),
TEST_ENTRY(EmptyMapsAndArraysTest),
TEST_ENTRY(NotWellFormedTests),