Reduce encode code size; new function QCBOREncode_AddSimple
Reduces object code size for minimal encode use case by about 50 bytes through some re factoring of the encoder. Size is reduced for maximal use cases too.
QCBOREncode_AddSimple() is made public.
* Readme and copyright updates
* Core encoder optimizations
* Tidy up and rearrange
* Documentation fixes
* Make AddSimple public
* Test fan out is working
---------
Co-authored-by: Laurence Lundblade <lgl@securitytheory.com>
diff --git a/src/qcbor_encode.c b/src/qcbor_encode.c
index af83099..715190c 100644
--- a/src/qcbor_encode.c
+++ b/src/qcbor_encode.c
@@ -114,7 +114,7 @@
if(1 >= QCBOR_MAX_ITEMS_IN_ARRAY - pNesting->pCurrentNesting->uCount) {
return QCBOR_ERR_ARRAY_TOO_LONG;
}
-#endif /* QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
+#endif /* !QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
pNesting->pCurrentNesting->uCount++;
@@ -426,6 +426,7 @@
/* The 5 bits in the initial byte that are not the major type */
int nAdditionalInfo;
+#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS
if(uMajorType > QCBOR_INDEFINITE_LEN_TYPE_MODIFIER) {
/* Special case for start & end of indefinite length */
uMajorType = uMajorType - QCBOR_INDEFINITE_LEN_TYPE_MODIFIER;
@@ -438,7 +439,9 @@
#endif
nAdditionalInfo = CBOR_SIMPLE_BREAK;
- } else if (uArgument < CBOR_TWENTY_FOUR && uMinLen == 0) {
+ } else
+#endif /* !QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS */
+ if (uArgument < CBOR_TWENTY_FOUR && uMinLen == 0) {
/* Simple case where argument is < 24 */
nAdditionalInfo = (int)uArgument;
@@ -500,16 +503,39 @@
/**
+ * @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 void
+QCBOREncode_Private_IncrementMapOrArrayCount(QCBOREncodeContext *pMe)
+{
+#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
+ if(pMe->uError == QCBOR_SUCCESS) {
+ pMe->uError = Nesting_Increment(&(pMe->nesting));
+ }
+#else
+ (void)Nesting_Increment(&(pMe->nesting));
+#endif /* !QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
+}
+
+
+/**
* @brief Append the CBOR head, the major type and argument
*
- * @param pMe Encoder context.
+ * @param pMe 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.
+ *
+ * This also increments the array/map item counter in most cases.
*/
-static void
+void
QCBOREncode_Private_AppendCBORHead(QCBOREncodeContext *pMe,
const uint8_t uMajorType,
const uint64_t uArgument,
@@ -531,136 +557,15 @@
*/
UsefulOutBuf_AppendUsefulBuf(&(pMe->OutBuf), EncodedHead);
-}
-
-/**
- * @brief Check for errors when decreasing nesting.
- *
- * @param pMe QCBOR encoding context.
- * @param uMajorType The major type of the nesting.
- *
- * Check that there is no previous error, that there is actually some
- * nesting and that the major type of the opening of the nesting
- * matches the major type of the nesting being closed.
- *
- * This is called when closing maps, arrays, byte string wrapping and
- * open/close of byte strings.
- */
-static bool
-QCBOREncode_Private_CheckDecreaseNesting(QCBOREncodeContext *pMe,
- const uint8_t uMajorType)
-{
-#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
- if(pMe->uError != QCBOR_SUCCESS) {
- return true;
+ if(!(uMajorType & QCBOR_INDEFINITE_LEN_TYPE_MODIFIER || uMajorType == CBOR_MAJOR_TYPE_TAG)) {
+ /* Don't increment the map count for tag or break because that is
+ * not needed. Don't do it for indefinite-length arrays and maps
+ * because it is done elsewhere. This is never called for definite-length
+ * arrays and maps.
+ */
+ QCBOREncode_Private_IncrementMapOrArrayCount(pMe);
}
-
- if(!Nesting_IsInNest(&(pMe->nesting))) {
- pMe->uError = QCBOR_ERR_TOO_MANY_CLOSES;
- return true;
- }
-
- if(Nesting_GetMajorType(&(pMe->nesting)) != uMajorType) {
- pMe->uError = QCBOR_ERR_CLOSE_MISMATCH;
- return true;
- }
-
-#else
- /* None of these checks are performed if the encode guards are
- * turned off as they all relate to correct calling.
- *
- * Turning off all these checks does not turn off any checking for
- * buffer overflows or pointer issues.
- */
-
- (void)uMajorType;
- (void)pMe;
-#endif
-
- return false;
-}
-
-
-/**
- * @brief Insert the CBOR head for a map, array or wrapped bstr
- *
- * @param pMe 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
-QCBOREncode_Private_InsertCBORHead(QCBOREncodeContext *pMe,
- uint8_t uMajorType,
- size_t uLen)
-{
- if(QCBOREncode_Private_CheckDecreaseNesting(pMe, uMajorType)) {
- return;
- }
-
- if(uMajorType == CBOR_MAJOR_NONE_TYPE_OPEN_BSTR) {
- uMajorType = CBOR_MAJOR_TYPE_BYTE_STRING;
- }
-
- /* A stack buffer large enough for a CBOR head (9 bytes) */
- UsefulBuf_MAKE_STACK_UB(pBufferForEncodedHead, QCBOR_HEAD_BUFFER_SIZE);
-
- UsefulBufC EncodedHead = QCBOREncode_EncodeHead(pBufferForEncodedHead,
- uMajorType,
- 0,
- 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 hole introduced.
- */
- UsefulOutBuf_InsertUsefulBuf(&(pMe->OutBuf),
- EncodedHead,
- Nesting_GetStartPos(&(pMe->nesting)));
-
- Nesting_Decrease(&(pMe->nesting));
-}
-
-
-/**
- * @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 void
-QCBOREncode_Private_IncrementMapOrArrayCount(QCBOREncodeContext *pMe)
-{
-#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
- if(pMe->uError == QCBOR_SUCCESS) {
- pMe->uError = Nesting_Increment(&(pMe->nesting));
- }
-#else
- (void)Nesting_Increment(&(pMe->nesting));
-#endif /* QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
-}
-
-
-/*
- * Public functions for adding unsigned integers. See qcbor/qcbor_encode.h
- */
-void
-QCBOREncode_AddUInt64(QCBOREncodeContext *pMe, const uint64_t uValue)
-{
- QCBOREncode_Private_AppendCBORHead(pMe,
- CBOR_MAJOR_TYPE_POSITIVE_INT,
- uValue,
- 0);
-
- QCBOREncode_Private_IncrementMapOrArrayCount(pMe);
}
@@ -687,111 +592,40 @@
uMajorType = CBOR_MAJOR_TYPE_POSITIVE_INT;
}
QCBOREncode_Private_AppendCBORHead(pMe, uMajorType, uValue, 0);
-
- QCBOREncode_Private_IncrementMapOrArrayCount(pMe);
}
/**
* @brief Semi-private method to add a buffer full of bytes to encoded output.
*
- * @param[in] pMe The encoding context to add the integer to.
+ * @param[in] pMe The encoding context to add the string to.
* @param[in] uMajorType The CBOR major type of the bytes.
* @param[in] Bytes The bytes to add.
*
- * Use QCBOREncode_AddText() or QCBOREncode_AddBytes() or
- * QCBOREncode_AddEncoded() instead. They are inline functions that
- * call this and supply the correct major type. This function is
- * public to make the inline functions work to keep the overall code
- * size down and because the C language has no way to make it private.
+ * Called by inline functions to add text and byte strings.
*
- * If this is called the major type should be @c CBOR_MAJOR_TYPE_TEXT_STRING,
- * @c CBOR_MAJOR_TYPE_BYTE_STRING or @c CBOR_MAJOR_NONE_TYPE_RAW. The
- * last one is special for adding already-encoded CBOR.
- *
- * This does the work of adding actual strings bytes to the CBOR
- * output (as opposed to adding 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 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().
+ * (This used to support QCBOREncode_AddEncoded() and
+ * QCBOREncode_AddBytesLenOnly(), but that was pulled out to make this
+ * smaller. This is one of the most used methods and they are some of
+ * the least used).
*/
void
QCBOREncode_Private_AddBuffer(QCBOREncodeContext *pMe,
const uint8_t uMajorType,
const UsefulBufC Bytes)
{
- /* 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) {
- uRealMajorType = CBOR_MAJOR_TYPE_BYTE_STRING;
- }
- QCBOREncode_Private_AppendCBORHead(pMe, uRealMajorType, Bytes.len, 0);
- }
-
- if(uMajorType != CBOR_MAJOR_NONE_TYPE_BSTR_LEN_ONLY) {
- /* Actually add the bytes */
- UsefulOutBuf_AppendUsefulBuf(&(pMe->OutBuf), Bytes);
- }
-
- QCBOREncode_Private_IncrementMapOrArrayCount(pMe);
+ QCBOREncode_Private_AppendCBORHead(pMe, uMajorType, Bytes.len, 0);
+ UsefulOutBuf_AppendUsefulBuf(&(pMe->OutBuf), Bytes);
}
/*
- * Public functions for adding a tag. See qcbor/qcbor_encode.h
+ * Public functions for adding raw encoded CBOR. See qcbor/qcbor_encode.h
*/
void
-QCBOREncode_AddTag(QCBOREncodeContext *pMe, const uint64_t uTag)
+QCBOREncode_AddEncoded(QCBOREncodeContext *pMe, const UsefulBufC Encoded)
{
- QCBOREncode_Private_AppendCBORHead(pMe, CBOR_MAJOR_TYPE_TAG, uTag, 0);
-}
-
-
-/**
- * @brief Semi-private method to add simple types.
- *
- * @param[in] pMe The encoding context to add the simple value to.
- * @param[in] uMinLen Minimum encoding size for uNum. Usually 0.
- * @param[in] uNum One of CBOR_SIMPLEV_FALSE through _UNDEF or other.
- *
- * This is used to add simple types like true and false.
- *
- * Call QCBOREncode_AddBool(), QCBOREncode_AddNULL(),
- * QCBOREncode_AddUndef() instead of this.
- *
- * This function can add simple values that are not defined by CBOR
- * yet. This expansion point in CBOR should not be used unless they are
- * standardized.
- *
- * Error handling is the same as QCBOREncode_AddInt64().
- */
-void
-QCBOREncode_Private_AddType7(QCBOREncodeContext *pMe,
- const uint8_t uMinLen,
- const uint64_t uNum)
-{
-#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
- if(pMe->uError == QCBOR_SUCCESS) {
- if(uNum >= CBOR_SIMPLEV_RESERVED_START && uNum <= CBOR_SIMPLEV_RESERVED_END) {
- pMe->uError = QCBOR_ERR_ENCODE_UNSUPPORTED;
- return;
- }
- }
-#endif /* QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
-
- /* AppendCBORHead() does endian swapping for the float / double */
- QCBOREncode_Private_AppendCBORHead(pMe, CBOR_MAJOR_TYPE_SIMPLE, uNum, uMinLen);
-
+ UsefulOutBuf_AppendUsefulBuf(&(pMe->OutBuf), Encoded);
QCBOREncode_Private_IncrementMapOrArrayCount(pMe);
}
@@ -801,18 +635,6 @@
* Public functions for adding a double. See qcbor/qcbor_encode.h
*/
void
-QCBOREncode_AddDoubleNoPreferred(QCBOREncodeContext *pMe, const double dNum)
-{
- QCBOREncode_Private_AddType7(pMe,
- sizeof(uint64_t),
- UsefulBufUtil_CopyDoubleToUint64(dNum));
-}
-
-
-/*
- * Public functions for adding a double. See qcbor/qcbor_encode.h
- */
-void
QCBOREncode_AddDouble(QCBOREncodeContext *pMe, const double dNum)
{
#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
@@ -829,18 +651,6 @@
* Public functions for adding a float. See qcbor/qcbor_encode.h
*/
void
-QCBOREncode_AddFloatNoPreferred(QCBOREncodeContext *pMe, const float fNum)
-{
- QCBOREncode_Private_AddType7(pMe,
- sizeof(uint32_t),
- UsefulBufUtil_CopyFloatToUint32(fNum));
-}
-
-
-/*
- * Public functions for adding a float. See qcbor/qcbor_encode.h
- */
-void
QCBOREncode_AddFloat(QCBOREncodeContext *pMe, const float fNum)
{
#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
@@ -962,6 +772,7 @@
}
+#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS
/**
* @brief Semi-private method to open a map, array with indefinite length
*
@@ -984,6 +795,101 @@
*/
QCBOREncode_Private_OpenMapOrArray(pMe, uMajorType);
}
+#endif
+
+
+/**
+ * @brief Check for errors when decreasing nesting.
+ *
+ * @param pMe QCBOR encoding context.
+ * @param uMajorType The major type of the nesting.
+ *
+ * Check that there is no previous error, that there is actually some
+ * nesting and that the major type of the opening of the nesting
+ * matches the major type of the nesting being closed.
+ *
+ * This is called when closing maps, arrays, byte string wrapping and
+ * open/close of byte strings.
+ */
+static bool
+QCBOREncode_Private_CheckDecreaseNesting(QCBOREncodeContext *pMe,
+ const uint8_t uMajorType)
+{
+#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
+ if(pMe->uError != QCBOR_SUCCESS) {
+ return true;
+ }
+
+ if(!Nesting_IsInNest(&(pMe->nesting))) {
+ pMe->uError = QCBOR_ERR_TOO_MANY_CLOSES;
+ return true;
+ }
+
+ if(Nesting_GetMajorType(&(pMe->nesting)) != uMajorType) {
+ pMe->uError = QCBOR_ERR_CLOSE_MISMATCH;
+ return true;
+ }
+
+#else /* !QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
+ /* None of these checks are performed if the encode guards are
+ * turned off as they all relate to correct calling.
+ *
+ * Turning off all these checks does not turn off any checking for
+ * buffer overflows or pointer issues.
+ */
+
+ (void)uMajorType;
+ (void)pMe;
+#endif /* !QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
+
+ return false;
+}
+
+
+/**
+ * @brief Insert the CBOR head for a map, array or wrapped bstr
+ *
+ * @param pMe 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
+QCBOREncode_Private_CloseAggregate(QCBOREncodeContext *pMe,
+ uint8_t uMajorType,
+ size_t uLen)
+{
+ if(QCBOREncode_Private_CheckDecreaseNesting(pMe, uMajorType)) {
+ return;
+ }
+
+ if(uMajorType == CBOR_MAJOR_NONE_TYPE_OPEN_BSTR) {
+ uMajorType = CBOR_MAJOR_TYPE_BYTE_STRING;
+ }
+
+ /* A stack buffer large enough for a CBOR head (9 bytes) */
+ UsefulBuf_MAKE_STACK_UB(pBufferForEncodedHead, QCBOR_HEAD_BUFFER_SIZE);
+
+ UsefulBufC EncodedHead = QCBOREncode_EncodeHead(pBufferForEncodedHead,
+ uMajorType,
+ 0,
+ 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 hole introduced.
+ */
+ UsefulOutBuf_InsertUsefulBuf(&(pMe->OutBuf),
+ EncodedHead,
+ Nesting_GetStartPos(&(pMe->nesting)));
+
+ Nesting_Decrease(&(pMe->nesting));
+}
/**
@@ -998,7 +904,7 @@
QCBOREncode_Private_CloseMapOrArray(QCBOREncodeContext *pMe,
const uint8_t uMajorType)
{
- QCBOREncode_Private_InsertCBORHead(pMe, uMajorType, Nesting_GetCount(&(pMe->nesting)));
+ QCBOREncode_Private_CloseAggregate(pMe, uMajorType, Nesting_GetCount(&(pMe->nesting)));
}
@@ -1021,7 +927,7 @@
const size_t uBstrLen = uEndPosition - uInsertPosition;
/* Actually insert */
- QCBOREncode_Private_InsertCBORHead(pMe, CBOR_MAJOR_TYPE_BYTE_STRING, uBstrLen);
+ QCBOREncode_Private_CloseAggregate(pMe, CBOR_MAJOR_TYPE_BYTE_STRING, uBstrLen);
if(pWrappedCBOR) {
/* Return pointer and length to the enclosed encoded CBOR. The
@@ -1107,10 +1013,12 @@
return;
}
- QCBOREncode_Private_InsertCBORHead(pMe, CBOR_MAJOR_NONE_TYPE_OPEN_BSTR, uAmount);
+ QCBOREncode_Private_CloseAggregate(pMe, CBOR_MAJOR_NONE_TYPE_OPEN_BSTR, uAmount);
}
+#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS
+
/**
* @brief Semi-private method to close a map, array with indefinite length
*
@@ -1132,6 +1040,7 @@
QCBOREncode_Private_AppendCBORHead(pMe, CBOR_MAJOR_NONE_TYPE_SIMPLE_BREAK, CBOR_SIMPLE_BREAK, 0);
Nesting_Decrease(&(pMe->nesting));
}
+#endif
/*