Add QCBOREncode_EncodeHead() and other for bstr wrapping

This improves the support for bstr wrapping (so it can be hashed for use cases like COSE).

QCBOREncode_EncodeHead() is now a public function.

There is some simplification to indefinite length encoding.

There is some refactoring that should give smaller code size when bstr wrapping is not used.
diff --git a/inc/qcbor.h b/inc/qcbor.h
index 00e3656..40b08e8 100644
--- a/inc/qcbor.h
+++ b/inc/qcbor.h
@@ -43,6 +43,7 @@
 
  when       who           what, where, why
  --------   ----          ---------------------------------------------------
+ 02/07/2020 llundblade    QCBOREncode_EncodeHead() and other for bstr hashing.
  01/25/2020 llundblade    Cleaner handling of too-long encoded string input.
  01/08/2020 llundblade    Documentation corrections & improved code formatting.
  12/30/19   llundblade    Add support for decimal fractions and bigfloats.
@@ -90,6 +91,9 @@
 
 #ifdef __cplusplus
 extern "C" {
+#ifdef 0
+} // Keep editor indention formatting happy
+#endif
 #endif
 
 /*
@@ -660,6 +664,13 @@
  */
 #define QCBOR_MAX_CUSTOM_TAGS    16
 
+/*
+ The size of the buffer to be passed to QCBOREncode_EncodeHead(). It is one
+ byte larger than sizeof(uint64_t) + 1, the actual maximum size of the
+ head of a CBOR data item. because QCBOREncode_EncodeHead() needs
+ one extra byte to work.
+ */
+#define QCBOR_HEAD_BUFFER_SIZE  (sizeof(uint64_t) + 2)
 
 /**
  Error codes returned by QCBOR Encoder and Decoder.
@@ -1924,31 +1935,28 @@
  @param[in] pCtx The encoding context to open the bstr-wrapped CBOR in.
 
  All added encoded items between this call and a call to
- QCBOREncode_CloseBstrWrap() will be wrapped in a bstr. They will
+ QCBOREncode_CloseBstrWrap2() will be wrapped in a bstr. They will
  appear in the final output as a byte string.  That byte string will
- contain encoded CBOR.
+ contain encoded CBOR. This increases nesting level by one.
 
  The typical use case is for encoded CBOR that is to be
  cryptographically hashed, as part of a [RFC 8152, COSE]
- (https://tools.ietf.org/html/rfc8152) implementation. This avoids
+ (https://tools.ietf.org/html/rfc8152) implementation.
+
+ Using QCBOREncode_BstrWrap() and QCBOREncode_CloseBstrWrap2() avoids
  having to encode the items first in one buffer (e.g., the COSE
  payload) and then add that buffer as a bstr to another encoding
  (e.g. the COSE to-be-signed bytes, the @c Sig_structure) potentially
- saving a lot of memory.
+ halving the memory needed.
 
- When constructing cryptographically signed CBOR objects, maps or
- arrays, they typically are encoded normally and then wrapped as a
- byte string. The COSE standard for example does this. The wrapping is
- simply treating the encoded CBOR map as a byte string.
-
- The stated purpose of this wrapping is to prevent code relaying the
- signed data but not verifying it from tampering with the signed data
- thus making the signature unverifiable. It is also quite beneficial
- for the signature verification code. Standard CBOR parsers usually do
- not give access to partially parsed CBOR as would be need to check
- the signature of some CBOR. With this wrapping, standard CBOR parsers
- can be used to get to all the data needed for a signature
- verification.
+ RFC 7049 states the purpose of this wrapping is to prevent code
+ relaying the signed data but not verifying it from tampering with the
+ signed data thus making the signature unverifiable. It is also quite
+ beneficial for the signature verification code. Standard CBOR
+ decoders usually do not give access to partially decoded CBOR as
+ would be needed to check the signature of some CBOR. With this
+ wrapping, standard CBOR decoders can be used to get to all the data
+ needed for a signature verification.
  */
 static void QCBOREncode_BstrWrap(QCBOREncodeContext *pCtx);
 
@@ -1960,10 +1968,12 @@
 /**
  @brief Close a wrapping bstr.
 
- @param[in] pCtx           The encoding context to close of bstr wrapping in.
- @param[out] pWrappedCBOR  A @ref UsefulBufC containing wrapped bytes.
+ @param[in] pCtx              The encoding context to close of bstr wrapping in.
+ @param[in] bIncludeCBORHead  Include the encoded CBOR head of the bstr
+                              as well as the bytes in @c pWrappedCBOR.
+ @param[out] pWrappedCBOR     A @ref UsefulBufC containing wrapped bytes.
 
- The closes a wrapping bstr opened by QCBOREncode_CloseBstrWrap(). It reduces
+ The closes a wrapping bstr opened by QCBOREncode_BstrWrap(). It reduces
  nesting level by one.
 
  A pointer and length of the enclosed encoded CBOR is returned in @c
@@ -1971,22 +1981,27 @@
  this data can be hashed (e.g., with SHA-256) as part of a [RFC 8152,
  COSE] (https://tools.ietf.org/html/rfc8152)
  implementation. **WARNING**, this pointer and length should be used
- right away before any other calls to @c QCBOREncode_Xxx() as they
- will move data around and the pointer and length will no longer be to
- the correct encoded CBOR.
+ right away before any other calls to @c QCBOREncode_CloseXxx() as
+ they will move data around and the pointer and length will no longer
+ be to the correct encoded CBOR.
 
  When an error occurs as a result of this call, the encoder records
  the error and enters the error state. The error will be returned when
  QCBOREncode_Finish() is called.
 
- If this has been called more times than QCBOREncode_BstrWrap(),
- then @ref QCBOR_ERR_TOO_MANY_CLOSES will be returned when
+ If this has been called more times than QCBOREncode_BstrWrap(), then
+ @ref QCBOR_ERR_TOO_MANY_CLOSES will be returned when
  QCBOREncode_Finish() is called.
 
  If this is called and it is not a wrapping bstr that is currently
  open, @ref QCBOR_ERR_CLOSE_MISMATCH will be returned when
  QCBOREncode_Finish() is called.
+
+ QCBOREncode_CloseBstrWrap() is a deprecated version of this function
+ that is equivalent to the call with @c bIncludeCBORHead @c true.
  */
+void QCBOREncode_CloseBstrWrap2(QCBOREncodeContext *pCtx, bool bIncludeCBORHead, UsefulBufC *pWrappedCBOR);
+
 static void QCBOREncode_CloseBstrWrap(QCBOREncodeContext *pCtx, UsefulBufC *pWrappedCBOR);
 
 
@@ -2124,6 +2139,46 @@
 static QCBORError QCBOREncode_GetErrorState(QCBOREncodeContext *pCtx);
 
 
+/**
+ Encode the "head" of a CBOR data item.
+
+ @param buffer       Buffer to output the encoded head to; must be
+                     @ref QCBOR_HEAD_BUFFER_SIZE bytes in size.
+ @param uMajorType   One of CBOR_MAJOR_TYPE_XX.
+ @param uMinLen      The minimum number of bytes to encode uNumber. Almost always
+                     this is 0 to use preferred minimal encoding. If this is 4,
+                     then even the values 0xffff and smaller will be encoded
+                     as in 4 bytes. This is used primarily when encoding a
+                     float or double put into uNumber as the leading zero bytes
+                     for them must be encoded.
+ @param uNumber      The numeric argument part of the CBOR head.
+ @return             Pointer and length of the encoded head or
+                     @NULLUsefulBufC if the output buffer is too small.
+
+ Callers to need to call this for normal CBOR encoding. Note that it doesn't even
+ take a @ref QCBOREncodeContext argument.
+
+ This encodes the major type and argument part of a data item. The
+ argument is an integer that is usually either the value or the length
+ of the data item.
+
+ This is exposed in the public interface to allow hashing of some CBOR
+ data types, bstr in particular, a chunk at a time so the full CBOR
+ doesn't have to be encoded in a contiguous buffer.
+
+ For example, if you have a 100,000 byte binary blob in a buffer that
+ needs to be a bstr encoded and then hashed. You could allocate a
+ 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,
+                                  uint8_t   uMajorType,
+                                  uint8_t   uMinLen,
+                                  uint64_t  uNumber);
+
 
 /**
  QCBORDecodeContext is the data type that holds context decoding the
@@ -2749,35 +2804,31 @@
 
  @param[in] pCtx           The context to add to.
  @param[in] uMajorType     The major CBOR type to close.
- @param[out] pWrappedCBOR  Pointer to @ref UsefulBufC containing wrapped bytes.
 
- Call QCBOREncode_CloseArray(), QCBOREncode_CloseMap() or
- QCBOREncode_CloseBstrWrap() instead of this.
+ Call QCBOREncode_CloseArray() or QCBOREncode_CloseMap() instead of this.
  */
-void QCBOREncode_CloseMapOrArray(QCBOREncodeContext *pCtx,
-                                 uint8_t uMajorType,
-                                 UsefulBufC *pWrappedCBOR);
+void QCBOREncode_CloseMapOrArray(QCBOREncodeContext *pCtx, uint8_t uMajorType);
+
 
 /**
  @brief Semi-private method to close a map, array with indefinite length
 
  @param[in] pCtx           The context to add to.
  @param[in] uMajorType     The major CBOR type to close.
- @param[out] pWrappedCBOR  Pointer to @ref UsefulBufC containing wrapped bytes.
 
  Call QCBOREncode_CloseArrayIndefiniteLength() or
  QCBOREncode_CloseMapIndefiniteLength() instead of this.
  */
 void QCBOREncode_CloseMapOrArrayIndefiniteLength(QCBOREncodeContext *pCtx,
-                                                 uint8_t uMajorType,
-                                                 UsefulBufC *pWrappedCBOR);
+                                                 uint8_t uMajorType);
+
 
 /**
  @brief  Semi-private method to add simple types.
 
- @param[in] pCtx   The encoding context to add the simple value to.
- @param[in] uSize  Minimum encoding size for uNum. Usually 0.
- @param[in] uNum   One of CBOR_SIMPLEV_FALSE through _UNDEF or other.
+ @param[in] pCtx     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.
 
@@ -2790,7 +2841,7 @@
 
  Error handling is the same as QCBOREncode_AddInt64().
  */
-void  QCBOREncode_AddType7(QCBOREncodeContext *pCtx, size_t uSize, uint64_t uNum);
+void  QCBOREncode_AddType7(QCBOREncodeContext *pCtx, uint8_t uMinLen, uint64_t uNum);
 
 
 /**
@@ -2837,6 +2888,8 @@
 
  This is only used for this odd case, but this is a supported
  tested function.
+
+ See also QCBOREncode_EncodeHead().
 */
 static inline void QCBOREncode_AddBytesLenOnly(QCBOREncodeContext *pCtx, UsefulBufC Bytes);
 
@@ -3385,7 +3438,7 @@
 
 static inline void QCBOREncode_CloseArray(QCBOREncodeContext *pCtx)
 {
-   QCBOREncode_CloseMapOrArray(pCtx, CBOR_MAJOR_TYPE_ARRAY, NULL);
+   QCBOREncode_CloseMapOrArray(pCtx, CBOR_MAJOR_TYPE_ARRAY);
 }
 
 
@@ -3408,7 +3461,7 @@
 
 static inline void QCBOREncode_CloseMap(QCBOREncodeContext *pCtx)
 {
-   QCBOREncode_CloseMapOrArray(pCtx, CBOR_MAJOR_TYPE_MAP, NULL);
+   QCBOREncode_CloseMapOrArray(pCtx, CBOR_MAJOR_TYPE_MAP);
 }
 
 static inline void QCBOREncode_OpenArrayIndefiniteLength(QCBOREncodeContext *pCtx)
@@ -3430,7 +3483,7 @@
 
 static inline void QCBOREncode_CloseArrayIndefiniteLength(QCBOREncodeContext *pCtx)
 {
-   QCBOREncode_CloseMapOrArrayIndefiniteLength(pCtx, CBOR_MAJOR_NONE_TYPE_ARRAY_INDEFINITE_LEN, NULL);
+   QCBOREncode_CloseMapOrArrayIndefiniteLength(pCtx, CBOR_MAJOR_NONE_TYPE_ARRAY_INDEFINITE_LEN);
 }
 
 
@@ -3453,9 +3506,10 @@
 
 static inline void QCBOREncode_CloseMapIndefiniteLength(QCBOREncodeContext *pCtx)
 {
-   QCBOREncode_CloseMapOrArrayIndefiniteLength(pCtx, CBOR_MAJOR_NONE_TYPE_MAP_INDEFINITE_LEN, NULL);
+   QCBOREncode_CloseMapOrArrayIndefiniteLength(pCtx, CBOR_MAJOR_NONE_TYPE_MAP_INDEFINITE_LEN);
 }
 
+
 static inline void QCBOREncode_BstrWrap(QCBOREncodeContext *pCtx)
 {
    QCBOREncode_OpenMapOrArray(pCtx, CBOR_MAJOR_TYPE_BYTE_STRING);
@@ -3475,7 +3529,7 @@
 
 static inline void QCBOREncode_CloseBstrWrap(QCBOREncodeContext *pCtx, UsefulBufC *pWrappedCBOR)
 {
-   QCBOREncode_CloseMapOrArray(pCtx, CBOR_MAJOR_TYPE_BYTE_STRING, pWrappedCBOR);
+   QCBOREncode_CloseBstrWrap2(pCtx, true, pWrappedCBOR);
 }
 
 
diff --git a/src/qcbor_encode.c b/src/qcbor_encode.c
index ce14e41..5cc0bd7 100644
--- a/src/qcbor_encode.c
+++ b/src/qcbor_encode.c
@@ -40,27 +40,28 @@
  This section contains comments describing changes made to the module.
  Notice that changes are listed in reverse chronological order.
 
- when       who             what, where, why
- --------   ----            ---------------------------------------------------
- 01/25/2020 llundblade      Refine use of integer types to quiet static analysis.
- 01/08/2020 llundblade      Documentation corrections & improved code formatting.
- 12/30/19   llundblade      Add support for decimal fractions and bigfloats.
- 8/7/19     llundblade      Prevent encoding simple type reserved values 24..31
- 7/25/19    janjongboom     Add indefinite length encoding for maps and arrays
- 4/6/19     llundblade      Wrapped bstr returned now includes the wrapping bstr
- 12/30/18   llundblade      Small efficient clever encode of type & argument.
- 11/29/18   llundblade      Rework to simpler handling of tags and labels.
- 11/9/18    llundblade      Error codes are now enums.
- 11/1/18    llundblade      Floating support.
- 10/31/18   llundblade      Switch to one license that is almost BSD-3.
- 09/28/18   llundblade      Added bstr wrapping feature for COSE implementation.
- 02/05/18   llundbla        Works on CPUs which require integer alignment.
-                            Requires new version of UsefulBuf.
- 07/05/17   llundbla        Add bstr wrapping of maps/arrays for COSE
- 03/01/17   llundbla        More data types
- 11/13/16   llundbla        Integrate most TZ changes back into github version.
- 09/30/16   gkanike         Porting to TZ.
- 03/15/16   llundbla        Initial Version.
+ when       who            what, where, why
+ --------   ----           ---------------------------------------------------
+ 02/07/2020 llundblade     QCBOREncode_EncodeHead() and other for bstr hashing.
+ 01/25/2020 llundblade     Refine use of integer types to quiet static analysis.
+ 01/08/2020 llundblade     Documentation corrections & improved code formatting.
+ 12/30/19   llundblade     Add support for decimal fractions and bigfloats.
+ 8/7/19     llundblade     Prevent encoding simple type reserved values 24..31
+ 7/25/19    janjongboom    Add indefinite length encoding for maps and arrays
+ 4/6/19     llundblade     Wrapped bstr returned now includes the wrapping bstr.
+ 12/30/18   llundblade     Small efficient clever encode of type & argument.
+ 11/29/18   llundblade     Rework to simpler handling of tags and labels.
+ 11/9/18    llundblade     Error codes are now enums.
+ 11/1/18    llundblade     Floating support.
+ 10/31/18   llundblade     Switch to one license that is almost BSD-3.
+ 09/28/18   llundblade     Added bstr wrapping feature for COSE implementation.
+ 02/05/18   llundbla       Works on CPUs which require integer alignment.
+                           Requires new version of UsefulBuf.
+ 07/05/17   llundbla       Add bstr wrapping of maps/arrays for COSE
+ 03/01/17   llundbla       More data types
+ 11/13/16   llundbla       Integrate most TZ changes back into github version.
+ 09/30/16   gkanike        Porting to TZ.
+ 03/15/16   llundbla       Initial Version.
 
  =============================================================================*/
 
@@ -253,66 +254,57 @@
 }
 
 
-/**
- @brief Encode a data item, the most atomic part of CBOR
-
- @param[in,out] me      Encoding context including output buffer
- @param[in] uMajorType  One of CBOR_MAJOR_TYPE_XX
- @param[in] nMinLen     Include zero bytes up to this length. If 0 include
-                        no zero bytes. Non-zero to encode floats and doubles.
- @param[in] uNumber     The number to encode, the argument.
- @param[in] uPos        The position in the output buffer (which is inside
-                        the encoding context) to insert the result. This is
-                        usually at the end, an append.
-
- 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.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.
+/*
+ Public function for initialization. See header qcbor.h
  */
-static void InsertEncodedTypeAndNumber(QCBOREncodeContext *me,
-                                       uint8_t             uMajorType,
-                                       int                 nMinLen,
-                                       uint64_t            uNumber,
-                                       size_t              uPos)
+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.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
@@ -355,12 +347,18 @@
     https://stackoverflow.com/questions/589575/what-does-the-c-standard-state-the-size-of-int-long-type-to-be
     */
 
-   // Holds up to 9 bytes of type and argument plus one extra so pointer
-   // always points to valid bytes.
-   uint8_t bytes[sizeof(uint64_t)+2];
-   // Point to the last bytes and work backwards
-   uint8_t *pByte = &bytes[sizeof(bytes)-1];
-   // This is the 5 bits in the initial byte that is not the major type
+   // 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.
+    if(buffer.len < QCBOR_HEAD_BUFFER_SIZE) {
+        return NULLUsefulBufC;
+    }
+
+   // 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
+   uint8_t *pByte = pBufferEnd;
+   // The 5 bits in the initial byte that are not the major type
    int nAdditionalInfo;
 
    if (uMajorType == CBOR_MAJOR_NONE_TYPE_ARRAY_INDEFINITE_LEN) {
@@ -369,29 +367,35 @@
    } else if (uMajorType == CBOR_MAJOR_NONE_TYPE_MAP_INDEFINITE_LEN) {
       uMajorType = CBOR_MAJOR_TYPE_MAP;
       nAdditionalInfo = LEN_IS_INDEFINITE;
-   } else if (uNumber < CBOR_TWENTY_FOUR && nMinLen == 0) {
+   } else if (uArgument < CBOR_TWENTY_FOUR && uMinLen == 0) {
       // Simple case where argument is < 24
-      nAdditionalInfo = (int)uNumber;
-   } else if (uMajorType == CBOR_MAJOR_TYPE_SIMPLE && uNumber == CBOR_SIMPLE_BREAK) {
+      nAdditionalInfo = (int)uArgument;
+   } else if (uMajorType == CBOR_MAJOR_TYPE_SIMPLE && uArgument == CBOR_SIMPLE_BREAK) {
       // Break statement can be encoded in single byte too (0xff)
-      nAdditionalInfo = (int)uNumber;
+      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.
+       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.
        */
       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.
+      int nMinLen = (int)uMinLen;
       int i;
-      for(i = 0; uNumber || nMinLen > 0; i++) {
-         const int nIterations = aIterate[i];
+      for(i = 0; uArgument || nMinLen > 0; i++) {
+         const int nIterations = (int)aIterate[i];
          for(int j = 0; j < nIterations; j++) {
-            *--pByte = (uint8_t)(uNumber & 0xff);
-            uNumber = uNumber >> 8;
+            *--pByte = (uint8_t)(uArgument & 0xff);
+            uArgument = uArgument >> 8;
          }
          nMinLen -= nIterations;
       }
@@ -401,44 +405,102 @@
    }
 
    /*
-    Expression integer-promotes to type int. The code above in
-    function gaurantees that uAdditionalInfo will never be larger than
+    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
-    incur.
+    occur.
     */
    *--pByte = (uint8_t)((uMajorType << 5) + nAdditionalInfo);
 
-   /*
-    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.
+#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.
     */
-   const size_t uHeadLen = (size_t)(&bytes[sizeof(bytes)-1] - pByte);
+   if(pBufferEnd - pByte > 9 || pBufferEnd - pByte < 1 || pByte < (uint8_t *)buffer.ptr) {
+      return NULLUsefulBufC;
+   }
+#endif
 
-   UsefulOutBuf_InsertData(&(me->OutBuf), pByte, uHeadLen, uPos);
+   // 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)};
 }
 
 
-/*
- Append the type and number info to the end of the buffer.
+/**
+ @brief Append the CBOR head, the major type and argument
 
- See InsertEncodedTypeAndNumber() function above for details.
-*/
-inline static void AppendEncodedTypeAndNumber(QCBOREncodeContext *me,
-                                              uint8_t uMajorType,
-                                              uint64_t uNumber)
+ @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)
 {
-   // An append is an insert at the end.
-   InsertEncodedTypeAndNumber(me,
-                              uMajorType,
-                              0,
-                              uNumber,
-                              UsefulOutBuf_GetEndPosition(&(me->OutBuf)));
+   // A stack buffer large enough for a CBOR head
+   UsefulBuf_MAKE_STACK_UB  (pBufferForEncodedHead, QCBOR_HEAD_BUFFER_SIZE);
+
+   UsefulBufC EncodedHead = QCBOREncode_EncodeHead(pBufferForEncodedHead,
+                                                    uMajorType,
+                                                    uMinLen,
+                                                    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.
+    */
+
+   UsefulOutBuf_AppendUsefulBuf(&(me->OutBuf), EncodedHead);
 }
 
 
+/**
+ @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)
+{
+   if(me->uError == QCBOR_SUCCESS) {
+      if(!Nesting_IsInNest(&(me->nesting))) {
+         me->uError = QCBOR_ERR_TOO_MANY_CLOSES;
+      } else if(Nesting_GetMajorType(&(me->nesting)) != uMajorType) {
+         me->uError = QCBOR_ERR_CLOSE_MISMATCH;
+      } else {
+         // A stack buffer large enough for a CBOR head
+         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 whole introduced.
+          */
+         UsefulOutBuf_InsertUsefulBuf(&(me->OutBuf), EncodedHead, Nesting_GetStartPos(&(me->nesting)) );
+
+         Nesting_Decrease(&(me->nesting));
+      }
+   }
+}
 
 
 /*
@@ -447,7 +509,7 @@
 void QCBOREncode_AddUInt64(QCBOREncodeContext *me, uint64_t uValue)
 {
    if(me->uError == QCBOR_SUCCESS) {
-      AppendEncodedTypeAndNumber(me, CBOR_MAJOR_TYPE_POSITIVE_INT, uValue);
+      AppendCBORHead(me, CBOR_MAJOR_TYPE_POSITIVE_INT, uValue, 0);
       me->uError = Nesting_Increment(&(me->nesting));
    }
 }
@@ -470,8 +532,8 @@
          uValue = (uint64_t)nNum;
          uMajorType = CBOR_MAJOR_TYPE_POSITIVE_INT;
       }
+      AppendCBORHead(me, uMajorType, uValue, 0);
 
-      AppendEncodedTypeAndNumber(me, uMajorType, uValue);
       me->uError = Nesting_Increment(&(me->nesting));
    }
 }
@@ -506,7 +568,7 @@
          if(uRealMajorType == CBOR_MAJOR_NONE_TYPE_BSTR_LEN_ONLY) {
             uRealMajorType = CBOR_MAJOR_TYPE_BYTE_STRING;
          }
-         AppendEncodedTypeAndNumber(me, uRealMajorType, Bytes.len);
+         AppendCBORHead(me, uRealMajorType, Bytes.len, 0);
       }
 
       if(uMajorType != CBOR_MAJOR_NONE_TYPE_BSTR_LEN_ONLY) {
@@ -525,7 +587,7 @@
  */
 void QCBOREncode_AddTag(QCBOREncodeContext *me, uint64_t uTag)
 {
-   AppendEncodedTypeAndNumber(me, CBOR_MAJOR_TYPE_OPTIONAL, uTag);
+   AppendCBORHead(me, CBOR_MAJOR_TYPE_OPTIONAL, uTag, 0);
 }
 
 
@@ -535,24 +597,14 @@
 
  See header qcbor.h
  */
-void QCBOREncode_AddType7(QCBOREncodeContext *me, size_t uSize, uint64_t uNum)
+void QCBOREncode_AddType7(QCBOREncodeContext *me, uint8_t uMinLen, uint64_t uNum)
 {
    if(me->uError == QCBOR_SUCCESS) {
       if(uNum >= CBOR_SIMPLEV_RESERVED_START && uNum <= CBOR_SIMPLEV_RESERVED_END) {
          me->uError = QCBOR_ERR_UNSUPPORTED;
       } else {
-         // This call takes care of endian swapping for the float / double
-         InsertEncodedTypeAndNumber(me,
-                                    // The major type for floats and doubles
-                                    CBOR_MAJOR_TYPE_SIMPLE,
-                                    // Must pass size to ensure floats
-                                    // with zero bytes encode correctly
-                                    (int)uSize,
-                                    // The floating-point number as a uint
-                                    uNum,
-                                    // end position because this is append
-                                    UsefulOutBuf_GetEndPosition(&(me->OutBuf)));
-
+         // AppendHead() does endian swapping for the float / double
+         AppendCBORHead(me, CBOR_MAJOR_TYPE_SIMPLE, uNum, uMinLen);
          me->uError = Nesting_Increment(&(me->nesting));
       }
    }
@@ -657,9 +709,11 @@
 */
 void QCBOREncode_OpenMapOrArrayIndefiniteLength(QCBOREncodeContext *me, uint8_t uMajorType)
 {
-   // insert the indefinite length marker (0x9f for arrays, 0xbf for maps)
-   InsertEncodedTypeAndNumber(me, uMajorType, 0, 0, UsefulOutBuf_GetEndPosition(&(me->OutBuf)));
-
+   // 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.
    QCBOREncode_OpenMapOrArray(me, uMajorType);
 }
 
@@ -667,62 +721,45 @@
 /*
  Public functions for closing arrays and maps. See qcbor.h
  */
-void QCBOREncode_CloseMapOrArray(QCBOREncodeContext *me,
-                                 uint8_t uMajorType,
-                                 UsefulBufC *pWrappedCBOR)
+void QCBOREncode_CloseMapOrArray(QCBOREncodeContext *me, uint8_t uMajorType)
 {
-   if(me->uError == QCBOR_SUCCESS) {
-      if(!Nesting_IsInNest(&(me->nesting))) {
-         me->uError = QCBOR_ERR_TOO_MANY_CLOSES;
-      } else if(Nesting_GetMajorType(&(me->nesting)) != uMajorType) {
-         me->uError = QCBOR_ERR_CLOSE_MISMATCH;
-      } else {
-         /*
-          When the array, map or bstr wrap was started, nothing was
-          gone except note the position of the start of it. This code
-          goes back and inserts the actual CBOR array, map or bstr and
-          its length.  That means all the data that is in the array,
-          map or wrapped needs to be slid to the right. This is done
-          by UsefulOutBuf's insert function that is called from inside
-          InsertEncodedTypeAndNumber()
-          */
-         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 were it should not even if
-          given hostile input lengths
-          */
-         const size_t uLenOfEncodedMapOrArray = uEndPosition - uInsertPosition;
+   InsertCBORHead(me, uMajorType, Nesting_GetCount(&(me->nesting)));
+}
 
-         // Number of bytes for a bstr or number of items a for map & array
-         const bool bIsBstr = uMajorType == CBOR_MAJOR_TYPE_BYTE_STRING;
-         const size_t uLength =  bIsBstr ? uLenOfEncodedMapOrArray
-                                         : Nesting_GetCount(&(me->nesting));
 
-         // Actually insert
-         InsertEncodedTypeAndNumber(me,
-                                    uMajorType,       // bstr, array or map
-                                    0,                // no minimum length
-                                    uLength,          // either len of bstr or
-                                                      // num map / array items
-                                    uInsertPosition); // position in out buffer
+/*
+ Public functions for closing bstr wrapping. See qcbor.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));
 
-         /*
-          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.
-          */
-         if(pWrappedCBOR) {
-            const UsefulBufC PartialResult = UsefulOutBuf_OutUBuf(&(me->OutBuf));
-            *pWrappedCBOR = UsefulBuf_Tail(PartialResult, uInsertPosition);
-         }
-         Nesting_Decrease(&(me->nesting));
+   // 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.
+   const size_t uBstrLen = uEndPosition - uInsertPosition;
+
+   // 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.
+       */
+      size_t uStartOfNew = uInsertPosition;
+      if(!bIncludeCBORHead) {
+         // Skip over the CBOR head to just get the inserted bstr
+         const size_t uNewEndPosition = UsefulOutBuf_GetEndPosition(&(me->OutBuf));
+         uStartOfNew += uNewEndPosition - uEndPosition;
       }
+      const UsefulBufC PartialResult = UsefulOutBuf_OutUBuf(&(me->OutBuf));
+      *pWrappedCBOR = UsefulBuf_Tail(PartialResult, uStartOfNew);
    }
 }
 
@@ -730,9 +767,7 @@
 /*
  Public functions for closing arrays and maps. See qcbor.h
  */
-void QCBOREncode_CloseMapOrArrayIndefiniteLength(QCBOREncodeContext *me,
-                                                 uint8_t uMajorType,
-                                                 UsefulBufC *pWrappedCBOR)
+void QCBOREncode_CloseMapOrArrayIndefiniteLength(QCBOREncodeContext *me, uint8_t uMajorType)
 {
    if(me->uError == QCBOR_SUCCESS) {
       if(!Nesting_IsInNest(&(me->nesting))) {
@@ -740,27 +775,9 @@
       } else if(Nesting_GetMajorType(&(me->nesting)) != uMajorType) {
          me->uError = QCBOR_ERR_CLOSE_MISMATCH;
       } else {
-         // insert the break marker (0xff for both arrays and maps)
-         InsertEncodedTypeAndNumber(me,
-                                    CBOR_MAJOR_TYPE_SIMPLE,
-                                    0,
-                                    CBOR_SIMPLE_BREAK,
-                                    UsefulOutBuf_GetEndPosition(&(me->OutBuf)));
+         // Append the break marker (0xff for both arrays and maps)
+         AppendCBORHead(me, CBOR_MAJOR_TYPE_SIMPLE, CBOR_SIMPLE_BREAK, 0);
 
-         /*
-          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.
-          */
-         if(pWrappedCBOR) {
-            const UsefulBufC PartialResult = UsefulOutBuf_OutUBuf(&(me->OutBuf));
-            *pWrappedCBOR = UsefulBuf_Tail(PartialResult, UsefulOutBuf_GetEndPosition(&(me->OutBuf)));
-         }
-
-         // Decrease nesting level
          Nesting_Decrease(&(me->nesting));
       }
    }
@@ -790,7 +807,6 @@
 }
 
 
-
 /*
  Public functions to finish and get the encoded result. See qcbor.h
  */
@@ -811,6 +827,34 @@
 
 
 /*
+Object code sizes on 64-bit x86 with GCC -Os Jan 2020. GCC compiles smaller
+than LLVM and optimizations have been made to decrease code size. Bigfloat,
+Decimal fractions and indefinite length encoding were added to increase code
+size. Bstr wrapping is now separate which means if you don't use it, it gets
+dead stripped.
+
+_QCBOREncode_EncodeHead           187
+_QCBOREncode_CloseBstrWrap2:      154
+_QCBOREncode_AddExponentAndMantissa: 144
+_QCBOREncode_AddBuffer            105
+_QCBOREncode_OpenMapOrArray       101
+_QCBOREncode_CloseMapOrArrayIndefiniteLength: 72
+_QCBOREncode_Finish                71
+_InsertCBORHead.part.0             66
+_QCBOREncode_CloseMapOrArray       64
+_QCBOREncode_AddType7              58
+_QCBOREncode_AddInt64              57
+_AppendCBORHead                    54
+_QCBOREncode_AddUInt64             40
+_QCBOREncode_Init                  38
+_Nesting_Increment.isra.0          36
+_QCBOREncode_FinishGetSize:        34
+_QCBOREncode_AddDouble:            26
+_QCBOREncode_AddTag:               15
+Total                            1322
+Min_encode use case               776
+
+
  Object code sizes on X86 with LLVM compiler and -Os (Dec 30, 2018)
 
  _QCBOREncode_Init   69
diff --git a/test/qcbor_encode_tests.c b/test/qcbor_encode_tests.c
index 36187e3..638a9b9 100644
--- a/test/qcbor_encode_tests.c
+++ b/test/qcbor_encode_tests.c
@@ -1827,184 +1827,242 @@
 }
 
 
-
-// Part of bstr_wrap_nest_test
 /*
- 83 array with three
- 53  byte string with 19 bytes
- 01  #1
- 50 byte string with 16 bytes
- 02
- 4D byte string with 13 bytes
- 03
- 4A byte string with 10 bytes
- 04
- 47 byte string with 7 bytes
- 05
- 44 byte string with 4 bytes
- 06
- 41 byte string with 1 byte
- 07
- 01
- 02
- 03
- 04
- 05
- 06
- 07
- A2 map with two items
- 18 20  label for byte string
- 54 byte string of length 20
- 82 Array with two items
- 10  The integer value 10
- A2 map with two items
- 18 21 label for byte string
- 44 byte string with 4 bytes
- 81 array with 1 item
- 11 integer value 11
- 18 30 integer value 30
- 18 40 integer label 40
- 65 68 65 6C 6C 6F text string hello
- 18 31 integer value 31
- 18 41 integer label 41
- 65 68 65 6C 6C 6F text string hello
+ This is bstr wrapped CBOR in 6 levels.
 
+ [
+   h'82004E82014B8202488203458204428105',
+   {
+     32:h'A3101018406568656C6C6F18215828A3111118416568656C6C6F18225819A312121
+     8426568656C6C6F18234BA2131318436568656C6C6F'
+   }
+ ]
 
- */
+ Unwrapping the first byte string in the above gives
+   [0, h'82014B8202488203458204428105']
 
+ Unwrapping again, the byte string immediately above gives
+   [1, h'8202488203458204428105']
 
-/*
- 83                                      # array(3)
-   56                                   # bytes(22)
-      00530150024D034A0447054406410700010203040506 # "\x00S\x01P\x02M\x03J\x04G\x05D\x06A\a\x00\x01\x02\x03\x04\x05\x06"
-   07                                   # unsigned(7)
-   A2                                   # map(2)
-      18 20                             # unsigned(32)
-      54                                # bytes(20)
-         8210A21821448111183018406568656C6C6F1831 # "\x82\x10\xA2\x18!D\x81\x11\x180\x18@ehello\x181"
-      18 41                             # unsigned(65)
-      65                                # text(5)
-         68656C6C6F                     # "hello"
+ ...
+
+ Unrapping the second byte string in the top-level CBOR above gives
+   {16: 16,
+    64: "hello",
+    33: h'A3111118416568656C6C6F18225819A3121218426568656C6C6F18234BA2....
+ }
+
+ Unwrapping again, the byte string immediately above gives
+   {17: 17,
+    65: "hello",
+    34: h'A3121218426568656C6C6F18234BA2131318436568656C6C6F'
+ }
+
+ ...
+
  */
 static const uint8_t spExpectedDeepBstr[] =
 {
-   0x83, 0x56, 0x00, 0x53, 0x01, 0x50, 0x02, 0x4D,
-   0x03, 0x4A, 0x04, 0x47, 0x05, 0x44, 0x06, 0x41,
-   0x07, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
-   0x07, 0xA2, 0x18, 0x20, 0x54, 0x82, 0x10, 0xA2,
-   0x18, 0x21, 0x44, 0x81, 0x11, 0x18, 0x30, 0x18,
-   0x40, 0x65, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x18,
-   0x31, 0x18, 0x41, 0x65, 0x68, 0x65, 0x6C, 0x6C,
-   0x6F
+   0x82, 0x51, 0x82, 0x00, 0x4E, 0x82, 0x01, 0x4B,
+   0x82, 0x02, 0x48, 0x82, 0x03, 0x45, 0x82, 0x04,
+   0x42, 0x81, 0x05, 0xA1, 0x18, 0x20, 0x58, 0x37,
+   0xA3, 0x10, 0x10, 0x18, 0x40, 0x65, 0x68, 0x65,
+   0x6C, 0x6C, 0x6F, 0x18, 0x21, 0x58, 0x28, 0xA3,
+   0x11, 0x11, 0x18, 0x41, 0x65, 0x68, 0x65, 0x6C,
+   0x6C, 0x6F, 0x18, 0x22, 0x58, 0x19, 0xA3, 0x12,
+   0x12, 0x18, 0x42, 0x65, 0x68, 0x65, 0x6C, 0x6C,
+   0x6F, 0x18, 0x23, 0x4B, 0xA2, 0x13, 0x13, 0x18,
+   0x43, 0x65, 0x68, 0x65, 0x6C, 0x6C, 0x6F
 };
 
-// Part of bstr_wrap_nest_test
-static int DecodeNextNested(UsefulBufC Wrapped)
+
+/*
+ Get an int64 out of the decoder or fail.
+ */
+static int32_t GetInt64(QCBORDecodeContext *pDC, int64_t *pInt)
 {
-   int nReturn;
-   QCBORDecodeContext DC;
-   QCBORDecode_Init(&DC, Wrapped, QCBOR_DECODE_MODE_NORMAL);
-
    QCBORItem Item;
-   nReturn = QCBORDecode_GetNext(&DC, &Item);
-   if(nReturn) {
-      return -11;
-   }
-   if(Item.uDataType != QCBOR_TYPE_INT64) {
-      return -12;
-   }
+   int32_t nReturn;
 
-   nReturn = QCBORDecode_GetNext(&DC, &Item);
-   if(nReturn == QCBOR_ERR_HIT_END || nReturn == QCBOR_ERR_NO_MORE_ITEMS) {
-      return 0;
-   }
-   if(Item.uDataType != QCBOR_TYPE_BYTE_STRING) {
-      return -13;
-   }
-   nReturn =  DecodeNextNested(Item.val.string);
+   nReturn = (int32_t)QCBORDecode_GetNext(pDC, &Item);
    if(nReturn) {
       return nReturn;
    }
-
-   nReturn = QCBORDecode_GetNext(&DC, &Item);
-   if(nReturn) {
-      return -14;
-   }
    if(Item.uDataType != QCBOR_TYPE_INT64) {
-      return -15;
+      return -1;
    }
 
-   if(QCBORDecode_Finish(&DC)) {
-      return -16;
-   }
-
+   *pInt = Item.val.int64;
    return 0;
 }
 
-// Part of bstr_wrap_nest_test
-static int32_t DecodeNextNested2(UsefulBufC Wrapped)
+/*
+ Get an array out of the decoder or fail.
+ */
+static int32_t GetArray(QCBORDecodeContext *pDC, uint16_t *pInt)
 {
-   int nReturn;
-   QCBORDecodeContext DC;
-   QCBORDecode_Init(&DC, Wrapped, QCBOR_DECODE_MODE_NORMAL);
-
    QCBORItem Item;
-   nReturn = QCBORDecode_GetNext(&DC, &Item);
-   if(nReturn) {
-      return -11;
-   }
-   if(Item.uDataType != QCBOR_TYPE_ARRAY) {
-      return -12;
-   }
+   int32_t nReturn;
 
-   nReturn = QCBORDecode_GetNext(&DC, &Item);
-   if(nReturn) {
-      return -11;
-   }
-   if(Item.uDataType != QCBOR_TYPE_INT64) {
-      return -12;
-   }
-
-   nReturn = QCBORDecode_GetNext(&DC, &Item);
-   if(nReturn) {
-      return -11;
-   }
-   if(Item.uDataType != QCBOR_TYPE_MAP) {
-      return 0;
-   }
-
-   nReturn = QCBORDecode_GetNext(&DC, &Item);
-   if(nReturn) {
-      return -11;
-   }
-   if(Item.uDataType != QCBOR_TYPE_BYTE_STRING) {
-      return -13;
-   }
-   nReturn =  DecodeNextNested2(Item.val.string);
+   nReturn = (int32_t)QCBORDecode_GetNext(pDC, &Item);
    if(nReturn) {
       return nReturn;
    }
+   if(Item.uDataType != QCBOR_TYPE_ARRAY) {
+      return -1;
+   }
 
-   nReturn = QCBORDecode_GetNext(&DC, &Item);
+   *pInt = Item.val.uCount;
+   return 0;
+}
+
+/*
+ Get a map out of the decoder or fail.
+ */
+static int32_t GetMap(QCBORDecodeContext *pDC, uint16_t *pInt)
+{
+   QCBORItem Item;
+   int32_t nReturn;
+
+   nReturn = (int32_t)QCBORDecode_GetNext(pDC, &Item);
    if(nReturn) {
-      return -11;
+      return nReturn;
+   }
+   if(Item.uDataType != QCBOR_TYPE_MAP) {
+      return -1;
+   }
+
+   *pInt = Item.val.uCount;
+   return 0;
+}
+
+/*
+ Get a byte string out of the decoder or fail.
+ */
+static int32_t GetByteString(QCBORDecodeContext *pDC, UsefulBufC *pBstr)
+{
+   QCBORItem Item;
+   int32_t nReturn;
+
+   nReturn = (int32_t)QCBORDecode_GetNext(pDC, &Item);
+   if(nReturn) {
+      return nReturn;
+   }
+   if(Item.uDataType != QCBOR_TYPE_BYTE_STRING) {
+      return -1; // TODO: better type?
+   }
+
+   *pBstr = Item.val.string;
+   return 0;
+}
+
+/*
+ Get a byte string out of the decoder or fail.
+ */
+static int32_t GetTextString(QCBORDecodeContext *pDC, UsefulBufC *pTstr)
+{
+   QCBORItem Item;
+   int nReturn;
+
+   nReturn = (int32_t)QCBORDecode_GetNext(pDC, &Item);
+   if(nReturn) {
+      return nReturn;
    }
    if(Item.uDataType != QCBOR_TYPE_TEXT_STRING) {
-      return -12;
+      return -1;
    }
-   nReturn = QCBORDecode_GetNext(&DC, &Item);
-   if(nReturn) {
+
+   *pTstr = Item.val.string;
+   return 0;
+}
+
+
+/*
+ Recursively decode array containing a little CBOR and a bstr wrapped array
+ with a little CBOR and a bstr wrapped array...
+
+ Part of bstr_wrap_nest_test.
+ */static int32_t DecodeNextNested(UsefulBufC Wrapped)
+{
+   int64_t            nInt;
+   UsefulBufC         Bstr;
+   uint16_t           nArrayCount;
+   QCBORDecodeContext DC;
+   int32_t            nResult;
+
+   QCBORDecode_Init(&DC, Wrapped, QCBOR_DECODE_MODE_NORMAL);
+
+   if(GetArray(&DC, &nArrayCount) || nArrayCount < 1 || nArrayCount > 2) {
+      return -10;
+   }
+
+   if(GetInt64(&DC, &nInt)) {
       return -11;
    }
-   if(Item.uDataType != QCBOR_TYPE_INT64) {
-      return -12;
+
+   nResult = GetByteString(&DC, &Bstr);
+   if(nResult == QCBOR_ERR_HIT_END || nResult == QCBOR_ERR_NO_MORE_ITEMS) {
+      if(nArrayCount != 1) {
+         return -12;
+      } else {
+         // successful exit
+         return 0;
+      }
+   }
+   if(nResult) {
+      return -13;
    }
 
-   if(QCBORDecode_Finish(&DC)) {
-      return -16;
+   // tail recursion; good compilers will reuse the stack frame
+   return DecodeNextNested(Bstr);
+}
+
+
+/*
+ Recursively decode map containing a little CBOR and a bstr wrapped map
+ with a little CBOR and a bstr wrapped map...
+
+ Part of bstr_wrap_nest_test.
+ */
+static int32_t DecodeNextNested2(UsefulBufC Wrapped)
+{
+   int32_t            nResult;
+   uint16_t           nMapCount;
+   int64_t            nInt;
+   UsefulBufC         Bstr;
+   QCBORDecodeContext DC;
+
+   QCBORDecode_Init(&DC, Wrapped, QCBOR_DECODE_MODE_NORMAL);
+
+   if(GetMap(&DC, &nMapCount) || nMapCount < 2 || nMapCount > 3) {
+      return -20;
    }
 
-   return 0;
+   if(GetInt64(&DC, &nInt)) {
+      return -21;
+   }
+
+   // The "hello"
+   if(GetTextString(&DC, &Bstr)) {
+      return -22;
+   }
+
+   nResult = GetByteString(&DC, &Bstr);
+   if(nResult == QCBOR_ERR_HIT_END || nResult == QCBOR_ERR_NO_MORE_ITEMS) {
+      if(nMapCount == 2) {
+         // successful exit
+         return 0;
+      } else {
+         return -23;
+      }
+   }
+
+   if(nResult) {
+      return -24;
+   }
+
+   // tail recursion; good compilers will reuse the stack frame
+   return DecodeNextNested2(Bstr);
 }
 
 
@@ -2014,34 +2072,34 @@
    QCBOREncode_Init(&EC, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
 
    // ---- Make a complicated nested CBOR structure ---
-#define BSTR_TEST_DEPTH 10
+   #define BSTR_TEST_DEPTH 6
 
    QCBOREncode_OpenArray(&EC);
 
-   for(int i = 0; i < BSTR_TEST_DEPTH-2; i++) {
+   for(int i = 0; i < BSTR_TEST_DEPTH; i++) {
       QCBOREncode_BstrWrap(&EC);
-      QCBOREncode_AddInt64(&EC, i);
-   }
-
-   for(int i = 0; i < BSTR_TEST_DEPTH-2; i++) {
-      QCBOREncode_CloseBstrWrap(&EC, NULL);
-      QCBOREncode_AddInt64(&EC, i);
-   }
-
-   for(int i = 0; i < (BSTR_TEST_DEPTH-2)/3; i++) {
-      QCBOREncode_OpenMap(&EC);
-      QCBOREncode_BstrWrapInMapN(&EC, i+0x20);
       QCBOREncode_OpenArray(&EC);
-      QCBOREncode_AddInt64(&EC, i+0x10);
+      QCBOREncode_AddInt64(&EC, i);
+   }
+   for(int i = 0; i < BSTR_TEST_DEPTH; i++) {
+      QCBOREncode_CloseArray(&EC);
+      QCBOREncode_CloseBstrWrap(&EC, NULL);
    }
 
-   for(int i = 0; i < (BSTR_TEST_DEPTH-2)/3; i++) {
-      QCBOREncode_CloseArray(&EC);
-      QCBOREncode_AddInt64(&EC, i+0x30);
-      QCBOREncode_CloseBstrWrap(&EC, NULL);
+   QCBOREncode_OpenMap(&EC);
+   for(int i = 0; i < (BSTR_TEST_DEPTH-2); i++) {
+      QCBOREncode_BstrWrapInMapN(&EC, i+0x20);
+      QCBOREncode_OpenMap(&EC);
+      QCBOREncode_AddInt64ToMapN(&EC, i+0x10, i+0x10);
       QCBOREncode_AddSZStringToMapN(&EC, i+0x40, "hello");
-      QCBOREncode_CloseMap(&EC);
    }
+
+   for(int i = 0; i < (BSTR_TEST_DEPTH-2); i++) {
+      QCBOREncode_CloseMap(&EC);
+      QCBOREncode_CloseBstrWrap(&EC, NULL);
+   }
+   QCBOREncode_CloseMap(&EC);
+
    QCBOREncode_CloseArray(&EC);
 
    UsefulBufC Encoded;
@@ -2051,62 +2109,50 @@
 
    // ---Compare it to expected. Expected was hand checked with use of CBOR playground ----
    if(UsefulBuf_Compare(UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedDeepBstr), Encoded)) {
-      return -25;
+      return -2;
    }
 
-
    // ---- Decode it and see if it is OK ------
    QCBORDecodeContext DC;
    QCBORDecode_Init(&DC, Encoded, QCBOR_DECODE_MODE_NORMAL);
 
-   QCBORItem Item;
-   QCBORDecode_GetNext(&DC, &Item);
-   if(Item.uDataType != QCBOR_TYPE_ARRAY || Item.val.uCount != 3) {
-      return -2;
-   }
+   UsefulBufC Bstr;
+   uint16_t nArrayCount;
 
-   QCBORDecode_GetNext(&DC, &Item);
-   if(Item.uDataType != QCBOR_TYPE_BYTE_STRING) {
+   // Array surrounding the the whole thing
+   if(GetArray(&DC, &nArrayCount) || nArrayCount != 2) {
       return -3;
    }
 
-   int nReturn = DecodeNextNested(Item.val.string);
+   // Get the byte string wrapping some array stuff
+   if(GetByteString(&DC, &Bstr)) {
+      return -4;
+   }
+
+   // Decode the wrapped nested structure
+   int nReturn = DecodeNextNested(Bstr);
    if(nReturn) {
       return nReturn;
    }
 
-   nReturn = QCBORDecode_GetNext(&DC, &Item);
-   if(nReturn) {
-      return -11;
-   }
-   if(Item.uDataType != QCBOR_TYPE_INT64) {
-      return -12;
+   // A map enclosing some map-oriented bstr wraps
+   if(GetMap(&DC, &nArrayCount)) {
+      return -5;
    }
 
-   QCBORDecode_GetNext(&DC, &Item);
-   if(Item.uDataType != QCBOR_TYPE_MAP || Item.val.uCount != 2) {
-      return -2;
+   // Get the byte string wrapping some array stuff
+   if(GetByteString(&DC, &Bstr)) {
+      return -6;
    }
 
-   QCBORDecode_GetNext(&DC, &Item);
-   if(Item.uDataType != QCBOR_TYPE_BYTE_STRING) {
-      return -3;
-   }
-   nReturn = DecodeNextNested2(Item.val.string);
+   // Decode the wrapped nested structure
+   nReturn = DecodeNextNested2(Bstr);
    if(nReturn) {
       return nReturn;
    }
 
-   nReturn = QCBORDecode_GetNext(&DC, &Item);
-   if(nReturn) {
-      return -11;
-   }
-   if(Item.uDataType != QCBOR_TYPE_TEXT_STRING) {
-      return -12;
-   }
-
    if(QCBORDecode_Finish(&DC)) {
-      return -16;
+      return -7;
    }
 
    return 0;
@@ -2160,9 +2206,8 @@
 
 /*
  This corresponds exactly to the example in RFC 8152 section
- C.2.1. This doesn't actually verify the signature though that would
- be nice as it would make the test really good. That would require
- bring in ECDSA crypto to this test.
+ C.2.1. This doesn't actually verify the signature (however
+ the t_cose implementation does).
  */
 int32_t CoseSign1TBSTest()
 {
@@ -2175,6 +2220,8 @@
    const UsefulBufC     Signature        = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spCoseSign1Signature);
 
    QCBOREncodeContext EC;
+
+   // --------QCBOREncode_CloseBstrWrap2(&EC, **false** ----------------------
    QCBOREncode_Init(&EC, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
 
    // top level array for cose sign1, 18 is the tag for COSE sign
@@ -2195,15 +2242,20 @@
    // Payload is not actually CBOR in example C.2.1 like it would be
    // for a CWT or EAT. It is just a text string.
    QCBOREncode_AddEncoded(&EC, Payload);
-   QCBOREncode_CloseBstrWrap(&EC, &WrappedPayload);
+   QCBOREncode_CloseBstrWrap2(&EC, false, &WrappedPayload);
 
    // Check we got back the actual payload expected
    // The extra "T" is 0x54, which is the initial byte a bstr of length 20.
    if(UsefulBuf_Compare(WrappedPayload,
-                        UsefulBuf_FROM_SZ_LITERAL("TThis is the content."))) {
+                        UsefulBuf_FROM_SZ_LITERAL("This is the content."))) {
       return -1;
    }
 
+/*   if(UsefulBuf_Compare(WrappedPayload,
+                        UsefulBuf_FROM_SZ_LITERAL("TThis is the content."))) {
+      return -1;
+   } */
+
    // The signature
    QCBOREncode_AddBytes(&EC, Signature);
    QCBOREncode_CloseArray(&EC);
@@ -2226,6 +2278,57 @@
       return -4;
    }
 
+
+   // --------QCBOREncode_CloseBstrWrap2(&EC, **true** ------------------------
+   QCBOREncode_Init(&EC, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+
+   // top level array for cose sign1, 18 is the tag for COSE sign
+   QCBOREncode_AddTag(&EC, CBOR_TAG_COSE_SIGN1);
+   QCBOREncode_OpenArray(&EC);
+
+   // Add protected headers
+   QCBOREncode_AddBytes(&EC, ProtectedHeaders);
+
+   // Empty map with unprotected headers
+   QCBOREncode_OpenMap(&EC);
+   QCBOREncode_AddBytesToMapN(&EC, 4, Kid);
+   QCBOREncode_CloseMap(&EC);
+
+   // The payload
+   QCBOREncode_BstrWrap(&EC);
+   // Payload is not actually CBOR in example C.2.1 like it would be
+   // for a CWT or EAT. It is just a text string.
+   QCBOREncode_AddEncoded(&EC, Payload);
+   QCBOREncode_CloseBstrWrap2(&EC, true, &WrappedPayload);
+
+   // Check we got back the actual payload expected
+   // The extra "T" is 0x54, which is the initial byte a bstr of length 20.
+   if(UsefulBuf_Compare(WrappedPayload,
+                        UsefulBuf_FROM_SZ_LITERAL("TThis is the content."))) {
+      return -11;
+   }
+
+   // The signature
+   QCBOREncode_AddBytes(&EC, Signature);
+   QCBOREncode_CloseArray(&EC);
+
+   // Finish and check the results
+   if(QCBOREncode_Finish(&EC, &COSE_Sign1)) {
+      return -12;
+   }
+
+   // 98 is the size from RFC 8152 C.2.1
+   if(COSE_Sign1.len != 98) {
+      return -13;
+   }
+
+   // It would be good to compare this to the output from a COSE
+   // implementation like COSE-C. This has been checked against the
+   // CBOR playground.
+   if(CheckResults(COSE_Sign1, spCoseSign1TBSExpected)) {
+      return -14;
+   }
+
    return 0;
 }
 
@@ -2596,3 +2699,52 @@
 }
 
 #endif /* QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA */
+
+
+int32_t QCBORHeadTest()
+{
+   /* This test doesn't have to be extensive, because just about every
+    * other test exercises QCBOREncode_EncodeHead().
+    */
+   // ---- basic test to encode a zero ----
+   MakeUsefulBufOnStack(RightSize, QCBOR_HEAD_BUFFER_SIZE);
+
+   UsefulBufC encoded = QCBOREncode_EncodeHead(RightSize,
+                                               CBOR_MAJOR_TYPE_POSITIVE_INT,
+                                               0,
+                                               0);
+
+   static const uint8_t expectedZero[] = {0x00};
+
+   if(UsefulBuf_Compare(encoded, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(expectedZero))) {
+      return -1;
+   }
+
+   // ---- Encode a zero padded out to an 8 byte integer ----
+   encoded = QCBOREncode_EncodeHead(RightSize,
+                                    CBOR_MAJOR_TYPE_POSITIVE_INT,
+                                    8, // uMinSize is 8 bytes
+                                    0);
+
+   static const uint8_t expected9bytes[] = {0x1b, 0x00, 0x00, 0x00, 0x00,
+                                                  0x00, 0x00, 0x00, 0x00};
+
+   if(UsefulBuf_Compare(encoded, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(expected9bytes))) {
+      return -2;
+   }
+
+
+   // ---- Try to encode into too-small a buffer ----
+   MakeUsefulBufOnStack(TooSmall, QCBOR_HEAD_BUFFER_SIZE-1);
+
+   encoded = QCBOREncode_EncodeHead(TooSmall,
+                                    CBOR_MAJOR_TYPE_POSITIVE_INT,
+                                    0,
+                                    0);
+
+   if(!UsefulBuf_IsNULLC(encoded)) {
+      return -3;
+   }
+
+   return 0;
+}
diff --git a/test/qcbor_encode_tests.h b/test/qcbor_encode_tests.h
index d768889..204bdee 100644
--- a/test/qcbor_encode_tests.h
+++ b/test/qcbor_encode_tests.h
@@ -173,6 +173,7 @@
 int32_t ExponentAndMantissaEncodeTests(void);
 #endif /* QCBOR_CONFIG_DISABLE_EXP_AND_MANTISSA */
 
+
 /*
  Test the error cases when encoding CBOR such as buffer too large,
  buffer too small, array nesting too deep. Aims to cover the error
@@ -181,4 +182,12 @@
 int32_t EncodeErrorTests(void);
 
 
+/*
+ Test QCBOREncode_EncodeHead(). This is a minimal test because every other
+ test here exercises it in some way.
+ */
+int32_t QCBORHeadTest(void);
+
+
+
 #endif /* defined(__QCBOR__qcbor_encode_tests__) */
diff --git a/test/run_tests.c b/test/run_tests.c
index f8ed83b..8e52dae 100644
--- a/test/run_tests.c
+++ b/test/run_tests.c
@@ -56,6 +56,7 @@
 
 
 static test_entry s_tests[] = {
+    TEST_ENTRY(QCBORHeadTest),
     TEST_ENTRY(EmptyMapsAndArraysTest),
     TEST_ENTRY(NotWellFormedTests),
     TEST_ENTRY(ParseMapAsArrayTest),