MemPool -- better tests, more robust pointer handling, update documentation
diff --git a/src/qcbor_decode.c b/src/qcbor_decode.c
index e9f50d4..5614fb0 100644
--- a/src/qcbor_decode.c
+++ b/src/qcbor_decode.c
@@ -1164,16 +1164,6 @@
-/*
-
- 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.
-
-
- */
-
-
/*
Decoder errors handled in this file
@@ -1200,33 +1190,46 @@
+
+/*
+ This is a very primitive memory allocator. It does not track individual
+ allocations, only a high-water mark. A free or reallotcation must be of
+ the last chunk allocated.
+
+ All of this following code will get dead-stripped if QCBORDecode_SetMemPool()
+ is not called.
+ */
+
typedef struct {
QCBORStringAllocator StringAllocator;
- uint8_t *pStart;
- uint8_t *pEnd;
- uint8_t *pFree;
+ uint8_t *pStart; // First byte that can be allocated
+ uint8_t *pEnd; // One past the last byte that can be allocated
+ uint8_t *pFree; // Where the next free chunk is
} MemPool;
/*
+ Internal function for an allocation
+
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;
+ 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) {
+ // This check will work even if uNewSize is a super-large value like UINT64_MAX
+ if((uNewSize <= (size_t)(me->pEnd - (uint8_t *)pMem)) && ((uint8_t *)pMem >= me->pStart)) {
me->pFree = (uint8_t *)pMem + uNewSize;
- pReturn = pMem;
+ pReturn = pMem;
}
} else {
// New chunk case
- if(me->pFree + uNewSize <= me->pEnd) {
- pReturn = me->pFree;
+ // This check will work even if uNewSize is a super large value like UINT64_MAX
+ if(uNewSize <= (size_t)(me->pEnd - me->pFree)) {
+ pReturn = me->pFree;
me->pFree += uNewSize;
}
}
@@ -1234,34 +1237,61 @@
return (UsefulBuf){pReturn, uNewSize};
}
-
+/*
+ Internal function to free memory
+ */
static void MemPool_Free(void *ctx, void *pOldMem)
{
MemPool *me = (MemPool *)ctx;
- me->pFree = pOldMem;
+ me->pFree = pOldMem;
}
-
+/*
+ Public function, see header qcbor.h file
+ */
QCBORError QCBORDecode_SetMemPool(QCBORDecodeContext *me, UsefulBuf Pool, bool bAllStrings)
{
+ // The first bytes of the Pool passed in are used
+ // as the context (vtable of sorts) for the memory pool
+ // allocator.
if(Pool.len < sizeof(MemPool)+1) {
return QCBOR_ERR_BUFFER_TOO_SMALL;
}
MemPool *pMP = (MemPool *)Pool.ptr;
+ // Fill in the "vtable"
pMP->StringAllocator.fAllocate = MemPool_Alloc;
pMP->StringAllocator.fFree = MemPool_Free;
pMP->StringAllocator.fDestructor = NULL;
+ // Set up the pointers to the memory to be allocated
pMP->pStart = (uint8_t *)Pool.ptr + sizeof(MemPool);
pMP->pFree = pMP->pStart;
pMP->pEnd = (uint8_t *)Pool.ptr + Pool.len;
- pMP->StringAllocator.pAllocaterContext = pMP;
- me->pStringAllocator = pMP;
+ // More book keeping of context
+ pMP->StringAllocator.pAllocaterContext = pMP;
+ me->pStringAllocator = pMP;
+
+ // The flag indicating when to use the allocator
me->bStringAllocateAll = bAllStrings;
return QCBOR_SUCCESS;
}
+
+/*
+ Extra little hook to make MemPool testing work right
+ without adding any code size or overhead to non-test
+ uses. This will get dead-stripped for non-test use.
+
+ This is not a public function.
+ */
+size_t MemPoolTestHook_GetPoolSize(void *ctx)
+{
+ MemPool *me = (MemPool *)ctx;
+
+ return me->pEnd - me->pStart;
+}
+