checkpoint work on arrays
diff --git a/README.md b/README.md
index 7a55e3e..1cb9a59 100644
--- a/README.md
+++ b/README.md
@@ -201,34 +201,94 @@
Some of the error codes have changed.
-## Floating Point Support
+## Floating Point Support & Configuration
By default, all QCBOR floating-point features are enabled. This includes
encoding and decoding of half-precision, CBOR preferred serialization
for floating-point and floating-point epoch dates.
If full floating-point is not needed the following #defines can be
-used to reduce object code size.
+used to reduce object code size and dependency.
-QCBOR_DISABLE_FLOAT_HW_USE -- Avoid all use of floating-point hardware
-and inclusion of <math.h> and <fenv.h>
+See discussion in qcbor_encode.h for other details.
-QCBOR_DISABLE_PREFERRED_FLOAT -- Eliminates support for half-precision
-and CBOR preferred serialization.
+### QCBOR_DISABLE_FLOAT_HW_USE
-Even if these are #defined, QCBOR can still encode and decode basic
-floating point numbers.
+This removes dependency on:
-Defining QCBOR_DISABLE_PREFERRED_FLOAT will reduce object code size by
-about 900 bytes, though 550 of these bytes can be avoided without the
-#define by just not calling any of the functions that encode
-floating-point numbers.
+* floating-point hardware and floating-point instructions
+* <math.h> and <fenv.h>
+* the math library (libm, -lm)
-Defining QCBOR_DISABLE_FLOAT_HW_USE will save a small amount of object
-code. Its main use is on CPUs that have no floating-point hardware.
+For most limited environments, this removes enough
+floating-point dependencies to be able to compile
+and run QCBOR.
-See discussion in qcbor_encode.h for details.
-G
+Note that this does not remove use of the types
+double and float from QCBOR, but it limits QCBOR's
+use of them to converting the encoded byte stream to them
+and copying them. Converting and copying them
+usually doesn't require any hardware, libraries
+or includes. The C compiler takes care of it on its
+own.
+
+QCBOR uses it's own implementation of
+half-precision float-pointing that doesn't depend on
+math libraries. It uses masks and shifts instead. Thus
+even with this define, half-precision encoding and
+decoding works.
+
+When this is defined, the QCBOR functionality lost
+is minimal and only for decoding:
+
+* Decoding floating-point format dates are not handled
+* There is no conversion between floats and integers when decoding. For
+example, QCBORDecode_GetUInt64ConvertAll() will be unable to convert
+to and from float-point.
+* Floats will not be converted to double when decoding.
+
+No interfaces are disabled or removed with this define.
+If input that requires floating-point conversion or functions
+are called that request floating-point conversion, an error
+code like QCBOR_ERR_HW_FLOAT_DISABLED will be
+returned.
+
+This saves only a small amount of object code. The primary
+purpose for defining this is to remove dependency on
+floating point hardware and libraries.
+
+### QCBOR_DISABLE_PREFERRED_FLOAT
+
+This eliminates support for half-precision
+and CBOR preferred serialization by disabling
+QCBOR's shift and mask based implementation of
+half-precision floating-point.
+
+With this defined, single and double-precision floating-point
+numbers can still be encoded and decoded. Conversion
+of floating-point to and from integers, big numbers and
+such is also supported. Floating-point dates are still
+supported.
+
+The primary reason to define this is to save object code.
+Roughly 900 bytes are saved, though about half of this
+can be saved just by not calling any functions that
+encode floating point numbers.
+
+### Compiler options
+
+Compilers support a number of options that control
+which float-point related code is generated. For example,
+it is usually possible to give options to the compiler to avoid all
+floating-point hardware and instructions, to use software
+and replacement libraries instead. These are usually
+bigger and slower, but these options may still be useful
+in getting QCBOR to run in some environments in
+combination with QCBOR_DISABLE_FLOAT_HW_USE.
+In particular, -mfloat-abi=soft, disables use of
+ hardware instructions for the float and double
+ types in C for some architectures.
+
## Comparison to TinyCBOR
TinyCBOR is a popular widely used implementation. Like QCBOR,
diff --git a/inc/qcbor/UsefulBuf.h b/inc/qcbor/UsefulBuf.h
index 990191a..2974adf 100644
--- a/inc/qcbor/UsefulBuf.h
+++ b/inc/qcbor/UsefulBuf.h
@@ -1580,6 +1580,18 @@
static void UsefulInputBuf_SetBufferLength(UsefulInputBuf *pUInBuf, size_t uNewLen);
+// TODO: document this; maybe add tests...
+#if defined(USEFULBUF_CONFIG_BSWAP)
+#define USEFUL_SWAP32(integer) \
+ __builtin_bswap32(integer);
+#else
+#define USEFUL_SWAP32(integer) \
+ ((integer & 0xff) >> 24) + \
+ ((integer & 0xff00) >> 16) + \
+ ((integer & 0xff0000) >> 8) + \
+ (integer & 0xff000000)
+#endif
+
/*----------------------------------------------------------
@@ -1789,6 +1801,7 @@
}
+
static inline void UsefulOutBuf_InsertUint16(UsefulOutBuf *me,
uint16_t uInteger16,
size_t uPos)
@@ -1836,20 +1849,21 @@
uint32_t uTmp = htonl(uInteger32);
pBytes = &uTmp;
-#elif defined(USEFULBUF_CONFIG_LITTLE_ENDIAN) && defined(USEFULBUF_CONFIG_BSWAP)
- uint32_t uTmp = __builtin_bswap32(uInteger32);
-
+#elif defined(USEFULBUF_CONFIG_LITTLE_ENDIAN)
+ uint32_t uTmp = XSWAP(uInteger32);
pBytes = &uTmp;
#else
- uint8_t aTmp[4];
- aTmp[0] = (uint8_t)((uInteger32 & 0xff000000) >> 24);
- aTmp[1] = (uint8_t)((uInteger32 & 0xff0000) >> 16);
- aTmp[2] = (uint8_t)((uInteger32 & 0xff00) >> 8);
- aTmp[3] = (uint8_t)(uInteger32 & 0xff);
+ uint32_t uTmp =
+ ((uInteger32 & 0xff) >> 24) + \
+ ((uInteger32 & 0xff00) >> 16) + \
+ ((uInteger32 & 0xff0000) >> 8) + \
+ (uInteger32 & 0xff000000);
- pBytes = aTmp;
+ pBytes = &uTmp;
+
+
#endif
UsefulOutBuf_InsertData(pMe, pBytes, 4, uPos);
diff --git a/inc/qcbor/qcbor_common.h b/inc/qcbor/qcbor_common.h
index a7c33e0..b1a2e96 100644
--- a/inc/qcbor/qcbor_common.h
+++ b/inc/qcbor/qcbor_common.h
@@ -197,6 +197,16 @@
tag. */
#define CBOR_TAG_CBOR_SEQUENCE 63
+#define CBOR_TAG_UINT8_ARRAY 64
+
+#define CBOR_TAG_UINT16_BIG_ENDIAN_ARRAY 65
+#define CBOR_TAG_UINT32_BIG_ENDIAN_ARRAY 66
+#define CBOR_TAG_UINT64_BIG_ENDIAN_ARRAY 67
+
+#define CBOR_TAG_UINT16_LITTLE_ENDIAN_ARRAY 69
+#define CBOR_TAG_UINT32_LITTLE_ENDIAN_ARRAY 70
+#define CBOR_TAG_UINT64_LITTLE_ENDIAN_ARRAY 71
+
#define CBOR_TAG_ENCRYPT 96
/** Not Decoded by QCBOR. Tag for COSE format MAC. See [RFC 8152, COSE]
@@ -505,6 +515,14 @@
indefinite length map or array in the input CBOR. */
QCBOR_ERR_INDEF_LEN_ARRAYS_DISABLED = 44,
+ /** The input CBOR needs to be a multiple of 2, 4 or 8 bytes and it is not. */
+ QCBOR_ERR_INPUT_SIZE_MULTIPLE = 45,
+
+ /** The input CBOR is not a tag indicating endianness and the input endianness
+ needs to be know to proceed. */
+ QCBOR_ERR_INPUT_ENDIANNESS_UNKNOWN = 45,
+
+
/* This is stored in uint8_t; never add values > 255 */
} QCBORError;
diff --git a/inc/qcbor/qcbor_encode.h b/inc/qcbor/qcbor_encode.h
index 6ac004e..c25fbc5 100644
--- a/inc/qcbor/qcbor_encode.h
+++ b/inc/qcbor/qcbor_encode.h
@@ -503,9 +503,9 @@
*/
void QCBOREncode_AddInt64(QCBOREncodeContext *pCtx, int64_t nNum);
-static void QCBOREncode_AddInt64ToMap(QCBOREncodeContext *pCtx, const char *szLabel, int64_t uNum);
+static void QCBOREncode_AddInt64ToMap(QCBOREncodeContext *pCtx, const char *szLabel, int64_t nNum);
-static void QCBOREncode_AddInt64ToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, int64_t uNum);
+static void QCBOREncode_AddInt64ToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, int64_t nNum);
/**
@@ -1957,6 +1957,79 @@
uint64_t uNumber);
+
+
+
+/**
+ @brief Encode a "homogeneous" array of signed integers.
+
+ @param[in] pCtx The encoding context.
+ @param[in] uTagRequirement Either @ref QCBOR_ENCODE_AS_TAG or
+ @ref QCBOR_ENCODE_AS_BORROWED.
+ @param[in] pnNum The array of integers to output.
+ @param[in] uNumInts The number of integers in \c pnNum.
+
+ This encodes a homogeneous array of signed integers as described in section 3.2 in [RFC 8746](https://tools.ietf.org/html/rfc8746).
+
+ To encode the actual integers, this just opens an array, adds all the integers
+ and closes the array as one might expect. The only thing unusual is the
+ encoding of this as a tag.
+
+ If \c uTagRequirement is @ref QCBOR_ENCODE_AS_TAG then
+ this is encoded as the homogenous array tag, which means the array
+ is preceded by the tag number \ref CBOR_TAG_HOMOGENEOUS_ARRAY.
+
+ If \c uTagRequirement is @ref QCBOR_ENCODE_AS_BORROWED then
+ there is no tag number and this is the straight forward encoding of an
+ array of integers. This is "borrowed" because the definition of a homogenous
+ array is as a tag.
+
+ This uses QCBOREncode_AddInt64() to encode the integers so
+ preferred encoding in the shortest form is used and this will encode
+ with either major type 0 or 1 depending on whether each integer is
+ positive or negative.
+*/
+void
+QCBOREncode_AddArrayOfInt64s(QCBOREncodeContext *pCtx,
+ uint8_t uTagRequirement,
+ const int64_t pnNum[],
+ size_t uNumInts);
+
+
+void
+QCBOREncode_AddArrayOfUInt64s(QCBOREncodeContext *pMe,
+ uint8_t uTagRequirement,
+ const uint64_t puInts[],
+ size_t uNumUInts);
+
+
+void
+QCBOREncode_AddArrayOfDoubles(QCBOREncodeContext *pMe,
+ uint8_t uTagRequirement,
+ const double pdDoubles[],
+ size_t uNumDoubles);
+
+
+void
+QCBOREncode_AddArrayOfByteStrings(QCBOREncodeContext *pMe,
+ uint8_t uTagRequirement,
+ const UsefulBufC pStrings[],
+ size_t uNumStrings);
+
+
+void
+QCBOREncode_AddArrayOfTextStrings(QCBOREncodeContext *pMe,
+ uint8_t uTagRequirement,
+ const UsefulBufC pStrings[],
+ size_t uNumStrings);
+
+
+void
+QCBOREncode_AddArrayOfSZStrings(QCBOREncodeContext *pMe,
+ uint8_t uTagRequirement,
+ const char *pStrings[],
+ size_t uNumStrings);
+
/*
Definitely need to have two functions, one for BE and one for LE.
Since we have to say what format to output. There is no wire format.
@@ -1968,19 +2041,16 @@
use correct tag.
-
+There are going to be about 20 functions here for
+ all the different integer types.
*/
-void
-QCBOREncode_AddArrayOfInts(QCBOREncodeContext *pMe,
- uint8_t uTagRequirement,
- const int64_t *puInts,
- size_t uNumInts);
-void QCBOREncode_AddUint32ArrayBigEndian(QCBOREncodeContext *pCtx,
+
+void QCBOREncode_AddTypedArrayOfUInt32BigEndian(QCBOREncodeContext *pCtx,
const uint32_t array[],
size_t uArrayLen);
@@ -2156,18 +2226,18 @@
static inline void
-QCBOREncode_AddInt64ToMap(QCBOREncodeContext *pMe, const char *szLabel, int64_t uNum)
+QCBOREncode_AddInt64ToMap(QCBOREncodeContext *pMe, const char *szLabel, int64_t nNum)
{
// Use _AddBuffer() because _AddSZString() is defined below, not above
QCBOREncode_AddBuffer(pMe, CBOR_MAJOR_TYPE_TEXT_STRING, UsefulBuf_FromSZ(szLabel));
- QCBOREncode_AddInt64(pMe, uNum);
+ QCBOREncode_AddInt64(pMe, nNum);
}
static inline void
-QCBOREncode_AddInt64ToMapN(QCBOREncodeContext *pMe, int64_t nLabel, int64_t uNum)
+QCBOREncode_AddInt64ToMapN(QCBOREncodeContext *pMe, int64_t nLabel, int64_t nNum)
{
QCBOREncode_AddInt64(pMe, nLabel);
- QCBOREncode_AddInt64(pMe, uNum);
+ QCBOREncode_AddInt64(pMe, nNum);
}
diff --git a/inc/qcbor/qcbor_spiffy_decode.h b/inc/qcbor/qcbor_spiffy_decode.h
index a18c3c6..424b5ae 100644
--- a/inc/qcbor/qcbor_spiffy_decode.h
+++ b/inc/qcbor/qcbor_spiffy_decode.h
@@ -1700,6 +1700,176 @@
+/**
+ @brief Get a "homgeneous" array of 64-bit integers
+
+ @param[in] pCtx The decode context.
+
+ This decodes was is called the "homogenous array" tag in RFC xxxx.
+ The input must be a normal CBOR array where every item
+ in the array is CBOR item of major type 0 or 1.
+
+ See also QCBORDecode_GetTypedArrayOfInt64s() for an alternate encoding of 64-bit integers that is
+ more compact in some cases, but is NOT endian neutral.
+
+*/
+static void
+QCBORDecode_GetArrayOfInt64(QCBORDecodeContext *pCtx,
+ uint8_t uTagRequirement,
+ size_t nInArrayCount,
+ int64_t *pnInt64s,
+ size_t *pnOutArrayCount);
+
+static void
+QCBORDecode_GetArrayOfInt64InMapN(QCBORDecodeContext *pCtx,
+ int64_t nLabel,
+ uint8_t uTagRequirement,
+ size_t nInArrayCount,
+ int64_t *pnInt64s,
+ size_t *pnOutArrayCount);
+
+static void
+QCBORDecode_GetArrayOfInt64InMapSZ(QCBORDecodeContext *pCtx,
+ const char *szLabel,
+ uint8_t uTagRequirement,
+ size_t nInArrayCount,
+ int64_t *pnInt64s,
+ size_t *pnOutArrayCount);
+
+
+
+static void inline
+QCBORDecode_GetArrayOfUInt64(QCBORDecodeContext *pCtx,
+ uint8_t uTagRequirement,
+ size_t nInArrayCount,
+ int64_t *puUInt64s,
+ size_t *pnOutArrayCount);
+
+
+static void inline
+QCBORDecode_GetArrayOfTextStrings(QCBORDecodeContext *pCtx,
+ uint8_t uTagRequirement,
+ size_t nInArrayCount,
+ UsefulBufC *pStrings,
+ size_t *pnOutArrayCount);
+
+static void inline
+QCBORDecode_GetArrayOfByteStrings(QCBORDecodeContext *pCtx,
+ uint8_t uTagRequirement,
+ size_t nInArrayCount,
+ UsefulBufC *pStrings,
+ size_t *pnOutArrayCount);
+
+
+static void inline
+QCBORDecode_GetArrayOfDoubles(QCBORDecodeContext *pCtx,
+ uint8_t uTagRequirement,
+ size_t nInArrayCount,
+ double *pDoubles,
+ size_t *pnOutArrayCount);
+
+
+
+#define IS_BIG_ENDIAN 0
+#define IS_LITTLE_ENDIAN 1
+#define ENDIAN_UNKNOWN 2
+
+/**
+ @brief Get a "typed" array of 64-bit integers
+
+ @param[in] pCtx The decode context.
+
+ This decodes was is called the "type array" tag in RFC xxxx.
+ The CBOR to be decoded is a byte string where every 8 bytes are interpreted as
+ as signed 64-bit integer.
+
+ Unlike everything else in CBOR, the integers are NOT in network byte
+ order. The sender may have encoded them as big or little endian. The point
+ of doing this is to be able to more-efficiently send large arrays of integers
+ between implementing end-points that need and support this. This
+ avoids any copying or byte swapping of the integers saving both
+ memory and CPU cycles.
+
+ The array may be a CBOR tag or not. \c uTagRequrement instructs the
+ decoder as to whether it will decode as a tag.
+
+ If encoded CBOR is a tag, it will be known whether what was sent
+ was big or little endian and returned in \c uEndianness. If it
+ is not a tag, then the knowledge of the endianess has to come from
+ else where. Perhaps it is part of the particular protocol that all
+ type arrays are to be of a particular endianness.
+*/
+void QCBORDecode_GetTypedArrayOfInt64s(QCBORDecodeContext *pCtx,
+ uint8_t uTagRequirement,
+ uint64_t **pInts,
+ size_t *pSize,
+ uint8_t *uEndianess); // TODO: constants for endianness
+/**
+ * This specifies the enianness processing that should be done for a few
+ * methods for array decoding that are configurable this way.
+ *
+ * Note that the endianness of the input must be known for some
+ * of these to work. Somtimes the input CBOR is not a tag
+ * and the endianness is not know.
+ */
+typedef enum {
+ /** The output is to be big endian. The input must be a tag indicating its endianness for this to work. */
+ QCBOR_ENDIAN_BIG_ENDIAN = 0,
+ /** The output is to be little endian. The input must be a tag indicating its endianness for this to work. */
+ QCBOR_ENDIAN_LITTLE_ENDIAN = 1,
+ /** A swap will always be performed. It is up to the caller to know the input endianness. */
+ QCBOR_ENDIAN_SWAP = 2,
+ /** A swap will never be performed. It is up to the caller to know the input endianness. */
+ QCBOR_ENDIAN_NO_SWAP = 3,
+ /** Swapping will be performed if necessary to make the output the correct endianness. The input must be a tag indicating its endianness for this to work. */
+ QCBOR_ENDIAN_MATCH_ENDIANNESS = 4,
+} QCBOREndianRequirement;
+
+
+
+/**
+ @brief Get an "typed" array of 64-bit integers with copying and swapping
+
+ @param[in] pCtx The decode context.
+
+This decodes the same data type as QCBORDecode_GetTypedArrayOfInt64s().
+ Additionaly, it copies the array to a supplied buffer and does byte swapping
+ for endianness as instructed.
+
+ The received array may or may not have indication of its endianness.
+
+
+
+ The possible swapping options are:
+ - big endian; the input must have indication of its endianness for this to work
+ - little endianess
+ - swap; the endianness will always be swapped regardless of what of the input is marked
+ - no swap; there will be no swapping regardless of how the input is marked
+ - match configured endianness of machine
+*/
+void QCBORDecode_GetTypedArrayOfInt64sCopy(QCBORDecodeContext *pCtx,
+ uint8_t uTagRequirement,
+ QCBOREndianRequirement uSwapRequirement,
+ size_t uSize,
+ uint64_t **pInts,
+ size_t *pSize);
+
+
+
+void QCBORDecode_GetUInt32Array(QCBORDecodeContext *pCtx,
+ uint8_t uTagRequirement,
+ uint32_t **pInts,
+ size_t *pSize);
+
+
+void QCBORDecode_GetUint32ArrayCopy(QCBORDecodeContext *pCtx,
+ uint8_t uTagRequirement,
+ QCBOREndianRequirement uSwapRequirement,
+ size_t uSize,
+ uint32_t *pInts,
+ size_t *pSize);
+
+
/* ===========================================================================
BEGINNING OF PRIVATE INLINE IMPLEMENTATION
========================================================================== */
@@ -2585,7 +2755,10 @@
-
+/**
+ * Semi-private data structure to hold the pointer into which to copy
+ * a decoded homogeneous array.
+ */
union QCBORHomogenousArray {
int64_t *pnInt64s;
int64_t *puUInt64s;
@@ -2593,7 +2766,33 @@
UsefulBufC *pStrings;
};
-void QCBORDecode_GetHomogenousArray(QCBORDecodeContext *pMe,
+/**
+ * Semi-private method to decode a homogeneous array
+ *
+ * @param[in] pCtx The decode context.
+ * @param[in] uTagRequirement One of @c QCBOR_TAG_REQUIREMENT_XXX
+ * @param[in] uType The QCBOR type of the array elements.
+ * @param[in] nInArrayCount The size in number of elements of @c array passed in.
+ * @param[in,out] array The buffer into which the array is decoded.
+ * @param[out] pnOutArrayCount The number of elements in @c array that were filled in.
+ *
+ * This decodes the next item as a homogenous array where all the elements in the
+ * array can be decoded as the QCBOR_TYPE passed in as @c uType. The type
+ * must be one of @ref QCBOR_TYPE_INT64, @ref QCBOR_TYPE_UINT64,
+ * @ref QCBOR_TYPE_DOUBLE, @ref QCBOR_TYPE_TEXT_STRING or
+ * @ref QCBOR_TYPE_BYTE_STRING. Note that for QCBOR_TYPE_INT64
+ * the members in the array can be of either major type 0 or 1.
+ *
+ * The buffer into which the array is decoded is in the @ref QCBORHomogenousArray
+ * union. The size of the buffer must be @c nInArrayCount times the size of the
+ * data type. The number of elements decoded is returned in @c pnOutArrayCount.
+ *
+ * @c array may be NULL in which case the array will be decoded and
+ * @c pnOutArrayCount will be filled in, but note that input will be consumed.
+ * To actually get the decoded array values QCBORDecode_Rewind() will have to
+ * be used and it goes back to the start of an entered enclosing array or map.
+ */
+void QCBORDecode_GetHomogenousArray(QCBORDecodeContext *pCtx,
uint8_t uTagRequirement,
uint8_t uType,
size_t nInArrayCount,
@@ -2601,6 +2800,22 @@
size_t *pnOutArrayCount);
+void QCBORDecode_GetHomogenousArrayInMapN(QCBORDecodeContext *pMe,
+ int64_t nLabel,
+ uint8_t uTagRequirement,
+ uint8_t uType,
+ size_t nInArrayCount,
+ union QCBORHomogenousArray array,
+ size_t *pnOutArrayCount);
+
+void QCBORDecode_GetHomogenousArrayInMapSZ(QCBORDecodeContext *pMe,
+ const char *szLabel,
+ uint8_t uTagRequirement,
+ uint8_t uType,
+ size_t nInArrayCount,
+ union QCBORHomogenousArray array,
+ size_t *pnOutArrayCount);
+
static void inline
QCBORDecode_GetArrayOfInt64(QCBORDecodeContext *pMe,
uint8_t uTagRequirement,
@@ -2622,6 +2837,50 @@
static void inline
+QCBORDecode_GetArrayOfInt64InMapN(QCBORDecodeContext *pMe,
+ int64_t nLabel,
+ uint8_t uTagRequirement,
+ size_t nInArrayCount,
+ int64_t *pnInt64s,
+ size_t *pnOutArrayCount)
+{
+ union QCBORHomogenousArray array;
+
+ array.pnInt64s = pnInt64s;
+
+ QCBORDecode_GetHomogenousArrayInMapN(pMe,
+ nLabel,
+ uTagRequirement,
+ QCBOR_TYPE_INT64,
+ nInArrayCount,
+ array,
+ pnOutArrayCount);
+}
+
+static void inline
+QCBORDecode_GetArrayOfInt64InMapSZ(QCBORDecodeContext *pMe,
+ const char *szLabel,
+ uint8_t uTagRequirement,
+ size_t nInArrayCount,
+ int64_t *pnInt64s,
+ size_t *pnOutArrayCount)
+{
+ union QCBORHomogenousArray array;
+
+ array.pnInt64s = pnInt64s;
+
+ QCBORDecode_GetHomogenousArrayInMapSZ(pMe,
+ szLabel,
+ uTagRequirement,
+ QCBOR_TYPE_INT64,
+ nInArrayCount,
+ array,
+ pnOutArrayCount);
+}
+
+
+
+static void inline
QCBORDecode_GetArrayOfUInt64(QCBORDecodeContext *pMe,
uint8_t uTagRequirement,
size_t nInArrayCount,
diff --git a/src/qcbor_decode.c b/src/qcbor_decode.c
index 223cc7f..d7b3958 100644
--- a/src/qcbor_decode.c
+++ b/src/qcbor_decode.c
@@ -5922,34 +5922,145 @@
-/* The one that matches the endianness should return without copying
- The other either has to copy or has to swap by violating const-ness.
- If copying, the caller has to know. If violating const-ness, then
-the caller only knows that const-ness is violated. */
-void QCBORDecode_GetUint32ArrayBE(QCBORDecodeContext *pMe,
+
+
+typedef enum {
+ QCBOR_IS_BIG_ENDIAN = 0,
+ QCBOR_IS_LITTLE_ENDIAN = 1,
+ QCBOR_UNKNOWN_ENDIAN = 2
+} QCBOREndianness;
+
+void QCBORDecode_GetUint32Array(QCBORDecodeContext *pMe,
uint8_t uTagRequirement,
- uint32_t *pInts,
- size_t *pSize)
+ uint32_t **puUIntsDecoded,
+ size_t *puSizeDecoded,
+ QCBOREndianness *puEndianness)
{
- QCBORItem item;
- QCBORDecode_VGetNext(pMe, &item);
+ QCBORItem Item;
+ QCBORDecode_VGetNext(pMe, &Item);
- if(item.uDataType != QCBOR_TYPE_BYTE_STRING ||
- !QCBORDecode_IsTagged(pMe, &item, 99)) { // TODO: correct tag number
+ if(Item.uDataType != QCBOR_TYPE_BYTE_STRING) {
pMe->uLastError = QCBOR_ERR_UNEXPECTED_TYPE;
return;
}
- if(item.val.string.len % 4 != 0) {
- pMe->uLastError = 99; // TODO: figure out error
+ const TagSpecification TagSpec =
+ {
+ uTagRequirement,
+ {CBOR_TAG_UINT32_BIG_ENDIAN_ARRAY, CBOR_TAG_UINT32_LITTLE_ENDIAN_ARRAY, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
+ {QCBOR_TYPE_BYTE_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
+ };
+
+ pMe->uLastError = (uint8_t)CheckTagRequirement(TagSpec, &Item);
+ if(pMe->uLastError) {
return;
}
- *pSize = item.val.string.len / 4;
+ if(QCBORDecode_IsTagged(pMe, &Item, CBOR_TAG_UINT32_BIG_ENDIAN_ARRAY)) {
+ *puEndianness = QCBOR_IS_BIG_ENDIAN;
+ } else if(QCBORDecode_IsTagged(pMe, &Item, CBOR_TAG_UINT32_LITTLE_ENDIAN_ARRAY)) {
+ *puEndianness = QCBOR_IS_LITTLE_ENDIAN;
+ } else {
+ *puEndianness = QCBOR_UNKNOWN_ENDIAN;
+ }
- /* may have to swap if endianness doesn't match */
- pInts = (uint32_t *) &(item.val.string.ptr);
+
+ if(Item.val.string.len % sizeof(uint32_t) != 0) {
+ pMe->uLastError = QCBOR_ERR_INPUT_SIZE_MULTIPLE;
+ return;
+ }
+
+ *puSizeDecoded = Item.val.string.len / sizeof(uint32_t);
+
+ *puUIntsDecoded = (uint32_t *)(Item.val.string.ptr);
+}
+
+
+
+
+
+// positive values are QCBOR error codes
+#define PERFORM_SWAP -1
+#define DO_NOT_SWAP 0
+static int
+ShallWeSwap(QCBOREndianRequirement uSwapRequirement, QCBOREndianness uEndianness)
+{
+ /* Asked to always swap */
+ if(uSwapRequirement == QCBOR_ENDIAN_SWAP) {
+ return PERFORM_SWAP;
+ }
+
+ /* Asked to never swap */
+ if(uSwapRequirement == QCBOR_ENDIAN_NO_SWAP) {
+ return DO_NOT_SWAP;
+ }
+
+ /* The following use cases require the input endianness to be known */
+ if(uEndianness == QCBOR_UNKNOWN_ENDIAN) {
+ return QCBOR_ERR_INPUT_ENDIANNESS_UNKNOWN;
+ }
+
+ QCBOREndianRequirement uOutput = uSwapRequirement;
+ if(uOutput == QCBOR_ENDIAN_MATCH_ENDIANNESS) {
+#if defined(USEFULBUF_CONFIG_BIG_ENDIAN)
+ uOutput = QCBOR_ENDIAN_BIG_ENDIAN;
+#else
+ uOutput = QCBOR_ENDIAN_LITTLE_ENDIAN;
+#endif
+ }
+
+ if((QCBOREndianness)uOutput != uEndianness) {
+ return PERFORM_SWAP;
+ } else {
+ return DO_NOT_SWAP;
+ }
+}
+
+
+void QCBORDecode_GetUint32ArrayCopy(QCBORDecodeContext *pMe,
+ uint8_t uTagRequirement,
+ QCBOREndianRequirement uSwapRequirement,
+ size_t uBufferSize,
+ uint32_t *puUIntsBuffer,
+ size_t *puReturnedSize)
+{
+ size_t uReceivedArraySize;
+ uint32_t *puReceivedArray;
+ QCBOREndianness uEndianness;
+
+ QCBORDecode_GetUint32Array(pMe,
+ uTagRequirement,
+ &puReceivedArray,
+ &uReceivedArraySize,
+ &uEndianness);
+ if(pMe->uLastError) {
+ return;
+ }
+
+
+ if(uBufferSize < uReceivedArraySize) {
+ /* Given buffer is too small */
+ pMe->uLastError = QCBOR_ERR_BUFFER_TOO_SMALL;
+ return;
+ }
+
+ memcpy(puUIntsBuffer, puReceivedArray, uReceivedArraySize * sizeof(uint32_t));
+
+ int nSwapRequest = ShallWeSwap(uSwapRequirement, uEndianness);
+
+ if(nSwapRequest > 0) {
+ /* Error figuring out whether to swap or not. */
+ pMe->uLastError = (uint8_t)nSwapRequest;
+ return;
+ }
+
+ if(nSwapRequest == PERFORM_SWAP) {
+ for(size_t i = 0; i < uReceivedArraySize; i++) {
+ const uint32_t uArrayElement = USEFUL_SWAP32(puUIntsBuffer[i]);
+ puUIntsBuffer[i] = uArrayElement;
+ }
+ }
}
@@ -5966,7 +6077,15 @@
*pSize = item.val.string.len / 4;
- // TODO: must swap all the ints
+#if defined(USEFULBUF_CONFIG_BIG_ENDIAN)
+ /* Must swap all the unsigned integers */
+ uint32_t *pUIntStart = item.val.string.ptr;
+ uint32_t *pUIntEnd = pUIntStart + *pSize;
+ for(uint32_t *pUInt = pUIntStart; pUInt < pUIntEnd; pUInt++) {
+ const uint32_t u = USEFUL_SWAP32(*pUInt);
+ *pUInt = u;
+ }
+#endif
pInts = (uint32_t *) &(item.val.string.ptr);
@@ -6043,18 +6162,14 @@
}
-
-
-void QCBORDecode_GetHomogenousArray(QCBORDecodeContext *pMe,
+void ProcessHomogenousArray(QCBORDecodeContext *pMe,
+ const QCBORItem *pItem,
uint8_t uTagRequirement,
uint8_t uType,
size_t nInArrayCount,
union QCBORHomogenousArray array,
size_t *pnOutArrayCount)
{
- QCBORItem item;
- QCBORDecode_GetNext(pMe, &item);
-
const uint64_t puAllowedTags[] = {CBOR_TAG_HOMOGENEOUS_ARRAY, CBOR_TAG_INVALID64};
const uint8_t puAllowedContents[] = {QCBOR_TYPE_ARRAY, QCBOR_TYPE_NONE};
@@ -6064,13 +6179,15 @@
uTagRequirement,
puAllowedTags,
puAllowedContents,
- &item);
+ pItem);
if(uError) {
pMe->uLastError = (uint8_t)uError;
goto Done;
}
- const uint8_t uIntNestLevel = item.uNextNestLevel;
+ const uint8_t uIntNestLevel = pItem->uNextNestLevel;
+ QCBORItem item;
+
size_t uIntCount = 0;
do {
@@ -6125,3 +6242,80 @@
return;
}
+
+void QCBORDecode_GetHomogenousArray(QCBORDecodeContext *pMe,
+ uint8_t uTagRequirement,
+ uint8_t uType,
+ size_t nInArrayCount,
+ union QCBORHomogenousArray array,
+ size_t *pnOutArrayCount)
+{
+ QCBORItem item;
+ QCBORDecode_VGetNext(pMe, &item);
+ if(pMe->uLastError != QCBOR_SUCCESS) {
+ return;
+ }
+
+ ProcessHomogenousArray(pMe,
+ &item,
+ uTagRequirement,
+ uType,
+ nInArrayCount,
+ array,
+ pnOutArrayCount);
+}
+
+void QCBORDecode_GetHomogenousArrayInMapN(QCBORDecodeContext *pMe,
+ int64_t nLabel,
+ uint8_t uTagRequirement,
+ uint8_t uType,
+ size_t nInArrayCount,
+ union QCBORHomogenousArray array,
+ size_t *pnOutArrayCount)
+{
+ if(pMe->uLastError != QCBOR_SUCCESS) {
+ return;
+ }
+
+ QCBORItem Item;
+ QCBORDecode_GetItemInMapN(pMe, nLabel, QCBOR_TYPE_ANY, &Item);
+ if(pMe->uLastError != QCBOR_SUCCESS) {
+ return;
+ }
+
+ ProcessHomogenousArray(pMe,
+ &Item,
+ uTagRequirement,
+ uType,
+ nInArrayCount,
+ array,
+ pnOutArrayCount);
+}
+
+
+void QCBORDecode_GetHomogenousArrayInMapSZ(QCBORDecodeContext *pMe,
+ const char *szLabel,
+ uint8_t uTagRequirement,
+ uint8_t uType,
+ size_t nInArrayCount,
+ union QCBORHomogenousArray array,
+ size_t *pnOutArrayCount)
+{
+ if(pMe->uLastError != QCBOR_SUCCESS) {
+ return;
+ }
+
+ QCBORItem Item;
+ QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item);
+ if(pMe->uLastError != QCBOR_SUCCESS) {
+ return;
+ }
+
+ ProcessHomogenousArray(pMe,
+ &Item,
+ uTagRequirement,
+ uType,
+ nInArrayCount,
+ array,
+ pnOutArrayCount);
+}
diff --git a/src/qcbor_encode.c b/src/qcbor_encode.c
index 55d51f6..2a3ae31 100644
--- a/src/qcbor_encode.c
+++ b/src/qcbor_encode.c
@@ -953,49 +953,43 @@
-static inline void UsefulOutBuf_InsertUint32LE(UsefulOutBuf *pMe,
- uint32_t uInteger32,
- size_t uPos)
+
+static inline void
+UsefulOutBuf_InsertUint32LittleEndian(UsefulOutBuf *pMe,
+ uint32_t uInteger32,
+ size_t uPos)
{
/* See UsefulOutBuf_InsertUint64() for comments on this code */
- /* htonl is not used (but it is used for outputting BE)
+#if defined(USEFULBUF_CONFIG_BIG_ENDIAN)
+ const uint32_t uOut = USEFUL_SWAP32(uInteger32);
- htonl will swap if CPU is little endian and do nothing if it is big endian
+#else /* Default is little endian; tests catch mis-configuration. */
+ const uint32_t uOut = uInteger32;
- The opposite is needed.
+#endif /* big or little endian */
- Could use htonl and then always swap the result, but
- that doesn't seem any better than the default
- shift and swap that always works.
-
-
- */
-
- const void *pBytes;
-
-#if defined(USEFULBUF_CONFIG_LITTLE_ENDIAN)
- pBytes = &uInteger32;
-
-#elif defined(USEFULBUF_CONFIG_BIG_ENDIAN) && defined(USEFULBUF_CONFIG_BSWAP)
- uint32_t uTmp = __builtin_bswap32(uInteger32);
-
- pBytes = &uTmp;
-
-#else
- uint8_t aTmp[4];
-
- aTmp[0] = (uint8_t)((uInteger32 & 0xff) >> 24);
- aTmp[1] = (uint8_t)((uInteger32 & 0xff00) >> 16);
- aTmp[2] = (uint8_t)((uInteger32 & 0xff0000) >> 8);
- aTmp[3] = (uint8_t)(uInteger32 & 0xff000000);
-
- pBytes = aTmp;
-#endif
-
- UsefulOutBuf_InsertData(pMe, pBytes, 4, uPos);
+ UsefulOutBuf_InsertData(pMe, &uOut, 4, uPos);
}
+static inline void
+UsefulOutBuf_InsertUint32BE(UsefulOutBuf *pMe,
+ uint32_t uInteger32,
+ size_t uPos)
+{
+ /* See UsefulOutBuf_InsertUint64() for comments on this code */
+
+#if !defined(USEFULBUF_CONFIG_BIG_ENDIAN)
+ const uint32_t uOut = USEFUL_SWAP32(uInteger32);
+
+#else /* Default is little endian; tests catch mis-configuration. */
+ const uint32_t uOut = uInteger32;
+#endif /* big or little endian */
+
+ UsefulOutBuf_InsertData(pMe, &uOut, 4, uPos);
+}
+
+
void
QCBOREncode_AddUint32ArrayLittleEndian(QCBOREncodeContext *pMe,
const uint32_t array[],
@@ -1008,12 +1002,13 @@
AppendCBORHead(pMe, CBOR_MAJOR_TYPE_BYTE_STRING, uNumBytes, 0);
for(size_t n = 0; n < uArrayLen; n++) {
- UsefulOutBuf_InsertUint32LE(&(pMe->OutBuf), array[n], 99); // TODO: correct position
+ UsefulOutBuf_InsertUint32LittleEndian(&(pMe->OutBuf), array[n], 99); // TODO: correct position
}
}
+
void
-QCBOREncode_AddUint32ArrayBigEndian(QCBOREncodeContext *pMe,
+QCBOREncode_AddTypedArrayOfUInt32BigEndian(QCBOREncodeContext *pMe,
const uint32_t array[],
size_t uArrayLen)
{
@@ -1024,21 +1019,21 @@
AppendCBORHead(pMe, CBOR_MAJOR_TYPE_BYTE_STRING, uNumBytes, 0);
for(size_t n = 0; n < uArrayLen; n++) {
- UsefulOutBuf_AppendUint32(&(pMe->OutBuf), array[n]);
+ UsefulOutBuf_InsertUint32BE(&(pMe->OutBuf), array[n], 99);
}
}
-// Note that this will produce an array of major type 0 and 1,
-// but that is what is necessary to have an array of positive
-// and negative integers. The types allowed for a homogeneous
-// array are caller-defined, not strictly by CBOR major type
-// or such, so this is just fine.
+
+
+/*
+ * Public function to encode an array. See qcbor/qcbor_encode.h
+ */
void
-QCBOREncode_AddArrayOfInts(QCBOREncodeContext *pMe,
- uint8_t uTagRequirement,
- const int64_t *puInts,
- size_t uNumInts)
+QCBOREncode_AddArrayOfInt64s(QCBOREncodeContext *pMe,
+ uint8_t uTagRequirement,
+ const int64_t pInts[],
+ size_t uNumInts)
{
if(uTagRequirement == QCBOR_ENCODE_AS_TAG) {
QCBOREncode_AddTag(pMe, CBOR_TAG_HOMOGENEOUS_ARRAY);
@@ -1046,34 +1041,40 @@
QCBOREncode_OpenArray(pMe);
for(size_t i = 0; i < uNumInts; i++) {
- QCBOREncode_AddInt64(pMe, puInts[i]);
+ QCBOREncode_AddInt64(pMe, pInts[i]);
}
QCBOREncode_CloseArray(pMe);
}
+/*
+ * Public function to encode an array. See qcbor/qcbor_encode.h
+ */
void
-QCBOREncode_AddArrayOfUInts(QCBOREncodeContext *pMe,
- uint8_t uTagRequirement,
- const uint64_t *puInts,
- size_t uNumInts)
+QCBOREncode_AddArrayOfUInt64s(QCBOREncodeContext *pMe,
+ uint8_t uTagRequirement,
+ const uint64_t puInts[],
+ size_t uNumUInts)
{
if(uTagRequirement == QCBOR_ENCODE_AS_TAG) {
QCBOREncode_AddTag(pMe, CBOR_TAG_HOMOGENEOUS_ARRAY);
}
QCBOREncode_OpenArray(pMe);
- for(size_t i = 0; i < uNumInts; i++) {
+ for(size_t i = 0; i < uNumUInts; i++) {
QCBOREncode_AddUInt64(pMe, puInts[i]);
}
QCBOREncode_CloseArray(pMe);
}
+/*
+ * Public function to encode an array. See qcbor/qcbor_encode.h
+ */
void
QCBOREncode_AddArrayOfDoubles(QCBOREncodeContext *pMe,
uint8_t uTagRequirement,
- const double *pdDoubles,
+ const double pdDoubles[],
size_t uNumDoubles)
{
if(uTagRequirement == QCBOR_ENCODE_AS_TAG) {
@@ -1088,10 +1089,13 @@
}
+/*
+ * Public function to encode an array. See qcbor/qcbor_encode.h
+ */
void
QCBOREncode_AddArrayOfByteStrings(QCBOREncodeContext *pMe,
uint8_t uTagRequirement,
- const UsefulBufC *pStrings,
+ const UsefulBufC pStrings[],
size_t uNumStrings)
{
if(uTagRequirement == QCBOR_ENCODE_AS_TAG) {
@@ -1105,10 +1109,14 @@
QCBOREncode_CloseArray(pMe);
}
+
+/*
+ * Public function to encode an array. See qcbor/qcbor_encode.h
+ */
void
QCBOREncode_AddArrayOfTextStrings(QCBOREncodeContext *pMe,
uint8_t uTagRequirement,
- const UsefulBufC *pStrings,
+ const UsefulBufC pStrings[],
size_t uNumStrings)
{
if(uTagRequirement == QCBOR_ENCODE_AS_TAG) {
@@ -1123,11 +1131,14 @@
}
+/*
+ * Public function to encode an array. See qcbor/qcbor_encode.h
+ */
void
-QCBOREncode_AddArrayOSZStrings(QCBOREncodeContext *pMe,
- uint8_t uTagRequirement,
- const char **pStrings,
- size_t uNumStrings)
+QCBOREncode_AddArrayOfSZStrings(QCBOREncodeContext *pMe,
+ uint8_t uTagRequirement,
+ const char *pStrings[],
+ size_t uNumStrings)
{
if(uTagRequirement == QCBOR_ENCODE_AS_TAG) {
QCBOREncode_AddTag(pMe, CBOR_TAG_HOMOGENEOUS_ARRAY);
@@ -1139,6 +1150,3 @@
}
QCBOREncode_CloseArray(pMe);
}
-
-
-
diff --git a/test/qcbor_decode_tests.c b/test/qcbor_decode_tests.c
index 3197cf3..9423b48 100644
--- a/test/qcbor_decode_tests.c
+++ b/test/qcbor_decode_tests.c
@@ -8014,7 +8014,7 @@
MakeUsefulBufOnStack(xx, 100);
QCBOREncode_Init(&EC, xx);
- QCBOREncode_AddArrayOfInts(&EC,
+ QCBOREncode_AddArrayOfInt64s(&EC,
QCBOR_TAG_REQUIREMENT_NOT_A_TAG,
ints,
4);
@@ -8022,11 +8022,34 @@
QCBOREncode_Finish(&EC, &Encoded);
+/*
+get an array of ints,
+ uint,
+ double,
+ text strings
+ and byte strings
+ get an array of ints where one is a uint
+ get an array of uints where one is negative
+
+ get an array of ints where the buffer is too small
+
+ get an array of ints and require a tag; input is a tag
+ get an array of ints and require no tag; input is a tag
+ get an array of ints and requiare a tag; intput isn't a tag
+ get an array of ints and tag is optional; intput is a tag
+ get an array of ints and tag is optional; input is not a tag
+
+
+
+ */
+
+
QCBORDecodeContext DC;
int64_t i2[6];
size_t count;
+
QCBORDecode_Init(&DC, Encoded, 0);
QCBORDecode_GetArrayOfInt64(&DC,