big improvements in comments for encode
diff --git a/inc/qcbor/qcbor_encode.h b/inc/qcbor/qcbor_encode.h
index 54a3635..b1a2574 100644
--- a/inc/qcbor/qcbor_encode.h
+++ b/inc/qcbor/qcbor_encode.h
@@ -1935,13 +1935,14 @@
/**
@brief Semi-private method to add bigfloats and decimal fractions.
- @param[in] pCtx The encoding context to add the value to.
- @param[in] uTag The type 6 tag indicating what this is to be.
- @param[in] BigNumMantissa Is @ref NULLUsefulBufC if mantissa is an
- @c int64_t or the actual big number mantissa
- if not.
- @param[in] nMantissa The @c int64_t mantissa if it is not a big number.
- @param[in] nExponent The exponent.
+ @param[in] pCtx The encoding context to add the value to.
+ @param[in] uTag The type 6 tag indicating what this is to be.
+ @param[in] BigNumMantissa Is @ref NULLUsefulBufC if mantissa is an
+ @c int64_t or the actual big number mantissa
+ if not.
+ @param[in] bBigNumIsNegative This is @c true if the big number is negative.
+ @param[in] nMantissa The @c int64_t mantissa if it is not a big number.
+ @param[in] nExponent The exponent.
This outputs either the @ref CBOR_TAG_DECIMAL_FRACTION or @ref
CBOR_TAG_BIGFLOAT tag. if @c uTag is @ref CBOR_TAG_INVALID64, then
@@ -1954,7 +1955,7 @@
This implementation cannot output an exponent further from 0 than
@c INT64_MAX.
- To output a mantissa that is bewteen INT64_MAX and UINT64_MAX from 0,
+ To output a mantissa that is between INT64_MAX and UINT64_MAX from 0,
it must be as a big number.
Typically, QCBOREncode_AddDecimalFraction(), QCBOREncode_AddBigFloat(),
diff --git a/src/qcbor_encode.c b/src/qcbor_encode.c
index 623b8d5..e04e21b 100644
--- a/src/qcbor_encode.c
+++ b/src/qcbor_encode.c
@@ -35,40 +35,49 @@
#include "ieee754.h"
+/**
+ * @file qcbor_encode.c
+ *
+ * The entire implementation of the QCBOR encoder.
+ */
+
/*
- Nesting -- This tracks the nesting of maps and arrays.
-
- The following functions and data type QCBORTrackNesting implement the
- nesting management for encoding.
-
- CBOR's two nesting types, arrays and maps, are tracked here. There is
- a limit of QCBOR_MAX_ARRAY_NESTING to the number of arrays and maps
- that can be nested in one encoding so the encoding context stays
- small enough to fit on the stack.
-
- When an array / map is opened, pCurrentNesting points to the element
- in pArrays that records the type, start position and accumulates a
- count of the number of items added. When closed the start position is
- used to go back and fill in the type and number of items in the array
- / map.
-
- Encoded output can be just items like ints and strings that are not
- part of any array / map. That is, the first thing encoded does not
- have to be an array or a map.
-
- QCBOR has a special feature to allow constructing bstr-wrapped CBOR
- directly into the output buffer, so an extra buffer for it is not
- needed. This is implemented as nesting with type
- CBOR_MAJOR_TYPE_BYTE_STRING and uses this code. Bstr-wrapped CBOR is
- used by COSE for data that is to be hashed.
+ * == Nesting Tracking ==
+ *
+ * The following functions and data type QCBORTrackNesting implement
+ * the nesting management for encoding.
+ *
+ * CBOR's two nesting types, arrays and maps, are tracked here. There
+ * is a limit of QCBOR_MAX_ARRAY_NESTING to the number of arrays and
+ * maps that can be nested in one encoding so the encoding context
+ * stays small enough to fit on the stack.
+ *
+ * When an array/map is opened, pCurrentNesting points to the element
+ * in pArrays that records the type, start position and accumulates a
+ * count of the number of items added. When closed the start position
+ * is used to go back and fill in the type and number of items in the
+ * array/map.
+ *
+ * Encoded output can be a CBOR Sequence (RFC 8742) in which case
+ * there is no top-level array or map. It starts out with a string,
+ * integer or other non-aggregate type. It may have an array or map
+ * other than at the start, in which case that nesting is tracked
+ * here.
+ *
+ * QCBOR has a special feature to allow constructing byte string
+ * wrapped CBOR directly into the output buffer, so no extra buffer is
+ * needed for byte string wrapping. This is implemented as nesting
+ * with the type CBOR_MAJOR_TYPE_BYTE_STRING and is tracked here. Byte
+ * string wrapped CBOR is used by COSE for data that is to be hashed.
*/
inline static void Nesting_Init(QCBORTrackNesting *pNesting)
{
- // Assumes pNesting has been zeroed
+ /* Assumes pNesting has been zeroed. */
pNesting->pCurrentNesting = &pNesting->pArrays[0];
- // Implied CBOR array at the top nesting level. This is never returned,
- // but makes the item count work correctly.
+ /* Implied CBOR array at the top nesting level. This is never
+ * returned, but makes the item count work correctly.
+ */
pNesting->pCurrentNesting->uMajorType = CBOR_MAJOR_TYPE_ARRAY;
}
@@ -77,7 +86,6 @@
uint32_t uPos)
{
if(pNesting->pCurrentNesting == &pNesting->pArrays[QCBOR_MAX_ARRAY_NESTING]) {
- // Trying to open one too many
return QCBOR_ERR_ARRAY_NESTING_TOO_DEEP;
} else {
pNesting->pCurrentNesting++;
@@ -108,16 +116,15 @@
inline static uint16_t Nesting_GetCount(QCBORTrackNesting *pNesting)
{
- // The nesting count recorded is always the actual number of individiual
- // data items in the array or map. For arrays CBOR uses the actual item
- // count. For maps, CBOR uses the number of pairs. This function returns
- // the number needed for the CBOR encoding, so it divides the number of
- // items by two for maps to get the number of pairs. This implementation
- // takes advantage of the map major type being one larger the array major
- // type, hence uDivisor is either 1 or 2.
-
+ /* The nesting count recorded is always the actual number of
+ * individual data items in the array or map. For arrays CBOR uses
+ * the actual item count. For maps, CBOR uses the number of pairs.
+ * This function returns the number needed for the CBOR encoding,
+ * so it divides the number of items by two for maps to get the
+ * number of pairs.
+ */
if(pNesting->pCurrentNesting->uMajorType == CBOR_MAJOR_TYPE_MAP) {
- // Cast back to uint16_t after integer promotion for bit shift
+ /* Cast back to uint16_t after integer promotion from bit shift */
return (uint16_t)(pNesting->pCurrentNesting->uCount >> 1);
} else {
return pNesting->pCurrentNesting->uCount;
@@ -145,71 +152,79 @@
/*
- Encoding of the major CBOR types is by these functions:
-
- CBOR Major Type Public Function
- 0 QCBOREncode_AddUInt64()
- 0, 1 QCBOREncode_AddUInt64(), QCBOREncode_AddInt64()
- 2, 3 QCBOREncode_AddBuffer(), Also QCBOREncode_OpenMapOrArray(),
- QCBOREncode_CloseMapOrArray()
- 4, 5 QCBOREncode_OpenMapOrArray(), QCBOREncode_CloseMapOrArray(),
- QCBOREncode_OpenMapOrArrayIndefiniteLength(),
- QCBOREncode_CloseMapOrArrayIndefiniteLength()
- 6 QCBOREncode_AddTag()
- 7 QCBOREncode_AddDouble(), QCBOREncode_AddType7()
-
- Additionally, encoding of decimal fractions and bigfloats is by
- QCBOREncode_AddExponentAndMantissa()
-*/
-
-/*
- Error tracking plan -- Errors are tracked internally and not returned
- until QCBOREncode_Finish() or QCBOREncode_GetErrorState() is
- called. The CBOR errors are in me->uError. UsefulOutBuf also tracks
- whether the buffer is full or not in its context. Once either of
- these errors is set they are never cleared. Only QCBOREncode_Init()
- resets them. Or said another way, they must never be cleared or we'll
- tell the caller all is good when it is not.
-
- Only one error code is reported by QCBOREncode_Finish() even if there
- are multiple errors. The last one set wins. The caller might have to
- fix one error to reveal the next one they have to fix. This is OK.
-
- The buffer full error tracked by UsefulBuf is only pulled out of
- UsefulBuf in QCBOREncode_Finish() so it is the one that usually wins.
- UsefulBuf will never go off the end of the buffer even if it is
- called again and again when full.
-
- QCBOR_DISABLE_ENCODE_USAGE_GUARDS disables about half of the error
- checks here to reduce code size by about 150 bytes leaving only the
- checks for size to avoid buffer overflow. If the calling code is
- completely correct, checks are completely unnecessary. For example,
- there is no need to check that all the opens are matched by a close.
-
- QCBOR_DISABLE_ENCODE_USAGE_GUARDS also disables the check for more
- than QCBOR_MAX_ITEMS_IN_ARRAY in an array. Since
- QCBOR_MAX_ITEMS_IN_ARRAY is very large (65,535) it is very unlikely
- to be reached. If it is reached, the count will wrap around to zero
- and CBOR that is not well formed will be produced, but there will be
- no buffers overrun and new security issues in the code.
-
- The 8 errors returned here fall into three categories:
-
- Sizes
- QCBOR_ERR_BUFFER_TOO_LARGE -- Encoded output exceeded UINT32_MAX
- QCBOR_ERR_BUFFER_TOO_SMALL -- Output buffer too small
- QCBOR_ERR_ARRAY_NESTING_TOO_DEEP -- Nesting > QCBOR_MAX_ARRAY_NESTING1
- QCBOR_ERR_ARRAY_TOO_LONG -- Too many items added to an array/map [1]
-
- Nesting constructed incorrectly
- QCBOR_ERR_TOO_MANY_CLOSES -- More close calls than opens [1]
- QCBOR_ERR_CLOSE_MISMATCH -- Type of close does not match open [1]
- QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN -- Finish called without enough closes [1]
-
- Would generate not-well-formed CBOR
- QCBOR_ERR_ENCODE_UNSUPPORTED -- Simple type between 24 and 31 [1]
-
- [1] indicated disabled by QCBOR_DISABLE_ENCODE_USAGE_GUARDS
+ * == Major CBOR Types ==
+ *
+ * Encoding of the major CBOR types is by these functions:
+ *
+ * CBOR Major Type Public Function
+ * 0 QCBOREncode_AddUInt64()
+ * 0, 1 QCBOREncode_AddUInt64(), QCBOREncode_AddInt64()
+ * 2, 3 QCBOREncode_AddBuffer()
+ * 4, 5 QCBOREncode_OpenMapOrArray(), QCBOREncode_CloseMapOrArray(),
+ * QCBOREncode_OpenMapOrArrayIndefiniteLength(),
+ * QCBOREncode_CloseMapOrArrayIndefiniteLength()
+ * 6 QCBOREncode_AddTag()
+ * 7 QCBOREncode_AddDouble(), QCBOREncode_AddFloat(),
+ * QCBOREncode_AddDoubleNoPreferred(),
+ * QCBOREncode_AddFloatNoPreferred(), QCBOREncode_AddType7()
+ *
+ * Additionally, encoding of decimal fractions and bigfloats is by
+ * QCBOREncode_AddExponentAndMantissa() and byte strings that wrap
+ * encoded CBOR are handled by QCBOREncode_OpenMapOrArray() and
+ * QCBOREncode_CloseBstrWrap2().
+ *
+ *
+ * == Error Tracking Plan ==
+ *
+ * Errors are tracked internally and not returned until
+ * QCBOREncode_Finish() or QCBOREncode_GetErrorState() is called. The
+ * CBOR errors are in me->uError. UsefulOutBuf also tracks whether
+ * the buffer is full or not in its context. Once either of these
+ * errors is set they are never cleared. Only QCBOREncode_Init()
+ * resets them. Or said another way, they must never be cleared or
+ * we'll tell the caller all is good when it is not.
+ *
+ * Only one error code is reported by QCBOREncode_Finish() even if
+ * there are multiple errors. The last one set wins. The caller might
+ * have to fix one error to reveal the next one they have to fix.
+ * This is OK.
+ *
+ * The buffer full error tracked by UsefulBuf is only pulled out of
+ * UsefulBuf in QCBOREncode_Finish() so it is the one that usually
+ * wins. UsefulBuf will never go off the end of the buffer even if it
+ * is called again and again when full.
+ *
+ * QCBOR_DISABLE_ENCODE_USAGE_GUARDS disables about half of the error
+ * checks here to reduce code size by about 150 bytes leaving only the
+ * checks for size to avoid buffer overflow. If the calling code is
+ * completely correct, checks are completely unnecessary. For
+ * example, there is no need to check that all the opens are matched
+ * by a close.
+ *
+ * QCBOR_DISABLE_ENCODE_USAGE_GUARDS also disables the check for more
+ * than QCBOR_MAX_ITEMS_IN_ARRAY in an array. Since
+ * QCBOR_MAX_ITEMS_IN_ARRAY is very large (65,535) it is very unlikely
+ * to be reached. If it is reached, the count will wrap around to zero
+ * and CBOR that is not well formed will be produced, but there will
+ * be no buffers overrun and new security issues in the code.
+ *
+ * The 8 errors returned here fall into three categories:
+ *
+ * Sizes
+ * QCBOR_ERR_BUFFER_TOO_LARGE -- Encoded output exceeded UINT32_MAX
+ * QCBOR_ERR_BUFFER_TOO_SMALL -- Output buffer too small
+ * QCBOR_ERR_ARRAY_NESTING_TOO_DEEP -- Nesting > QCBOR_MAX_ARRAY_NESTING1
+ * QCBOR_ERR_ARRAY_TOO_LONG -- Too many items added to an array/map [1]
+ *
+ * Nesting constructed incorrectly
+ * QCBOR_ERR_TOO_MANY_CLOSES -- More close calls than opens [1]
+ * QCBOR_ERR_CLOSE_MISMATCH -- Type of close does not match open [1]
+ * QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN -- Finish called without enough closes [1]
+ *
+ * Would generate not-well-formed CBOR
+ * QCBOR_ERR_ENCODE_UNSUPPORTED -- Simple type between 24 and 31 [1]
+ *
+ * [1] indicated disabled by QCBOR_DISABLE_ENCODE_USAGE_GUARDS
*/
@@ -225,141 +240,201 @@
/*
- Public function to encode a CBOR head. See qcbor/qcbor_encode.h
+ * Public function to encode a CBOR head. See qcbor/qcbor_encode.h
*/
UsefulBufC QCBOREncode_EncodeHead(UsefulBuf buffer,
uint8_t uMajorType,
uint8_t uMinLen,
uint64_t uArgument)
{
- /**
- All CBOR data items have a type and an "argument". The argument is
- either the value of the item for integer types, the length of the
- content for string, byte, array and map types, a tag for major type
- 6, and has several uses for major type 7.
-
- This function encodes the type and the argument. There are several
- encodings for the argument depending on how large it is and how it is
- used.
-
- Every encoding of the type and argument has at least one byte, the
- "initial byte".
-
- The top three bits of the initial byte are the major type for the
- CBOR data item. The eight major types defined by the standard are
- defined as CBOR_MAJOR_TYPE_xxxx in qcbor/qcbor_common.h.
-
- The remaining five bits, known as "additional information", and
- possibly more bytes encode the argument. If the argument is less than
- 24, then it is encoded entirely in the five bits. This is neat
- because it allows you to encode an entire CBOR data item in 1 byte
- for many values and types (integers 0-23, true, false, and tags).
-
- If the argument is larger than 24, then it is encoded in 1,2,4 or 8
- additional bytes, with the number of these bytes indicated by the
- values of the 5 bits 24, 25, 25 and 27.
-
- It is possible to encode a particular argument in many ways with this
- representation. This implementation always uses the smallest
- possible representation. This conforms with CBOR preferred encoding.
-
- This function inserts them into the output buffer at the specified
- position. AppendEncodedTypeAndNumber() appends to the end.
-
- This function takes care of converting to network byte order.
-
- This function is also used to insert floats and doubles. Before this
- function is called the float or double must be copied into a
- uint64_t. That is how they are passed in. They are then converted to
- network byte order correctly. The uMinLen parameter makes sure that
- even if all the digits of a half, float or double are 0 it is still
- correctly encoded in 2, 4 or 8 bytes.
- */
/*
- This code does endian conversion without hton or knowing the
- endianness of the machine using masks and shifts. This avoids the
- dependency on hton and the mess of figuring out how to find the
- machine's endianness.
-
- This is a good efficient implementation on little-endian machines.
- A faster and small implementation is possible on big-endian
- machines because CBOR/network byte order is big endian. However
- big endian machines are uncommon.
-
- On x86, it is about 200 bytes instead of 500 bytes for the more
- formal unoptimized code.
-
- This also does the CBOR preferred shortest encoding for integers
- and is called to do endian conversion for floats.
-
- It works backwards from the LSB to the MSB as needed.
-
- Code Reviewers: THIS FUNCTION DOES POINTER MATH
- */
- /*
- The type int is used here for several variables because of the way
- integer promotion works in C for integer variables that are
- uint8_t or uint16_t. The basic rule is that they will always be
- promoted to int if they will fit. All of these integer variables
- need only hold values less than 255 or are promoted from uint8_t,
- so they will always fit into an int. Note that promotion is only
- to unsigned int if the value won't fit into an int even if the
- promotion is for an unsigned like uint8_t.
-
- By declaring them int, there are few implicit conversions and fewer
- casts needed. Code size is reduced a little. It also makes static
- analyzers happier.
-
- Note also that declaring them uint8_t won't stop integer wrap
- around if the code is wrong. It won't make the code more correct.
-
- https://stackoverflow.com/questions/46073295/implicit-type-promotion-rules
- https://stackoverflow.com/questions/589575/what-does-the-c-standard-state-the-size-of-int-long-type-to-be
+ * == Description of the CBOR Head ==
+ *
+ * The head of a CBOR data item
+ * +---+-----+ +--------+ +--------+ +--------+ +--------+
+ * |M T| A R G U M E N T . . . |
+ * +---+-----+ +--------+ +--------+ +--------+ ... +--------+
+ *
+ * Every CBOR data item has a "head". It is made up of the "major
+ * type" and the "argument".
+ *
+ * The major type indicates whether the data item is an integer,
+ * string, array or such. It is encoded in 3 bits giving it a range
+ * from 0 to 7. 0 indicates the major type is a positive integer,
+ * 1 a negative integer, 2 a byte string and so on.
+ *
+ * These 3 bits are the first part of the "initial byte" in a data
+ * item. Every data item has an initial byte, and some only have
+ * the initial byte.
+ *
+ * The argument is essentially a number between 0 and UINT64_MAX
+ * (18446744073709551615). This number is interpreted to mean
+ * different things for the different major types. For major type
+ * 0, a positive integer, it is value of the data item. For major
+ * type 2, a byte string, it is the length in bytes of the byte
+ * string. For major type 4, an array, it is the number of data
+ * items in the array.
+ *
+ * Special encoding is used so that the argument values less than
+ * 24 can be encoded very compactly in the same byte as the major
+ * type is encoded. When the lower 5 bits of the initial byte have
+ * a value less than 24, then that is the value of the argument.
+ *
+ * If the lower 5 bits of the initial byte are less than 24, then
+ * they are the value of the argument. This allows integer values 0
+ * - 23 to be CBOR encoded in just one byte.
+ *
+ * When the value of lower 5 bits are 24, 25, 26, or 27 the
+ * argument is encoded in 1, 2, 4 or 8 bytes following the initial
+ * byte in network byte order (bit endian). The cases when it is
+ * 28, 29 and 30 are reserved for future use. The value 31 is a
+ * special indicator for indefinite length strings, arrays and
+ * maps.
+ *
+ * The lower 5 bits are called the "additional information."
+ *
+ * Thus the CBOR head may be 1, 2, 3, 5 or 9 bytes long.
+ *
+ * It is legal in CBOR to encode the argument using any of these
+ * lengths even if it could be encoded in a shorter length. For
+ * example it is legal to encode a data item representing the
+ * positive integer 0 in 9 bytes even though it could be encoded in
+ * only 0. This is legal to allow for for very simple code or even
+ * hardware-only implementations that just output a register
+ * directly.
+ *
+ * CBOR defines preferred encoding as the encoding of the argument
+ * in the smallest number of bytes needed to encode it.
+ *
+ * This function takes the major type and argument as inputs and
+ * outputs the encoded CBOR head for them. It does conversion to
+ * network byte order. It implements CBOR preferred encoding,
+ * outputting the shortest representation of the argument.
+ *
+ * == Endian Conversion ==
+ *
+ * This code does endian conversion without hton() or knowing the
+ * endianness of the machine by using masks and shifts. This avoids
+ * the dependency on hton() and the mess of figuring out how to
+ * find the machine's endianness.
+ *
+ * This is a good efficient implementation on little-endian
+ * machines. A faster and smaller implementation is possible on
+ * big-endian machines because CBOR/network byte order is
+ * big-endian. However big-endian machines are uncommon.
+ *
+ * On x86, this is about 150 bytes instead of 500 bytes for the
+ * original, more formal unoptimized code.
+ *
+ * This also does the CBOR preferred shortest encoding for integers
+ * and is called to do endian conversion for floats.
+ *
+ * It works backwards from the least significant byte to the most
+ * significant byte.
+ *
+ * == Floating Point ==
+ *
+ * When the major type is 7 and the 5 lower bits have the values
+ * 25, 26 or 27, the argument is a floating-point number that is
+ * half, single or double-precision. Note that it is not the
+ * conversion from a floating-point value to an integer value like
+ * converting 0x00 to 0.00, it is the interpretation of the bits in
+ * the argument as an IEEE 754 float-point number.
+ *
+ * Floating-point numbers must be converted to network byte
+ * order. That is accomplished here by exactly the same code that
+ * converts integer arguments to network byte order.
+ *
+ * There is preferred encoding for floating-point numbers in CBOR,
+ * but it is very different than for integers and it is not
+ * implemented here. Half-precision is preferred to
+ * single-precision which is preferred to double-precision only if
+ * the conversion can be performed without loss of precision. Zero
+ * and infinity can always be converted to half-precision, without
+ * loss but 3.141592653589 cannot.
+ *
+ * The way this function knows to not do preferred encoding on the
+ * argument passed here when it is a floating point number is the
+ * uMinLen parameter. It should be 2, 4 or 8 for half, single and
+ * double precision floating point values. This prevents and the
+ * incorrect removal of leading zeros when encoding arguments that
+ * are floating-point numbers.
+ *
+ * == Use of Type int and Static Analyzers ==
+ *
+ * The type int is used here for several variables because of the
+ * way integer promotion works in C for variables that are uint8_t
+ * or uint16_t. The basic rule is that they will always be promoted
+ * to int if they will fit. These integer variables here need only
+ * hold values less than 255 so they will always fit into an int.
+ *
+ * Most of values stored are never negative, so one might think
+ * that unsigned int would be more correct than int. However the C
+ * integer promotion rules only promote to unsigned int if the
+ * result won't fit into an int even if the promotion is for an
+ * unsigned variable like uint8_t.
+ *
+ * By declaring these int, there are few implicit conversions and
+ * fewer casts needed. Code size is reduced a little. It makes
+ * static analyzers happier.
+ *
+ * Note also that declaring these uint8_t won't stop integer wrap
+ * around if the code is wrong. It won't make the code more
+ * correct.
+ *
+ * https://stackoverflow.com/questions/46073295/implicit-type-promotion-rules
+ * https://stackoverflow.com/questions/589575/what-does-the-c-standard-state-the-size-of-int-long-type-to-be
+ *
+ * Code Reviewers: THIS FUNCTION DOES POINTER MATH
*/
- // Buffer must have room for the largest CBOR HEAD + one extra as the
- // one extra is needed for this code to work as it does a pre-decrement.
+ /* The buffer must have room for the largest CBOR HEAD + one
+ * extra. The one extra is needed for this code to work as it does
+ * a pre-decrement.
+ */
if(buffer.len < QCBOR_HEAD_BUFFER_SIZE) {
return NULLUsefulBufC;
}
- // Pointer to last valid byte in the buffer
+ /* Pointer to last valid byte in the buffer */
uint8_t * const pBufferEnd = &((uint8_t *)buffer.ptr)[QCBOR_HEAD_BUFFER_SIZE-1];
- // Point to the last byte and work backwards
+ /* Point to the last byte and work backwards */
uint8_t *pByte = pBufferEnd;
- // The 5 bits in the initial byte that are not the major type
+ /* The 5 bits in the initial byte that are not the major type */
int nAdditionalInfo;
if(uMajorType > QCBOR_INDEFINITE_LEN_TYPE_MODIFIER) {
- // Special case for start & end of indefinite length
+ /* Special case for start & end of indefinite length */
uMajorType = uMajorType - QCBOR_INDEFINITE_LEN_TYPE_MODIFIER;
- // Take advantage of design of CBOR where additional info
- // is 31 for both opening and closing indefinite length
- // maps and arrays.
-#if CBOR_SIMPLE_BREAK != LEN_IS_INDEFINITE
-#error additional info for opening array not the same as for closing
-#endif
+ /* This takes advantage of design of CBOR where additional info
+ * is 31 for both opening and closing indefinite length
+ * maps and arrays.
+ */
+ #if CBOR_SIMPLE_BREAK != LEN_IS_INDEFINITE
+ #error additional info for opening array not the same as for closing
+ #endif
nAdditionalInfo = CBOR_SIMPLE_BREAK;
+
} else if (uArgument < CBOR_TWENTY_FOUR && uMinLen == 0) {
- // Simple case where argument is < 24
+ /* Simple case where argument is < 24 */
nAdditionalInfo = (int)uArgument;
+
} else {
- /*
- Encode argument in 1,2,4 or 8 bytes. Outer loop
- runs once for 1 byte and 4 times for 8 bytes.
- Inner loop runs 1, 2 or 4 times depending on
- outer loop counter. This works backwards taking
- 8 bits off the argument being encoded at a time
- until all bits from uNumber have been encoded
- and the minimum encoding size is reached.
- Minimum encoding size is for floating point
- numbers with zero bytes.
+ /* This encodes the argument in 1,2,4 or 8 bytes. The outer loop
+ * runs once for 1 byte and 4 times for 8 bytes. The inner loop
+ * runs 1, 2 or 4 times depending on outer loop counter. This
+ * works backwards shifting 8 bits off the argument being
+ * encoded at a time until all bits from uArgument have been
+ * encoded and the minimum encoding size is reached. Minimum
+ * encoding size is for floating-point numbers that have some
+ * zero-value bytes that must be output.
*/
static const uint8_t aIterate[] = {1,1,2,4};
- // The parameter passed in is unsigned, but goes negative in the loop
- // so it must be converted to a signed value.
+ /* uMinLen passed in is unsigned, but goes negative in the loop
+ * so it must be converted to a signed value.
+ */
int nMinLen = (int)uMinLen;
int i;
for(i = 0; uArgument || nMinLen > 0; i++) {
@@ -370,51 +445,51 @@
}
nMinLen -= nIterations;
}
- // Additional info is the encoding of the number of additional
- // bytes to encode argument.
+
nAdditionalInfo = LEN_IS_ONE_BYTE-1 + i;
}
- /*
- This expression integer-promotes to type int. The code above in
- function guarantees that nAdditionalInfo will never be larger than
- 0x1f. The caller may pass in a too-large uMajor type. The
- conversion to unint8_t will cause an integer wrap around and
- incorrect CBOR will be generated, but no security issue will
- occur.
+ /* This expression integer-promotes to type int. The code above in
+ * function guarantees that nAdditionalInfo will never be larger
+ * than 0x1f. The caller may pass in a too-large uMajor type. The
+ * conversion to unint8_t will cause an integer wrap around and
+ * incorrect CBOR will be generated, but no security issue will
+ * occur.
*/
- *--pByte = (uint8_t)((uMajorType << 5) + nAdditionalInfo);
+ const int nInitialByte = (uMajorType << 5) + nAdditionalInfo;
+ *--pByte = (uint8_t)nInitialByte;
#ifdef EXTRA_ENCODE_HEAD_CHECK
- /* This is a sanity check that can be turned on to verify the pointer
- * math in this function is not going wrong. Turn it on and run the
- * whole test suite to perform the check.
+ /* This is a sanity check that can be turned on to verify the
+ * pointer math in this function is not going wrong. Turn it on and
+ * run the whole test suite to perform the check.
*/
if(pBufferEnd - pByte > 9 || pBufferEnd - pByte < 1 || pByte < (uint8_t *)buffer.ptr) {
return NULLUsefulBufC;
}
#endif
- // Length will not go negative because the loops run for at most 8 decrements
- // of pByte, only one other decrement is made, and the array is sized
- // for this.
+ /* Length will not go negative because the loops run for at most 8 decrements
+ * of pByte, only one other decrement is made, and the array is sized
+ * for this.
+ */
return (UsefulBufC){pByte, (size_t)(pBufferEnd - pByte)};
}
/**
- @brief Append the CBOR head, the major type and argument
-
- @param me Encoder context.
- @param uMajorType Major type to insert.
- @param uArgument The argument (an integer value or a length).
- @param uMinLen The minimum number of bytes for encoding the CBOR argument.
-
- This formats the CBOR "head" and appends it to the output.
+ * @brief Append the CBOR head, the major type and argument
+ *
+ * @param me Encoder context.
+ * @param uMajorType Major type to insert.
+ * @param uArgument The argument (an integer value or a length).
+ * @param uMinLen The minimum number of bytes for encoding the CBOR argument.
+ *
+ * This formats the CBOR "head" and appends it to the output.
*/
static void AppendCBORHead(QCBOREncodeContext *me, uint8_t uMajorType, uint64_t uArgument, uint8_t uMinLen)
{
- // A stack buffer large enough for a CBOR head
+ /* A stack buffer large enough for a CBOR head */
UsefulBuf_MAKE_STACK_UB (pBufferForEncodedHead, QCBOR_HEAD_BUFFER_SIZE);
UsefulBufC EncodedHead = QCBOREncode_EncodeHead(pBufferForEncodedHead,
@@ -423,10 +498,10 @@
uArgument);
/* No check for EncodedHead == NULLUsefulBufC is performed here to
- * save object code. It is very clear that pBufferForEncodedHead
- * is the correct size. If EncodedHead == NULLUsefulBufC then
- * UsefulOutBuf_AppendUsefulBuf() will do nothing so there is
- * no security hole introduced.
+ * save object code. It is very clear that pBufferForEncodedHead is
+ * the correct size. If EncodedHead == NULLUsefulBufC then
+ * UsefulOutBuf_AppendUsefulBuf() will do nothing so there is no
+ * security hole introduced.
*/
UsefulOutBuf_AppendUsefulBuf(&(me->OutBuf), EncodedHead);
@@ -434,15 +509,15 @@
/**
- @brief Insert the CBOR head for a map, array or wrapped bstr
-
- @param me QCBOR encoding context.
- @param uMajorType One of CBOR_MAJOR_TYPE_XXXX.
- @param uLen The length of the data item.
-
- When an array, map or bstr was opened, nothing was done but note
- the position. This function goes back to that position and inserts
- the CBOR Head with the major type and length.
+ * @brief Insert the CBOR head for a map, array or wrapped bstr
+ *
+ * @param me QCBOR encoding context.
+ * @param uMajorType One of CBOR_MAJOR_TYPE_XXXX.
+ * @param uLen The length of the data item.
+ *
+ * When an array, map or bstr was opened, nothing was done but note
+ * the position. This function goes back to that position and inserts
+ * the CBOR Head with the major type and length.
*/
static void InsertCBORHead(QCBOREncodeContext *me, uint8_t uMajorType, size_t uLen)
{
@@ -458,7 +533,7 @@
}
#endif /* QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
- // A stack buffer large enough for a CBOR head
+ /* A stack buffer large enough for a CBOR head */
UsefulBuf_MAKE_STACK_UB(pBufferForEncodedHead, QCBOR_HEAD_BUFFER_SIZE);
UsefulBufC EncodedHead = QCBOREncode_EncodeHead(pBufferForEncodedHead,
@@ -467,10 +542,10 @@
uLen);
/* No check for EncodedHead == NULLUsefulBufC is performed here to
- * save object code. It is very clear that pBufferForEncodedHead
- * is the correct size. If EncodedHead == NULLUsefulBufC then
- * UsefulOutBuf_InsertUsefulBuf() will do nothing so there is
- * no security whole introduced.
+ * save object code. It is very clear that pBufferForEncodedHead is
+ * the correct size. If EncodedHead == NULLUsefulBufC then
+ * UsefulOutBuf_InsertUsefulBuf() will do nothing so there is no
+ * security whole introduced.
*/
UsefulOutBuf_InsertUsefulBuf(&(me->OutBuf),
EncodedHead,
@@ -480,10 +555,13 @@
}
-/*
- Increment the count of items in a map or array. This is mostly
- a separate function to have fewer occurance of
- #ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
+/**
+ * @brief Increment item counter for maps and arrays.
+ *
+ * @param pMe QCBOR encoding context.
+ *
+ * This is mostly a separate function to make code more readable and
+ * to have fewer occurrences of #ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
*/
static inline void IncrementMapOrArrayCount(QCBOREncodeContext *pMe)
{
@@ -498,7 +576,7 @@
/*
- Public functions for adding integers. See qcbor/qcbor_encode.h
+ * Public functions for adding unsigned integers. See qcbor/qcbor_encode.h
*/
void QCBOREncode_AddUInt64(QCBOREncodeContext *me, uint64_t uValue)
{
@@ -509,15 +587,15 @@
/*
- Public functions for adding unsigned. See qcbor/qcbor_encode.h
+ * Public functions for adding signed integers. See qcbor/qcbor_encode.h
*/
void QCBOREncode_AddInt64(QCBOREncodeContext *me, int64_t nNum)
{
- uint8_t uMajorType;
- uint64_t uValue;
+ uint8_t uMajorType;
+ uint64_t uValue;
if(nNum < 0) {
- // In CBOR -1 encodes as 0x00 with major type negative int.
+ /* In CBOR -1 encodes as 0x00 with major type negative int. */
uValue = (uint64_t)(-nNum - 1);
uMajorType = CBOR_MAJOR_TYPE_NEGATIVE_INT;
} else {
@@ -531,28 +609,29 @@
/*
- Semi-private function. It is exposed to user of the interface, but
- they will usually call one of the inline wrappers rather than this.
+ * Semi-private function. It is exposed to user of the interface, but
+ * one of its inline wrappers will usually be called instead of this.
+ *
+ * See qcbor/qcbor_encode.h
+ *
+ * This does the work of adding actual strings bytes to the CBOR
+ * output (as opposed to adding numbers and opening / closing
+ * aggregate types).
- See qcbor/qcbor_encode.h
-
- Does the work of adding actual strings bytes to the CBOR output (as
- opposed to numbers and opening / closing aggregate types).
-
- There are four use cases:
- CBOR_MAJOR_TYPE_BYTE_STRING -- Byte strings
- CBOR_MAJOR_TYPE_TEXT_STRING -- Text strings
- CBOR_MAJOR_NONE_TYPE_RAW -- Already-encoded CBOR
- CBOR_MAJOR_NONE_TYPE_BSTR_LEN_ONLY -- Special case
-
- The first two add the type and length plus the actual bytes. The
- third just adds the bytes as the type and length are presumed to be
- in the bytes. The fourth just adds the type and length for the very
- special case of QCBOREncode_AddBytesLenOnly().
+ * There are four use cases:
+ * CBOR_MAJOR_TYPE_BYTE_STRING -- Byte strings
+ * CBOR_MAJOR_TYPE_TEXT_STRING -- Text strings
+ * CBOR_MAJOR_NONE_TYPE_RAW -- Already-encoded CBOR
+ * CBOR_MAJOR_NONE_TYPE_BSTR_LEN_ONLY -- Special case
+ *
+ * The first two add the head plus the actual bytes. The third just
+ * adds the bytes as the heas is presumed to be in the bytes. The
+ * fourth just adds the head for the very special case of
+ * QCBOREncode_AddBytesLenOnly().
*/
void QCBOREncode_AddBuffer(QCBOREncodeContext *me, uint8_t uMajorType, UsefulBufC Bytes)
{
- // If it is not Raw CBOR, add the type and the length
+ /* If it is not Raw CBOR, add the type and the length */
if(uMajorType != CBOR_MAJOR_NONE_TYPE_RAW) {
uint8_t uRealMajorType = uMajorType;
if(uRealMajorType == CBOR_MAJOR_NONE_TYPE_BSTR_LEN_ONLY) {
@@ -562,7 +641,7 @@
}
if(uMajorType != CBOR_MAJOR_NONE_TYPE_BSTR_LEN_ONLY) {
- // Actually add the bytes
+ /* Actually add the bytes */
UsefulOutBuf_AppendUsefulBuf(&(me->OutBuf), Bytes);
}
@@ -571,7 +650,7 @@
/*
- Public functions for adding a tag. See qcbor/qcbor_encode.h
+ * Public functions for adding a tag. See qcbor/qcbor_encode.h
*/
void QCBOREncode_AddTag(QCBOREncodeContext *me, uint64_t uTag)
{
@@ -580,10 +659,10 @@
/*
- Semi-private function. It is exposed to user of the interface,
- but they will usually call one of the inline wrappers rather than this.
-
- See header qcbor/qcbor_encode.h
+ * Semi-private function. It is exposed to user of the interface, but
+ * one of its inline wrappers will usually be called instead of this.
+ *
+ * See header qcbor/qcbor_encode.h
*/
void QCBOREncode_AddType7(QCBOREncodeContext *me, uint8_t uMinLen, uint64_t uNum)
{
@@ -596,7 +675,7 @@
}
#endif /* QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
- // AppendHead() does endian swapping for the float / double
+ /* AppendCBORHead() does endian swapping for the float / double */
AppendCBORHead(me, CBOR_MAJOR_TYPE_SIMPLE, uNum, uMinLen);
IncrementMapOrArrayCount(me);
@@ -604,8 +683,8 @@
/*
- Public functions for adding a double. See qcbor/qcbor_encode.h
-*/
+ * Public functions for adding a double. See qcbor/qcbor_encode.h
+ */
void QCBOREncode_AddDoubleNoPreferred(QCBOREncodeContext *me, double dNum)
{
QCBOREncode_AddType7(me,
@@ -615,7 +694,7 @@
/*
- Public functions for adding a double. See qcbor/qcbor_encode.h
+ * Public functions for adding a double. See qcbor/qcbor_encode.h
*/
void QCBOREncode_AddDouble(QCBOREncodeContext *me, double dNum)
{
@@ -630,8 +709,8 @@
/*
- Public functions for adding a float. See qcbor/qcbor_encode.h
-*/
+ * Public functions for adding a float. See qcbor/qcbor_encode.h
+ */
void QCBOREncode_AddFloatNoPreferred(QCBOREncodeContext *me, float fNum)
{
QCBOREncode_AddType7(me,
@@ -641,7 +720,7 @@
/*
- Public functions for adding a float. See qcbor/qcbor_encode.h
+ * Public functions for adding a float. See qcbor/qcbor_encode.h
*/
void QCBOREncode_AddFloat(QCBOREncodeContext *me, float fNum)
{
@@ -657,14 +736,15 @@
#ifndef QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA
/*
- Semi-public function. It is exposed to the user of the interface, but
- one of the inline wrappers will usually be called rather than this.
-
- See qcbor/qcbor_encode.h
-
- Improvement: create another version of this that only
- takes a big number mantissa and converts the output to
- a type 0 or 1 integer when mantissa is small enough.
+ * Semi-public function. It is exposed to the user of the interface,
+ * but one of the inline wrappers will usually be called rather than
+ * this.
+ *
+ * See qcbor/qcbor_encode.h
+ *
+ * Improvement: create another version of this that only takes a big
+ * number mantissa and converts the output to a type 0 or 1 integer
+ * when mantissa is small enough.
*/
void QCBOREncode_AddExponentAndMantissa(QCBOREncodeContext *pMe,
uint64_t uTag,
@@ -673,12 +753,11 @@
int64_t nMantissa,
int64_t nExponent)
{
- /*
- This is for encoding either a big float or a decimal fraction,
- both of which are an array of two items, an exponent and a
- mantissa. The difference between the two is that the exponent is
- base-2 for big floats and base-10 for decimal fractions, but that
- has no effect on the code here.
+ /* This is for encoding either a big float or a decimal fraction,
+ * both of which are an array of two items, an exponent and a
+ * mantissa. The difference between the two is that the exponent
+ * is base-2 for big floats and base-10 for decimal fractions, but
+ * that has no effect on the code here.
*/
if(uTag != CBOR_TAG_INVALID64) {
QCBOREncode_AddTag(pMe, uTag);
@@ -700,64 +779,67 @@
/*
- Semi-public function. It is exposed to user of the interface,
- but they will usually call one of the inline wrappers rather than this.
-
- See qcbor/qcbor_encode.h
+ * Semi-public function. It is exposed to the user of the interface,
+ * but one of the inline wrappers will usually be called rather than
+ * this.
+ *
+ * See qcbor/qcbor_encode.h
*/
void QCBOREncode_OpenMapOrArray(QCBOREncodeContext *me, uint8_t uMajorType)
{
- // Add one item to the nesting level we are in for the new map or array
+ /* Add one item to the nesting level we are in for the new map or array */
IncrementMapOrArrayCount(me);
- /*
- The offset where the length of an array or map will get written
- is stored in a uint32_t, not a size_t to keep stack usage
- smaller. This checks to be sure there is no wrap around when
- recording the offset. Note that on 64-bit machines CBOR larger
- than 4GB can be encoded as long as no array / map offsets occur
- past the 4GB mark, but the public interface says that the
- maximum is 4GB to keep the discussion simpler.
- */
+ /* The offset where the length of an array or map will get written
+ * is stored in a uint32_t, not a size_t to keep stack usage
+ * smaller. This checks to be sure there is no wrap around when
+ * recording the offset. Note that on 64-bit machines CBOR larger
+ * than 4GB can be encoded as long as no array/map offsets occur
+ * past the 4GB mark, but the public interface says that the
+ * maximum is 4GB to keep the discussion simpler.
+ */
size_t uEndPosition = UsefulOutBuf_GetEndPosition(&(me->OutBuf));
- /*
- QCBOR_MAX_ARRAY_OFFSET is slightly less than UINT32_MAX so this
- code can run on a 32-bit machine and tests can pass on a 32-bit
- machine. If it was exactly UINT32_MAX, then this code would not
- compile or run on a 32-bit machine and an #ifdef or some
- machine size detection would be needed reducing portability.
- */
+ /* QCBOR_MAX_ARRAY_OFFSET is slightly less than UINT32_MAX so this
+ * code can run on a 32-bit machine and tests can pass on a 32-bit
+ * machine. If it was exactly UINT32_MAX, then this code would not
+ * compile or run on a 32-bit machine and an #ifdef or some machine
+ * size detection would be needed reducing portability.
+ */
if(uEndPosition >= QCBOR_MAX_ARRAY_OFFSET) {
me->uError = QCBOR_ERR_BUFFER_TOO_LARGE;
} else {
- // Increase nesting level because this is a map or array. Cast
- // from size_t to uin32_t is safe because of check above
+ /* Increase nesting level because this is a map or array. Cast
+ * from size_t to uin32_t is safe because of check above.
+ */
me->uError = Nesting_Increase(&(me->nesting), uMajorType, (uint32_t)uEndPosition);
}
}
/*
- Semi-public function. It is exposed to user of the interface,
- but they will usually call one of the inline wrappers rather than this.
-
- See qcbor/qcbor_encode.h
-*/
+ * Semi-public function. It is exposed to the user of the interface,
+ * but one of the inline wrappers will usually be called rather than
+ * this.
+ *
+ * See qcbor/qcbor_encode.h
+ */
void QCBOREncode_OpenMapOrArrayIndefiniteLength(QCBOREncodeContext *me, uint8_t uMajorType)
{
- // Insert the indefinite length marker (0x9f for arrays, 0xbf for maps)
+ /* Insert the indefinite length marker (0x9f for arrays, 0xbf for maps) */
AppendCBORHead(me, uMajorType, 0, 0);
- // Call the definite-length opener just to do the bookkeeping for
- // nesting. It will record the position of the opening item in
- // the encoded output but this is not used when closing this open.
+
+ /* Call the definite-length opener just to do the bookkeeping for
+ * nesting. It will record the position of the opening item in the
+ * encoded output but this is not used when closing this open.
+ */
QCBOREncode_OpenMapOrArray(me, uMajorType);
}
/*
- Public functions for closing arrays and maps. See qcbor/qcbor_encode.h
+ * Public functions for closing arrays and maps. See qcbor/qcbor_encode.h
*/
void QCBOREncode_CloseMapOrArray(QCBOREncodeContext *me, uint8_t uMajorType)
{
@@ -766,33 +848,34 @@
/*
- Public functions for closing bstr wrapping. See qcbor/qcbor_encode.h
+ * Public functions for closing bstr wrapping. See qcbor/qcbor_encode.h
*/
void QCBOREncode_CloseBstrWrap2(QCBOREncodeContext *me, bool bIncludeCBORHead, UsefulBufC *pWrappedCBOR)
{
const size_t uInsertPosition = Nesting_GetStartPos(&(me->nesting));
const size_t uEndPosition = UsefulOutBuf_GetEndPosition(&(me->OutBuf));
- // This can't go negative because the UsefulOutBuf always only grows
- // and never shrinks. UsefulOutBut itself also has defenses such that
- // it won't write where it should not even if given hostile input lengths.
+ /* This subtraction can't go negative because the UsefulOutBuf
+ * always only grows and never shrinks. UsefulOutBut itself also
+ * has defenses such that it won't write where it should not even
+ * if given incorrect input lengths.
+ */
const size_t uBstrLen = uEndPosition - uInsertPosition;
- // Actually insert
+ /* Actually insert */
InsertCBORHead(me, CBOR_MAJOR_TYPE_BYTE_STRING, uBstrLen);
if(pWrappedCBOR) {
- /*
- Return pointer and length to the enclosed encoded CBOR. The
- intended use is for it to be hashed (e.g., SHA-256) in a COSE
- implementation. This must be used right away, as the pointer
- and length go invalid on any subsequent calls to this function
- because there might be calls to InsertEncodedTypeAndNumber()
- that slides data to the right.
+ /* Return pointer and length to the enclosed encoded CBOR. The
+ * intended use is for it to be hashed (e.g., SHA-256) in a COSE
+ * implementation. This must be used right away, as the pointer
+ * and length go invalid on any subsequent calls to this
+ * function because there might be calls to
+ * InsertEncodedTypeAndNumber() that slides data to the right.
*/
size_t uStartOfNew = uInsertPosition;
if(!bIncludeCBORHead) {
- // Skip over the CBOR head to just get the inserted bstr
+ /* Skip over the CBOR head to just get the inserted bstr */
const size_t uNewEndPosition = UsefulOutBuf_GetEndPosition(&(me->OutBuf));
uStartOfNew += uNewEndPosition - uEndPosition;
}
@@ -803,7 +886,7 @@
/*
- Public functions for closing arrays and maps. See qcbor/qcbor_encode.h
+ * Public functions for closing arrays and maps. See qcbor/qcbor_encode.h
*/
void QCBOREncode_CloseMapOrArrayIndefiniteLength(QCBOREncodeContext *me, uint8_t uMajorType)
{
@@ -821,14 +904,14 @@
(void) uMajorType;
#endif
- // Append the break marker (0xff for both arrays and maps)
+ /* Append the break marker (0xff for both arrays and maps) */
AppendCBORHead(me, CBOR_MAJOR_NONE_TYPE_SIMPLE_BREAK, CBOR_SIMPLE_BREAK, 0);
Nesting_Decrease(&(me->nesting));
}
/*
- Public functions to finish and get the encoded result. See qcbor/qcbor_encode.h
+ * Public functions to finish and get the encoded result. See qcbor/qcbor_encode.h
*/
QCBORError QCBOREncode_Finish(QCBOREncodeContext *me, UsefulBufC *pEncodedCBOR)
{
@@ -853,7 +936,7 @@
/*
- Public functions to finish and get the encoded result. See qcbor/qcbor_encode.h
+ * Public functions to get size of the encoded result. See qcbor/qcbor_encode.h
*/
QCBORError QCBOREncode_FinishGetSize(QCBOREncodeContext *me, size_t *puEncodedLen)
{