Lots of progress on indefinite lengths; refactor decoder to get of weird semi-recursion
diff --git a/QCBOR.xcodeproj/project.pbxproj b/QCBOR.xcodeproj/project.pbxproj
index c6a348b..17dc1f2 100644
--- a/QCBOR.xcodeproj/project.pbxproj
+++ b/QCBOR.xcodeproj/project.pbxproj
@@ -7,6 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
+ 0F60E27B2164705400483952 /* qcbor_decode_malloc.c in Sources */ = {isa = PBXBuildFile; fileRef = 0F60E27A2164705400483952 /* qcbor_decode_malloc.c */; };
E73B5756216071900080D658 /* bstrwrap_tests.c in Sources */ = {isa = PBXBuildFile; fileRef = E73B5755216071900080D658 /* bstrwrap_tests.c */; };
E73B57592161CA690080D658 /* ieee754.c in Sources */ = {isa = PBXBuildFile; fileRef = E73B57582161CA690080D658 /* ieee754.c */; };
E73B575E2161CA7C0080D658 /* half_precision_test.c in Sources */ = {isa = PBXBuildFile; fileRef = E73B575A2161CA7C0080D658 /* half_precision_test.c */; };
@@ -32,6 +33,7 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
+ 0F60E27A2164705400483952 /* qcbor_decode_malloc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = qcbor_decode_malloc.c; sourceTree = "<group>"; };
E73B5754216071900080D658 /* bstrwrap_tests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = bstrwrap_tests.h; path = test/bstrwrap_tests.h; sourceTree = "<group>"; };
E73B5755216071900080D658 /* bstrwrap_tests.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = bstrwrap_tests.c; path = test/bstrwrap_tests.c; sourceTree = "<group>"; };
E73B57572161CA680080D658 /* ieee754.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ieee754.h; path = src/ieee754.h; sourceTree = "<group>"; };
@@ -45,8 +47,8 @@
E776E07C214ADF7F00E67947 /* QCBOR */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = QCBOR; sourceTree = BUILT_PRODUCTS_DIR; };
E776E08C214AE07400E67947 /* qcbor_encode.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.c; name = qcbor_encode.c; path = src/qcbor_encode.c; sourceTree = "<group>"; tabWidth = 3; };
E776E08D214AE07500E67947 /* UsefulBuf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = UsefulBuf.c; path = src/UsefulBuf.c; sourceTree = "<group>"; };
- E776E08E214AE07500E67947 /* qcbor_decode.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = qcbor_decode.c; path = src/qcbor_decode.c; sourceTree = "<group>"; };
- E776E093214AE08B00E67947 /* qcbor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = qcbor.h; path = inc/qcbor.h; sourceTree = "<group>"; };
+ E776E08E214AE07500E67947 /* qcbor_decode.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.c; name = qcbor_decode.c; path = src/qcbor_decode.c; sourceTree = "<group>"; tabWidth = 3; };
+ E776E093214AE08B00E67947 /* qcbor.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.h; name = qcbor.h; path = inc/qcbor.h; sourceTree = "<group>"; tabWidth = 3; };
E776E094214AE09700E67947 /* UsefulBuf.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.c.h; name = UsefulBuf.h; path = inc/UsefulBuf.h; sourceTree = "<group>"; tabWidth = 3; };
E776E096214AE0C700E67947 /* cmd_line_main.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_line_main.c; sourceTree = "<group>"; };
E776E09B214AEEEA00E67947 /* basic_test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = basic_test.h; path = test/basic_test.h; sourceTree = "<group>"; };
@@ -88,6 +90,7 @@
E776E08B214AE06600E67947 /* src */ = {
isa = PBXGroup;
children = (
+ 0F60E27A2164705400483952 /* qcbor_decode_malloc.c */,
E73B57582161CA690080D658 /* ieee754.c */,
E73B57572161CA680080D658 /* ieee754.h */,
E776E08E214AE07500E67947 /* qcbor_decode.c */,
@@ -180,6 +183,7 @@
buildActionMask = 2147483647;
files = (
E776E08F214AE07500E67947 /* qcbor_encode.c in Sources */,
+ 0F60E27B2164705400483952 /* qcbor_decode_malloc.c in Sources */,
E73B57592161CA690080D658 /* ieee754.c in Sources */,
E73B575F2161CA7C0080D658 /* half_to_double_from_rfc7049.c in Sources */,
E73B57652161F8F80080D658 /* run_tests.c in Sources */,
diff --git a/inc/UsefulBuf.h b/inc/UsefulBuf.h
index 39c518f..aeb20eb 100644
--- a/inc/UsefulBuf.h
+++ b/inc/UsefulBuf.h
@@ -372,6 +372,9 @@
*/
UsefulBufC UsefulBuf_Copy(UsefulBuf Dest, const UsefulBufC Src);
+UsefulBufC UsefulBuf_CopyOffset(UsefulBuf Dest, size_t uOffset, const UsefulBufC Src);
+
+
/**
@brief Set all bytes in a UsefulBuf to a value, for example 0
@@ -633,6 +636,12 @@
*/
void UsefulOutBuf_Init(UsefulOutBuf *me, UsefulBuf Storage);
+static inline void UsefulOutBuf_Realloc(UsefulOutBuf *me, UsefulBuf Storage)
+{
+ me->UB = Storage;
+}
+
+
/** Convenience marco to make a UsefulOutBuf on the stack and
diff --git a/inc/qcbor.h b/inc/qcbor.h
index eec84c1..4ea4079 100644
--- a/inc/qcbor.h
+++ b/inc/qcbor.h
@@ -180,8 +180,15 @@
UsefulInputBuf InBuf;
uint8_t uDecodeMode;
+ uint8_t bStringAllocateAll; // TODO: fully implement this
QCBORDecodeNesting nesting;
+
+ // This is NULL or points to a QCBORStringAllocator. It is void
+ // here because _QCBORDecodeContext is defined early n the
+ // private part of this file and QCBORStringAllocat is defined
+ // later in the public part of this file.
+ void *pStringAllocator;
};
@@ -581,6 +588,17 @@
/** Closing something different than is open */
#define QCBOR_ERR_CLOSE_MISMATCH 17
+/** Unable to decode an indefinitely length string because no string allocator was configured */
+#define QCBOR_ERR_NO_STRING_ALLOCATOR 18
+
+/** One of the segments in an indefinite length string is of the wrong type */
+#define QCBOR_ERR_INDEFINITE_STRING_SEG 19
+
+/** Error allocating space for indefinite length string segment */
+#define QCBOR_ERR_STRING_ALLOC 20
+
+/** The a break occurred outside an indefinite length item */
+#define QCBOR_ERR_BAD_BREAK 21
/** See QCBORDecode_Init() */
@@ -630,9 +648,10 @@
/** Type for the simple value undef; nothing more; nothing in val union. */
#define QCBOR_TYPE_UNDEF 23
+#define QCBOR_TYPE_BREAK 31 // Used internally; never returned
#define QCBOR_TYPE_OPTTAG 254 // Used internally; never returned
-#define QCBOR_TYPE_BREAK 255 // Used internally; never returned
+//#define QCBOR_TYPE_BREAK 255 // Used internally; never returned
@@ -651,6 +670,7 @@
uint8_t uDataType; /** Tells what element of the val union to use. One of QCBOR_TYPE_XXXX */
uint8_t uNestingLevel; /** How deep the nesting from arrays and maps are. 0 is the top level with no arrays or maps entered */
uint8_t uLabelType; /** Tells what element of the label union to use */
+ uint8_t uAllocated; /** 1 if allocated with string allocator, 0 if not. See xxx TODO: */
union {
int64_t int64; /** The value for uDataType QCBOR_TYPE_INT64 */
@@ -682,6 +702,19 @@
} QCBORItem;
+/*
+ Optional to set up an allocator for bstr and tstr types.
+ Required to process indefinite length bstr and tstr types.
+ (indefinite length maps can be processed without this).
+ */
+typedef struct {
+ void *pAllocaterContext;
+ UsefulBuf (*fAllocate)(void *pAllocaterContext, void *pOldMem, size_t uNewSize);
+ void (*fFree)(void *pAllocaterContext, void *pMem);
+ void (*fDestructor)(void *pAllocaterContext);
+} QCBORStringAllocator;
+
+
/** See the descriptions for CBOR_SIMPLEV_FALSE, CBOR_TAG_DATE_EPOCH... for
the meaning of the individual tags. The values here are bit flags
associated with each tag. These flags are set in uTagsBits in QCBORItem */
@@ -1609,6 +1642,49 @@
/**
+ Set up a memory pool for indefinite length strings
+
+ @param[in] pCtx The decode context to initialize.
+ @param[in] MemPool The pointer and length of the memory pool
+ @param[in] bAllStrings true means to put even definite length strings in the pool
+
+ Indefinite length strings (text and byte) cannot be decoded unless there is
+ either a memory pool set up or a string allocator configured.
+
+ The MemPool must be sized large enough. For a 64-bit CPU it must be sized
+ at 64 bytes plus space to hold all the indefinite length strings. For a 32-bit CPU
+ the size is 32 bytes plus space for the strings. There is no overhead per
+ string. This includes indefinite length strings that are serve as labels
+ for map items.
+
+ If bAllStrings is set then the size will be 64 or 32 bytes plus the space to
+ hold **all** strings, definite and indefinite length, value or label.
+ The advantage of this
+ is that after the parse is complete, the original memory holding the
+ encoded CBOR does not need to remain valid.
+
+ The memory from MemPool will be where all the pointers in returned
+ QCBORItems will point.
+
+
+
+ */
+void QCBORDecode_SetMemPool(QCBORDecodeContext *pCtx, UsefulBuf MemPool, bool bAllStrings);
+
+
+int QCBORDecode_SetMemPool_Init(QCBORDecodeContext *pCtx, UsefulBufC EncodedCBOR, int8_t nMode, UsefulBuf MemPool);
+
+
+/**
+
+ */
+void QCBORDecode_SetUpAllocator(QCBORDecodeContext *pCtx, const QCBORStringAllocator *pAllocator);
+
+QCBORStringAllocator *QCBORDecode_GetAllocator(QCBORDecodeContext *pCtx);
+
+
+
+/**
Gets the next item (integer, byte string, array...) in pre order traversal of CBOR tree
@param[in] pCtx The context to initialize
diff --git a/src/UsefulBuf.c b/src/UsefulBuf.c
index 323e173..3aa6406 100644
--- a/src/UsefulBuf.c
+++ b/src/UsefulBuf.c
@@ -93,6 +93,18 @@
return((UsefulBufC){Dest.ptr, Src.len});
}
+
+UsefulBufC UsefulBuf_CopyOffset(UsefulBuf Dest, size_t uOffset, const UsefulBufC Src)
+{
+ if(Src.len > Dest.len - uOffset) {
+ return NULLUsefulBufC;
+ }
+
+ memcpy(Dest.ptr + uOffset, Src.ptr, Src.len);
+
+ return((UsefulBufC){Dest.ptr, Src.len});
+}
+
/*
Public function -- see UsefulBuf.h
*/
@@ -170,6 +182,7 @@
// The following check fails on ThreadX
#if 0
+ // TODO: fix this for new way of doing storage
// Sanity check on the pointer and size to be sure we are not
// passed a buffer that goes off the end of the address space.
// Given this test, we know that all unsigned lengths less than
diff --git a/src/qcbor_decode.c b/src/qcbor_decode.c
index 7e441cd..eccd469 100644
--- a/src/qcbor_decode.c
+++ b/src/qcbor_decode.c
@@ -93,15 +93,30 @@
return pNesting->pCurrent != &(pNesting->pMapsAndArrays[0]);
}
+inline static int DecodeNesting_IsIndefiniteLength(const QCBORDecodeNesting *pNesting)
+{
+ if(!DecodeNesting_IsNested(pNesting)) {
+ return 0;
+ }
+
+ return pNesting->pCurrent->uCount == UINT16_MAX;
+}
+
inline static int DecodeNesting_TypeIsMap(const QCBORDecodeNesting *pNesting)
{
- if(!DecodeNesting_IsNested(pNesting))
+ if(!DecodeNesting_IsNested(pNesting)) {
return 0;
+ }
return CBOR_MAJOR_TYPE_MAP == pNesting->pCurrent->uMajorType;
}
-inline static void DecodeNesting_Decrement(QCBORDecodeNesting *pNesting, uint8_t uDataType)
+inline static void DecodeNesting_Ascend(QCBORDecodeNesting *pNesting)
+{
+ pNesting->pCurrent--;
+}
+
+inline static void DecodeNesting_Decrement(QCBORDecodeNesting *pNesting)
{
if(!DecodeNesting_IsNested(pNesting)) {
return; // at top level where there is no tracking
@@ -109,7 +124,7 @@
// Decrement
pNesting->pCurrent->uCount--;
-
+
// Pop up nesting levels if the counts at the levels is zero
while(0 == pNesting->pCurrent->uCount && DecodeNesting_IsNested(pNesting)) {
pNesting->pCurrent--;
@@ -167,6 +182,15 @@
/*
+ Public function, see header file
+ */
+void QCBORDecode_SetUpAllocator(QCBORDecodeContext *pCtx, const QCBORStringAllocator *pAllocator)
+{
+ pCtx->pStringAllocator = (void *)pAllocator;
+}
+
+
+/*
This decodes the fundamental part of a CBOR data item, the type and number
This is the Counterpart to InsertEncodedTypeAndNumber().
@@ -175,6 +199,11 @@
also results in the conversion for floats in addition to that for
lengths, tags and integer values.
+ This returns:
+ pnMajorType -- the major type for the item
+ puNumber -- the "number" which is used a the value for integers, tags and floats and length for strings and arrays
+ puAdditionalInfo -- Pass this along to know what kind of float or if length is indefinite
+
*/
inline static int DecodeTypeAndNumber(UsefulInputBuf *pUInBuf, int *pnMajorType, uint64_t *puNumber, uint8_t *puAdditionalInfo)
{
@@ -188,8 +217,7 @@
const uint8_t uAdditionalInfo = InitialByte & 0x1f;
// Get the integer that follows the major type. Do not know if this is a length, value, float or tag at this point
- // Also convert from network byte order. Call ntohxx on simple variables in case they are macros that
- // reference their argument multiple times.
+ // Also convert from network byte order.
uint64_t uTmpValue;
switch(uAdditionalInfo) {
@@ -212,11 +240,14 @@
case ADDINFO_RESERVED1: // reserved by CBOR spec
case ADDINFO_RESERVED2: // reserved by CBOR spec
case ADDINFO_RESERVED3: // reserved by CBOR spec
- case LEN_IS_INDEFINITE: // indefinite types not supported (yet)
nReturn = QCBOR_ERR_UNSUPPORTED;
goto Done;
+
+ case LEN_IS_INDEFINITE:
+ // Fall through to see what happens: TODO: check this.
default:
+ // This is when the "number" is in the additional info
uTmpValue = uAdditionalInfo;
break;
}
@@ -300,6 +331,10 @@
#error QCBOR_TYPE_UNDEF macro value wrong
#endif
+#if QCBOR_TYPE_BREAK != CBOR_SIMPLE_BREAK
+#error QCBOR_TYPE_BREAK macro value wrong
+#endif
+
#if QCBOR_TYPE_DOUBLE != DOUBLE_PREC_FLOAT
#error QCBOR_TYPE_DOUBLE macro value wrong
#endif
@@ -324,7 +359,6 @@
case ADDINFO_RESERVED1: // 28
case ADDINFO_RESERVED2: // 29
case ADDINFO_RESERVED3: // 30
- case CBOR_SIMPLE_BREAK: // 31
nReturn = QCBOR_ERR_UNSUPPORTED;
break;
@@ -343,6 +377,7 @@
case CBOR_SIMPLEV_TRUE: // 21
case CBOR_SIMPLEV_NULL: // 22
case CBOR_SIMPLEV_UNDEF: // 23
+ case CBOR_SIMPLE_BREAK: // 31
break; // nothing to do
case CBOR_SIMPLEV_ONEBYTE: // 24
@@ -370,7 +405,7 @@
/*
Decode text and byte strings
*/
-inline static int DecodeBytes(int nMajorType, uint64_t uNumber, UsefulInputBuf *pUInBuf, QCBORItem *pDecodedItem)
+inline static int DecodeBytesOld(int nMajorType, uint64_t uNumber, UsefulInputBuf *pUInBuf, QCBORItem *pDecodedItem)
{
const void *pBytes = UsefulInputBuf_GetBytes(pUInBuf, uNumber);
@@ -385,6 +420,30 @@
return nReturn;
}
+inline static int DecodeBytes(QCBORStringAllocator *pAlloc, int nMajorType, uint64_t uNumber, UsefulInputBuf *pUInBuf, QCBORItem *pDecodedItem)
+{
+ UsefulBufC Bytes = UsefulInputBuf_GetUsefulBuf(pUInBuf, uNumber);
+
+ int nReturn = QCBOR_ERR_HIT_END;
+
+ if(!UsefulBuf_IsNULLC(Bytes)) {
+ if(pAlloc) {
+ UsefulBuf NewMem = pAlloc->fAllocate(pAlloc->pAllocaterContext, NULL, uNumber);
+ if(UsefulBuf_IsNULL(NewMem)) {
+ pDecodedItem->val.string = UsefulBuf_Copy(NewMem, Bytes);
+ } else {
+ // TODO: failure
+ }
+ } else {
+ pDecodedItem->val.string = Bytes;
+ }
+ pDecodedItem->uDataType = (nMajorType == CBOR_MAJOR_TYPE_BYTE_STRING) ? QCBOR_TYPE_BYTE_STRING : QCBOR_TYPE_TEXT_STRING;
+ nReturn = QCBOR_SUCCESS;
+ }
+
+ return nReturn;
+}
+
/*
Mostly just assign the right data type for the date string.
@@ -453,21 +512,203 @@
}
+
+
+// Make sure the constants align as this is assumed by the GetAnItem() implementation
+#if QCBOR_TYPE_ARRAY != CBOR_MAJOR_TYPE_ARRAY
+#error QCBOR_TYPE_ARRAY value not lined up with major type
+#endif
+#if QCBOR_TYPE_MAP != CBOR_MAJOR_TYPE_MAP
+#error QCBOR_TYPE_MAP value not lined up with major type
+#endif
+
/*
- Decode the optional tagging that preceeds the real data value. There could be lots of them.
+ This gets a single data item and decodes it including preceding optional tagging. This does not
+ deal with arrays and maps and nesting except to decode the data item introducing them. Arrays and
+ maps are handled at the next level up in GetNext().
+
+ Errors detected here include: an array that is too long to decode, hit end of buffer unexpectedly,
+ a few forms of invalid encoded CBOR
*/
-static int GetAnItem(UsefulInputBuf *pUInBuf, QCBORItem *pDecodedItem, int bCalledFromDecodeOptional);
+
+static int GetNext_Item(UsefulInputBuf *pUInBuf, QCBORItem *pDecodedItem, QCBORStringAllocator *pAlloc)
+{
+ int nReturn;
+
+ // Get the major type and the number. Number could be length of more bytes or the value depending on the major type
+ // nAdditionalInfo is an encoding of the length of the uNumber and is needed to decode floats and doubles
+ int uMajorType;
+ uint64_t uNumber;
+ uint8_t uAdditionalInfo;
+
+ nReturn = DecodeTypeAndNumber(pUInBuf, &uMajorType, &uNumber, &uAdditionalInfo);
+
+ // Error out here if we got into trouble on the type and number.
+ // The code after this will not work if the type and number is not good.
+ if(nReturn)
+ goto Done;
+
+ pDecodedItem->uTagBits = 0;
+ pDecodedItem->uTag = 0;
+ pDecodedItem->uAllocated = 0;
+
+ // At this point the major type and the value are valid. We've got the type and the number that
+ // starts every CBOR data item.
+ switch (uMajorType) {
+ case CBOR_MAJOR_TYPE_POSITIVE_INT: // Major type 0
+ case CBOR_MAJOR_TYPE_NEGATIVE_INT: // Major type 1
+ nReturn = DecodeInteger(uMajorType, uNumber, pDecodedItem);
+ break;
+
+ case CBOR_MAJOR_TYPE_BYTE_STRING: // Major type 2
+ case CBOR_MAJOR_TYPE_TEXT_STRING: // Major type 3
+ if(uAdditionalInfo == LEN_IS_INDEFINITE) {
+ pDecodedItem->uDataType = (uMajorType == CBOR_MAJOR_TYPE_BYTE_STRING) ? QCBOR_TYPE_BYTE_STRING : QCBOR_TYPE_TEXT_STRING;
+ pDecodedItem->val.string = (UsefulBufC){NULL, SIZE_MAX}; // TODO: SIZE_MAX this is OK, right?
+ } else {
+ nReturn = DecodeBytes(pAlloc, uMajorType, uNumber, pUInBuf, pDecodedItem);
+ }
+ break;
+
+ case CBOR_MAJOR_TYPE_ARRAY: // Major type 4
+ case CBOR_MAJOR_TYPE_MAP: // Major type 5
+ // Record the number of items in the array or map
+ if(uNumber > QCBOR_MAX_ITEMS_IN_ARRAY) {
+ nReturn = QCBOR_ERR_ARRAY_TOO_LONG;
+ goto Done;
+ }
+ if(uAdditionalInfo == LEN_IS_INDEFINITE) {
+ pDecodedItem->val.uCount = UINT16_MAX; // Indicate indefinite length; TODO: a better way?
+ } else {
+ pDecodedItem->val.uCount = (uint16_t)uNumber; // type conversion OK because of check above
+ }
+ pDecodedItem->uDataType = uMajorType; // C preproc #if above makes sure constants align
+ break;
+
+ case CBOR_MAJOR_TYPE_OPTIONAL: // Major type 6, optional prepended tags
+ pDecodedItem->uTag = uNumber;
+ pDecodedItem->uDataType = QCBOR_TYPE_OPTTAG;
+ break;
+
+ case CBOR_MAJOR_TYPE_SIMPLE: // Major type 7, float, double, true, false, null...
+ nReturn = DecodeSimple(uAdditionalInfo, uNumber, pDecodedItem);
+ break;
+
+ default: // Should never happen because DecodeTypeAndNumber() should never return > 7
+ nReturn = QCBOR_ERR_UNSUPPORTED;
+ break;
+ }
+
+Done:
+ return nReturn;
+}
+
+
+
+/*
+ This layer deals with indefinite length strings
+
+ */
+static int GetNext_FullItem(QCBORDecodeContext *me, QCBORItem *pDecodedItem)
+{
+ int nReturn;
+ QCBORStringAllocator *pAlloc = (QCBORStringAllocator *)me->pStringAllocator;
+
+ nReturn = GetNext_Item(&(me->InBuf), pDecodedItem, me->bStringAllocateAll ? pAlloc: NULL);
+ if(nReturn) {
+ goto Done;
+ }
+
+ // To reduce code size by removing support for indefinite length strings, the
+ // code in this function from here down can be eliminated. Run tests to be sure
+ // all is OK if you remove this.
+ if(pDecodedItem->uDataType == QCBOR_TYPE_BREAK) {
+ nReturn = QCBOR_ERR_BAD_BREAK;
+ goto Done;
+ }
+
+ if(pDecodedItem->uDataType != QCBOR_TYPE_BYTE_STRING && pDecodedItem->uDataType != QCBOR_TYPE_TEXT_STRING) {
+ goto Done; // no need to do any work here on non-string types
+ }
+
+ if(pDecodedItem->val.string.len != SIZE_MAX) { // TODO: is this right? Is there a better way to mark this?
+ goto Done; // length is not indefinite, so no work to do
+ }
+
+ if(pAlloc == NULL) {
+ // can't do indefinite length strings without an allocator
+ nReturn = QCBOR_ERR_NO_STRING_ALLOCATOR;
+ goto Done;
+ }
+
+ // There is an indefinite length string to work on...
+ const uint8_t uStringType = pDecodedItem->uDataType;
+
+ // Loop getting segments of indefinite string
+ UsefulBufC FullString = NULLUsefulBufC;
+ for(;;) {
+ // Get item for next segment
+ QCBORItem Item;
+ nReturn = GetNext_Item(&(me->InBuf), &Item, NULL); // Never alloc segments of indefinite length strings
+ if(nReturn) {
+ // Error getting the next segment
+ break;
+ }
+
+ // See if it is marker at end of indefinite length string
+ if(Item.uDataType == QCBOR_TYPE_BREAK) {
+ // SUCCESS!!!
+ pDecodedItem->val.string = FullString;
+ pDecodedItem->uAllocated = 1;
+ break;
+ }
+
+ // Match data type of segment to type at beginning
+ if(Item.uDataType != uStringType) {
+ nReturn = QCBOR_ERR_INDEFINITE_STRING_SEG;
+ break;
+ }
+
+ // Expand the buffer so it can fit
+ UsefulBuf NewMem = (*pAlloc->fAllocate)(pAlloc->pAllocaterContext, (void *)FullString.ptr, FullString.len + Item.val.string.len);
+ if(UsefulBuf_IsNULL(NewMem)) {
+ nReturn = QCBOR_ERR_STRING_ALLOC;
+ break;
+ }
+
+ // Copy data to the end of it.
+ FullString = UsefulBuf_CopyOffset(NewMem, FullString.len, Item.val.string);
+ }
+
+Done:
+ if(nReturn && FullString.ptr) {
+ // Getting item failed, clean up the allocated memory
+ (pAlloc->fFree)(pAlloc->pAllocaterContext, (void *)FullString.ptr); // TODO unconst construct
+ }
+
+ return nReturn;
+}
+
/*
Returns an error if there was something wrong with the optional item or it couldn't
be handled.
*/
-static int DecodeOptional(UsefulInputBuf *pUInBuf, uint64_t uInputTag, QCBORItem *pDecodedItem)
+static int GetNext_TaggedItem(QCBORDecodeContext *me, QCBORItem *pDecodedItem)
{
- int nReturn = QCBOR_SUCCESS;
+ int nReturn;
+
+ nReturn = GetNext_FullItem(me, pDecodedItem);
+ if(nReturn) {
+ goto Done;
+ }
+
+ if(pDecodedItem->uDataType != QCBOR_TYPE_OPTTAG) {
+ goto Done;
+ }
uint64_t uTagFlags = 0; // accumulate the tags in the form of flags
- uint64_t uTagToProcess = uInputTag; // First process tag passed in
+ uint64_t uTagToProcess = pDecodedItem->uTag; // First process tag passed in
QCBORItem Item;
@@ -478,11 +719,11 @@
uTagFlags |= QCBOR_TAGFLAG_CBOR_MAGIC;
}
/* This code ignores the all but the first tag of value
- greater than 63. Ignoring tags that are not understoof
- is allowed by the standard. Multiple tags are
- presumably rare. */
+ greater than 63. Ignoring tags that are not understoof
+ is allowed by the standard. Multiple tags are
+ presumably rare. */
- nReturn = GetAnItem(pUInBuf, &Item, 1);
+ nReturn = GetNext_FullItem(me, &Item);
if(nReturn) {
// Bail out of the whole item fetch on any sort of error here
goto Done;
@@ -494,22 +735,22 @@
uTagToProcess = Item.uTag;
} while (1);
-
+
/*
- CBOR allows multiple tags on a data item. It also defines
- a number of standard tag values, most of which are
- less than 64. This code can deal with multiple tag
- values that are less than 64 and the last tag of multiple
- if the value is more than 64. Or said another way
- if there is one tag with a value >64 this code works.
+ CBOR allows multiple tags on a data item. It also defines
+ a number of standard tag values, most of which are
+ less than 64. This code can deal with multiple tag
+ values that are less than 64 and the last tag of multiple
+ if the value is more than 64. Or said another way
+ if there is one tag with a value >64 this code works.
- The assumption is that multiple tag values > 64 are rare.
+ The assumption is that multiple tag values > 64 are rare.
- At this point in this code. uTagFlags has all the flags
- < 64 and uTagToProcess has the last tag.
+ At this point in this code. uTagFlags has all the flags
+ < 64 and uTagToProcess has the last tag.
- Does this deal with multiple tags on an item we process?
+ Does this deal with multiple tags on an item we process?
*/
Item.uTagBits = uTagFlags;
@@ -538,93 +779,6 @@
// Encountering some mixed up CBOR like something that
// is tagged as both a string and integer date.
nReturn = QCBOR_ERR_BAD_OPT_TAG ;
- }
-
-Done:
- return nReturn;
-}
-
-
-
-// Make sure the constants align as this is assumed by the GetAnItem() implementation
-#if QCBOR_TYPE_ARRAY != CBOR_MAJOR_TYPE_ARRAY
-#error QCBOR_TYPE_ARRAY value not lined up with major type
-#endif
-#if QCBOR_TYPE_MAP != CBOR_MAJOR_TYPE_MAP
-#error QCBOR_TYPE_MAP value not lined up with major type
-#endif
-
-/*
- This gets a single data item and decodes it including preceding optional tagging. This does not
- deal with arrays and maps and nesting except to decode the data item introducing them. Arrays and
- maps are handled at the next level up in GetNext().
-
- Errors detected here include: an array that is too long to decode, hit end of buffer unexpectedly,
- a few forms of invalid encoded CBOR
- */
-
-static int GetAnItem(UsefulInputBuf *pUInBuf, QCBORItem *pDecodedItem, int bCalledFromDecodeOptional)
-{
- int nReturn;
-
- // Get the major type and the number. Number could be length of more bytes or the value depending on the major type
- // nAdditionalInfo is an encoding of the length of the uNumber and is needed to decode floats and doubles
- int uMajorType;
- uint64_t uNumber;
- uint8_t uAdditionalInfo;
-
- nReturn = DecodeTypeAndNumber(pUInBuf, &uMajorType, &uNumber, &uAdditionalInfo);
-
- // Error out here if we got into trouble on the type and number.
- // The code after this will not work if the type and number is not good.
- if(nReturn)
- goto Done;
-
- pDecodedItem->uTagBits = 0;
- pDecodedItem->uTag = 0;
-
- // At this point the major type and the value are valid. We've got the type and the number that
- // starts every CBOR data item.
- switch (uMajorType) {
- case CBOR_MAJOR_TYPE_POSITIVE_INT: // Major type 0
- case CBOR_MAJOR_TYPE_NEGATIVE_INT: // Major type 1
- nReturn = DecodeInteger(uMajorType, uNumber, pDecodedItem);
- break;
-
- case CBOR_MAJOR_TYPE_BYTE_STRING: // Major type 2
- case CBOR_MAJOR_TYPE_TEXT_STRING: // Major type 3
- nReturn = DecodeBytes(uMajorType, uNumber, pUInBuf, pDecodedItem);
- break;
-
- case CBOR_MAJOR_TYPE_ARRAY: // Major type 4
- case CBOR_MAJOR_TYPE_MAP: // Major type 5
- // Record the number of items in the array or map
- if(uNumber > QCBOR_MAX_ITEMS_IN_ARRAY) {
- nReturn = QCBOR_ERR_ARRAY_TOO_LONG;
- goto Done;
- }
- pDecodedItem->val.uCount = uNumber; // type conversion OK because of check above
- pDecodedItem->uDataType = uMajorType; // C preproc #if above makes sure constants align
- break;
-
- case CBOR_MAJOR_TYPE_OPTIONAL: // Major type 6, optional prepended tags
- pDecodedItem->uTag = uNumber;
- pDecodedItem->uDataType = QCBOR_TYPE_OPTTAG;
- if(!bCalledFromDecodeOptional) {
- // There can be a more than one optional tag in front of an actual data item
- // they are all handled by looping in DecodeOptional which calls back here
- // this test avoids infinite recursion.
- nReturn = DecodeOptional(pUInBuf, uNumber, pDecodedItem);
- }
- break;
-
- case CBOR_MAJOR_TYPE_SIMPLE: // Major type 7, float, double, true, false, null...
- nReturn = DecodeSimple(uAdditionalInfo, uNumber, pDecodedItem);
- break;
-
- default: // Should never happen because DecodeTypeAndNumber() should never return > 7
- nReturn = QCBOR_ERR_UNSUPPORTED;
- break;
}
Done:
@@ -633,18 +787,11 @@
/*
- Public function, see header qcbor.h file
+ This layer takes care of map entries. It combines the label and data items into one item.
*/
-int QCBORDecode_GetNext(QCBORDecodeContext *me, QCBORItem *pDecodedItem)
+static inline int GetNext_MapEntry(QCBORDecodeContext *me, QCBORItem *pDecodedItem)
{
- int nReturn;
-
- if(!UsefulInputBuf_BytesUnconsumed(&(me->InBuf))) {
- nReturn = QCBOR_ERR_HIT_END;
- goto Done;
- }
-
- nReturn = GetAnItem(&(me->InBuf), pDecodedItem, 0);
+ int nReturn = GetNext_TaggedItem(me, pDecodedItem);
if(nReturn)
goto Done;
@@ -654,7 +801,7 @@
// Get the next item which will be the real data; Item will be the label
QCBORItem LabelItem = *pDecodedItem;
- nReturn = GetAnItem(&(me->InBuf), pDecodedItem, 0);
+ nReturn = GetNext_TaggedItem(me, pDecodedItem); // TODO: test indefinite length string labels
if(nReturn)
goto Done;
@@ -666,7 +813,7 @@
// It's not a string and we only want strings, probably for easy translation to JSON
nReturn = QCBOR_ERR_MAP_LABEL_TYPE;
goto Done;
- } else if(LabelItem.uDataType == QCBOR_TYPE_INT64) {
+ } else if(LabelItem.uDataType == QCBOR_TYPE_INT64) {
pDecodedItem->label.int64 = LabelItem.val.int64;
pDecodedItem->uLabelType = QCBOR_TYPE_INT64;
} else if(LabelItem.uDataType == QCBOR_TYPE_UINT64) {
@@ -683,6 +830,58 @@
}
}
+Done:
+ return nReturn;
+}
+
+
+/*
+ Public function, see header qcbor.h file
+ */
+/*
+ Decoding items is done in layers:
+ - This top layer takes care of tracking for decsending into and
+ ascending out of maps and arrays GetNext()
+ - The next layer takes care of map entries that are made up
+ of a label and a data item. They are returned as one. GetNextMap() GetNext_MapEntry()
+ - The next layer takes care of tagging and tagged types GetNext_TaggedItem()
+ - The next layer takes care of indefinite length strings GetFullItem() GetNext_FullItem()
+ - The next layer does the main decoding of the non-compound GetAnItem() GetNext_Item()
+ items, all the different types of them
+
+ */
+int QCBORDecode_GetNext(QCBORDecodeContext *me, QCBORItem *pDecodedItem)
+{
+ int nReturn;
+
+ if(!UsefulInputBuf_BytesUnconsumed(&(me->InBuf))) {
+ nReturn = QCBOR_ERR_HIT_END;
+ goto Done;
+ }
+
+ nReturn = GetNext_MapEntry(me, pDecodedItem);
+ if(nReturn) {
+ goto Done;
+ }
+
+ // Handle any breaks that are closing out arrays or maps
+ while(pDecodedItem->uDataType == QCBOR_TYPE_BREAK) {
+ if(!DecodeNesting_IsIndefiniteLength(&(me->nesting))) {
+ nReturn = QCBOR_ERR_BAD_BREAK;
+ goto Done;
+ }
+
+ // Close out and ascend
+ DecodeNesting_Ascend(&(me->nesting));
+
+ nReturn = GetNext_MapEntry(me, pDecodedItem);
+ if(nReturn) {
+ goto Done;
+ }
+ }
+
+ // Now have the next item that is not a break. The one that is going to be returned
+
// Record the nesting level for this data item
pDecodedItem->uNestingLevel = DecodeNesting_GetLevel(&(me->nesting));
@@ -691,8 +890,11 @@
nReturn = DecodeNesting_Descend(&(me->nesting), pDecodedItem->uDataType, pDecodedItem->val.uCount);
} else {
// Track number of items in maps and arrays and ascend nesting if all are consumed
- // Note that an empty array or map is like a integer or string in effect here
- DecodeNesting_Decrement(&(me->nesting), pDecodedItem->uDataType);
+ // Note that an empty array or map is like an integer or string in effect here
+ // No counting is done for indefinite length arrays
+ if(!DecodeNesting_IsIndefiniteLength(&(me->nesting))) {
+ DecodeNesting_Decrement(&(me->nesting));
+ }
}
Done:
@@ -705,19 +907,59 @@
*/
int QCBORDecode_Finish(QCBORDecodeContext *me)
{
- return UsefulInputBuf_BytesUnconsumed(&(me->InBuf)) ? QCBOR_ERR_EXTRA_BYTES : QCBOR_SUCCESS;
+ int nReturn = QCBOR_SUCCESS;
+
+ // Consume any breaks ending indefinite length arrays or maps here
+ // Definite length arrays and maps get closed off above when they
+ // the last item in them is consumed; they are not handled here.
+ while(DecodeNesting_GetLevel(&(me->nesting))) {
+ QCBORItem Item;
+ GetNext_Item(&(me->InBuf), &Item, false);
+ if(Item.uDataType != QCBOR_TYPE_BREAK) {
+ nReturn = QCBOR_ERR_EXTRA_BYTES;
+ break;
+ }
+ DecodeNesting_Ascend(&(me->nesting));
+ }
+
+ // Call the desctructor for the string allocator if there is one
+ if(me->pStringAllocator) {
+ QCBORStringAllocator *pAllocator = (QCBORStringAllocator *)me->pStringAllocator;
+ if(pAllocator->fDestructor) {
+ (pAllocator->fDestructor)(pAllocator->pAllocaterContext);
+ }
+ }
+
+ if(UsefulInputBuf_BytesUnconsumed(&(me->InBuf))) {
+ nReturn = QCBOR_ERR_EXTRA_BYTES;
+ }
+
+ return nReturn;
}
+/*
+
+ Use the 64-bit map. 48 8-bit tags built in, 1 16 bit tag, 15 64-bit tags can be assigned as of interest
+
+ There is a tag map.
+
+ TODO: how does tinyCBOR do it?
+
+
+
+
+
+ */
+
+
/*
Decoder errors handled in this file
- Hit end of input before it was expected while decoding type and number QCBOR_ERR_HIT_END
-
- - indefinite length, currently not supported QCBOR_ERR_UNSUPPORTED
-
+
- negative integer that is too large for C QCBOR_ERR_INT_OVERFLOW
- Hit end of input while decoding a text or byte string QCBOR_ERR_HIT_END
@@ -738,3 +980,70 @@
*/
+
+
+typedef struct {
+ QCBORStringAllocator StringAllocator;
+ uint8_t *pStart;
+ uint8_t *pEnd;
+ uint8_t *pFree;
+} MemPool;
+
+
+/*
+ Code Reviewers: THIS FUNCTION DOES POINTER MATH
+ */
+static UsefulBuf MemPool_Alloc(void *ctx, void *pMem, size_t uNewSize)
+{
+ MemPool *me = (MemPool *)ctx;
+ void *pReturn = NULL;
+
+ if(pMem) {
+ // Realloc case
+ // TODO: review this pointer math
+ if((uint8_t *)pMem + uNewSize <= me->pEnd && (uint8_t *)pMem > me->pStart) {
+ me->pFree = pMem + uNewSize;
+ pReturn = pMem;
+ }
+ } else {
+ // New chunk case
+ if(me->pFree + uNewSize <= me->pEnd) {
+ pReturn = me->pFree;
+ me->pFree += uNewSize;
+ }
+ }
+
+ return (UsefulBuf){pReturn, uNewSize};
+}
+
+
+static void MemPool_Free(void *ctx, void *pOldMem)
+{
+ MemPool *me = (MemPool *)ctx;
+ me->pFree = pOldMem;
+}
+
+
+void QCBORDecode_SetMemPool(QCBORDecodeContext *me, UsefulBuf Pool, bool bAllStrings)
+{
+ if(Pool.len < sizeof(MemPool)+1) {
+ return; // Failure will happen when first string needs to be allocated
+ }
+
+ MemPool *pMP = (MemPool *)Pool.ptr;
+
+ pMP->StringAllocator.fAllocate = MemPool_Alloc;
+ pMP->StringAllocator.fFree = MemPool_Free;
+ pMP->StringAllocator.fDestructor = NULL;
+
+ pMP->pStart = Pool.ptr + sizeof(MemPool);
+ pMP->pFree = pMP->pStart;
+ pMP->pEnd = Pool.ptr + Pool.len;
+ pMP->StringAllocator.pAllocaterContext = pMP;
+
+ me->pStringAllocator = pMP;
+ me->bStringAllocateAll = bAllStrings;
+}
+
+
+
diff --git a/src/qcbor_decode_malloc.c b/src/qcbor_decode_malloc.c
new file mode 100644
index 0000000..4e405e0
--- /dev/null
+++ b/src/qcbor_decode_malloc.c
@@ -0,0 +1,65 @@
+//
+// qcbor_decode_malloc.c
+// QCBOR
+//
+// Created by Laurence Lundblade on 10/1/18.
+// Copyright © 2018 Laurence Lundblade. All rights reserved.
+//
+
+#include "qcbor.h"
+#include <stdlib.h> // for realloc and free
+
+static UsefulBuf MemMallocAlloc(void *ctx, void *pOldMem, size_t uNewSize)
+{
+ void *pNewMem = realloc(pOldMem, uNewSize);
+ return (UsefulBuf){pNewMem, uNewSize};
+}
+
+static void MemMallocFree(void *ctx, void *old)
+{
+ free(old);
+}
+
+static void MemMallocDestructor(void *ctx)
+{
+ free(ctx);
+}
+
+
+QCBORStringAllocator *QCBORDecode_MakeMallocStringAllocator()
+{
+ QCBORStringAllocator *pAllocaterContext = malloc(sizeof(QCBORStringAllocator));
+ if(pAllocaterContext) {
+ pAllocaterContext->fAllocate = MemMallocAlloc;
+ pAllocaterContext->fFree = MemMallocFree;
+ pAllocaterContext->fDestructor = MemMallocDestructor;
+ }
+
+ return pAllocaterContext;
+}
+
+
+/*
+void QCBORDecodeMalloc_Init(QCBORDecodeContext *me, UsefulBufC EncodedCBOR, int8_t nDecodeMode)
+{
+ QCBORDecode_Init(me, EncodedCBOR, nDecodeMode);
+
+ QCBORStringAllocator *pAllocaterContext = malloc(sizeof(QCBORStringAllocator));
+
+ pAllocaterContext->fAllocate = MemMalloc;
+ pAllocaterContext->fFree = MemFree;
+
+ QCBORDecode_SetUpAllocator(me, pAllocaterContext);
+}
+
+
+int QCBORDecodeMalloc_Finish(QCBORDecodeContext *me)
+{
+ const QCBORStringAllocator *pAllocator = QCBORDecode_GetAllocator(me);
+
+ free((void *)pAllocator); // TODO: better way to cast away const here
+ return QCBORDecode_Finish(me);
+} */
+
+
+
diff --git a/test/basic_test.c b/test/basic_test.c
index 508c6fd..8241302 100644
--- a/test/basic_test.c
+++ b/test/basic_test.c
@@ -138,3 +138,88 @@
+static const uint8_t pIndefiniteLenString[] = {
+ 0x81, // Array of length one
+ 0x7f, // text string marked with indefinite length
+ 0x65, 0x73, 0x74, 0x72, 0x65, 0x61, // first segment
+ 0x64, 0x6d, 0x69, 0x6e, 0x67, // second segment
+ 0xff // ending break
+};
+
+static const uint8_t pIndefiniteArray[] = {0x9f, 0x01, 0x82, 0x02, 0x03, 0xff};
+
+//0x9f018202039f0405ffff
+
+int indefinite_length_decode_test() {
+ UsefulBufC IndefLen = UsefulBuf_FromByteArrayLiteral(pIndefiniteArray);
+
+
+ // Decode it and see if it is OK
+ UsefulBuf_MakeStackUB(MemPool, 200);
+ QCBORDecodeContext DC;
+ QCBORItem Item;
+ QCBORDecode_Init(&DC, IndefLen, QCBOR_DECODE_MODE_NORMAL);
+
+ QCBORDecode_SetMemPool(&DC, MemPool, false);
+
+
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_ARRAY) {
+ return -1;
+ }
+
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_INT64) {
+ return -1;
+ }
+
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_ARRAY) {
+ return -1;
+ }
+
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_INT64) {
+ return -1;
+ }
+
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_INT64) {
+ return -1;
+ }
+
+ if(QCBORDecode_Finish(&DC)) {
+ return -2;
+ }
+
+ return 0;
+}
+
+int indefinite_length_decode_string_test() {
+ UsefulBufC IndefLen = UsefulBuf_FromByteArrayLiteral(pIndefiniteLenString);
+
+
+ // Decode it and see if it is OK
+ QCBORDecodeContext DC;
+ QCBORItem Item;
+ UsefulBuf_MakeStackUB(MemPool, 200);
+
+ QCBORDecode_Init(&DC, IndefLen, QCBOR_DECODE_MODE_NORMAL);
+
+ QCBORDecode_SetMemPool(&DC, MemPool, false);
+
+
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_ARRAY) {
+ return -1;
+ }
+
+ QCBORDecode_GetNext(&DC, &Item);
+ if(Item.uDataType != QCBOR_TYPE_TEXT_STRING) {
+ return -1;
+ }
+
+ return 0;
+}
+
+
diff --git a/test/basic_test.h b/test/basic_test.h
index cde44ba..6f3dd66 100644
--- a/test/basic_test.h
+++ b/test/basic_test.h
@@ -31,4 +31,7 @@
int basic_test_one(void);
+int indefinite_length_decode_test(void);
+int indefinite_length_decode_string_test(void);
+
#endif /* basic_test_h */
diff --git a/test/run_tests.c b/test/run_tests.c
index 013b6e5..c3379f3 100644
--- a/test/run_tests.c
+++ b/test/run_tests.c
@@ -93,6 +93,8 @@
} test_entry;
test_entry s_tests[] = {
+ TEST_ENTRY(indefinite_length_decode_string_test),
+ TEST_ENTRY(indefinite_length_decode_test),
TEST_ENTRY(basic_test_one),
TEST_ENTRY(half_precision_encode_basic),
TEST_ENTRY(half_precision_decode_basic),
diff --git a/test/run_tests.h b/test/run_tests.h
index 013b6e5..4338dce 100644
--- a/test/run_tests.h
+++ b/test/run_tests.h
@@ -28,126 +28,6 @@
==============================================================================*/
// Created by Laurence Lundblade on 9/30/18.
+typedef int (*outputstring)(const char *szString, void *ctx);
-#include "run_tests.h"
-#include "UsefulBuf.h"
-#include <stdbool.h>
-
-#include "half_precision_test.h"
-#include "basic_test.h"
-#include "bstrwrap_tests.h"
-
-// Used to test the test runner
-int fail_test()
-{
- return -44;
-}
-
-
-/*
- Convert a number up to 999999999 to a string. This is so sprintf doesn't
- have to be linked in so as to minimized dependencies even in test code.
-
- This function does pointer math. TODO: test this.
- */
-const char *NumToString(int32_t nNum, UsefulBuf StringMem)
-{
- const uint32_t uMax = 1000000000;
-
- UsefulOutBuf OutBuf;
- UsefulOutBuf_Init(&OutBuf, StringMem);
-
- if(nNum < 0) {
- UsefulOutBuf_AppendByte(&OutBuf, '-');
- nNum = -nNum;
- }
- if(nNum > uMax-1) {
- return "XXX";
- }
-
- bool bDidSomeOutput = false;
- for(int n = uMax; n > 0; n/=10) {
- int x = nNum/n;
- if(x || bDidSomeOutput){
- bDidSomeOutput = true;
- UsefulOutBuf_AppendByte(&OutBuf, '0' + x);
- nNum -= x * n;
- }
- }
- if(!bDidSomeOutput){
- UsefulOutBuf_AppendByte(&OutBuf, '0');
- }
- UsefulOutBuf_AppendByte(&OutBuf, '\0');
-
- return UsefulOutBuf_GetError(&OutBuf) ? "" : StringMem.ptr;
-}
-
-
-
-typedef int (test_fun_t)(void);
-
-#define TEST_ENTRY(test_name) {#test_name, test_name}
-typedef struct {
- const char *szTestName;
- test_fun_t *test_fun;
-} test_entry;
-
-test_entry s_tests[] = {
- TEST_ENTRY(basic_test_one),
- TEST_ENTRY(half_precision_encode_basic),
- TEST_ENTRY(half_precision_decode_basic),
- TEST_ENTRY(half_precision_to_float_transitive_test),
- TEST_ENTRY(double_as_smallest_encode_basic),
- TEST_ENTRY(half_precision_to_float_vs_rfc_test),
- TEST_ENTRY(bstrwraptest),
- TEST_ENTRY(bstr_wrap_error_test),
- TEST_ENTRY(bstr_wrap_nest_test),
- TEST_ENTRY(cose_sign1_tbs_test),
- //TEST_ENTRY(fail_test),
-};
-
-
-int run_tests(outputstring output, void *poutCtx, int *pNumTestsRun)
-{
- int nTestsFailed = 0;
- int nTestsRun = 0;
- UsefulBuf_MakeStackUB(StringStorage, 5);
-
- test_entry *t;
- const test_entry *s_tests_end = s_tests + sizeof(s_tests)/sizeof(test_entry);
-
- for(t = s_tests; t < s_tests_end; t++) {
- int x = (t->test_fun)();
- nTestsRun++;
- if(output) {
- (*output)(t->szTestName, poutCtx);
- }
-
- if(x) {
- if(output) {
- (*output)(" FAILED (returned ", poutCtx);
- (*output)(NumToString(x, StringStorage), poutCtx);
- (*output)(")\n", poutCtx);
- }
- nTestsFailed++;
- } else {
- if(output) {
- (*output)( " PASSED\n", poutCtx);
- }
- }
- }
-
- if(pNumTestsRun) {
- *pNumTestsRun = nTestsRun;
- }
-
- if(output) {
- (*output)( "SUMMARY: ", poutCtx);
- (*output)( NumToString(nTestsRun, StringStorage), poutCtx);
- (*output)( " tests run; ", poutCtx);
- (*output)( NumToString(nTestsFailed, StringStorage), poutCtx);
- (*output)( " tests failed\n", poutCtx);
- }
-
- return nTestsFailed;
-}
+int run_tests(outputstring output, void *poutCtx, int *pNumTestsRun);