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/README.md b/README.md
index 8ced909..0af4717 100644
--- a/README.md
+++ b/README.md
@@ -378,17 +378,21 @@
 ## Code Size
 
 These are approximate sizes on a 64-bit x86 CPU with the -Os optimization.
+All QCBOR_DISABLE_XXX are set and compiler stack frame checking is disabled
+for smallest but not for largest. Smallest is the library functions for a
+protocol with strings, integers, arrays, maps and Booleans, but not floats
+and standard tag types.
 
     |               | smallest | largest |
     |---------------|----------|---------|
-    | encode only   |      900 |    2100 |
+    | encode only   |      850 |    2200 |
     | decode only   |     1550 |   13300 |
-    | combined      |     2450 |   15500 |
+    | combined      |     2500 |   15500 |
 
  From the table above, one can see that the amount of code pulled in
  from the QCBOR library varies a lot, ranging from 1KB to 15KB.  The
- main factor is in this is the number of QCBOR functions called and
- which ones they are. QCBOR is constructed with less internal
+ main factor is the number of QCBOR functions called and
+ which ones they are. QCBOR minimizes internal
  interdependency so only code necessary for the called functions is
  brought in.
 
diff --git a/inc/qcbor/qcbor_encode.h b/inc/qcbor/qcbor_encode.h
index 774f272..e86108b 100644
--- a/inc/qcbor/qcbor_encode.h
+++ b/inc/qcbor/qcbor_encode.h
@@ -535,7 +535,7 @@
  *
  * Error handling is the same as for QCBOREncode_AddInt64().
  */
-void
+static void
 QCBOREncode_AddUInt64(QCBOREncodeContext *pCtx, uint64_t uNum);
 
 static void
@@ -695,7 +695,7 @@
  * See also QCBOREncode_AddDouble(), QCBOREncode_AddFloat(), and
  * QCBOREncode_AddFloatNoPreferred() and @ref Floating-Point.
  */
-void
+static void
 QCBOREncode_AddDoubleNoPreferred(QCBOREncodeContext *pCtx, double dNum);
 
 static void
@@ -719,7 +719,7 @@
  * See also QCBOREncode_AddDouble(), QCBOREncode_AddFloat(), and
  * QCBOREncode_AddDoubleNoPreferred() and @ref Floating-Point.
  */
-void
+static void
 QCBOREncode_AddFloatNoPreferred(QCBOREncodeContext *pCtx, float fNum);
 
 static void
@@ -753,7 +753,7 @@
  * tags. See QCBORDecode_GetNext() for discussion of decoding custom
  * tags.
  */
-void
+static void
 QCBOREncode_AddTag(QCBOREncodeContext *pCtx, uint64_t uTag);
 
 
@@ -1748,7 +1748,6 @@
 QCBOREncode_AddBoolToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, bool b);
 
 
-
 /**
  * @brief  Add a NULL to the encoded output.
  *
@@ -1796,6 +1795,33 @@
 
 
 /**
+ * @brief Add a simple value.
+ *
+ * @param[in] pMe    The encode context.
+ * @param[in] uNum   The simple value.
+ *
+ * Use QCBOREncode_AddBool(), QCBOREncode_AddUndef()... instead of this.
+ *
+ * Use this to add simple values beyond those in defined RFC
+ * 8949. Simple values must be registered with IANA. There is no range
+ * of values for proprietary use.
+ * https://www.iana.org/assignments/cbor-simple-values/cbor-simple-values.xhtml
+ */
+static void
+QCBOREncode_AddSimple(QCBOREncodeContext *pMe, const uint64_t uNum);
+
+static void
+QCBOREncode_AddSimpleToMap(QCBOREncodeContext *pMe,
+                           const char         *szLabel,
+                           const uint8_t       uSimple);
+
+static void
+QCBOREncode_AddSimpleToMapN(QCBOREncodeContext *pMe,
+                            const int64_t       nLabel,
+                            const uint8_t       uSimple);
+
+
+/**
  * @brief  Indicates that the next items added are in an array.
  *
  * @param[in] pCtx The encoding context to open the array in.
@@ -2122,7 +2148,7 @@
  * must be enclosed in a map or array. At the top level the raw
  * CBOR must be a single data item.
  */
-static void
+void
 QCBOREncode_AddEncoded(QCBOREncodeContext *pCtx, UsefulBufC Encoded);
 
 static void
@@ -2287,8 +2313,6 @@
  * 100,010 byte buffer and encode it normally. Alternatively, you can
  * encode the head in a 10 byte buffer with this function, hash that and
  * then hash the 100,000 bytes using the same hash context.
- *
- * See also QCBOREncode_AddBytesLenOnly();
  */
 UsefulBufC
 QCBOREncode_EncodeHead(UsefulBuf Buffer,
@@ -2304,6 +2328,13 @@
    ========================================================================= */
 
 /* Semi-private funcion used by public inline functions. See qcbor_encode.c */
+void QCBOREncode_Private_AppendCBORHead(QCBOREncodeContext *pMe,
+                                        const uint8_t       uMajorType,
+                                        const uint64_t      uArgument,
+                                        const uint8_t       uMinLen);
+
+
+/* Semi-private funcion used by public inline functions. See qcbor_encode.c */
 void
 QCBOREncode_Private_AddBuffer(QCBOREncodeContext *pCtx,
                               uint8_t             uMajorType,
@@ -2336,13 +2367,6 @@
 
 /* Semi-private funcion used by public inline functions. See qcbor_encode.c */
 void
-QCBOREncode_Private_AddType7(QCBOREncodeContext *pCtx,
-                             uint8_t             uMinLen,
-                             uint64_t            uNum);
-
-
-/* Semi-private funcion used by public inline functions. See qcbor_encode.c */
-void
 QCBOREncode_Private_AddExpMantissa(QCBOREncodeContext *pCtx,
                                    uint64_t            uTag,
                                    UsefulBufC          BigNumMantissa,
@@ -2350,6 +2374,31 @@
                                    int64_t             nMantissa,
                                    int64_t             nExponent);
 
+
+/**
+ * @brief  Semi-private method to add simple items and floating-point.
+ *
+ * @param[in] pMe        The encoding context.
+ * @param[in] uMinLen    Minimum encoding size for uNum. Usually 0.
+ * @param[in] uArgument  The value to add.
+ *
+ * This is used to add simple types like true and false and float-point
+ * values, both of which are type 7.
+ *
+ * Call QCBOREncode_AddBool(), QCBOREncode_AddNULL(),
+ * QCBOREncode_AddUndef() QCBOREncode_AddDouble() instead of this.
+ *
+ * Error handling is the same as QCBOREncode_AddInt64().
+ */
+static inline void
+QCBOREncode_Private_AddType7(QCBOREncodeContext *pMe,
+                             const uint8_t       uMinLen,
+                             const uint64_t      uArgument)
+{
+   QCBOREncode_Private_AppendCBORHead(pMe, CBOR_MAJOR_TYPE_SIMPLE, uArgument, uMinLen);
+}
+
+
 /**
  * @brief Semi-private method to add only the type and length of a byte string.
  *
@@ -2370,7 +2419,7 @@
  * output buffer, rather than two, roughly cutting memory use in half.
  *
  * This is only used for this odd case, but this is a supported
- * tested function.
+ * tested function for QCBOR 1.0.
  *
  * See also QCBOREncode_EncodeHead().
  */
@@ -2389,6 +2438,10 @@
                                  UsefulBufC           Bytes);
 
 
+/* Forward declaration */
+static void
+QCBOREncode_AddSZString(QCBOREncodeContext *pMe, const char *szString);
+
 
 
 
@@ -2397,10 +2450,7 @@
                           const char        *szLabel,
                           const int64_t      uNum)
 {
-   /* Use _AddBuffer() because _AddSZString() is defined below, not above */
-   QCBOREncode_Private_AddBuffer(pMe,
-                                 CBOR_MAJOR_TYPE_TEXT_STRING,
-                                 UsefulBuf_FromSZ(szLabel));
+   QCBOREncode_AddSZString(pMe, szLabel);
    QCBOREncode_AddInt64(pMe, uNum);
 }
 
@@ -2415,14 +2465,18 @@
 
 
 static inline void
+QCBOREncode_AddUInt64(QCBOREncodeContext *pMe, const uint64_t uValue)
+{
+   QCBOREncode_Private_AppendCBORHead(pMe, CBOR_MAJOR_TYPE_POSITIVE_INT, uValue, 0);
+}
+
+
+static inline void
 QCBOREncode_AddUInt64ToMap(QCBOREncodeContext *pMe,
                            const char         *szLabel,
                            const uint64_t      uNum)
 {
-   /* Use _AddBuffer() because _AddSZString() is defined below, not above */
-   QCBOREncode_Private_AddBuffer(pMe,
-                                 CBOR_MAJOR_TYPE_TEXT_STRING,
-                                 UsefulBuf_FromSZ(szLabel));
+   QCBOREncode_AddSZString(pMe, szLabel);
    QCBOREncode_AddUInt64(pMe, uNum);
 }
 
@@ -2486,7 +2540,37 @@
 }
 
 
+
+/*
+ * Public functions for adding a tag. See qcbor/qcbor_encode.h
+ */
+static inline void
+QCBOREncode_AddTag(QCBOREncodeContext *pMe, const uint64_t uTag)
+{
+   QCBOREncode_Private_AppendCBORHead(pMe, CBOR_MAJOR_TYPE_TAG, uTag, 0);
+}
+
+
+
 #ifndef USEFULBUF_DISABLE_ALL_FLOAT
+
+static inline void
+QCBOREncode_AddDoubleNoPreferred(QCBOREncodeContext *pMe, const double dNum)
+{
+   QCBOREncode_Private_AddType7(pMe,
+                                sizeof(uint64_t),
+                                UsefulBufUtil_CopyDoubleToUint64(dNum));
+}
+
+static inline void
+QCBOREncode_AddFloatNoPreferred(QCBOREncodeContext *pMe, const float fNum)
+{
+   QCBOREncode_Private_AddType7(pMe,
+                                sizeof(uint32_t),
+                                UsefulBufUtil_CopyFloatToUint32(fNum));
+}
+
+
 static inline void
 QCBOREncode_AddDoubleToMap(QCBOREncodeContext *pMe,
                            const char         *szLabel,
@@ -2562,6 +2646,8 @@
 
 
 
+
+
 static inline void
 QCBOREncode_AddTDateEpoch(QCBOREncodeContext *pMe,
                           const uint8_t       uTag,
@@ -2694,12 +2780,17 @@
    QCBOREncode_OpenBytes(pMe, pPlace);
 }
 
+
+/*
+ * Public functions for adding only a byte string length. See qcbor/qcbor_encode.h
+ */
 static inline void
 QCBOREncode_AddBytesLenOnly(QCBOREncodeContext *pMe, const UsefulBufC Bytes)
 {
-    QCBOREncode_Private_AddBuffer(pMe, CBOR_MAJOR_NONE_TYPE_BSTR_LEN_ONLY, Bytes);
+   QCBOREncode_Private_AppendCBORHead(pMe, CBOR_MAJOR_TYPE_BYTE_STRING, Bytes.len, 0);
 }
 
+
 static inline void
 QCBOREncode_AddBytesLenOnlyToMap(QCBOREncodeContext *pMe,
                                  const char         *szLabel,
@@ -3628,29 +3719,36 @@
 }
 
 
-
 static inline void
-QCBOREncode_Private_AddSimple(QCBOREncodeContext *pMe, const uint64_t uNum)
+QCBOREncode_AddSimple(QCBOREncodeContext *pMe, const uint64_t uNum)
 {
+   /* This check often is optimized out because uNum is known at compile time. */
+#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
+   if(uNum >= CBOR_SIMPLEV_RESERVED_START && uNum <= CBOR_SIMPLEV_RESERVED_END) {
+      pMe->uError = QCBOR_ERR_ENCODE_UNSUPPORTED;
+      return;
+   }
+#endif /* !QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
+
    QCBOREncode_Private_AddType7(pMe, 0, uNum);
 }
 
 static inline void
-QCBOREncode_Private_AddSimpleToMap(QCBOREncodeContext *pMe,
+QCBOREncode_AddSimpleToMap(QCBOREncodeContext *pMe,
                                    const char         *szLabel,
                                    const uint8_t       uSimple)
 {
    QCBOREncode_AddSZString(pMe, szLabel);
-   QCBOREncode_Private_AddSimple(pMe, uSimple);
+   QCBOREncode_AddSimple(pMe, uSimple);
 }
 
 static inline void
-QCBOREncode_Private_AddSimpleToMapN(QCBOREncodeContext *pMe,
+QCBOREncode_AddSimpleToMapN(QCBOREncodeContext *pMe,
                                     const int64_t       nLabel,
                                     const uint8_t       uSimple)
 {
    QCBOREncode_AddInt64(pMe, nLabel);
-   QCBOREncode_Private_AddSimple(pMe, uSimple);
+   QCBOREncode_AddSimple(pMe, uSimple);
 }
 
 
@@ -3661,7 +3759,7 @@
    if(b) {
       uSimple = CBOR_SIMPLEV_TRUE;
    }
-   QCBOREncode_Private_AddSimple(pMe, uSimple);
+   QCBOREncode_AddSimple(pMe, uSimple);
 }
 
 static inline void
@@ -3682,7 +3780,7 @@
 static inline void
 QCBOREncode_AddNULL(QCBOREncodeContext *pMe)
 {
-   QCBOREncode_Private_AddSimple(pMe, CBOR_SIMPLEV_NULL);
+   QCBOREncode_AddSimple(pMe, CBOR_SIMPLEV_NULL);
 }
 
 static inline void
@@ -3703,7 +3801,7 @@
 static inline void
 QCBOREncode_AddUndef(QCBOREncodeContext *pMe)
 {
-   QCBOREncode_Private_AddSimple(pMe, CBOR_SIMPLEV_UNDEF);
+   QCBOREncode_AddSimple(pMe, CBOR_SIMPLEV_UNDEF);
 }
 
 static inline void
@@ -3859,11 +3957,6 @@
 }
 
 
-static inline void
-QCBOREncode_AddEncoded(QCBOREncodeContext *pMe, const UsefulBufC Encoded)
-{
-   QCBOREncode_Private_AddBuffer(pMe, CBOR_MAJOR_NONE_TYPE_RAW, Encoded);
-}
 
 static inline void
 QCBOREncode_AddEncodedToMap(QCBOREncodeContext *pMe,
diff --git a/inc/qcbor/qcbor_private.h b/inc/qcbor/qcbor_private.h
index 35c60a7..a061809 100644
--- a/inc/qcbor/qcbor_private.h
+++ b/inc/qcbor/qcbor_private.h
@@ -377,9 +377,7 @@
 /* Used internally in the impementation here Must not conflict with
  * any of the official CBOR types
  */
-#define CBOR_MAJOR_NONE_TYPE_RAW            9
 #define CBOR_MAJOR_NONE_TAG_LABEL_REORDER  10
-#define CBOR_MAJOR_NONE_TYPE_BSTR_LEN_ONLY 11
 #define CBOR_MAJOR_NONE_TYPE_OPEN_BSTR     12
 
 
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
 
 
 /*
diff --git a/test/qcbor_encode_tests.c b/test/qcbor_encode_tests.c
index 10e4a40..be5548c 100644
--- a/test/qcbor_encode_tests.c
+++ b/test/qcbor_encode_tests.c
@@ -643,17 +643,17 @@
    QCBOREncode_OpenMap(pECtx);
    QCBOREncode_AddSZString(pECtx, "s1");
    QCBOREncode_AddTag(pECtx, 88);
-   QCBOREncode_Private_AddSimple(pECtx, 255);
-   QCBOREncode_Private_AddSimpleToMap(pECtx, "s2", 0);
+   QCBOREncode_AddSimple(pECtx, 255);
+   QCBOREncode_AddSimpleToMap(pECtx, "s2", 0);
    QCBOREncode_AddSZString(pECtx, "s3");
    QCBOREncode_AddTag(pECtx, 88);
-   QCBOREncode_Private_AddSimple(pECtx, 33);
+   QCBOREncode_AddSimple(pECtx, 33);
    QCBOREncode_AddInt64(pECtx, 88378374); // label before tag
    QCBOREncode_AddTag(pECtx, 88);
-   QCBOREncode_Private_AddSimple(pECtx, 255);
+   QCBOREncode_AddSimple(pECtx, 255);
    QCBOREncode_AddInt64(pECtx, 89); // label before tag
    QCBOREncode_AddTag(pECtx, 88);
-   QCBOREncode_Private_AddSimple(pECtx, 19);
+   QCBOREncode_AddSimple(pECtx, 19);
    QCBOREncode_CloseMap(pECtx);
 
    /* UUIDs */
@@ -935,6 +935,7 @@
    return(nReturn);
 }
 
+#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS
 /*
  9F                  # array(5)
    F5               # primitive(21)
@@ -981,6 +982,7 @@
 
    return(nReturn);
 }
+#endif
 
 /*
 A5                                      # map(5)
@@ -1647,7 +1649,7 @@
 
       // The result: 0 if scan happened and found nothing; 1 if it happened and
       // found something wrong; 2 if it didn't happen
-      QCBOREncode_Private_AddSimpleToMap(&ECtx, "integrity", uRResult);
+      QCBOREncode_AddSimpleToMap(&ECtx, "integrity", uRResult);
 
       // Add the diagnostic code
       QCBOREncode_AddSZStringToMap(&ECtx, "type", szType);
@@ -2653,7 +2655,7 @@
    /* ------ QCBOR_ERR_UNSUPPORTED -------- */
    QCBOREncode_Init(&EC, Large);
    QCBOREncode_OpenArray(&EC);
-   QCBOREncode_Private_AddSimple(&EC, 24); /* CBOR_SIMPLEV_RESERVED_START */
+   QCBOREncode_AddSimple(&EC, 24); /* CBOR_SIMPLEV_RESERVED_START */
    uErr = QCBOREncode_FinishGetSize(&EC, &xx);
 #ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
    if(uErr != QCBOR_ERR_ENCODE_UNSUPPORTED) {
@@ -2668,7 +2670,7 @@
 
    QCBOREncode_Init(&EC, Large);
    QCBOREncode_OpenArray(&EC);
-   QCBOREncode_Private_AddSimple(&EC, 31); /* CBOR_SIMPLEV_RESERVED_END */
+   QCBOREncode_AddSimple(&EC, 31); /* CBOR_SIMPLEV_RESERVED_END */
    uErr = QCBOREncode_FinishGetSize(&EC, &xx);
 #ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
    if(uErr != QCBOR_ERR_ENCODE_UNSUPPORTED) {
diff --git a/test/qcbor_encode_tests.h b/test/qcbor_encode_tests.h
index bac1085..8452009 100644
--- a/test/qcbor_encode_tests.h
+++ b/test/qcbor_encode_tests.h
@@ -80,9 +80,8 @@
 
 
 /*
- This tests the QCBOREncode_AddRaw() function by adding two chunks or
- RAWCBOR to an array and comparing with expected values. This is an
- encoding test.
+ This tests the QCBOREncode_AddRaw() function by adding two chunks of
+ raw CBOR to an array and comparing with expected values.
  */
 int32_t EncodeRawTest(void);
 
diff --git a/test/run_tests.c b/test/run_tests.c
index ce3db14..e4a431b 100644
--- a/test/run_tests.c
+++ b/test/run_tests.c
@@ -137,7 +137,9 @@
     TEST_ENTRY_DISABLED(BigComprehensiveInputTest),
     TEST_ENTRY_DISABLED(TooLargeInputTest),
     TEST_ENTRY(EncodeErrorTests),
+#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS
     TEST_ENTRY(SimpleValuesIndefiniteLengthTest1),
+#endif
     TEST_ENTRY(EncodeLengthThirtyoneTest),
     TEST_ENTRY(CBORSequenceDecodeTests),
     TEST_ENTRY(IntToTests),