Newer
Older
mbed-os / platform / FEATURE_EXPERIMENTAL_API / FEATURE_PSA / TARGET_MBED_PSA_SRV / services / attestation / qcbor / src / qcbor_encode.c
@Rajkumar Kanagaraj Rajkumar Kanagaraj on 21 Aug 2020 24 KB Move FEATURE_EXPERIMENTAL_API for PSA to platform
/*==============================================================================
 Copyright (c) 2016-2018, The Linux Foundation.
 Copyright (c) 2018-2019, Laurence Lundblade.
 All rights reserved.
 SPDX-License-Identifier: BSD-3-Clause

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above
      copyright notice, this list of conditions and the following
      disclaimer in the documentation and/or other materials provided
      with the distribution.
    * Neither the name of The Linux Foundation nor the names of its
      contributors, nor the name "Laurence Lundblade" may be used to
      endorse or promote products derived from this software without
      specific prior written permission.

THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ==============================================================================*/

/*===================================================================================
 FILE:  qcbor_encode.c

 DESCRIPTION:  This file contains the implementation of QCBOR.

 EDIT HISTORY FOR FILE:

 This section contains comments describing changes made to the module.
 Notice that changes are listed in reverse chronological order.

 when               who             what, where, why
 --------           ----            ---------------------------------------------------
 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.

 =====================================================================================*/

#include "qcbor.h"
#include "ieee754.h"


/*...... This is a ruler that is 80 characters long...........................*/


/*
 CBOR's two nesting types, arrays and maps, are tracked here. There is a
 limit of QCBOR_MAX_ARRAY_NESTING to the number of arrays and maps
 that can be nested in one encoding so the encoding context stays
 small enough to fit on the stack.

 When an array / map is opened, pCurrentNesting points to the element
 in pArrays that records the type, start position and accumluates a
 count of the number of items added. When closed the start position is
 used to go back and fill in the type and number of items in the array
 / map.

 Encoded output be just items like ints and strings that are
 not part of any array / map. That is, the first thing encoded
 does not have to be an array or a map.
 */
inline static void Nesting_Init(QCBORTrackNesting *pNesting)
{
   // assumes pNesting has been zeroed
   pNesting->pCurrentNesting = &pNesting->pArrays[0];
   // Implied CBOR array at the top nesting level. This is never returned,
   // but makes the item count work correctly.
   pNesting->pCurrentNesting->uMajorType = CBOR_MAJOR_TYPE_ARRAY;
}

inline static QCBORError Nesting_Increase(QCBORTrackNesting *pNesting,
                                          uint8_t uMajorType,
                                          uint32_t uPos)
{
   QCBORError nReturn = QCBOR_SUCCESS;

   if(pNesting->pCurrentNesting == &pNesting->pArrays[QCBOR_MAX_ARRAY_NESTING]) {
      // trying to open one too many
      nReturn = QCBOR_ERR_ARRAY_NESTING_TOO_DEEP;
   } else {
      pNesting->pCurrentNesting++;
      pNesting->pCurrentNesting->uCount     = 0;
      pNesting->pCurrentNesting->uStart     = uPos;
      pNesting->pCurrentNesting->uMajorType = uMajorType;
   }
   return nReturn;
}

inline static void Nesting_Decrease(QCBORTrackNesting *pNesting)
{
   pNesting->pCurrentNesting--;
}

inline static QCBORError Nesting_Increment(QCBORTrackNesting *pNesting)
{
   if(1 >= QCBOR_MAX_ITEMS_IN_ARRAY - pNesting->pCurrentNesting->uCount) {
      return QCBOR_ERR_ARRAY_TOO_LONG;
   }

   pNesting->pCurrentNesting->uCount += 1;

   return QCBOR_SUCCESS;
}

inline static uint16_t Nesting_GetCount(QCBORTrackNesting *pNesting)
{
   // The nesting count recorded is always the actual number of individiual
   // data items in the array or map. For arrays CBOR uses the actual item
   // count. For maps, CBOR uses the number of pairs.  This function returns
   // the number needed for the CBOR encoding, so it divides the number of
   // items by two for maps to get the number of pairs.  This implementation
   // takes advantage of the map major type being one larger the array major
   // type, hence uDivisor is either 1 or 2.
   const uint16_t uDivisor = pNesting->pCurrentNesting->uMajorType - CBOR_MAJOR_TYPE_ARRAY+1;

   return pNesting->pCurrentNesting->uCount / uDivisor;
}

inline static uint32_t Nesting_GetStartPos(QCBORTrackNesting *pNesting)
{
   return pNesting->pCurrentNesting->uStart;
}

inline static uint8_t Nesting_GetMajorType(QCBORTrackNesting *pNesting)
{
   return pNesting->pCurrentNesting->uMajorType;
}

inline static int Nesting_IsInNest(QCBORTrackNesting *pNesting)
{
   return pNesting->pCurrentNesting == &pNesting->pArrays[0] ? 0 : 1;
}




/*
 Error tracking plan -- Errors are tracked internally and not returned
 until Finish is called. The CBOR errors are in me->uError.
 UsefulOutBuf also tracks whether the buffer is full or not in its
 context.  Once either of these errors is set they are never
 cleared. Only QCBOREncode_Init() resets them. Or said another way, they must
 never be cleared or we'll tell the caller all is good when it is not.

 Only one error code is reported by QCBOREncode_Finish() even if there are
 multiple errors. The last one set wins. The caller might have to fix
 one error to reveal the next one they have to fix.  This is OK.

 The buffer full error tracked by UsefulBuf is only pulled out of
 UsefulBuf in Finish() so it is the one that usually wins.  UsefulBuf
 will never go off the end of the buffer even if it is called again
 and again when full.

 It is really tempting to not check for overflow on the count in the
 number of items in an array. It would save a lot of code, it is
 extremely unlikely that any one will every put 65,000 items in an
 array, and the only bad thing that would happen is the CBOR would be
 bogus.

 Since this does not parse any input, you could in theory remove all
 error checks in this code if you knew the caller called it
 correctly. Maybe someday CDDL or some such language will be able to
 generate the code to call this and the calling code would always be
 correct. This could also automatically size some of the data
 structures like array/map nesting resulting in some stack memory
 savings.

 Errors returned here fall into two categories:

 Sizes
   QCBOR_ERR_BUFFER_TOO_LARGE -- Encoded output exceeded UINT32_MAX
   QCBOR_ERR_BUFFER_TOO_SMALL -- output buffer too small

   QCBOR_ERR_ARRAY_NESTING_TOO_DEEP -- Array/map nesting > QCBOR_MAX_ARRAY_NESTING1
   QCBOR_ERR_ARRAY_TOO_LONG -- Too many things added to an array/map

 Nesting constructed incorrectly
   QCBOR_ERR_TOO_MANY_CLOSES -- more close calls than opens
   QCBOR_ERR_CLOSE_MISMATCH -- Type of close does not match open
   QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN -- Finish called without enough closes
 */




/*
 Public function for initialization. See header qcbor.h
 */
void QCBOREncode_Init(QCBOREncodeContext *me, UsefulBuf Storage)
{
   memset(me, 0, sizeof(QCBOREncodeContext));
   UsefulOutBuf_Init(&(me->OutBuf), Storage);
   Nesting_Init(&(me->nesting));
}




/*
 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 param 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.
 */

static void InsertEncodedTypeAndNumber(QCBOREncodeContext *me,
                                       uint8_t uMajorType,
                                       int nMinLen,
                                       uint64_t uNumber,
                                       size_t uPos)
{
   /*
    This code does endian conversion without hton or knowing the
    endianness of the machine using masks and shifts. This avoids the
    dependency on hton and the mess of figuring out how to find the
    machine's endianness.

    This is a good efficient implementation on little-endian machines.
    A faster and small implementation is possible on big-endian
    machines because CBOR/network byte order is big endian. However
    big endian machines are uncommon.

    On x86, it is about 200 bytes instead of 500 bytes for the more
    formal unoptimized code.

    This also does the CBOR preferred shortest encoding for integers
    and is called to do endian conversion for floats.

    It works backwards from the LSB to the MSB as needed.

    Code Reviewers: THIS FUNCTION DOES POINTER MATH
    */
   // 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
   uint8_t uAdditionalInfo;

   if(uNumber < CBOR_TWENTY_FOUR && nMinLen == 0) {
      // Simple case where argument is < 24
      uAdditionalInfo = uNumber;
   } 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.
       */
      static const uint8_t aIterate[] = {1,1,2,4};
      uint8_t i;
      for(i = 0; uNumber || nMinLen > 0; i++) {
         const uint8_t uIterations = aIterate[i];
         for(int j = 0; j < uIterations; j++) {
            *--pByte = uNumber & 0xff;
            uNumber = uNumber >> 8;
         }
         nMinLen -= uIterations;
      }
      // Additional info is the encoding of the
      // number of additional bytes to encode
      // argument.
      uAdditionalInfo = LEN_IS_ONE_BYTE-1 + i;
   }
   *--pByte = (uMajorType << 5) + uAdditionalInfo;

   UsefulOutBuf_InsertData(&(me->OutBuf), pByte, &bytes[sizeof(bytes)-1] - pByte, uPos);
}


/*
 Append the type and number info to the end of the buffer.

 See InsertEncodedTypeAndNumber() function above for details
*/
inline static void AppendEncodedTypeAndNumber(QCBOREncodeContext *me,
                                              uint8_t uMajorType,
                                              uint64_t uNumber)
{
   // An append is an insert at the end.
   InsertEncodedTypeAndNumber(me,
                              uMajorType,
                              0,
                              uNumber,
                              UsefulOutBuf_GetEndPosition(&(me->OutBuf)));
}




/*
 Public functions for closing arrays and maps. See header qcbor.h
 */
void QCBOREncode_AddUInt64(QCBOREncodeContext *me, uint64_t uValue)
{
   if(me->uError == QCBOR_SUCCESS) {
      AppendEncodedTypeAndNumber(me, CBOR_MAJOR_TYPE_POSITIVE_INT, uValue);
      me->uError = Nesting_Increment(&(me->nesting));
   }
}


/*
 Public functions for closing arrays and maps. See header qcbor.h
 */
void QCBOREncode_AddInt64(QCBOREncodeContext *me, int64_t nNum)
{
   if(me->uError == QCBOR_SUCCESS) {
      uint8_t      uMajorType;
      uint64_t     uValue;

      if(nNum < 0) {
         // In CBOR -1 encodes as 0x00 with major type negative int.
         uValue = (uint64_t)(-nNum - 1);
         uMajorType = CBOR_MAJOR_TYPE_NEGATIVE_INT;
      } else {
         uValue = (uint64_t)nNum;
         uMajorType = CBOR_MAJOR_TYPE_POSITIVE_INT;
      }

      AppendEncodedTypeAndNumber(me, uMajorType, uValue);
      me->uError = Nesting_Increment(&(me->nesting));
   }
}


/*
 Semi-private function. It is exposed to user of the interface,
 but they will usually call one of the inline wrappers rather than this.

 See header qcbor.h

 Does the work of adding some bytes to the CBOR output. Works for a
 byte and text strings, which are the same in in CBOR though they have
 different major types.  This is also used to insert raw
 pre-encoded CBOR.
 */
void QCBOREncode_AddBuffer(QCBOREncodeContext *me, uint8_t uMajorType, UsefulBufC Bytes)
{
   if(me->uError == QCBOR_SUCCESS) {
      // If it is not Raw CBOR, add the type and the length
      if(uMajorType != CBOR_MAJOR_NONE_TYPE_RAW) {
         AppendEncodedTypeAndNumber(me, uMajorType, Bytes.len);
      }

      // Actually add the bytes
      UsefulOutBuf_AppendUsefulBuf(&(me->OutBuf), Bytes);

      // Update the array counting if there is any nesting at all
      me->uError = Nesting_Increment(&(me->nesting));
   }
}


/*
 Public functions for closing arrays and maps. See header qcbor.h
 */
void QCBOREncode_AddTag(QCBOREncodeContext *me, uint64_t uTag)
{
   AppendEncodedTypeAndNumber(me, CBOR_MAJOR_TYPE_OPTIONAL, uTag);
}


/*
 Semi-private function. It is exposed to user of the interface,
 but they will usually call one of the inline wrappers rather than this.

 See header qcbor.h
 */
void QCBOREncode_AddType7(QCBOREncodeContext *me, size_t uSize, uint64_t uNum)
{
   if(me->uError == QCBOR_SUCCESS) {
      // This function call takes care of endian swapping for the float / double
      InsertEncodedTypeAndNumber(me,
                                 // The major type for floats and doubles
                                 CBOR_MAJOR_TYPE_SIMPLE,
                                 // size makes sure floats with zeros encode correctly
                                 (int)uSize,
                                 // Bytes of the floating point number as a uint
                                 uNum,
                                 // end position because this is append
                                 UsefulOutBuf_GetEndPosition(&(me->OutBuf)));

      me->uError = Nesting_Increment(&(me->nesting));
   }
}


/*
 Public functions for closing arrays and maps. See header qcbor.h
 */
void QCBOREncode_AddDouble(QCBOREncodeContext *me, double dNum)
{
   const IEEE754_union uNum = IEEE754_DoubleToSmallest(dNum);

   QCBOREncode_AddType7(me, uNum.uSize, uNum.uValue);
}


/*
 Semi-public function. It is exposed to user of the interface,
 but they will usually call one of the inline wrappers rather than this.

 See header qcbor.h
*/
void QCBOREncode_OpenMapOrArray(QCBOREncodeContext *me, uint8_t uMajorType)
{
   // Add one item to the nesting level we are in for the new map or array
   me->uError = Nesting_Increment(&(me->nesting));
   if(me->uError == QCBOR_SUCCESS) {
      // The offset where the length of an array or map will get written
      // is stored in a uint32_t, not a size_t to keep stack usage smaller. This
      // checks to be sure there is no wrap around when recording the offset.
      // Note that on 64-bit machines CBOR larger than 4GB can be encoded as long as no
      // array / map offsets occur past the 4GB mark, but the public interface
      // says that the maximum is 4GB to keep the discussion simpler.
      size_t uEndPosition = UsefulOutBuf_GetEndPosition(&(me->OutBuf));

      // QCBOR_MAX_ARRAY_OFFSET is slightly less than UINT32_MAX so this
      // code can run on a 32-bit machine and tests can pass on a 32-bit
      // machine. If it was exactly UINT32_MAX, then this code would
      // not compile or run on a 32-bit machine and an #ifdef or some
      // machine size detection would be needed reducing portability.
      if(uEndPosition >= QCBOR_MAX_ARRAY_OFFSET) {
         me->uError = QCBOR_ERR_BUFFER_TOO_LARGE;

      } else {
         // Increase nesting level because this is a map or array.
         // Cast from size_t to uin32_t is safe because of check above
         me->uError = Nesting_Increase(&(me->nesting), uMajorType, (uint32_t)uEndPosition);
      }
   }
}


/*
 Public functions for closing arrays and maps. See header qcbor.h
 */
void QCBOREncode_CloseMapOrArray(QCBOREncodeContext *me,
                                 uint8_t uMajorType,
                                 UsefulBufC *pWrappedCBOR)
{
   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 done
         // 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;

         // Length is number of bytes for a bstr and number of items a for map & array
         const size_t uLength = uMajorType == CBOR_MAJOR_TYPE_BYTE_STRING ?
                                    uLenOfEncodedMapOrArray : Nesting_GetCount(&(me->nesting));

         // Actually insert
         InsertEncodedTypeAndNumber(me,
                                    uMajorType,       // major type bstr, array or map
                                    0,                // no minimum length for encoding
                                    uLength,          // either len of bstr or num map / array items
                                    uInsertPosition); // position in out buffer

         // 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 of the
         // InsertEncodedTypeAndNumber() call that slides data to the right.
         if(pWrappedCBOR) {
            const UsefulBufC PartialResult = UsefulOutBuf_OutUBuf(&(me->OutBuf));
            const size_t uBstrLen = UsefulOutBuf_GetEndPosition(&(me->OutBuf)) - uEndPosition;
            *pWrappedCBOR = UsefulBuf_Tail(PartialResult, uInsertPosition+uBstrLen);
         }
         Nesting_Decrease(&(me->nesting));
      }
   }
}




/*
 Public functions to finish and get the encoded result. See header qcbor.h
 */
QCBORError QCBOREncode_Finish(QCBOREncodeContext *me, UsefulBufC *pEncodedCBOR)
{
   QCBORError uReturn = me->uError;

   if(uReturn != QCBOR_SUCCESS) {
      goto Done;
   }

   if (Nesting_IsInNest(&(me->nesting))) {
      uReturn = QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN;
      goto Done;
   }

   if(UsefulOutBuf_GetError(&(me->OutBuf))) {
      // Stuff didn't fit in the buffer.
      // This check catches this condition for all the appends and inserts
      // so checks aren't needed when the appends and inserts are performed.
      // And of course UsefulBuf will never overrun the input buffer given
      // to it. No complex analysis of the error handling in this file is
      // needed to know that is true. Just read the UsefulBuf code.
      uReturn = QCBOR_ERR_BUFFER_TOO_SMALL;
      goto Done;
   }

   *pEncodedCBOR = UsefulOutBuf_OutUBuf(&(me->OutBuf));

Done:
   return uReturn;
}


/*
 Public functions to finish and get the encoded result. See header qcbor.h
 */
QCBORError QCBOREncode_FinishGetSize(QCBOREncodeContext *me, size_t *puEncodedLen)
{
   UsefulBufC Enc;

   QCBORError nReturn = QCBOREncode_Finish(me, &Enc);

   if(nReturn == QCBOR_SUCCESS) {
      *puEncodedLen = Enc.len;
   }

   return nReturn;
}




/*
 Notes on the code

 CBOR Major Type     Public Function
 0                   QCBOREncode_AddUInt64
 0, 1                QCBOREncode_AddUInt64, QCBOREncode_AddInt64
 2, 3                QCBOREncode_AddBuffer, Also QCBOREncode_OpenMapOrArray
 4, 5                QCBOREncode_OpenMapOrArray
 6                   QCBOREncode_AddTag
 7                   QCBOREncode_AddDouble, QCBOREncode_AddType7

 Object code sizes on X86 with LLVM compiler and -Os (Dec 30, 2018)

 _QCBOREncode_Init   69
 _QCBOREncode_AddUInt64   76
 _QCBOREncode_AddInt64   87
 _QCBOREncode_AddBuffer   113
 _QCBOREncode_AddTag 27
 _QCBOREncode_AddType7   87
 _QCBOREncode_AddDouble 36
 _QCBOREncode_OpenMapOrArray   103
 _QCBOREncode_CloseMapOrArray   181
 _InsertEncodedTypeAndNumber   190
 _QCBOREncode_Finish   72
 _QCBOREncode_FinishGetSize  70

 Total is about 1.1KB

 _QCBOREncode_CloseMapOrArray is larger because it has a lot
 of nesting tracking to do and much of Nesting_ inlines
 into it. It probably can't be reduced much.

 If the error returned by Nesting_Increment() can be ignored
 because the limit is so high and the consequence of exceeding
 is proved to be inconsequential, then a lot of if(me->uError)
 instance can be removed, saving some code.

 */