1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-07-07 00:35:37 +02:00

Improve memory usage of mem contexts.

Each mem context can track child contexts, allocations, and a callback. Before this change memory was allocated for tracking all three even if they were not used for a particular context. This made mem contexts unsuitable for String and Variant objects since they are plentiful and need to be as small as possible.

This change allows mem contexts to be configured to track any combination of child contexts, allocations, and a callback. In addition, the mem context can be configured to track a single child context and/or allocation, which saves memory and is a common use case.

Another benefit is that Variants can own objects (e.g. KeyValue) that they encapsulate. All of this makes memory accounting simpler because mem contexts have names while allocations do not. No more memory is used than before since Variants and Strings still had to store the memory context they were originally allocated in so they could be easily freed.

Update the String and Variant objects to use this new functionality. The custom strFree() and varFree() functions are no longer required and can now be a wrapper around objFree().

Lastly, this will allow strMove() and varMove() to be implemented and used in cases where strDup() and varDup() are being used to move a String or Variant to a new context. Since this will be a bit noisy it is saved for a future commit.
This commit is contained in:
David Steele
2022-05-18 10:52:01 -04:00
committed by GitHub
parent 83af3f1b7a
commit c7a66ac1af
100 changed files with 1067 additions and 574 deletions

View File

@ -15,6 +15,20 @@
<release-list> <release-list>
<release date="XXXX-XX-XX" version="2.40dev" title="UNDER DEVELOPMENT"> <release date="XXXX-XX-XX" version="2.40dev" title="UNDER DEVELOPMENT">
<release-core-list>
<release-development-list>
<release-item>
<github-pull-request id="1749"/>
<release-item-contributor-list>
<release-item-contributor id="david.steele"/>
<release-item-reviewer id="reid.thompson"/>
</release-item-contributor-list>
<p>Improve memory usage of mem contexts.</p>
</release-item>
</release-development-list>
</release-core-list>
</release> </release>
<release date="2022-05-16" version="2.39" title="Verify and File Bundling"> <release date="2022-05-16" version="2.39" title="Verify and File Bundling">

View File

@ -47,7 +47,7 @@ yamlNew(const Buffer *const buffer)
Yaml *this = NULL; Yaml *this = NULL;
OBJ_NEW_BEGIN(Yaml) OBJ_NEW_BEGIN(Yaml, .childQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1)
{ {
// Create object // Create object
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();

View File

@ -235,7 +235,7 @@ pageChecksumNew(const unsigned int segmentNo, const unsigned int segmentPageTota
IoFilter *this = NULL; IoFilter *this = NULL;
OBJ_NEW_BEGIN(PageChecksum) OBJ_NEW_BEGIN(PageChecksum, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX)
{ {
PageChecksum *driver = OBJ_NEW_ALLOC(); PageChecksum *driver = OBJ_NEW_ALLOC();

View File

@ -46,7 +46,7 @@ cmdServerInit(void)
{ {
MEM_CONTEXT_BEGIN(memContextTop()) MEM_CONTEXT_BEGIN(memContextTop())
{ {
MEM_CONTEXT_NEW_BEGIN("Server") MEM_CONTEXT_NEW_BEGIN(Server, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
serverLocal.memContext = MEM_CONTEXT_NEW(); serverLocal.memContext = MEM_CONTEXT_NEW();
serverLocal.processList = lstNewP(sizeof(pid_t)); serverLocal.processList = lstNewP(sizeof(pid_t));

View File

@ -167,7 +167,7 @@ bz2CompressNew(int level)
IoFilter *this = NULL; IoFilter *this = NULL;
OBJ_NEW_BEGIN(Bz2Compress) OBJ_NEW_BEGIN(Bz2Compress, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1)
{ {
Bz2Compress *driver = OBJ_NEW_ALLOC(); Bz2Compress *driver = OBJ_NEW_ALLOC();

View File

@ -150,7 +150,7 @@ bz2DecompressNew(void)
IoFilter *this = NULL; IoFilter *this = NULL;
OBJ_NEW_BEGIN(Bz2Decompress) OBJ_NEW_BEGIN(Bz2Decompress, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1)
{ {
// Allocate state and set context // Allocate state and set context
Bz2Decompress *driver = OBJ_NEW_ALLOC(); Bz2Decompress *driver = OBJ_NEW_ALLOC();

View File

@ -172,7 +172,7 @@ gzCompressNew(int level)
IoFilter *this = NULL; IoFilter *this = NULL;
OBJ_NEW_BEGIN(GzCompress) OBJ_NEW_BEGIN(GzCompress, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1)
{ {
GzCompress *driver = OBJ_NEW_ALLOC(); GzCompress *driver = OBJ_NEW_ALLOC();

View File

@ -150,7 +150,7 @@ gzDecompressNew(void)
IoFilter *this = NULL; IoFilter *this = NULL;
OBJ_NEW_BEGIN(GzDecompress) OBJ_NEW_BEGIN(GzDecompress, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1)
{ {
// Allocate state and set context // Allocate state and set context
GzDecompress *driver = OBJ_NEW_ALLOC(); GzDecompress *driver = OBJ_NEW_ALLOC();

View File

@ -253,7 +253,7 @@ lz4CompressNew(int level)
IoFilter *this = NULL; IoFilter *this = NULL;
OBJ_NEW_BEGIN(Lz4Compress) OBJ_NEW_BEGIN(Lz4Compress, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1)
{ {
Lz4Compress *driver = OBJ_NEW_ALLOC(); Lz4Compress *driver = OBJ_NEW_ALLOC();

View File

@ -163,7 +163,7 @@ lz4DecompressNew(void)
IoFilter *this = NULL; IoFilter *this = NULL;
OBJ_NEW_BEGIN(Lz4Decompress) OBJ_NEW_BEGIN(Lz4Decompress, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1)
{ {
Lz4Decompress *driver = OBJ_NEW_ALLOC(); Lz4Decompress *driver = OBJ_NEW_ALLOC();
*driver = (Lz4Decompress){0}; *driver = (Lz4Decompress){0};

View File

@ -174,7 +174,7 @@ zstCompressNew(int level)
IoFilter *this = NULL; IoFilter *this = NULL;
OBJ_NEW_BEGIN(ZstCompress) OBJ_NEW_BEGIN(ZstCompress, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1)
{ {
ZstCompress *driver = OBJ_NEW_ALLOC(); ZstCompress *driver = OBJ_NEW_ALLOC();

View File

@ -162,7 +162,7 @@ zstDecompressNew(void)
IoFilter *this = NULL; IoFilter *this = NULL;
OBJ_NEW_BEGIN(ZstDecompress) OBJ_NEW_BEGIN(ZstDecompress, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1)
{ {
ZstDecompress *driver = OBJ_NEW_ALLOC(); ZstDecompress *driver = OBJ_NEW_ALLOC();

View File

@ -421,7 +421,7 @@ cipherBlockNew(CipherMode mode, CipherType cipherType, const Buffer *pass, const
// Allocate memory to hold process state // Allocate memory to hold process state
IoFilter *this = NULL; IoFilter *this = NULL;
OBJ_NEW_BEGIN(CipherBlock) OBJ_NEW_BEGIN(CipherBlock, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1)
{ {
CipherBlock *driver = OBJ_NEW_ALLOC(); CipherBlock *driver = OBJ_NEW_ALLOC();

View File

@ -185,7 +185,7 @@ cryptoHashNew(const String *type)
// Allocate memory to hold process state // Allocate memory to hold process state
IoFilter *this = NULL; IoFilter *this = NULL;
OBJ_NEW_BEGIN(CryptoHash) OBJ_NEW_BEGIN(CryptoHash, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1)
{ {
CryptoHash *driver = OBJ_NEW_ALLOC(); CryptoHash *driver = OBJ_NEW_ALLOC();
*driver = (CryptoHash){0}; *driver = (CryptoHash){0};

View File

@ -127,7 +127,7 @@ execNew(const String *command, const StringList *param, const String *name, Time
Exec *this = NULL; Exec *this = NULL;
OBJ_NEW_BEGIN(Exec) OBJ_NEW_BEGIN(Exec, .childQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1)
{ {
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();

View File

@ -29,7 +29,7 @@ iniNew(void)
Ini *this = NULL; Ini *this = NULL;
OBJ_NEW_BEGIN(Ini) OBJ_NEW_BEGIN(Ini, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();

View File

@ -94,7 +94,7 @@ ioBufferReadNew(const Buffer *buffer)
IoRead *this = NULL; IoRead *this = NULL;
OBJ_NEW_BEGIN(IoBufferRead) OBJ_NEW_BEGIN(IoBufferRead, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX)
{ {
IoBufferRead *driver = OBJ_NEW_ALLOC(); IoBufferRead *driver = OBJ_NEW_ALLOC();

View File

@ -58,7 +58,7 @@ ioBufferWriteNew(Buffer *buffer)
IoWrite *this = NULL; IoWrite *this = NULL;
OBJ_NEW_BEGIN(IoBufferWrite) OBJ_NEW_BEGIN(IoBufferWrite, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX)
{ {
IoBufferWrite *driver = OBJ_NEW_ALLOC(); IoBufferWrite *driver = OBJ_NEW_ALLOC();

View File

@ -156,7 +156,7 @@ ioFdReadNew(const String *name, int fd, TimeMSec timeout)
IoRead *this = NULL; IoRead *this = NULL;
OBJ_NEW_BEGIN(IoFdRead) OBJ_NEW_BEGIN(IoFdRead, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX)
{ {
IoFdRead *driver = OBJ_NEW_ALLOC(); IoFdRead *driver = OBJ_NEW_ALLOC();

View File

@ -112,7 +112,7 @@ ioFdWriteNew(const String *name, int fd, TimeMSec timeout)
IoWrite *this = NULL; IoWrite *this = NULL;
OBJ_NEW_BEGIN(IoFdWrite) OBJ_NEW_BEGIN(IoFdWrite, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX)
{ {
IoFdWrite *driver = OBJ_NEW_ALLOC(); IoFdWrite *driver = OBJ_NEW_ALLOC();

View File

@ -110,7 +110,7 @@ ioBufferNew(void)
IoFilter *this = NULL; IoFilter *this = NULL;
OBJ_NEW_BEGIN(IoBuffer) OBJ_NEW_BEGIN(IoBuffer, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX)
{ {
IoBuffer *driver = OBJ_NEW_ALLOC(); IoBuffer *driver = OBJ_NEW_ALLOC();
*driver = (IoBuffer){0}; *driver = (IoBuffer){0};

View File

@ -63,7 +63,7 @@ ioFilterGroupNew(void)
IoFilterGroup *this = NULL; IoFilterGroup *this = NULL;
OBJ_NEW_BEGIN(IoFilterGroup) OBJ_NEW_BEGIN(IoFilterGroup, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();

View File

@ -54,7 +54,7 @@ ioSinkNew(void)
IoFilter *this = NULL; IoFilter *this = NULL;
OBJ_NEW_BEGIN(IoSink) OBJ_NEW_BEGIN(IoSink, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX)
{ {
IoSink *driver = OBJ_NEW_ALLOC(); IoSink *driver = OBJ_NEW_ALLOC();
this = ioFilterNewP(SINK_FILTER_TYPE, driver, NULL, .inOut = ioSinkProcess); this = ioFilterNewP(SINK_FILTER_TYPE, driver, NULL, .inOut = ioSinkProcess);

View File

@ -93,7 +93,7 @@ ioSizeNew(void)
IoFilter *this = NULL; IoFilter *this = NULL;
OBJ_NEW_BEGIN(IoSize) OBJ_NEW_BEGIN(IoSize, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX)
{ {
IoSize *driver = OBJ_NEW_ALLOC(); IoSize *driver = OBJ_NEW_ALLOC();
*driver = (IoSize){0}; *driver = (IoSize){0};

View File

@ -43,7 +43,7 @@ httpClientNew(IoClient *ioClient, TimeMSec timeout)
HttpClient *this = NULL; HttpClient *this = NULL;
OBJ_NEW_BEGIN(HttpClient) OBJ_NEW_BEGIN(HttpClient, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();

View File

@ -25,7 +25,7 @@ httpHeaderNew(const StringList *redactList)
HttpHeader *this = NULL; HttpHeader *this = NULL;
OBJ_NEW_BEGIN(HttpHeader) OBJ_NEW_BEGIN(HttpHeader, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
// Allocate state and set context // Allocate state and set context
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();
@ -54,7 +54,7 @@ httpHeaderDup(const HttpHeader *header, const StringList *redactList)
if (header != NULL) if (header != NULL)
{ {
OBJ_NEW_BEGIN(HttpHeader) OBJ_NEW_BEGIN(HttpHeader, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
// Allocate state and set context // Allocate state and set context
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();

View File

@ -27,7 +27,7 @@ httpQueryNew(HttpQueryNewParam param)
HttpQuery *this = NULL; HttpQuery *this = NULL;
OBJ_NEW_BEGIN(HttpQuery) OBJ_NEW_BEGIN(HttpQuery, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
// Allocate state and set context // Allocate state and set context
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();
@ -55,7 +55,7 @@ httpQueryNewStr(const String *query)
HttpQuery *this = NULL; HttpQuery *this = NULL;
OBJ_NEW_BEGIN(HttpQuery) OBJ_NEW_BEGIN(HttpQuery, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();
@ -108,7 +108,7 @@ httpQueryDup(const HttpQuery *query, HttpQueryDupParam param)
if (query != NULL) if (query != NULL)
{ {
OBJ_NEW_BEGIN(HttpQuery) OBJ_NEW_BEGIN(HttpQuery, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
// Allocate state and set context // Allocate state and set context
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();

View File

@ -192,7 +192,7 @@ httpRequestNew(HttpClient *client, const String *verb, const String *path, HttpR
HttpRequest *this = NULL; HttpRequest *this = NULL;
OBJ_NEW_BEGIN(HttpRequest) OBJ_NEW_BEGIN(HttpRequest, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();

View File

@ -220,7 +220,7 @@ httpResponseNew(HttpSession *session, const String *verb, bool contentCache)
HttpResponse *this = NULL; HttpResponse *this = NULL;
OBJ_NEW_BEGIN(HttpResponse) OBJ_NEW_BEGIN(HttpResponse, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();

View File

@ -31,7 +31,7 @@ httpSessionNew(HttpClient *httpClient, IoSession *ioSession)
HttpSession *this = NULL; HttpSession *this = NULL;
OBJ_NEW_BEGIN(HttpSession) OBJ_NEW_BEGIN(HttpSession, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();

View File

@ -62,7 +62,7 @@ httpUrlNewParse(const String *const url, HttpUrlNewParseParam param)
HttpUrl *this = NULL; HttpUrl *this = NULL;
OBJ_NEW_BEGIN(HttpUrl) OBJ_NEW_BEGIN(HttpUrl, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
// Allocate state and set context // Allocate state and set context
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();

View File

@ -35,7 +35,7 @@ ioReadNew(void *driver, IoReadInterface interface)
IoRead *this = NULL; IoRead *this = NULL;
OBJ_NEW_BEGIN(IoRead) OBJ_NEW_BEGIN(IoRead, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();

View File

@ -172,7 +172,7 @@ sckClientNew(const String *const host, const unsigned int port, const TimeMSec t
IoClient *this = NULL; IoClient *this = NULL;
OBJ_NEW_BEGIN(SocketClient) OBJ_NEW_BEGIN(SocketClient, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX)
{ {
SocketClient *driver = OBJ_NEW_ALLOC(); SocketClient *driver = OBJ_NEW_ALLOC();

View File

@ -155,7 +155,7 @@ sckServerNew(const String *const address, const unsigned int port, const TimeMSe
IoServer *this = NULL; IoServer *this = NULL;
OBJ_NEW_BEGIN(SocketServer) OBJ_NEW_BEGIN(SocketServer, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1)
{ {
SocketServer *const driver = OBJ_NEW_ALLOC(); SocketServer *const driver = OBJ_NEW_ALLOC();

View File

@ -178,7 +178,7 @@ sckSessionNew(IoSessionRole role, int fd, const String *host, unsigned int port,
IoSession *this = NULL; IoSession *this = NULL;
OBJ_NEW_BEGIN(SocketSession) OBJ_NEW_BEGIN(SocketSession, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1)
{ {
SocketSession *driver = OBJ_NEW_ALLOC(); SocketSession *driver = OBJ_NEW_ALLOC();

View File

@ -365,7 +365,7 @@ tlsClientNew(
IoClient *this = NULL; IoClient *this = NULL;
OBJ_NEW_BEGIN(TlsClient) OBJ_NEW_BEGIN(TlsClient, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1)
{ {
TlsClient *driver = OBJ_NEW_ALLOC(); TlsClient *driver = OBJ_NEW_ALLOC();

View File

@ -282,7 +282,7 @@ tlsServerNew(
IoServer *this = NULL; IoServer *this = NULL;
OBJ_NEW_BEGIN(TlsServer) OBJ_NEW_BEGIN(TlsServer, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1)
{ {
TlsServer *const driver = OBJ_NEW_ALLOC(); TlsServer *const driver = OBJ_NEW_ALLOC();

View File

@ -371,7 +371,7 @@ tlsSessionNew(SSL *session, IoSession *ioSession, TimeMSec timeout)
IoSession *this = NULL; IoSession *this = NULL;
OBJ_NEW_BEGIN(TlsSession) OBJ_NEW_BEGIN(TlsSession, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1)
{ {
TlsSession *driver = OBJ_NEW_ALLOC(); TlsSession *driver = OBJ_NEW_ALLOC();

View File

@ -41,7 +41,7 @@ ioWriteNew(void *driver, IoWriteInterface interface)
IoWrite *this = NULL; IoWrite *this = NULL;
OBJ_NEW_BEGIN(IoWrite) OBJ_NEW_BEGIN(IoWrite, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();

View File

@ -415,7 +415,7 @@ lockAcquire(
{ {
MEM_CONTEXT_BEGIN(memContextTop()) MEM_CONTEXT_BEGIN(memContextTop())
{ {
MEM_CONTEXT_NEW_BEGIN("Lock") MEM_CONTEXT_NEW_BEGIN(Lock, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
lockLocal.memContext = MEM_CONTEXT_NEW(); lockLocal.memContext = MEM_CONTEXT_NEW();
lockLocal.execId = strDup(execId); lockLocal.execId = strDup(execId);

View File

@ -8,6 +8,7 @@ Memory Context Manager
#include "common/debug.h" #include "common/debug.h"
#include "common/error.h" #include "common/error.h"
#include "common/macro.h"
#include "common/memContext.h" #include "common/memContext.h"
/*********************************************************************************************************************************** /***********************************************************************************************************************************
@ -27,52 +28,180 @@ typedef struct MemContextAlloc
// Get the allocation header pointer given the allocation buffer pointer // Get the allocation header pointer given the allocation buffer pointer
#define MEM_CONTEXT_ALLOC_HEADER(buffer) ((MemContextAlloc *)buffer - 1) #define MEM_CONTEXT_ALLOC_HEADER(buffer) ((MemContextAlloc *)buffer - 1)
// Make sure the allocation is valid for the current memory context. This check only works correctly if the allocation is valid but // Make sure the allocation is valid for the current memory context. This check only works correctly if the allocation is valid and
// belongs to another context. Otherwise, there is likely to be a segfault. // allocated as one of many but belongs to another context. Otherwise, there is likely to be a segfault.
#define ASSERT_ALLOC_VALID(alloc) \ #define ASSERT_ALLOC_MANY_VALID(alloc) \
ASSERT( \ ASSERT( \
alloc != NULL && (uintptr_t)alloc != (uintptr_t)-sizeof(MemContextAlloc) && \ alloc != NULL && (uintptr_t)alloc != (uintptr_t)-sizeof(MemContextAlloc) && \
alloc->allocIdx < memContextStack[memContextCurrentStackIdx].memContext->allocListSize && \ alloc->allocIdx < memContextAllocMany(memContextStack[memContextCurrentStackIdx].memContext)->listSize && \
memContextStack[memContextCurrentStackIdx].memContext->allocList[alloc->allocIdx]); memContextAllocMany(memContextStack[memContextCurrentStackIdx].memContext)->list[alloc->allocIdx]);
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Contains information about the memory context Contains information about the memory context
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
// Quantity of child contexts, allocations, or callbacks
typedef enum
{
memQtyNone = 0, // None for this type
memQtyOne = 1, // One for this type
memQtyMany = 2, // Many for this type
} MemQty;
// Main structure required by every mem context
struct MemContext struct MemContext
{ {
#ifdef DEBUG #ifdef DEBUG
const char *name; // Indicates what the context is being used for const char *name; // Indicates what the context is being used for
bool active:1; // Is the context currently active? bool active:1; // Is the context currently active?
#endif #endif
MemQty childQty:2; // How many child contexts can this context have?
bool childInitialized:1; // Has the child context list been initialized?
MemQty allocQty:2; // How many allocations can this context have?
bool allocInitialized:1; // Has the allocation list been initialized?
MemQty callbackQty:2; // How many callbacks can this context have?
bool callbackInitialized:1; // Has the callback been initialized?
size_t allocExtra:16; // Size of extra allocation (1kB max) size_t allocExtra:16; // Size of extra allocation (1kB max)
unsigned int contextParentIdx; // Index in the parent context list unsigned int contextParentIdx; // Index in the parent context list
MemContext *contextParent; // All contexts have a parent except top MemContext *contextParent; // All contexts have a parent except top
MemContext **contextChildList; // List of contexts created in this context
unsigned int contextChildListSize; // Size of child context list (not the actual count of contexts)
unsigned int contextChildFreeIdx; // Index of first free space in the context list
MemContextAlloc **allocList; // List of memory allocations created in this context
unsigned int allocListSize; // Size of alloc list (not the actual count of allocations)
unsigned int allocFreeIdx; // Index of first free space in the alloc list
void (*callbackFunction)(void *); // Function to call before the context is freed
void *callbackArgument; // Argument to pass to callback function
}; };
// Mem context with one allocation
typedef struct MemContextAllocOne
{
MemContextAlloc *alloc; // Memory allocation created in this context
} MemContextAllocOne;
// Mem context with many allocations
typedef struct MemContextAllocMany
{
MemContextAlloc **list; // List of memory allocations created in this context
unsigned int listSize; // Size of alloc list (not the actual count of allocations)
unsigned int freeIdx; // Index of first free space in the alloc list
} MemContextAllocMany;
// Mem context with one child context
typedef struct MemContextChildOne
{
MemContext *context; // Context created in this context
} MemContextChildOne;
// Mem context with many child contexts
typedef struct MemContextChildMany
{
MemContext **list; // List of contexts created in this context
unsigned int listSize; // Size of child context list (not the actual count of contexts)
unsigned int freeIdx; // Index of first free space in the context list
} MemContextChildMany;
// Mem context with one callback
typedef struct MemContextCallbackOne
{
void (*function)(void *); // Function to call before the context is freed
void *argument; // Argument to pass to callback function
} MemContextCallbackOne;
/***********************************************************************************************************************************
Possible sizes for the manifest based on options. Formatting has been compressed to save space.
***********************************************************************************************************************************/
static const uint8_t memContextSizePossible[memQtyMany + 1][memQtyMany + 1][memQtyOne + 1] =
{
// child none
{// alloc none
{/* callback none */ 0, /* callback one */ sizeof(MemContextCallbackOne)},
// alloc one
{/* callback none */ sizeof(MemContextAllocOne),
/* callback one */ sizeof(MemContextAllocOne) + sizeof(MemContextCallbackOne)},
// alloc many
{/* callback none */ sizeof(MemContextAllocMany),
/* callback one */ sizeof(MemContextAllocMany) + sizeof(MemContextCallbackOne)}},
// child one
{// alloc none
{/* callback none */ sizeof(MemContextChildOne),
/* callback one */ sizeof(MemContextChildOne) + sizeof(MemContextCallbackOne)},
// alloc one
{/* callback none */ sizeof(MemContextChildOne) + sizeof(MemContextAllocOne),
/* callback one */ sizeof(MemContextChildOne) + sizeof(MemContextAllocOne) + sizeof(MemContextCallbackOne)},
// alloc many
{/* callback none */ sizeof(MemContextChildOne) + sizeof(MemContextAllocMany),
/* callback one */ sizeof(MemContextChildOne) + sizeof(MemContextAllocMany) + sizeof(MemContextCallbackOne)}},
// child many
{// alloc none
{/* callback none */ sizeof(MemContextChildMany),
/* callback one */ sizeof(MemContextChildMany) + sizeof(MemContextCallbackOne)},
// alloc one
{/* callback none */ sizeof(MemContextChildMany) + sizeof(MemContextAllocOne),
/* callback one */ sizeof(MemContextChildMany) + sizeof(MemContextAllocOne) + sizeof(MemContextCallbackOne)},
// alloc many
{/* callback none */ sizeof(MemContextChildMany) + sizeof(MemContextAllocMany),
/* callback one */ sizeof(MemContextChildMany) + sizeof(MemContextAllocMany) + sizeof(MemContextCallbackOne)}},
};
/***********************************************************************************************************************************
Get pointers to optional parts of the manifest
***********************************************************************************************************************************/
// Get pointer to child part
#define MEM_CONTEXT_CHILD_OFFSET(memContext) ((unsigned char *)(memContext + 1) + memContext->allocExtra)
static MemContextChildOne *
memContextChildOne(MemContext *const memContext)
{
return (MemContextChildOne *)MEM_CONTEXT_CHILD_OFFSET(memContext);
}
static MemContextChildMany *
memContextChildMany(MemContext *const memContext)
{
return (MemContextChildMany *)MEM_CONTEXT_CHILD_OFFSET(memContext);
}
// Get pointer to allocation part
#define MEM_CONTEXT_ALLOC_OFFSET(memContext) \
((unsigned char *)(memContext + 1) + memContextSizePossible[memContext->childQty][0][0] + memContext->allocExtra)
static MemContextAllocOne *
memContextAllocOne(MemContext *const memContext)
{
return (MemContextAllocOne *)MEM_CONTEXT_ALLOC_OFFSET(memContext);
}
static MemContextAllocMany *
memContextAllocMany(MemContext *const memContext)
{
return (MemContextAllocMany *)MEM_CONTEXT_ALLOC_OFFSET(memContext);
}
// Get pointer to callback part
static MemContextCallbackOne *
memContextCallbackOne(MemContext *const memContext)
{
return
(MemContextCallbackOne *)((unsigned char *)(memContext + 1) +
memContextSizePossible[memContext->childQty][memContext->allocQty][0] + memContext->allocExtra);
}
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Top context Top context
The top context always exists and can never be freed. All other contexts are children of the top context. The top context is The top context always exists and can never be freed. All other contexts are children of the top context. The top context is
generally used to allocate memory that exists for the life of the program. generally used to allocate memory that exists for the life of the program.
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
static MemContext contextTop = static struct MemContextTop
{ {
MemContext memContext;
MemContextChildMany memContextChildMany;
MemContextAllocMany memContextAllocMany;
} contextTop =
{
.memContext =
{
#ifdef DEBUG #ifdef DEBUG
.name = "TOP", .name = "TOP",
.active = true, .active = true,
#endif #endif
.childQty = memQtyMany,
.allocQty = memQtyMany,
},
}; };
/*********************************************************************************************************************************** /***********************************************************************************************************************************
@ -94,7 +223,7 @@ static struct MemContextStack
MemContext *memContext; MemContext *memContext;
MemContextStackType type; MemContextStackType type;
unsigned int tryDepth; unsigned int tryDepth;
} memContextStack[MEM_CONTEXT_STACK_MAX] = {{.memContext = &contextTop}}; } memContextStack[MEM_CONTEXT_STACK_MAX] = {{.memContext = (MemContext *)&contextTop}};
static unsigned int memContextCurrentStackIdx = 0; static unsigned int memContextCurrentStackIdx = 0;
static unsigned int memContextMaxStackIdx = 0; static unsigned int memContextMaxStackIdx = 0;
@ -209,49 +338,51 @@ memFreeInternal(void *buffer)
Find space for a new mem context Find space for a new mem context
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
static unsigned int static unsigned int
memContextNewIndex(MemContext *memContext) memContextNewIndex(MemContext *const memContext, MemContextChildMany *const memContextChild)
{ {
FUNCTION_TEST_BEGIN(); FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(MEM_CONTEXT, memContext); FUNCTION_TEST_PARAM(MEM_CONTEXT, memContext);
FUNCTION_TEST_PARAM_P(VOID, memContextChild);
FUNCTION_TEST_END(); FUNCTION_TEST_END();
ASSERT(memContext != NULL); ASSERT(memContext != NULL);
ASSERT(memContextChild != NULL);
// Try to find space for the new context // Initialize (free space will always be index 0)
for (; memContext->contextChildFreeIdx < memContext->contextChildListSize; memContext->contextChildFreeIdx++) if (!memContext->childInitialized)
{ {
if (memContext->contextChildList[memContext->contextChildFreeIdx] == NULL) *memContextChild = (MemContextChildMany)
{
.list = memAllocPtrArrayInternal(MEM_CONTEXT_INITIAL_SIZE),
.listSize = MEM_CONTEXT_INITIAL_SIZE,
};
memContext->childInitialized = true;
}
else
{
// Try to find space for the new context
for (; memContextChild->freeIdx < memContextChild->listSize; memContextChild->freeIdx++)
{
if (memContextChild->list[memContextChild->freeIdx] == NULL)
break; break;
} }
// If no space was found then allocate more // If no space was found then allocate more
if (memContext->contextChildFreeIdx == memContext->contextChildListSize) if (memContextChild->freeIdx == memContextChild->listSize)
{
// If no space has been allocated to the list
if (memContext->contextChildListSize == 0)
{
// Allocate memory before modifying anything else in case there is an error
memContext->contextChildList = memAllocPtrArrayInternal(MEM_CONTEXT_INITIAL_SIZE);
// Set new list size
memContext->contextChildListSize = MEM_CONTEXT_INITIAL_SIZE;
}
// Else grow the list
else
{ {
// Calculate new list size // Calculate new list size
unsigned int contextChildListSizeNew = memContext->contextChildListSize * 2; const unsigned int listSizeNew = memContextChild->listSize * 2;
// ReAllocate memory before modifying anything else in case there is an error // ReAllocate memory before modifying anything else in case there is an error
memContext->contextChildList = memReAllocPtrArrayInternal( memContextChild->list = memReAllocPtrArrayInternal(memContextChild->list, memContextChild->listSize, listSizeNew);
memContext->contextChildList, memContext->contextChildListSize, contextChildListSizeNew);
// Set new list size // Set new list size
memContext->contextChildListSize = contextChildListSizeNew; memContextChild->listSize = listSizeNew;
} }
} }
FUNCTION_TEST_RETURN(UINT, memContext->contextChildFreeIdx); FUNCTION_TEST_RETURN(UINT, memContextChild->freeIdx);
} }
/**********************************************************************************************************************************/ /**********************************************************************************************************************************/
@ -264,22 +395,36 @@ memContextNew(
{ {
FUNCTION_TEST_BEGIN(); FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRINGZ, name); FUNCTION_TEST_PARAM(STRINGZ, name);
FUNCTION_TEST_PARAM(UINT, param.childQty);
FUNCTION_TEST_PARAM(UINT, param.allocQty);
FUNCTION_TEST_PARAM(UINT, param.callbackQty);
FUNCTION_TEST_PARAM(SIZE, param.allocExtra); FUNCTION_TEST_PARAM(SIZE, param.allocExtra);
FUNCTION_TEST_END(); FUNCTION_TEST_END();
ASSERT(name != NULL); ASSERT(name != NULL);
ASSERT(param.childQty <= 1 || param.childQty == UINT8_MAX);
ASSERT(param.allocQty <= 1 || param.allocQty == UINT8_MAX);
ASSERT(param.callbackQty <= 1);
// Check context name length // Check context name length
ASSERT(name[0] != '\0'); ASSERT(name[0] != '\0');
// Find space for the new context // Pad allocExtra so that optional structures will be aligned. There may not be any optional structures but it doesn't seem
MemContext *contextCurrent = memContextStack[memContextCurrentStackIdx].memContext; // worth the cost of checking since memory allocations are aligned so the extra bytes would be wasted anyway.
unsigned int contextIdx = memContextNewIndex(contextCurrent); size_t allocExtra = param.allocExtra;
// Allocate memory for the context if (allocExtra % ALIGN_OF(void *) != 0)
contextCurrent->contextChildList[contextIdx] = memAllocInternal(sizeof(MemContext) + param.allocExtra); allocExtra += ALIGN_OFFSET(void *, allocExtra);
// Get the context // Create the new context
MemContext *this = contextCurrent->contextChildList[contextIdx]; MemContext *const contextCurrent = memContextStack[memContextCurrentStackIdx].memContext;
ASSERT(contextCurrent->childQty != memQtyNone);
const MemQty childQty = param.childQty > 1 ? memQtyMany : (MemQty)param.childQty;
const MemQty allocQty = param.allocQty > 1 ? memQtyMany : (MemQty)param.allocQty;
const MemQty callbackQty = (MemQty)param.callbackQty;
MemContext *const this = memAllocInternal(
sizeof(MemContext) + allocExtra + memContextSizePossible[childQty][allocQty][callbackQty]);
*this = (MemContext) *this = (MemContext)
{ {
@ -290,16 +435,37 @@ memContextNew(
// Set new context active // Set new context active
.active = true, .active = true,
#endif #endif
// Set flags
.childQty = childQty,
.allocQty = allocQty,
.callbackQty = callbackQty,
// Set extra allocation // Set extra allocation
.allocExtra = param.allocExtra, .allocExtra = (uint16_t)allocExtra,
// Set current context as the parent // Set current context as the parent
.contextParent = contextCurrent, .contextParent = contextCurrent,
.contextParentIdx = contextIdx,
}; };
// Find space for the new context
if (contextCurrent->childQty == memQtyOne)
{
ASSERT(!contextCurrent->childInitialized || memContextChildOne(contextCurrent)->context == NULL);
memContextChildOne(contextCurrent)->context = this;
contextCurrent->childInitialized = true;
}
else
{
ASSERT(contextCurrent->childQty == memQtyMany);
MemContextChildMany *const memContextChild = memContextChildMany(contextCurrent);
this->contextParentIdx = memContextNewIndex(contextCurrent, memContextChild);
memContextChild->list[this->contextParentIdx] = this;
// Possible free context must be in the next position // Possible free context must be in the next position
contextCurrent->contextChildFreeIdx++; memContextChild->freeIdx++;
}
// Add to the mem context stack so it will be automatically freed on error if memContextKeep() has not been called // Add to the mem context stack so it will be automatically freed on error if memContextKeep() has not been called
memContextMaxStackIdx++; memContextMaxStackIdx++;
@ -369,20 +535,18 @@ memContextCallbackSet(MemContext *this, void (*callbackFunction)(void *), void *
ASSERT(this != NULL); ASSERT(this != NULL);
ASSERT(this->active); ASSERT(this->active);
ASSERT(callbackFunction != NULL); ASSERT(callbackFunction != NULL);
ASSERT(this->active);
// Top context cannot have a callback ASSERT(this->callbackQty != memQtyNone);
if (this == &contextTop)
THROW(AssertError, "top context may not have a callback");
#ifdef DEBUG #ifdef DEBUG
// Error if callback has already been set - there may be valid use cases for this but error until one is found // Error if callback has already been set - there may be valid use cases for this in the future but error until one is found
if (this->callbackFunction) if (this->callbackInitialized)
THROW_FMT(AssertError, "callback is already set for context '%s'", this->name); THROW_FMT(AssertError, "callback is already set for context '%s'", this->name);
#endif #endif
// Set callback function and argument // Set callback function and argument
this->callbackFunction = callbackFunction; *(memContextCallbackOne(this)) = (MemContextCallbackOne){.function = callbackFunction, .argument = callbackArgument};
this->callbackArgument = callbackArgument; this->callbackInitialized = true;
FUNCTION_TEST_RETURN_VOID(); FUNCTION_TEST_RETURN_VOID();
} }
@ -396,13 +560,12 @@ memContextCallbackClear(MemContext *this)
FUNCTION_TEST_END(); FUNCTION_TEST_END();
ASSERT(this != NULL); ASSERT(this != NULL);
ASSERT(this->callbackQty != memQtyNone);
// Top context cannot have a callback ASSERT(this->active);
ASSERT(this != &contextTop);
// Clear callback function and argument // Clear callback function and argument
this->callbackFunction = NULL; *(memContextCallbackOne(this)) = (MemContextCallbackOne){0};
this->callbackArgument = NULL; this->callbackInitialized = false;
FUNCTION_TEST_RETURN_VOID(); FUNCTION_TEST_RETURN_VOID();
} }
@ -411,60 +574,77 @@ memContextCallbackClear(MemContext *this)
Find an available slot in the memory context's allocation list and allocate memory Find an available slot in the memory context's allocation list and allocate memory
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
static MemContextAlloc * static MemContextAlloc *
memContextAllocNew(size_t size) memContextAllocNew(const size_t size)
{ {
FUNCTION_TEST_BEGIN(); FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(SIZE, size); FUNCTION_TEST_PARAM(SIZE, size);
FUNCTION_TEST_END(); FUNCTION_TEST_END();
// Find space for the new allocation // Allocate memory
MemContext *contextCurrent = memContextStack[memContextCurrentStackIdx].memContext; MemContextAlloc *const result = memAllocInternal(sizeof(MemContextAlloc) + size);
for (; contextCurrent->allocFreeIdx < contextCurrent->allocListSize; contextCurrent->allocFreeIdx++) // Find space for the new allocation
if (contextCurrent->allocList[contextCurrent->allocFreeIdx] == NULL) MemContext *const contextCurrent = memContextStack[memContextCurrentStackIdx].memContext;
ASSERT(contextCurrent->allocQty != memQtyNone);
if (contextCurrent->allocQty == memQtyOne)
{
MemContextAllocOne *const contextAlloc = memContextAllocOne(contextCurrent);
ASSERT(!contextCurrent->allocInitialized || contextAlloc->alloc == NULL);
// Initialize allocation header
*result = (MemContextAlloc){.size = (unsigned int)(sizeof(MemContextAlloc) + size)};
// Set pointer in allocation
contextAlloc->alloc = result;
contextCurrent->allocInitialized = true;
}
else
{
ASSERT(contextCurrent->allocQty == memQtyMany);
MemContextAllocMany *const contextAlloc = memContextAllocMany(contextCurrent);
// Initialize (free space will always be index 0)
if (!contextCurrent->allocInitialized)
{
*contextAlloc = (MemContextAllocMany)
{
.list = memAllocPtrArrayInternal(MEM_CONTEXT_ALLOC_INITIAL_SIZE),
.listSize = contextAlloc->listSize = MEM_CONTEXT_ALLOC_INITIAL_SIZE,
};
contextCurrent->allocInitialized = true;
}
else
{
for (; contextAlloc->freeIdx < contextAlloc->listSize; contextAlloc->freeIdx++)
if (contextAlloc->list[contextAlloc->freeIdx] == NULL)
break; break;
// If no space was found then allocate more // If no space was found then allocate more
if (contextCurrent->allocFreeIdx == contextCurrent->allocListSize) if (contextAlloc->freeIdx == contextAlloc->listSize)
{
// Only the top context will not have initial space for allocations
if (contextCurrent->allocListSize == 0)
{
// Allocate memory before modifying anything else in case there is an error
contextCurrent->allocList = memAllocPtrArrayInternal(MEM_CONTEXT_ALLOC_INITIAL_SIZE);
// Set new size
contextCurrent->allocListSize = MEM_CONTEXT_ALLOC_INITIAL_SIZE;
}
// Else grow the list
else
{ {
// Calculate new list size // Calculate new list size
unsigned int allocListSizeNew = contextCurrent->allocListSize * 2; unsigned int listSizeNew = contextAlloc->listSize * 2;
// Reallocate memory before modifying anything else in case there is an error // Reallocate memory before modifying anything else in case there is an error
contextCurrent->allocList = memReAllocPtrArrayInternal( contextAlloc->list = memReAllocPtrArrayInternal(contextAlloc->list, contextAlloc->listSize, listSizeNew);
contextCurrent->allocList, contextCurrent->allocListSize, allocListSizeNew);
// Set new size // Set new size
contextCurrent->allocListSize = allocListSizeNew; contextAlloc->listSize = listSizeNew;
} }
} }
// Create new allocation // Initialize allocation header
MemContextAlloc *result = memAllocInternal(sizeof(MemContextAlloc) + size); *result = (MemContextAlloc){.allocIdx = contextAlloc->freeIdx, .size = (unsigned int)(sizeof(MemContextAlloc) + size)};
*result = (MemContextAlloc)
{
.allocIdx = contextCurrent->allocFreeIdx,
.size = (unsigned int)(sizeof(MemContextAlloc) + size),
};
// Set pointer in allocation list // Set pointer in allocation list
contextCurrent->allocList[contextCurrent->allocFreeIdx] = result; contextAlloc->list[contextAlloc->freeIdx] = result;
// Update free index to next location. This location may not actually be free but it is where the search should start next time. // Update free index to next location. This location may not be free but it is where the search should start next time.
contextCurrent->allocFreeIdx++; contextAlloc->freeIdx++;
}
FUNCTION_TEST_RETURN_TYPE_P(MemContextAlloc, result); FUNCTION_TEST_RETURN_TYPE_P(MemContextAlloc, result);
} }
@ -480,14 +660,27 @@ memContextAllocResize(MemContextAlloc *alloc, size_t size)
FUNCTION_TEST_PARAM(SIZE, size); FUNCTION_TEST_PARAM(SIZE, size);
FUNCTION_TEST_END(); FUNCTION_TEST_END();
ASSERT_ALLOC_VALID(alloc);
// Resize the allocation // Resize the allocation
alloc = memReAllocInternal(alloc, sizeof(MemContextAlloc) + size); alloc = memReAllocInternal(alloc, sizeof(MemContextAlloc) + size);
alloc->size = (unsigned int)(sizeof(MemContextAlloc) + size); alloc->size = (unsigned int)(sizeof(MemContextAlloc) + size);
// Update pointer in allocation list in case the realloc moved the allocation // Update pointer in allocation list in case the realloc moved the allocation
memContextStack[memContextCurrentStackIdx].memContext->allocList[alloc->allocIdx] = alloc; MemContext *const currentContext = memContextStack[memContextCurrentStackIdx].memContext;
ASSERT(currentContext->allocQty != memQtyNone);
ASSERT(currentContext->allocInitialized);
if (currentContext->allocQty == memQtyOne)
{
ASSERT(memContextAllocOne(currentContext)->alloc != NULL);
memContextAllocOne(currentContext)->alloc = alloc;
}
else
{
ASSERT(currentContext->allocQty == memQtyMany);
ASSERT_ALLOC_MANY_VALID(alloc);
memContextAllocMany(currentContext)->list[alloc->allocIdx] = alloc;
}
FUNCTION_TEST_RETURN_TYPE_P(MemContextAlloc, alloc); FUNCTION_TEST_RETURN_TYPE_P(MemContextAlloc, alloc);
} }
@ -500,7 +693,9 @@ memNew(size_t size)
FUNCTION_TEST_PARAM(SIZE, size); FUNCTION_TEST_PARAM(SIZE, size);
FUNCTION_TEST_END(); FUNCTION_TEST_END();
FUNCTION_TEST_RETURN_P(VOID, MEM_CONTEXT_ALLOC_BUFFER(memContextAllocNew(size))); void *result = MEM_CONTEXT_ALLOC_BUFFER(memContextAllocNew(size));
FUNCTION_TEST_RETURN_P(VOID, result);
} }
/**********************************************************************************************************************************/ /**********************************************************************************************************************************/
@ -535,24 +730,40 @@ memResize(const void *buffer, size_t size)
/**********************************************************************************************************************************/ /**********************************************************************************************************************************/
void void
memFree(void *buffer) memFree(void *const buffer)
{ {
FUNCTION_TEST_BEGIN(); FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM_P(VOID, buffer); FUNCTION_TEST_PARAM_P(VOID, buffer);
FUNCTION_TEST_END(); FUNCTION_TEST_END();
ASSERT_ALLOC_VALID(MEM_CONTEXT_ALLOC_HEADER(buffer));
// Get the allocation // Get the allocation
MemContext *contextCurrent = memContextStack[memContextCurrentStackIdx].memContext; MemContext *const contextCurrent = memContextStack[memContextCurrentStackIdx].memContext;
MemContextAlloc *alloc = MEM_CONTEXT_ALLOC_HEADER(buffer); ASSERT(contextCurrent->allocQty != memQtyNone);
ASSERT(contextCurrent->allocInitialized);
MemContextAlloc *const alloc = MEM_CONTEXT_ALLOC_HEADER(buffer);
// Remove allocation from the context
if (contextCurrent->allocQty == memQtyOne)
{
ASSERT(memContextAllocOne(contextCurrent)->alloc == alloc);
memContextAllocOne(contextCurrent)->alloc = NULL;
}
else
{
ASSERT(contextCurrent->allocQty == memQtyMany);
ASSERT_ALLOC_MANY_VALID(alloc);
// If this allocation is before the current free allocation then make it the current free allocation // If this allocation is before the current free allocation then make it the current free allocation
if (alloc->allocIdx < contextCurrent->allocFreeIdx) MemContextAllocMany *const contextAlloc = memContextAllocMany(contextCurrent);
contextCurrent->allocFreeIdx = alloc->allocIdx;
if (alloc->allocIdx < contextAlloc->freeIdx)
contextAlloc->freeIdx = alloc->allocIdx;
// Null the allocation
contextAlloc->list[alloc->allocIdx] = NULL;
}
// Free the allocation // Free the allocation
contextCurrent->allocList[alloc->allocIdx] = NULL;
memFreeInternal(alloc); memFreeInternal(alloc);
FUNCTION_TEST_RETURN_VOID(); FUNCTION_TEST_RETURN_VOID();
@ -572,15 +783,48 @@ memContextMove(MemContext *this, MemContext *parentNew)
// Only move if a valid mem context is provided and the old and new parents are not the same // Only move if a valid mem context is provided and the old and new parents are not the same
if (this != NULL && this->contextParent != parentNew) if (this != NULL && this->contextParent != parentNew)
{ {
// Null out the context in the old parent ASSERT(this->active);
ASSERT(this->contextParent->contextChildList[this->contextParentIdx] == this); ASSERT(this->contextParent->active);
this->contextParent->contextChildList[this->contextParentIdx] = NULL; ASSERT(this->contextParent->childQty != memQtyNone);
ASSERT(this->contextParent->childInitialized);
// Find a place in the new parent context and assign it. The child list may be moved while finding a new index so store the // Null out the context in the old parent
// index and use it with (what might be) the new pointer. if (this->contextParent->childQty == memQtyOne)
{
ASSERT(memContextChildOne(this->contextParent)->context != NULL);
memContextChildOne(this->contextParent)->context = NULL;
}
else
{
ASSERT(this->contextParent->childQty == memQtyMany);
ASSERT(memContextChildMany(this->contextParent)->list[this->contextParentIdx] == this);
memContextChildMany(this->contextParent)->list[this->contextParentIdx] = NULL;
}
// Find a place in the new parent context and assign it
ASSERT(parentNew->active);
ASSERT(parentNew->childQty != memQtyNone);
if (parentNew->childQty == memQtyOne)
{
ASSERT(!parentNew->childInitialized || memContextChildOne(parentNew)->context == NULL);
memContextChildOne(parentNew)->context = this;
parentNew->childInitialized = true;
}
else
{
ASSERT(parentNew->childQty == memQtyMany);
MemContextChildMany *const memContextChild = memContextChildMany(parentNew);
// The child list may move while finding a new index so store the index and use it with (what might be) the new pointer
this->contextParentIdx = memContextNewIndex(parentNew, memContextChild);
memContextChild->list[this->contextParentIdx] = this;
}
// Set new parent
this->contextParent = parentNew; this->contextParent = parentNew;
this->contextParentIdx = memContextNewIndex(parentNew);
parentNew->contextChildList[this->contextParentIdx] = this;
} }
FUNCTION_TEST_RETURN_VOID(); FUNCTION_TEST_RETURN_VOID();
@ -691,7 +935,7 @@ MemContext *
memContextTop(void) memContextTop(void)
{ {
FUNCTION_TEST_VOID(); FUNCTION_TEST_VOID();
FUNCTION_TEST_RETURN(MEM_CONTEXT, &contextTop); FUNCTION_TEST_RETURN(MEM_CONTEXT, (MemContext *)&contextTop);
} }
/**********************************************************************************************************************************/ /**********************************************************************************************************************************/
@ -720,32 +964,86 @@ memContextPrior(void)
/**********************************************************************************************************************************/ /**********************************************************************************************************************************/
size_t size_t
memContextSize(const MemContext *this) memContextSize(const MemContext *const this)
{ {
FUNCTION_TEST_BEGIN(); FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(MEM_CONTEXT, this); FUNCTION_TEST_PARAM(MEM_CONTEXT, this);
FUNCTION_TEST_END(); FUNCTION_TEST_END();
// Size of struct and child context/alloc arrays ASSERT(this != NULL);
size_t result = ASSERT(this->active);
sizeof(MemContext) + this->allocExtra + (this->contextChildListSize * sizeof(MemContext *)) +
(this->allocListSize * sizeof(MemContextAlloc *));
// Add child contexts // Size of struct and extra
for (unsigned int contextIdx = 0; contextIdx < this->contextChildListSize; contextIdx++) size_t total = 0;
const unsigned char *offset = (unsigned char *)(this + 1) + this->allocExtra;
// Size of child contexts
if (this->childQty == memQtyOne)
{ {
if (this->contextChildList[contextIdx]) if (this->childInitialized)
result += memContextSize(this->contextChildList[contextIdx]); {
const MemContextChildOne *const contextChild = (const MemContextChildOne *const)offset;
if (contextChild->context != NULL)
total += memContextSize(contextChild->context);
} }
// Add allocations offset += sizeof(MemContextChildOne);
for (unsigned int allocIdx = 0; allocIdx < this->allocListSize; allocIdx++) }
else if (this->childQty == memQtyMany)
{ {
if (this->allocList[allocIdx] != NULL) if (this->childInitialized)
result += this->allocList[allocIdx]->size; {
const MemContextChildMany *const contextChild = (const MemContextChildMany *const)offset;
for (unsigned int contextIdx = 0; contextIdx < contextChild->listSize; contextIdx++)
{
if (contextChild->list[contextIdx] != NULL)
total += memContextSize(contextChild->list[contextIdx]);
} }
FUNCTION_TEST_RETURN(SIZE, result); total += contextChild->listSize * sizeof(MemContextChildMany *);
}
offset += sizeof(MemContextChildMany);
}
// Size of allocations
if (this->allocQty == memQtyOne)
{
if (this->allocInitialized)
{
const MemContextAllocOne *const contextAlloc = (const MemContextAllocOne *const)offset;
if (contextAlloc->alloc != NULL)
total += contextAlloc->alloc->size;
}
offset += sizeof(MemContextAllocOne);
}
else if (this->allocQty == memQtyMany)
{
if (this->allocInitialized)
{
const MemContextAllocMany *const contextAlloc = (const MemContextAllocMany *const)offset;
for (unsigned int allocIdx = 0; allocIdx < contextAlloc->listSize; allocIdx++)
{
if (contextAlloc->list[allocIdx] != NULL)
total += contextAlloc->list[allocIdx]->size;
}
total += contextAlloc->listSize * sizeof(MemContextAllocMany *);
}
offset += sizeof(MemContextAllocMany);
}
// Size of callback
if (this->callbackQty != memQtyNone)
offset += sizeof(MemContextCallbackOne);
FUNCTION_TEST_RETURN(SIZE, (size_t)(offset - (unsigned char *)this) + total);
} }
/**********************************************************************************************************************************/ /**********************************************************************************************************************************/
@ -798,23 +1096,40 @@ memContextCallbackRecurse(MemContext *const this)
#endif #endif
// Callback // Callback
if (this->callbackFunction) if (this->callbackInitialized)
{ {
this->callbackFunction(this->callbackArgument); MemContextCallbackOne *const callback = memContextCallbackOne(this);
this->callbackFunction = NULL; callback->function(callback->argument);
this->callbackInitialized = false;
} }
// Child callbacks // Child callbacks
for (unsigned int contextIdx = 0; contextIdx < this->contextChildListSize; contextIdx++) if (this->childInitialized)
{ {
if (this->contextChildList[contextIdx] != NULL) if (this->childQty == memQtyOne)
memContextCallbackRecurse(this->contextChildList[contextIdx]); {
MemContextChildOne *const memContextChild = memContextChildOne(this);
if (memContextChild->context != NULL)
memContextCallbackRecurse(memContextChild->context);
}
else
{
ASSERT(this->childQty == memQtyMany);
MemContextChildMany *const memContextChild = memContextChildMany(this);
for (unsigned int contextIdx = 0; contextIdx < memContextChild->listSize; contextIdx++)
{
if (memContextChild->list[contextIdx] != NULL)
memContextCallbackRecurse(memContextChild->list[contextIdx]);
}
}
} }
FUNCTION_TEST_RETURN_VOID(); FUNCTION_TEST_RETURN_VOID();
} }
// Helper to free the context and all its children /**********************************************************************************************************************************/
static void static void
memContextFreeRecurse(MemContext *const this) memContextFreeRecurse(MemContext *const this)
{ {
@ -826,55 +1141,95 @@ memContextFreeRecurse(MemContext *const this)
#ifdef DEBUG #ifdef DEBUG
// Current context cannot be freed unless it is top (top is never really freed, just the stuff under it) // Current context cannot be freed unless it is top (top is never really freed, just the stuff under it)
if (this == memContextStack[memContextCurrentStackIdx].memContext && this != &contextTop) if (this == memContextStack[memContextCurrentStackIdx].memContext && this != memContextTop())
THROW_FMT(AssertError, "cannot free current context '%s'", this->name); THROW_FMT(AssertError, "cannot free current context '%s'", this->name);
#endif #endif
// Free child contexts and list
if (this->contextChildListSize > 0)
{
// Free child contexts // Free child contexts
for (unsigned int contextIdx = 0; contextIdx < this->contextChildListSize; contextIdx++) if (this->childInitialized)
{ {
if (this->contextChildList[contextIdx] != NULL) if (this->childQty == memQtyOne)
memContextFreeRecurse(this->contextChildList[contextIdx]); {
MemContextChildOne *const memContextChild = memContextChildOne(this);
if (memContextChild->context != NULL)
memContextFreeRecurse(memContextChild->context);
}
else
{
ASSERT(this->childQty == memQtyMany);
MemContextChildMany *const memContextChild = memContextChildMany(this);
for (unsigned int contextIdx = 0; contextIdx < memContextChild->listSize; contextIdx++)
{
if (memContextChild->list[contextIdx] != NULL)
memContextFreeRecurse(memContextChild->list[contextIdx]);
} }
// Free child context allocation list // Free child context allocation list
memFreeInternal(this->contextChildList); memFreeInternal(memContextChildMany(this)->list);
this->contextChildListSize = 0; }
} }
// Free memory allocations and list // Free memory allocations and list
if (this->allocListSize > 0) if (this->allocInitialized)
{ {
for (unsigned int allocIdx = 0; allocIdx < this->allocListSize; allocIdx++) ASSERT(this->allocQty != memQtyNone);
if (this->allocList[allocIdx] != NULL)
memFreeInternal(this->allocList[allocIdx]);
memFreeInternal(this->allocList); if (this->allocQty == memQtyOne)
this->allocListSize = 0; {
MemContextAllocOne *const contextAlloc = memContextAllocOne(this);
if (contextAlloc->alloc != NULL)
memFreeInternal(contextAlloc->alloc);
}
else
{
ASSERT(this->allocQty == memQtyMany);
MemContextAllocMany *const contextAlloc = memContextAllocMany(this);
for (unsigned int allocIdx = 0; allocIdx < contextAlloc->listSize; allocIdx++)
if (contextAlloc->list[allocIdx] != NULL)
memFreeInternal(contextAlloc->list[allocIdx]);
memFreeInternal(contextAlloc->list);
}
} }
// If the context index is lower than the current free index in the parent then replace it
if (this->contextParent != NULL && this->contextParentIdx < this->contextParent->contextChildFreeIdx)
this->contextParent->contextChildFreeIdx = this->contextParentIdx;
// Free the memory context so the slot can be reused (if not the top mem context) // Free the memory context so the slot can be reused (if not the top mem context)
if (this != &contextTop) if (this != memContextTop())
{ {
ASSERT(this->contextParent != NULL); ASSERT(this->contextParent != NULL);
this->contextParent->contextChildList[this->contextParentIdx] = NULL; if (this->contextParent->childQty == memQtyOne)
memFreeInternal(this); memContextChildOne(this->contextParent)->context = NULL;
}
#ifdef DEBUG
// Else make the top mem context active again
else else
{ {
this->active = true; ASSERT(this->contextParent->childQty == memQtyMany);
MemContextChildMany *const memContextChild = memContextChildMany(this->contextParent);
// If the context index is lower than the current free index in the parent then replace it
if (this->contextParentIdx < memContextChild->freeIdx)
memContextChild->freeIdx = this->contextParentIdx;
memContextChildMany(this->contextParent)->list[this->contextParentIdx] = NULL;
} }
memFreeInternal(this);
}
// Else reset top context. In practice it is uncommon for the top mem context to be freed and then used again.
else
{
this->childInitialized = false;
this->allocInitialized = false;
#ifdef DEBUG
// Make the top mem context active again
this->active = true;
#endif #endif
}
FUNCTION_TEST_RETURN_VOID(); FUNCTION_TEST_RETURN_VOID();
} }

View File

@ -118,7 +118,7 @@ Note that memory context names are expected to live for the lifetime of the cont
#define MEM_CONTEXT_NEW_BEGIN(memContextName, ...) \ #define MEM_CONTEXT_NEW_BEGIN(memContextName, ...) \
do \ do \
{ \ { \
MemContext *MEM_CONTEXT_NEW() = memContextNewP(memContextName, __VA_ARGS__); \ MemContext *MEM_CONTEXT_NEW() = memContextNewP(STRINGIFY(memContextName), __VA_ARGS__); \
memContextSwitch(MEM_CONTEXT_NEW()); memContextSwitch(MEM_CONTEXT_NEW());
#define MEM_CONTEXT_NEW_ALLOC() \ #define MEM_CONTEXT_NEW_ALLOC() \
@ -150,7 +150,8 @@ MEM_CONTEXT_TEMP_END();
#define MEM_CONTEXT_TEMP_BEGIN() \ #define MEM_CONTEXT_TEMP_BEGIN() \
do \ do \
{ \ { \
MemContext *MEM_CONTEXT_TEMP() = memContextNewP("temporary"); \ MemContext *MEM_CONTEXT_TEMP() = memContextNewP( \
"temporary", .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX); \
memContextSwitch(MEM_CONTEXT_TEMP()); memContextSwitch(MEM_CONTEXT_TEMP());
#define MEM_CONTEXT_TEMP_RESET_BEGIN() \ #define MEM_CONTEXT_TEMP_RESET_BEGIN() \
@ -166,7 +167,7 @@ MEM_CONTEXT_TEMP_END();
{ \ { \
memContextSwitchBack(); \ memContextSwitchBack(); \
memContextDiscard(); \ memContextDiscard(); \
MEM_CONTEXT_TEMP() = memContextNewP("temporary"); \ MEM_CONTEXT_TEMP() = memContextNewP("temporary", .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX); \
memContextSwitch(MEM_CONTEXT_TEMP()); \ memContextSwitch(MEM_CONTEXT_TEMP()); \
MEM_CONTEXT_TEMP_loopTotal = 0; \ MEM_CONTEXT_TEMP_loopTotal = 0; \
} \ } \
@ -204,9 +205,18 @@ Use the MEM_CONTEXT*() macros when possible rather than reimplement the boilerpl
typedef struct MemContextNewParam typedef struct MemContextNewParam
{ {
VAR_PARAM_HEADER; VAR_PARAM_HEADER;
uint8_t childQty; // How many child contexts can this context have?
uint8_t allocQty; // How many allocations can this context have?
uint8_t callbackQty; // How many callbacks can this context have?
uint16_t allocExtra; // Extra memory to allocate with the context uint16_t allocExtra; // Extra memory to allocate with the context
} MemContextNewParam; } MemContextNewParam;
// Maximum amount of extra memory that can be allocated with the context using allocExtra
#define MEM_CONTEXT_ALLOC_EXTRA_MAX UINT16_MAX
// Specify maximum quantity of child contexts or allocations using childQty or allocQty
#define MEM_CONTEXT_QTY_MAX UINT8_MAX
#ifdef DEBUG #ifdef DEBUG
#define memContextNewP(name, ...) \ #define memContextNewP(name, ...) \
memContextNew(name, (MemContextNewParam){VAR_PARAM_INIT, __VA_ARGS__}) memContextNew(name, (MemContextNewParam){VAR_PARAM_INIT, __VA_ARGS__})

View File

@ -74,7 +74,7 @@ regExpNew(const String *expression)
RegExp *this = NULL; RegExp *this = NULL;
OBJ_NEW_BEGIN(RegExp) OBJ_NEW_BEGIN(RegExp, .childQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1)
{ {
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();
*this = (RegExp){{0}}; // Extra braces are required for older gcc versions *this = (RegExp){{0}}; // Extra braces are required for older gcc versions

View File

@ -37,7 +37,7 @@ statInit(void)
MEM_CONTEXT_BEGIN(memContextTop()) MEM_CONTEXT_BEGIN(memContextTop())
{ {
MEM_CONTEXT_NEW_BEGIN("StatLocalData") MEM_CONTEXT_NEW_BEGIN(StatLocalData, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
statLocalData.memContext = MEM_CONTEXT_NEW(); statLocalData.memContext = MEM_CONTEXT_NEW();
statLocalData.stat = lstNewP(sizeof(Stat), .sortOrder = sortOrderAsc, .comparator = lstComparatorStr); statLocalData.stat = lstNewP(sizeof(Stat), .sortOrder = sortOrderAsc, .comparator = lstComparatorStr);

View File

@ -39,7 +39,7 @@ bufNew(size_t size)
Buffer *this = NULL; Buffer *this = NULL;
OBJ_NEW_BEGIN(Buffer) OBJ_NEW_BEGIN(Buffer, .allocQty = 1)
{ {
// Create object // Create object
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();

View File

@ -92,7 +92,7 @@ jsonReadNew(const String *const json)
JsonRead *this = NULL; JsonRead *this = NULL;
OBJ_NEW_BEGIN(JsonRead) OBJ_NEW_BEGIN(JsonRead, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();
@ -1309,7 +1309,7 @@ jsonWriteNew(JsonWriteNewParam param)
JsonWrite *this = NULL; JsonWrite *this = NULL;
OBJ_NEW_BEGIN(JsonWrite) OBJ_NEW_BEGIN(JsonWrite, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();

View File

@ -36,7 +36,7 @@ kvNew(void)
KeyValue *this = NULL; KeyValue *this = NULL;
OBJ_NEW_BEGIN(KeyValue) OBJ_NEW_BEGIN(KeyValue, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
// Allocate state and set context // Allocate state and set context
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();

View File

@ -35,7 +35,7 @@ lstNew(size_t itemSize, ListParam param)
List *this = NULL; List *this = NULL;
OBJ_NEW_BEGIN(List) OBJ_NEW_BEGIN(List, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX)
{ {
// Create object // Create object
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();

View File

@ -29,8 +29,11 @@ OBJ_NEW_BEGIN(MyObj)
} }
OBJ_NEW_END(); OBJ_NEW_END();
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
#define OBJ_NEW_BEGIN(type) \ #define OBJ_NEW_EXTRA_BEGIN(type, extra, ...) \
MEM_CONTEXT_NEW_BEGIN(STRINGIFY(type), .allocExtra = sizeof(type)) MEM_CONTEXT_NEW_BEGIN(type, .allocExtra = extra, __VA_ARGS__)
#define OBJ_NEW_BEGIN(type, ...) \
OBJ_NEW_EXTRA_BEGIN(type, sizeof(type), __VA_ARGS__)
#define OBJ_NEW_ALLOC() \ #define OBJ_NEW_ALLOC() \
memContextAllocExtra(memContextCurrent()) memContextAllocExtra(memContextCurrent())

View File

@ -336,7 +336,7 @@ pckReadNewInternal(void)
PackRead *this = NULL; PackRead *this = NULL;
OBJ_NEW_BEGIN(PackRead) OBJ_NEW_BEGIN(PackRead, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();
@ -1295,7 +1295,7 @@ pckWriteNewInternal(void)
PackWrite *this = NULL; PackWrite *this = NULL;
OBJ_NEW_BEGIN(PackWrite) OBJ_NEW_BEGIN(PackWrite, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();

View File

@ -66,7 +66,6 @@ Object type
struct String struct String
{ {
StringPub pub; // Publicly accessible variables StringPub pub; // Publicly accessible variables
MemContext *memContext; // Required for dynamically allocated strings
}; };
/**********************************************************************************************************************************/ /**********************************************************************************************************************************/
@ -75,8 +74,11 @@ strNew(void)
{ {
FUNCTION_TEST_VOID(); FUNCTION_TEST_VOID();
// Create object String *this = NULL;
String *this = memNew(sizeof(String));
OBJ_NEW_BEGIN(String, .allocQty = 1)
{
this = OBJ_NEW_ALLOC();
*this = (String) *this = (String)
{ {
@ -85,8 +87,9 @@ strNew(void)
// Set empty so nothing is allocated until needed // Set empty so nothing is allocated until needed
.buffer = STR_EMPTY_BUFFER, .buffer = STR_EMPTY_BUFFER,
}, },
.memContext = memContextCurrent(),
}; };
}
OBJ_NEW_END();
FUNCTION_TEST_RETURN(STRING, this); FUNCTION_TEST_RETURN(STRING, this);
} }
@ -103,7 +106,34 @@ strNewFixed(const size_t size)
CHECK_SIZE(size); CHECK_SIZE(size);
String *this = memNew(sizeof(String) + size + 1); String *this = NULL;
// If the string is larger than the extra allowed with a mem context then allocate the buffer separately
size_t allocExtra = sizeof(String) + size + 1;
if (allocExtra > MEM_CONTEXT_ALLOC_EXTRA_MAX)
{
OBJ_NEW_BEGIN(String, .allocQty = 1)
{
this = OBJ_NEW_ALLOC();
*this = (String)
{
.pub =
{
.size = (unsigned int)size,
.buffer = memNew(size + 1),
},
};
}
OBJ_NEW_END();
FUNCTION_TEST_RETURN(STRING, this);
}
OBJ_NEW_EXTRA_BEGIN(String, (uint16_t)(allocExtra))
{
this = OBJ_NEW_ALLOC();
*this = (String) *this = (String)
{ {
@ -112,8 +142,9 @@ strNewFixed(const size_t size)
.size = (unsigned int)size, .size = (unsigned int)size,
.buffer = STR_FIXED_BUFFER, .buffer = STR_FIXED_BUFFER,
}, },
.memContext = memContextCurrent(),
}; };
}
OBJ_NEW_END();
FUNCTION_TEST_RETURN(STRING, this); FUNCTION_TEST_RETURN(STRING, this);
} }
@ -330,14 +361,14 @@ strResize(String *this, size_t requested)
if (this->pub.extra < STRING_EXTRA_MIN) if (this->pub.extra < STRING_EXTRA_MIN)
this->pub.extra = STRING_EXTRA_MIN; this->pub.extra = STRING_EXTRA_MIN;
MEM_CONTEXT_BEGIN(this->memContext) MEM_CONTEXT_OBJ_BEGIN(this)
{ {
if (STR_IS_EMPTY_BUFFER()) if (STR_IS_EMPTY_BUFFER())
this->pub.buffer = memNew(strSize(this) + this->pub.extra + 1); this->pub.buffer = memNew(strSize(this) + this->pub.extra + 1);
else else
this->pub.buffer = memResize(this->pub.buffer, strSize(this) + this->pub.extra + 1); this->pub.buffer = memResize(this->pub.buffer, strSize(this) + this->pub.extra + 1);
} }
MEM_CONTEXT_END(); MEM_CONTEXT_OBJ_END();
} }
FUNCTION_TEST_RETURN_VOID(); FUNCTION_TEST_RETURN_VOID();
@ -1065,28 +1096,3 @@ strSizeFormat(const uint64_t size)
FUNCTION_TEST_RETURN(STRING, result); FUNCTION_TEST_RETURN(STRING, result);
} }
/***********************************************************************************************************************************
Free the string
***********************************************************************************************************************************/
void
strFree(String *this)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRING, this);
FUNCTION_TEST_END();
if (this != NULL)
{
MEM_CONTEXT_BEGIN(this->memContext)
{
if (!STR_IS_EMPTY_BUFFER() && !STR_IS_FIXED_BUFFER())
memFree(this->pub.buffer);
memFree(this);
}
MEM_CONTEXT_END();
}
FUNCTION_TEST_RETURN_VOID();
}

View File

@ -202,7 +202,11 @@ String *strTrunc(String *this, int idx);
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Destructor Destructor
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
void strFree(String *this); __attribute__((always_inline)) static inline void
strFree(String *const this)
{
objFree(this);
}
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Macros for constant strings Macros for constant strings

View File

@ -37,51 +37,43 @@ struct Variant
typedef struct VariantBool typedef struct VariantBool
{ {
VariantBoolPub pub; // Publicly accessible variables VariantBoolPub pub; // Publicly accessible variables
MemContext *memContext;
} VariantBool; } VariantBool;
typedef struct VariantInt typedef struct VariantInt
{ {
VariantIntPub pub; // Publicly accessible variables VariantIntPub pub; // Publicly accessible variables
MemContext *memContext;
} VariantInt; } VariantInt;
typedef struct VariantInt64 typedef struct VariantInt64
{ {
VariantInt64Pub pub; // Publicly accessible variables VariantInt64Pub pub; // Publicly accessible variables
MemContext *memContext;
} VariantInt64; } VariantInt64;
typedef struct VariantKeyValue typedef struct VariantKeyValue
{ {
VARIANT_COMMON VARIANT_COMMON
KeyValue *data; // KeyValue data KeyValue *data; // KeyValue data
MemContext *memContext;
} VariantKeyValue; } VariantKeyValue;
typedef struct VariantString typedef struct VariantString
{ {
VariantStringPub pub; // Publicly accessible variables VariantStringPub pub; // Publicly accessible variables
MemContext *memContext;
} VariantString; } VariantString;
typedef struct VariantUInt typedef struct VariantUInt
{ {
VariantUIntPub pub; // Publicly accessible variables VariantUIntPub pub; // Publicly accessible variables
MemContext *memContext;
} VariantUInt; } VariantUInt;
typedef struct VariantUInt64 typedef struct VariantUInt64
{ {
VariantUInt64Pub pub; // Publicly accessible variables VariantUInt64Pub pub; // Publicly accessible variables
MemContext *memContext;
} VariantUInt64; } VariantUInt64;
typedef struct VariantVariantList typedef struct VariantVariantList
{ {
VARIANT_COMMON VARIANT_COMMON
VariantList *data; // VariantList data VariantList *data; // VariantList data
MemContext *memContext;
} VariantVariantList; } VariantVariantList;
/*********************************************************************************************************************************** /***********************************************************************************************************************************
@ -127,16 +119,7 @@ varDup(const Variant *this)
case varTypeKeyValue: case varTypeKeyValue:
{ {
VariantKeyValue *keyValue = memNew(sizeof(VariantKeyValue)); result = varNewKv(kvDup(varKv(this)));
*keyValue = (VariantKeyValue)
{
.memContext = memContextCurrent(),
.type = varTypeKeyValue,
.data = kvDup(varKv(this)),
};
result = (Variant *)keyValue;
break; break;
} }
@ -224,8 +207,11 @@ varNewBool(bool data)
FUNCTION_TEST_PARAM(BOOL, data); FUNCTION_TEST_PARAM(BOOL, data);
FUNCTION_TEST_END(); FUNCTION_TEST_END();
// Allocate memory for the variant and set the type and data VariantBool *this = NULL;
VariantBool *this = memNew(sizeof(VariantBool));
OBJ_NEW_BEGIN(VariantBool)
{
this = OBJ_NEW_ALLOC();
*this = (VariantBool) *this = (VariantBool)
{ {
@ -234,8 +220,9 @@ varNewBool(bool data)
.type = varTypeBool, .type = varTypeBool,
.data = data, .data = data,
}, },
.memContext = memContextCurrent(),
}; };
}
OBJ_NEW_END();
FUNCTION_TEST_RETURN(VARIANT, (Variant *)this); FUNCTION_TEST_RETURN(VARIANT, (Variant *)this);
} }
@ -329,8 +316,11 @@ varNewInt(int data)
FUNCTION_TEST_PARAM(INT, data); FUNCTION_TEST_PARAM(INT, data);
FUNCTION_TEST_END(); FUNCTION_TEST_END();
// Allocate memory for the variant and set the type and data VariantInt *this = NULL;
VariantInt *this = memNew(sizeof(VariantInt));
OBJ_NEW_BEGIN(VariantInt)
{
this = OBJ_NEW_ALLOC();
*this = (VariantInt) *this = (VariantInt)
{ {
@ -339,8 +329,9 @@ varNewInt(int data)
.type = varTypeInt, .type = varTypeInt,
.data = data, .data = data,
}, },
.memContext = memContextCurrent(),
}; };
}
OBJ_NEW_END();
FUNCTION_TEST_RETURN(VARIANT, (Variant *)this); FUNCTION_TEST_RETURN(VARIANT, (Variant *)this);
} }
@ -441,8 +432,11 @@ varNewInt64(int64_t data)
FUNCTION_TEST_PARAM(INT64, data); FUNCTION_TEST_PARAM(INT64, data);
FUNCTION_TEST_END(); FUNCTION_TEST_END();
// Allocate memory for the variant and set the type and data VariantInt64 *this = NULL;
VariantInt64 *this = memNew(sizeof(VariantInt64));
OBJ_NEW_BEGIN(VariantInt64)
{
this = OBJ_NEW_ALLOC();
*this = (VariantInt64) *this = (VariantInt64)
{ {
@ -451,8 +445,9 @@ varNewInt64(int64_t data)
.type = varTypeInt64, .type = varTypeInt64,
.data = data, .data = data,
}, },
.memContext = memContextCurrent(),
}; };
}
OBJ_NEW_END();
FUNCTION_TEST_RETURN(VARIANT, (Variant *)this); FUNCTION_TEST_RETURN(VARIANT, (Variant *)this);
} }
@ -537,8 +532,11 @@ varNewUInt(unsigned int data)
FUNCTION_TEST_PARAM(UINT, data); FUNCTION_TEST_PARAM(UINT, data);
FUNCTION_TEST_END(); FUNCTION_TEST_END();
// Allocate memory for the variant and set the type and data VariantUInt *this = NULL;
VariantUInt *this = memNew(sizeof(VariantUInt));
OBJ_NEW_BEGIN(VariantUInt)
{
this = OBJ_NEW_ALLOC();
*this = (VariantUInt) *this = (VariantUInt)
{ {
@ -547,8 +545,9 @@ varNewUInt(unsigned int data)
.type = varTypeUInt, .type = varTypeUInt,
.data = data, .data = data,
}, },
.memContext = memContextCurrent(),
}; };
}
OBJ_NEW_END();
FUNCTION_TEST_RETURN(VARIANT, (Variant *)this); FUNCTION_TEST_RETURN(VARIANT, (Variant *)this);
} }
@ -658,8 +657,11 @@ varNewUInt64(uint64_t data)
FUNCTION_TEST_PARAM(UINT64, data); FUNCTION_TEST_PARAM(UINT64, data);
FUNCTION_TEST_END(); FUNCTION_TEST_END();
// Allocate memory for the variant and set the type and data VariantUInt64 *this = NULL;
VariantUInt64 *this = memNew(sizeof(VariantUInt64));
OBJ_NEW_BEGIN(VariantUInt64)
{
this = OBJ_NEW_ALLOC();
*this = (VariantUInt64) *this = (VariantUInt64)
{ {
@ -668,8 +670,9 @@ varNewUInt64(uint64_t data)
.type = varTypeUInt64, .type = varTypeUInt64,
.data = data, .data = data,
}, },
.memContext = memContextCurrent(),
}; };
}
OBJ_NEW_END();
FUNCTION_TEST_RETURN(VARIANT, (Variant *)this); FUNCTION_TEST_RETURN(VARIANT, (Variant *)this);
} }
@ -766,17 +769,21 @@ varNewKv(KeyValue *data)
FUNCTION_TEST_PARAM(KEY_VALUE, data); FUNCTION_TEST_PARAM(KEY_VALUE, data);
FUNCTION_TEST_END(); FUNCTION_TEST_END();
// Allocate memory for the variant and set the type and data VariantKeyValue *this = NULL;
VariantKeyValue *this = memNew(sizeof(VariantKeyValue));
OBJ_NEW_BEGIN(VariantKeyValue, .childQty = 1)
{
this = OBJ_NEW_ALLOC();
*this = (VariantKeyValue) *this = (VariantKeyValue)
{ {
.memContext = memContextCurrent(),
.type = varTypeKeyValue, .type = varTypeKeyValue,
}; };
if (data != NULL) if (data != NULL)
this->data = kvMove(data, memContextCurrent()); this->data = kvMove(data, memContextCurrent());
}
OBJ_NEW_END();
FUNCTION_TEST_RETURN(VARIANT, (Variant *)this); FUNCTION_TEST_RETURN(VARIANT, (Variant *)this);
} }
@ -808,8 +815,36 @@ varNewStr(const String *data)
FUNCTION_TEST_PARAM(STRING, data); FUNCTION_TEST_PARAM(STRING, data);
FUNCTION_TEST_END(); FUNCTION_TEST_END();
// Allocate memory for the variant VariantString *this = NULL;
VariantString *this = memNew(sizeof(VariantString) + (data != NULL ? sizeof(StringPub) + strSize(data) + 1 : 0));
// If the variant is larger than the extra allowed with a mem context then allocate the buffer separately
size_t allocExtra = sizeof(VariantString) + (data != NULL ? sizeof(StringPub) + strSize(data) + 1 : 0);
if (allocExtra > MEM_CONTEXT_ALLOC_EXTRA_MAX)
{
ASSERT(data != NULL);
OBJ_NEW_BEGIN(VariantString, .childQty = 1)
{
this = OBJ_NEW_ALLOC();
*this = (VariantString)
{
.pub =
{
.type = varTypeString,
.data = strDup(data),
},
};
}
OBJ_NEW_END();
FUNCTION_TEST_RETURN(VARIANT, (Variant *)this);
}
OBJ_NEW_EXTRA_BEGIN(VariantString, (uint16_t)(allocExtra))
{
this = OBJ_NEW_ALLOC();
*this = (VariantString) *this = (VariantString)
{ {
@ -817,7 +852,6 @@ varNewStr(const String *data)
{ {
.type = varTypeString, .type = varTypeString,
}, },
.memContext = memContextCurrent(),
}; };
if (data != NULL) if (data != NULL)
@ -837,6 +871,8 @@ varNewStr(const String *data)
strncpy(pubData->buffer, strZ(data), strSize(data)); strncpy(pubData->buffer, strZ(data), strSize(data));
pubData->buffer[strSize(data)] = '\0'; pubData->buffer[strSize(data)] = '\0';
} }
}
OBJ_NEW_END();
FUNCTION_TEST_RETURN(VARIANT, (Variant *)this); FUNCTION_TEST_RETURN(VARIANT, (Variant *)this);
} }
@ -942,17 +978,21 @@ varNewVarLst(const VariantList *data)
FUNCTION_TEST_PARAM(VARIANT_LIST, data); FUNCTION_TEST_PARAM(VARIANT_LIST, data);
FUNCTION_TEST_END(); FUNCTION_TEST_END();
// Allocate memory for the variant and set the type and data VariantVariantList *this = NULL;
VariantVariantList *this = memNew(sizeof(VariantVariantList));
OBJ_NEW_BEGIN(VariantVariantList, .childQty = 1)
{
this = OBJ_NEW_ALLOC();
*this = (VariantVariantList) *this = (VariantVariantList)
{ {
.memContext = memContextCurrent(),
.type = varTypeVariantList, .type = varTypeVariantList,
}; };
if (data != NULL) if (data != NULL)
this->data = varLstDup(data); this->data = varLstDup(data);
}
OBJ_NEW_END();
FUNCTION_TEST_RETURN(VARIANT, (Variant *)this); FUNCTION_TEST_RETURN(VARIANT, (Variant *)this);
} }
@ -1014,62 +1054,3 @@ varToLog(const Variant *this)
return result; return result;
} }
/**********************************************************************************************************************************/
void
varFree(Variant *this)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(VARIANT, this);
FUNCTION_TEST_END();
if (this != NULL)
{
MemContext *memContext = NULL;
switch (varType(this))
{
case varTypeBool:
memContext = ((VariantBool *)this)->memContext;
break;
case varTypeInt:
memContext = ((VariantInt *)this)->memContext;
break;
case varTypeInt64:
memContext = ((VariantInt64 *)this)->memContext;
break;
case varTypeKeyValue:
memContext = ((VariantKeyValue *)this)->memContext;
kvFree(((VariantKeyValue *)this)->data);
break;
case varTypeString:
memContext = ((VariantString *)this)->memContext;
break;
case varTypeUInt:
memContext = ((VariantUInt *)this)->memContext;
break;
case varTypeUInt64:
memContext = ((VariantUInt64 *)this)->memContext;
break;
case varTypeVariantList:
memContext = ((VariantVariantList *)this)->memContext;
varLstFree(((VariantVariantList *)this)->data);
break;
}
MEM_CONTEXT_BEGIN(memContext)
{
memFree(this);
}
MEM_CONTEXT_END();
}
FUNCTION_TEST_RETURN_VOID();
}

View File

@ -151,7 +151,11 @@ bool varEq(const Variant *this1, const Variant *this2);
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Destructor Destructor
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
void varFree(Variant *this); __attribute__((always_inline)) static inline void
varFree(Variant *const this)
{
objFree(this);
}
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Macros for constant variants Macros for constant variants

View File

@ -332,7 +332,7 @@ xmlDocumentNew(const String *rootName)
// Create object // Create object
XmlDocument *this = NULL; XmlDocument *this = NULL;
OBJ_NEW_BEGIN(XmlDocument) OBJ_NEW_BEGIN(XmlDocument, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1)
{ {
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();
@ -368,7 +368,7 @@ xmlDocumentNewBuf(const Buffer *buffer)
// Create object // Create object
XmlDocument *this = NULL; XmlDocument *this = NULL;
OBJ_NEW_BEGIN(XmlDocument) OBJ_NEW_BEGIN(XmlDocument, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1)
{ {
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();
*this = (XmlDocument){{0}}; // Extra braces are required for older gcc versions *this = (XmlDocument){{0}}; // Extra braces are required for older gcc versions

View File

@ -34,7 +34,7 @@ userInitInternal(void)
MEM_CONTEXT_BEGIN(memContextTop()) MEM_CONTEXT_BEGIN(memContextTop())
{ {
MEM_CONTEXT_NEW_BEGIN("UserLocalData") MEM_CONTEXT_NEW_BEGIN(UserLocalData, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
userLocalData.memContext = MEM_CONTEXT_NEW(); userLocalData.memContext = MEM_CONTEXT_NEW();

View File

@ -32,7 +32,7 @@ waitNew(TimeMSec waitTime)
// Allocate wait object // Allocate wait object
Wait *this = NULL; Wait *this = NULL;
OBJ_NEW_BEGIN(Wait) OBJ_NEW_BEGIN(Wait, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
// Create object // Create object
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();

View File

@ -1380,7 +1380,7 @@ configParse(const Storage *storage, unsigned int argListSize, const char *argLis
// Create the config struct // Create the config struct
Config *config; Config *config;
OBJ_NEW_BEGIN(Config) OBJ_NEW_BEGIN(Config, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX)
{ {
config = OBJ_NEW_ALLOC(); config = OBJ_NEW_ALLOC();

View File

@ -76,7 +76,7 @@ dbNew(PgClient *client, ProtocolClient *remoteClient, const Storage *const stora
Db *this = NULL; Db *this = NULL;
OBJ_NEW_BEGIN(Db) OBJ_NEW_BEGIN(Db, .childQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1)
{ {
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();

View File

@ -121,7 +121,7 @@ infoNew(const String *cipherPass)
Info *this = NULL; Info *this = NULL;
OBJ_NEW_BEGIN(Info) OBJ_NEW_BEGIN(Info, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
this = infoNewInternal(); this = infoNewInternal();
@ -256,7 +256,7 @@ infoNewLoad(IoRead *read, InfoLoadNewCallback *callbackFunction, void *callbackD
Info *this = NULL; Info *this = NULL;
OBJ_NEW_BEGIN(Info) OBJ_NEW_BEGIN(Info, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
this = infoNewInternal(); this = infoNewInternal();

View File

@ -69,7 +69,7 @@ infoArchiveNew(unsigned int pgVersion, uint64_t pgSystemId, const String *cipher
InfoArchive *this = NULL; InfoArchive *this = NULL;
OBJ_NEW_BEGIN(InfoArchive) OBJ_NEW_BEGIN(InfoArchive, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
this = infoArchiveNewInternal(); this = infoArchiveNewInternal();
@ -94,7 +94,7 @@ infoArchiveNewLoad(IoRead *read)
InfoArchive *this = NULL; InfoArchive *this = NULL;
OBJ_NEW_BEGIN(InfoArchive) OBJ_NEW_BEGIN(InfoArchive, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
this = infoArchiveNewInternal(); this = infoArchiveNewInternal();
this->pub.infoPg = infoPgNewLoad(read, infoPgArchive, NULL, NULL); this->pub.infoPg = infoPgNewLoad(read, infoPgArchive, NULL, NULL);

View File

@ -76,7 +76,7 @@ infoBackupNew(unsigned int pgVersion, uint64_t pgSystemId, unsigned int pgCatalo
InfoBackup *this = NULL; InfoBackup *this = NULL;
OBJ_NEW_BEGIN(InfoBackup) OBJ_NEW_BEGIN(InfoBackup, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
this = infoBackupNewInternal(); this = infoBackupNewInternal();
@ -225,7 +225,7 @@ infoBackupNewLoad(IoRead *read)
InfoBackup *this = NULL; InfoBackup *this = NULL;
OBJ_NEW_BEGIN(InfoBackup) OBJ_NEW_BEGIN(InfoBackup, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
this = infoBackupNewInternal(); this = infoBackupNewInternal();
this->pub.infoPg = infoPgNewLoad(read, infoPgBackup, infoBackupLoadCallback, this); this->pub.infoPg = infoPgNewLoad(read, infoPgBackup, infoBackupLoadCallback, this);

View File

@ -69,7 +69,7 @@ infoPgNew(InfoPgType type, const String *cipherPassSub)
InfoPg *this = NULL; InfoPg *this = NULL;
OBJ_NEW_BEGIN(InfoPg) OBJ_NEW_BEGIN(InfoPg, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
this = infoPgNewInternal(type); this = infoPgNewInternal(type);
this->pub.info = infoNew(cipherPassSub); this->pub.info = infoNew(cipherPassSub);
@ -164,7 +164,7 @@ infoPgNewLoad(IoRead *read, InfoPgType type, InfoLoadNewCallback *callbackFuncti
InfoPg *this = NULL; InfoPg *this = NULL;
OBJ_NEW_BEGIN(InfoPg) OBJ_NEW_BEGIN(InfoPg, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
this = infoPgNewInternal(type); this = infoPgNewInternal(type);

View File

@ -1112,7 +1112,7 @@ manifestNewBuild(
Manifest *this = NULL; Manifest *this = NULL;
OBJ_NEW_BEGIN(Manifest) OBJ_NEW_BEGIN(Manifest, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
this = manifestNewInternal(); this = manifestNewInternal();
this->pub.info = infoNew(NULL); this->pub.info = infoNew(NULL);
@ -2050,14 +2050,14 @@ manifestNewLoad(IoRead *read)
Manifest *this = NULL; Manifest *this = NULL;
OBJ_NEW_BEGIN(Manifest) OBJ_NEW_BEGIN(Manifest, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
this = manifestNewInternal(); this = manifestNewInternal();
// Load the manifest // Load the manifest
ManifestLoadData loadData = ManifestLoadData loadData =
{ {
.memContext = memContextNewP("load"), .memContext = memContextNewP("load", .childQty = MEM_CONTEXT_QTY_MAX),
.manifest = this, .manifest = this,
}; };

View File

@ -55,7 +55,7 @@ pgClientNew(const String *host, const unsigned int port, const String *database,
PgClient *this = NULL; PgClient *this = NULL;
OBJ_NEW_BEGIN(PgClient) OBJ_NEW_BEGIN(PgClient, .childQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1)
{ {
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();

View File

@ -90,7 +90,7 @@ protocolClientNew(const String *name, const String *service, IoRead *read, IoWri
ProtocolClient *this = NULL; ProtocolClient *this = NULL;
OBJ_NEW_BEGIN(ProtocolClient) OBJ_NEW_BEGIN(ProtocolClient, .childQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1)
{ {
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();

View File

@ -30,7 +30,7 @@ protocolCommandNew(const StringId command)
ProtocolCommand *this = NULL; ProtocolCommand *this = NULL;
OBJ_NEW_BEGIN(ProtocolCommand) OBJ_NEW_BEGIN(ProtocolCommand, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();

View File

@ -63,7 +63,7 @@ protocolHelperInit(void)
// Create a mem context to store protocol objects // Create a mem context to store protocol objects
MEM_CONTEXT_BEGIN(memContextTop()) MEM_CONTEXT_BEGIN(memContextTop())
{ {
MEM_CONTEXT_NEW_BEGIN("ProtocolHelper") MEM_CONTEXT_NEW_BEGIN("ProtocolHelper", .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX)
{ {
protocolHelper.memContext = MEM_CONTEXT_NEW(); protocolHelper.memContext = MEM_CONTEXT_NEW();
} }

View File

@ -47,7 +47,7 @@ protocolParallelNew(TimeMSec timeout, ParallelJobCallback *callbackFunction, voi
ProtocolParallel *this = NULL; ProtocolParallel *this = NULL;
OBJ_NEW_BEGIN(ProtocolParallel) OBJ_NEW_BEGIN(ProtocolParallel, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX)
{ {
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();

View File

@ -27,7 +27,7 @@ protocolParallelJobNew(const Variant *key, ProtocolCommand *command)
ProtocolParallelJob *this = NULL; ProtocolParallelJob *this = NULL;
OBJ_NEW_BEGIN(ProtocolParallelJob) OBJ_NEW_BEGIN(ProtocolParallelJob, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();

View File

@ -42,7 +42,7 @@ protocolServerNew(const String *name, const String *service, IoRead *read, IoWri
ProtocolServer *this = NULL; ProtocolServer *this = NULL;
OBJ_NEW_BEGIN(ProtocolServer) OBJ_NEW_BEGIN(ProtocolServer, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();

View File

@ -125,7 +125,7 @@ storageReadAzureNew(
StorageRead *this = NULL; StorageRead *this = NULL;
OBJ_NEW_BEGIN(StorageReadAzure) OBJ_NEW_BEGIN(StorageReadAzure, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX)
{ {
StorageReadAzure *driver = OBJ_NEW_ALLOC(); StorageReadAzure *driver = OBJ_NEW_ALLOC();

View File

@ -719,7 +719,7 @@ storageAzureNew(
Storage *this = NULL; Storage *this = NULL;
OBJ_NEW_BEGIN(StorageAzure) OBJ_NEW_BEGIN(StorageAzure, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX)
{ {
StorageAzure *driver = OBJ_NEW_ALLOC(); StorageAzure *driver = OBJ_NEW_ALLOC();

View File

@ -274,7 +274,7 @@ storageWriteAzureNew(StorageAzure *storage, const String *name, uint64_t fileId,
StorageWrite *this = NULL; StorageWrite *this = NULL;
OBJ_NEW_BEGIN(StorageWriteAzure) OBJ_NEW_BEGIN(StorageWriteAzure, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX)
{ {
StorageWriteAzure *driver = OBJ_NEW_ALLOC(); StorageWriteAzure *driver = OBJ_NEW_ALLOC();

View File

@ -132,7 +132,7 @@ storageReadGcsNew(
StorageRead *this = NULL; StorageRead *this = NULL;
OBJ_NEW_BEGIN(StorageReadGcs) OBJ_NEW_BEGIN(StorageReadGcs, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX)
{ {
StorageReadGcs *driver = OBJ_NEW_ALLOC(); StorageReadGcs *driver = OBJ_NEW_ALLOC();

View File

@ -954,7 +954,7 @@ storageGcsNew(
Storage *this = NULL; Storage *this = NULL;
OBJ_NEW_BEGIN(StorageGcs) OBJ_NEW_BEGIN(StorageGcs, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX)
{ {
StorageGcs *driver = OBJ_NEW_ALLOC(); StorageGcs *driver = OBJ_NEW_ALLOC();

View File

@ -333,7 +333,7 @@ storageWriteGcsNew(StorageGcs *storage, const String *name, size_t chunkSize)
StorageWrite *this = NULL; StorageWrite *this = NULL;
OBJ_NEW_BEGIN(StorageWriteGcs) OBJ_NEW_BEGIN(StorageWriteGcs, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX)
{ {
StorageWriteGcs *driver = OBJ_NEW_ALLOC(); StorageWriteGcs *driver = OBJ_NEW_ALLOC();

View File

@ -71,7 +71,7 @@ storageHelperContextInit(void)
{ {
MEM_CONTEXT_BEGIN(memContextTop()) MEM_CONTEXT_BEGIN(memContextTop())
{ {
MEM_CONTEXT_NEW_BEGIN("StorageHelper") MEM_CONTEXT_NEW_BEGIN(StorageHelper, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX)
{ {
storageHelper.memContext = MEM_CONTEXT_NEW(); storageHelper.memContext = MEM_CONTEXT_NEW();
} }

View File

@ -223,7 +223,7 @@ storageReadPosixNew(
StorageRead *this = NULL; StorageRead *this = NULL;
OBJ_NEW_BEGIN(StorageReadPosix) OBJ_NEW_BEGIN(StorageReadPosix, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1)
{ {
StorageReadPosix *driver = OBJ_NEW_ALLOC(); StorageReadPosix *driver = OBJ_NEW_ALLOC();

View File

@ -592,7 +592,7 @@ storagePosixNewInternal(
// Create the object // Create the object
Storage *this = NULL; Storage *this = NULL;
OBJ_NEW_BEGIN(StoragePosix) OBJ_NEW_BEGIN(StoragePosix, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX)
{ {
StoragePosix *driver = OBJ_NEW_ALLOC(); StoragePosix *driver = OBJ_NEW_ALLOC();

View File

@ -245,7 +245,7 @@ storageWritePosixNew(
StorageWrite *this = NULL; StorageWrite *this = NULL;
OBJ_NEW_BEGIN(StorageWritePosix) OBJ_NEW_BEGIN(StorageWritePosix, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1)
{ {
StorageWritePosix *driver = OBJ_NEW_ALLOC(); StorageWritePosix *driver = OBJ_NEW_ALLOC();

View File

@ -114,7 +114,7 @@ storageRemoteFeatureProtocol(PackRead *const param, ProtocolServer *const server
{ {
MEM_CONTEXT_PRIOR_BEGIN() MEM_CONTEXT_PRIOR_BEGIN()
{ {
MEM_CONTEXT_NEW_BEGIN("StorageRemoteProtocol") MEM_CONTEXT_NEW_BEGIN(StorageRemoteProtocol, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX)
{ {
storageRemoteProtocolLocal.memContext = memContextCurrent(); storageRemoteProtocolLocal.memContext = memContextCurrent();
storageRemoteProtocolLocal.driver = storageDriver(storage); storageRemoteProtocolLocal.driver = storageDriver(storage);

View File

@ -232,7 +232,7 @@ storageReadRemoteNew(
StorageReadRemote *this = NULL; StorageReadRemote *this = NULL;
OBJ_NEW_BEGIN(StorageReadRemote) OBJ_NEW_BEGIN(StorageReadRemote, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX)
{ {
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();

View File

@ -451,7 +451,7 @@ storageRemoteNew(
Storage *this = NULL; Storage *this = NULL;
OBJ_NEW_BEGIN(StorageRemote) OBJ_NEW_BEGIN(StorageRemote, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX)
{ {
StorageRemote *driver = OBJ_NEW_ALLOC(); StorageRemote *driver = OBJ_NEW_ALLOC();

View File

@ -211,7 +211,7 @@ storageWriteRemoteNew(
StorageWriteRemote *this = NULL; StorageWriteRemote *this = NULL;
OBJ_NEW_BEGIN(StorageWriteRemote) OBJ_NEW_BEGIN(StorageWriteRemote, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1)
{ {
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();

View File

@ -128,7 +128,7 @@ storageReadS3New(
StorageRead *this = NULL; StorageRead *this = NULL;
OBJ_NEW_BEGIN(StorageReadS3) OBJ_NEW_BEGIN(StorageReadS3, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX)
{ {
StorageReadS3 *driver = OBJ_NEW_ALLOC(); StorageReadS3 *driver = OBJ_NEW_ALLOC();

View File

@ -1111,7 +1111,7 @@ storageS3New(
Storage *this = NULL; Storage *this = NULL;
OBJ_NEW_BEGIN(StorageS3) OBJ_NEW_BEGIN(StorageS3, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX)
{ {
StorageS3 *driver = OBJ_NEW_ALLOC(); StorageS3 *driver = OBJ_NEW_ALLOC();

View File

@ -278,7 +278,7 @@ storageWriteS3New(StorageS3 *storage, const String *name, size_t partSize)
StorageWrite *this = NULL; StorageWrite *this = NULL;
OBJ_NEW_BEGIN(StorageWriteS3) OBJ_NEW_BEGIN(StorageWriteS3, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX)
{ {
StorageWriteS3 *driver = OBJ_NEW_ALLOC(); StorageWriteS3 *driver = OBJ_NEW_ALLOC();

View File

@ -253,7 +253,7 @@ hrnLogReplaceAdd(const char *expression, const char *expressionSub, const char *
{ {
MEM_CONTEXT_BEGIN(memContextTop()) MEM_CONTEXT_BEGIN(memContextTop())
{ {
MEM_CONTEXT_NEW_BEGIN("HarnessLog") MEM_CONTEXT_NEW_BEGIN(HarnessLog, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX)
{ {
harnessLog.memContext = MEM_CONTEXT_NEW(); harnessLog.memContext = MEM_CONTEXT_NEW();
} }

View File

@ -1757,7 +1757,7 @@ testRun(void)
Manifest *manifest = NULL; Manifest *manifest = NULL;
OBJ_NEW_BEGIN(Manifest) OBJ_NEW_BEGIN(Manifest, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
manifest = manifestNewInternal(); manifest = manifestNewInternal();
manifest->pub.data.backupType = backupTypeFull; manifest->pub.data.backupType = backupTypeFull;
@ -1778,7 +1778,7 @@ testRun(void)
Manifest *manifestResume = NULL; Manifest *manifestResume = NULL;
OBJ_NEW_BEGIN(Manifest) OBJ_NEW_BEGIN(Manifest, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
manifestResume = manifestNewInternal(); manifestResume = manifestNewInternal();
manifestResume->pub.info = infoNew(NULL); manifestResume->pub.info = infoNew(NULL);
@ -1912,7 +1912,7 @@ testRun(void)
// Create manifest with file // Create manifest with file
Manifest *manifest = NULL; Manifest *manifest = NULL;
OBJ_NEW_BEGIN(Manifest) OBJ_NEW_BEGIN(Manifest, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
manifest = manifestNewInternal(); manifest = manifestNewInternal();
manifestFileAdd(manifest, &(ManifestFile){.name = STRDEF("pg_data/test")}); manifestFileAdd(manifest, &(ManifestFile){.name = STRDEF("pg_data/test")});

View File

@ -138,7 +138,7 @@ testManifestMinimal(const String *label, unsigned int pgVersion, const String *p
Manifest *result = NULL; Manifest *result = NULL;
OBJ_NEW_BEGIN(Manifest) OBJ_NEW_BEGIN(Manifest, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
result = manifestNewInternal(); result = manifestNewInternal();
result->pub.info = infoNew(NULL); result->pub.info = infoNew(NULL);
@ -1294,7 +1294,7 @@ testRun(void)
Manifest *manifest = NULL; Manifest *manifest = NULL;
OBJ_NEW_BEGIN(Manifest) OBJ_NEW_BEGIN(Manifest, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
manifest = manifestNewInternal(); manifest = manifestNewInternal();
manifest->pub.data.pgVersion = PG_VERSION_90; manifest->pub.data.pgVersion = PG_VERSION_90;
@ -2026,7 +2026,7 @@ testRun(void)
Manifest *manifest = NULL; Manifest *manifest = NULL;
OBJ_NEW_BEGIN(Manifest) OBJ_NEW_BEGIN(Manifest, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
manifest = manifestNewInternal(); manifest = manifestNewInternal();
manifest->pub.info = infoNew(NULL); manifest->pub.info = infoNew(NULL);
@ -2431,7 +2431,7 @@ testRun(void)
#define TEST_PGDATA MANIFEST_TARGET_PGDATA "/" #define TEST_PGDATA MANIFEST_TARGET_PGDATA "/"
#define TEST_REPO_PATH STORAGE_REPO_BACKUP "/" TEST_LABEL "/" TEST_PGDATA #define TEST_REPO_PATH STORAGE_REPO_BACKUP "/" TEST_LABEL "/" TEST_PGDATA
OBJ_NEW_BEGIN(Manifest) OBJ_NEW_BEGIN(Manifest, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX)
{ {
manifest = manifestNewInternal(); manifest = manifestNewInternal();
manifest->pub.info = infoNew(NULL); manifest->pub.info = infoNew(NULL);

View File

@ -118,7 +118,7 @@ ioTestFilterSizeNew(const StringId type)
{ {
IoFilter *this = NULL; IoFilter *this = NULL;
OBJ_NEW_BEGIN(IoTestFilterSize) OBJ_NEW_BEGIN(IoTestFilterSize, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX)
{ {
IoTestFilterSize *driver = OBJ_NEW_ALLOC(); IoTestFilterSize *driver = OBJ_NEW_ALLOC();
*driver = (IoTestFilterSize){0}; *driver = (IoTestFilterSize){0};
@ -223,7 +223,7 @@ ioTestFilterMultiplyNew(const StringId type, unsigned int multiplier, unsigned i
{ {
IoFilter *this = NULL; IoFilter *this = NULL;
OBJ_NEW_BEGIN(IoTestFilterMultiply) OBJ_NEW_BEGIN(IoTestFilterMultiply, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX)
{ {
IoTestFilterMultiply *driver = OBJ_NEW_ALLOC(); IoTestFilterMultiply *driver = OBJ_NEW_ALLOC();

View File

@ -13,7 +13,8 @@ testFree(void *thisVoid)
{ {
MemContext *this = thisVoid; MemContext *this = thisVoid;
TEST_RESULT_BOOL(this->active, false, "mem context should be inactive while freeing"); TEST_RESULT_BOOL(this->active, false, "mem context inactive in callback");
TEST_ERROR(memContextFree(this), AssertError, "assertion 'this->active' failed"); TEST_ERROR(memContextFree(this), AssertError, "assertion 'this->active' failed");
TEST_ERROR(memContextCallbackSet(this, testFree, this), AssertError, "assertion 'this->active' failed"); TEST_ERROR(memContextCallbackSet(this, testFree, this), AssertError, "assertion 'this->active' failed");
TEST_ERROR(memContextSwitch(this), AssertError, "assertion 'this->active' failed"); TEST_ERROR(memContextSwitch(this), AssertError, "assertion 'this->active' failed");
@ -72,13 +73,15 @@ testRun(void)
{ {
TEST_TITLE("struct size"); TEST_TITLE("struct size");
TEST_RESULT_UINT(sizeof(MemContext), TEST_64BIT() ? 72 : 48, "MemContext size"); TEST_RESULT_UINT(sizeof(MemContext), TEST_64BIT() ? 24 : 16, "MemContext size");
TEST_RESULT_UINT(sizeof(MemContextChildMany), TEST_64BIT() ? 16 : 12, "MemContextChildMany size");
TEST_RESULT_UINT(sizeof(MemContextAllocMany), TEST_64BIT() ? 16 : 12, "MemContextAllocMany size");
TEST_RESULT_UINT(sizeof(MemContextCallbackOne), TEST_64BIT() ? 16 : 8, "MemContextCallbackOne size");
// ------------------------------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------------------------------
// Make sure top context was created // Make sure top context was created
TEST_RESULT_Z(memContextTop()->name, "TOP", "top context should exist"); TEST_RESULT_Z(memContextTop()->name, "TOP", "top context should exist");
TEST_RESULT_INT(memContextTop()->contextChildListSize, 0, "top context should init with zero children"); TEST_RESULT_BOOL(memContextTop()->childInitialized, false, "top context should init with zero children");
TEST_RESULT_PTR(memContextTop()->contextChildList, NULL, "top context child list empty");
// Current context should equal top context // Current context should equal top context
TEST_RESULT_PTR(memContextCurrent(), memContextTop(), "top context == current context"); TEST_RESULT_PTR(memContextCurrent(), memContextTop(), "top context == current context");
@ -86,11 +89,13 @@ testRun(void)
// Context name length errors // Context name length errors
TEST_ERROR(memContextNewP(""), AssertError, "assertion 'name[0] != '\\0'' failed"); TEST_ERROR(memContextNewP(""), AssertError, "assertion 'name[0] != '\\0'' failed");
MemContext *memContext = memContextNewP("test1"); MemContext *memContext = memContextNewP(
"test1", .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1);
memContextKeep(); memContextKeep();
TEST_RESULT_Z(memContext->name, "test1", "test1 context name"); TEST_RESULT_Z(memContext->name, "test1", "test1 context name");
TEST_RESULT_PTR(memContext->contextParent, memContextTop(), "test1 context parent is top"); TEST_RESULT_PTR(memContext->contextParent, memContextTop(), "test1 context parent is top");
TEST_RESULT_INT(memContextTop()->contextChildListSize, MEM_CONTEXT_INITIAL_SIZE, "initial top context child list size"); TEST_RESULT_INT(
memContextChildMany(memContextTop())->listSize, MEM_CONTEXT_INITIAL_SIZE, "initial top context child list size");
memContextSwitch(memContext); memContextSwitch(memContext);
@ -99,62 +104,92 @@ testRun(void)
TEST_RESULT_PTR(memContext, memContextCurrent(), "current context is now test1"); TEST_RESULT_PTR(memContext, memContextCurrent(), "current context is now test1");
// Create enough mem contexts to use up the initially allocated block // Create enough mem contexts to use up the initially allocated block
memContextSwitch(memContextTop());
for (int contextIdx = 1; contextIdx < MEM_CONTEXT_INITIAL_SIZE; contextIdx++) for (int contextIdx = 1; contextIdx < MEM_CONTEXT_INITIAL_SIZE; contextIdx++)
{ {
memContextSwitch(memContextTop()); memContextNewP("test-filler", .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1);
memContextNewP("test-filler");
memContextKeep(); memContextKeep();
TEST_RESULT_BOOL(memContextTop()->contextChildList[contextIdx]->active, true, "new context is active"); TEST_RESULT_PTR_NE(memContextChildMany(memContextTop())->list[contextIdx], NULL, "new context exists");
TEST_RESULT_Z(memContextTop()->contextChildList[contextIdx]->name, "test-filler", "new context name"); TEST_RESULT_BOOL(memContextChildMany(memContextTop())->list[contextIdx]->active, true, "new context is active");
TEST_RESULT_Z(memContextChildMany(memContextTop())->list[contextIdx]->name, "test-filler", "new context name");
} }
// This forces the child context array to grow // This forces the child context array to grow
memContext = memContextNewP("test5", .allocExtra = 16); memContext = memContextNewP(
"test5", .allocExtra = 16, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1);
TEST_RESULT_PTR(memContextAllocExtra(memContext), memContext + 1, "mem context alloc extra"); TEST_RESULT_PTR(memContextAllocExtra(memContext), memContext + 1, "mem context alloc extra");
TEST_RESULT_PTR(memContextFromAllocExtra(memContext + 1), memContext, "mem context from alloc extra"); TEST_RESULT_PTR(memContextFromAllocExtra(memContext + 1), memContext, "mem context from alloc extra");
TEST_RESULT_PTR(memContextConstFromAllocExtra(memContext + 1), memContext, "const mem context from alloc extra"); TEST_RESULT_PTR(memContextConstFromAllocExtra(memContext + 1), memContext, "const mem context from alloc extra");
memContextKeep(); memContextKeep();
TEST_RESULT_INT(memContextTop()->contextChildListSize, MEM_CONTEXT_INITIAL_SIZE * 2, "increased child context list size"); TEST_RESULT_INT(
TEST_RESULT_UINT(memContextTop()->contextChildFreeIdx, MEM_CONTEXT_INITIAL_SIZE + 1, "check context free idx"); memContextChildMany(memContextTop())->listSize, MEM_CONTEXT_INITIAL_SIZE * 2, "increased child context list size");
TEST_RESULT_UINT(memContextChildMany(memContextTop())->freeIdx, MEM_CONTEXT_INITIAL_SIZE + 1, "check context free idx");
// Free a context // Free a context
memContextFree(memContextTop()->contextChildList[1]); memContextFree(memContextChildMany(memContextTop())->list[1]);
TEST_RESULT_PTR(memContextTop()->contextChildList[1], NULL, "child context NULL after free"); TEST_RESULT_PTR(memContextChildMany(memContextTop())->list[1], NULL, "child context NULL after free");
TEST_RESULT_PTR(memContextCurrent(), memContextTop(), "current context is top"); TEST_RESULT_PTR(memContextCurrent(), memContextTop(), "current context is top");
TEST_RESULT_UINT(memContextTop()->contextChildFreeIdx, 1, "check context free idx"); TEST_RESULT_UINT(memContextChildMany(memContextTop())->freeIdx, 1, "check context free idx");
// Create a new context and it should end up in the same spot // Create a new context and it should end up in the same spot
memContextNewP("test-reuse"); MemContext *const memContextOneChild = memContextNewP("test-reuse", .childQty = 1);
memContextKeep(); memContextKeep();
TEST_RESULT_BOOL( TEST_RESULT_BOOL(
memContextTop()->contextChildList[1]->active, true, "new context in same index as freed context is active"); memContextChildMany(memContextTop())->list[1]->active, true, "new context in same index as freed context is active");
TEST_RESULT_Z(memContextTop()->contextChildList[1]->name, "test-reuse", "new context name"); TEST_RESULT_Z(memContextChildMany(memContextTop())->list[1]->name, "test-reuse", "new context name");
TEST_RESULT_UINT(memContextTop()->contextChildFreeIdx, 2, "check context free idx"); TEST_RESULT_UINT(memContextChildMany(memContextTop())->freeIdx, 2, "check context free idx");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("child context in context with only one child allowed");
TEST_RESULT_VOID(memContextSwitch(memContextOneChild), "switch");
MemContext *memContextSingleChild;
TEST_ASSIGN(memContextSingleChild, memContextNewP("single-child", .childQty = 1, .allocQty = 1), "new");
TEST_RESULT_VOID(memContextKeep(), "keep");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("child context freed in context with only one child allowed");
TEST_RESULT_VOID(memContextSwitch(memContextSingleChild), "switch");
TEST_RESULT_VOID(memContextNewP("free-child", .childQty = 1), "new");
TEST_RESULT_VOID(memContextDiscard(), "discard");
TEST_RESULT_VOID(memContextSwitch(memContextTop()), "switch to top");
// -------------------------------------------------------------------------------------------------------------------------
// Next context will be at the end // Next context will be at the end
memContextNewP("test-at-end"); memContextNewP("test-at-end");
memContextKeep(); memContextKeep();
TEST_RESULT_UINT(memContextTop()->contextChildFreeIdx, MEM_CONTEXT_INITIAL_SIZE + 2, "check context free idx"); TEST_RESULT_UINT(memContextChildMany(memContextTop())->freeIdx, MEM_CONTEXT_INITIAL_SIZE + 2, "check context free idx");
// Create a child context to test recursive free // Create a child context to test recursive free
memContextSwitch(memContextTop()->contextChildList[MEM_CONTEXT_INITIAL_SIZE]); memContextSwitch(memContextChildMany(memContextTop())->list[MEM_CONTEXT_INITIAL_SIZE]);
memContextNewP("test-reuse"); memContextNewP("test-reuse", .childQty = 1);
memContextKeep(); memContextKeep();
TEST_RESULT_PTR_NE( TEST_RESULT_PTR_NE(
memContextTop()->contextChildList[MEM_CONTEXT_INITIAL_SIZE]->contextChildList, NULL, "context child list is allocated"); memContextChildMany(memContextChildMany(memContextTop())->list[MEM_CONTEXT_INITIAL_SIZE])->list, NULL,
"context child list is allocated");
TEST_RESULT_INT( TEST_RESULT_INT(
memContextTop()->contextChildList[MEM_CONTEXT_INITIAL_SIZE]->contextChildListSize, MEM_CONTEXT_INITIAL_SIZE, memContextChildMany(memContextChildMany(memContextTop())->list[MEM_CONTEXT_INITIAL_SIZE])->listSize,
"context child list initial size"); MEM_CONTEXT_INITIAL_SIZE, "context child list initial size");
// This test will change if the contexts above change // This test will change if the contexts above change
TEST_RESULT_UINT(memContextSize(memContextTop()), TEST_64BIT() ? 688 : 448, "check size"); TEST_RESULT_UINT(memContextSize(memContextTop()), TEST_64BIT() ? 584 : 376, "check size");
TEST_ERROR( TEST_ERROR(
memContextFree(memContextTop()->contextChildList[MEM_CONTEXT_INITIAL_SIZE]), memContextFree(memContextChildMany(memContextTop())->list[MEM_CONTEXT_INITIAL_SIZE]), AssertError,
AssertError, "cannot free current context 'test5'"); "cannot free current context 'test5'");
TEST_RESULT_VOID(memContextSwitch(memContextTop()), "switch to top"); TEST_RESULT_VOID(memContextSwitch(memContextTop()), "switch to top");
TEST_RESULT_VOID(memContextFree(memContextTop()), "free top"); TEST_RESULT_VOID(memContextFree(memContextTop()), "free top");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("alloc extra not aligned");
TEST_ASSIGN(memContext, memContextNewP("test-alloc", .allocExtra = 7), "no aligned");
TEST_RESULT_UINT(memContext->allocExtra, 8, "check");
TEST_RESULT_VOID(memContextDiscard(), "discard");
} }
// ***************************************************************************************************************************** // *****************************************************************************************************************************
@ -172,7 +207,7 @@ testRun(void)
memContextSwitch(memContextTop()); memContextSwitch(memContextTop());
memNewPtrArray(1); memNewPtrArray(1);
MemContext *memContext = memContextNewP("test-alloc"); MemContext *memContext = memContextNewP("test-alloc", .allocQty = MEM_CONTEXT_QTY_MAX);
TEST_ERROR(memContextSwitchBack(), AssertError, "current context expected but new context 'test-alloc' found"); TEST_ERROR(memContextSwitchBack(), AssertError, "current context expected but new context 'test-alloc' found");
memContextKeep(); memContextKeep();
memContextSwitch(memContext); memContextSwitch(memContext);
@ -182,7 +217,7 @@ testRun(void)
memNew(sizeof(size_t)); memNew(sizeof(size_t));
TEST_RESULT_INT( TEST_RESULT_INT(
memContextCurrent()->allocListSize, memContextAllocMany(memContextCurrent())->listSize,
allocIdx == MEM_CONTEXT_ALLOC_INITIAL_SIZE ? MEM_CONTEXT_ALLOC_INITIAL_SIZE * 2 : MEM_CONTEXT_ALLOC_INITIAL_SIZE, allocIdx == MEM_CONTEXT_ALLOC_INITIAL_SIZE ? MEM_CONTEXT_ALLOC_INITIAL_SIZE * 2 : MEM_CONTEXT_ALLOC_INITIAL_SIZE,
"allocation list size"); "allocation list size");
} }
@ -202,46 +237,83 @@ testRun(void)
TEST_RESULT_INT(expectedTotal, sizeof(size_t), "all bytes are 0xFE in original portion"); TEST_RESULT_INT(expectedTotal, sizeof(size_t), "all bytes are 0xFE in original portion");
// Free memory // Free memory
TEST_RESULT_UINT(memContextCurrent()->allocFreeIdx, MEM_CONTEXT_ALLOC_INITIAL_SIZE + 2, "check alloc free idx"); TEST_RESULT_UINT(
TEST_RESULT_VOID(memFree(MEM_CONTEXT_ALLOC_BUFFER(memContextCurrent()->allocList[0])), "free allocation"); memContextAllocMany(memContextCurrent())->freeIdx, MEM_CONTEXT_ALLOC_INITIAL_SIZE + 2, "check alloc free idx");
TEST_RESULT_UINT(memContextCurrent()->allocFreeIdx, 0, "check alloc free idx"); TEST_RESULT_VOID(memFree(MEM_CONTEXT_ALLOC_BUFFER(memContextAllocMany(memContextCurrent())->list[0])), "free allocation");
TEST_RESULT_UINT(memContextAllocMany(memContextCurrent())->freeIdx, 0, "check alloc free idx");
TEST_RESULT_VOID(memFree(MEM_CONTEXT_ALLOC_BUFFER(memContextCurrent()->allocList[1])), "free allocation"); TEST_RESULT_VOID(memFree(MEM_CONTEXT_ALLOC_BUFFER(memContextAllocMany(memContextCurrent())->list[1])), "free allocation");
TEST_RESULT_UINT(memContextCurrent()->allocFreeIdx, 0, "check alloc free idx"); TEST_RESULT_UINT(memContextAllocMany(memContextCurrent())->freeIdx, 0, "check alloc free idx");
TEST_RESULT_VOID(memNew(3), "new allocation"); TEST_RESULT_VOID(memNew(3), "new allocation");
TEST_RESULT_UINT(memContextCurrent()->allocFreeIdx, 1, "check alloc free idx"); TEST_RESULT_UINT(memContextAllocMany(memContextCurrent())->freeIdx, 1, "check alloc free idx");
TEST_RESULT_VOID(memNew(3), "new allocation"); TEST_RESULT_VOID(memNew(3), "new allocation");
TEST_RESULT_UINT(memContextCurrent()->allocFreeIdx, 2, "check alloc free idx"); TEST_RESULT_UINT(memContextAllocMany(memContextCurrent())->freeIdx, 2, "check alloc free idx");
TEST_RESULT_VOID(memNew(3), "new allocation"); TEST_RESULT_VOID(memNew(3), "new allocation");
TEST_RESULT_UINT(memContextCurrent()->allocFreeIdx, MEM_CONTEXT_ALLOC_INITIAL_SIZE + 3, "check alloc free idx"); TEST_RESULT_UINT(
memContextAllocMany(memContextCurrent())->freeIdx, MEM_CONTEXT_ALLOC_INITIAL_SIZE + 3, "check alloc free idx");
// This test will change if the allocations above change // This test will change if the allocations above change
TEST_RESULT_UINT(memContextSize(memContextCurrent()), TEST_64BIT() ? 241 : 165, "check size"); TEST_RESULT_UINT(memContextSize(memContextCurrent()), TEST_64BIT() ? 209 : 145, "check size");
TEST_ERROR( TEST_ERROR(
memFree(NULL), AssertError, memFree(NULL), AssertError,
"assertion '((MemContextAlloc *)buffer - 1) != NULL" "assertion 'alloc != NULL && "
" && (uintptr_t)((MemContextAlloc *)buffer - 1) != (uintptr_t)-sizeof(MemContextAlloc)" "(uintptr_t)alloc != (uintptr_t)-sizeof(MemContextAlloc) && "
" && ((MemContextAlloc *)buffer - 1)->allocIdx <" "alloc->allocIdx < memContextAllocMany(memContextStack[memContextCurrentStackIdx].memContext)->listSize && "
" memContextStack[memContextCurrentStackIdx].memContext->allocListSize" "memContextAllocMany(memContextStack[memContextCurrentStackIdx].memContext)->list[alloc->allocIdx]' failed");
" && memContextStack[memContextCurrentStackIdx].memContext->allocList[((MemContextAlloc *)buffer - 1)->allocIdx]'"
" failed");
memFree(buffer); memFree(buffer);
memContextSwitch(memContextTop()); memContextSwitch(memContextTop());
memContextFree(memContext); memContextFree(memContext);
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("mem context with one allocation");
TEST_ASSIGN(memContext, memContextNewP("test-alloc", .allocQty = 1), "new");
TEST_RESULT_VOID(memContextKeep(), "keep new");
TEST_RESULT_VOID(memContextSwitch(memContext), "switch to new");
TEST_ASSIGN(buffer, memNew(100), "new");
TEST_ASSIGN(buffer, memResize(buffer, 150), "resize");
TEST_RESULT_VOID(memFree(buffer), "free");
TEST_ASSIGN(buffer, memNew(200), "new");
// This test will change if the allocations above change
TEST_RESULT_UINT(memContextSize(memContextCurrent()), TEST_64BIT() ? 240 : 228, "check size");
TEST_RESULT_VOID(memContextSwitch(memContextTop()), "switch to top");
TEST_RESULT_VOID(memContextFree(memContext), "context free");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("mem context with one allocation freed before context free");
TEST_ASSIGN(memContext, memContextNewP("test-alloc", .allocQty = 1), "new");
TEST_RESULT_VOID(memContextKeep(), "keep new");
TEST_RESULT_VOID(memContextSwitch(memContext), "switch to new");
TEST_ASSIGN(buffer, memNew(100), "new");
TEST_RESULT_VOID(memFree(buffer), "free");
// This test will change if the allocations above change
TEST_RESULT_UINT(memContextSize(memContextCurrent()), TEST_64BIT() ? 32 : 20, "check size");
TEST_RESULT_VOID(memContextSwitch(memContextTop()), "switch to top");
TEST_RESULT_VOID(memContextFree(memContext), "context free");
} }
// ***************************************************************************************************************************** // *****************************************************************************************************************************
if (testBegin("memContextCallbackSet()")) if (testBegin("memContextCallbackSet()"))
{ {
TEST_ERROR( TEST_ERROR(
memContextCallbackSet(memContextTop(), testFree, NULL), AssertError, "top context may not have a callback"); memContextCallbackSet(memContextTop(), testFree, NULL), AssertError,
"assertion 'this->callbackQty != memQtyNone' failed");
MemContext *memContext = memContextNewP("test-callback"); MemContext *memContext = memContextNewP(
"test-callback", .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1);
memContextKeep(); memContextKeep();
memContextCallbackSet(memContext, testFree, memContext); memContextCallbackSet(memContext, testFree, memContext);
TEST_ERROR( TEST_ERROR(
@ -257,7 +329,8 @@ testRun(void)
// Now test with an error // Now test with an error
// ------------------------------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------------------------------
memContext = memContextNewP("test-callback-error"); memContext = memContextNewP(
"test-callback-error", .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1);
TEST_RESULT_VOID(memContextKeep(), "keep mem context"); TEST_RESULT_VOID(memContextKeep(), "keep mem context");
testFreeThrow = true; testFreeThrow = true;
TEST_RESULT_VOID(memContextCallbackSet(memContext, testFree, memContext), " set callback"); TEST_RESULT_VOID(memContextCallbackSet(memContext, testFree, memContext), " set callback");
@ -268,7 +341,8 @@ testRun(void)
if (testBegin("MEM_CONTEXT_BEGIN() and MEM_CONTEXT_END()")) if (testBegin("MEM_CONTEXT_BEGIN() and MEM_CONTEXT_END()"))
{ {
memContextSwitch(memContextTop()); memContextSwitch(memContextTop());
MemContext *memContext = memContextNewP("test-block"); MemContext *memContext = memContextNewP(
"test-block", .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1);
memContextKeep(); memContextKeep();
// Check normal block // Check normal block
@ -296,12 +370,12 @@ testRun(void)
// ------------------------------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------------------------------
MEM_CONTEXT_TEMP_RESET_BEGIN() MEM_CONTEXT_TEMP_RESET_BEGIN()
{ {
TEST_RESULT_PTR(MEM_CONTEXT_TEMP()->allocList, NULL, "nothing allocated"); TEST_RESULT_BOOL(MEM_CONTEXT_TEMP()->allocInitialized, false, "nothing allocated");
memNew(99); memNew(99);
TEST_RESULT_PTR_NE(MEM_CONTEXT_TEMP()->allocList[0], NULL, "1 allocation"); TEST_RESULT_PTR_NE(memContextAllocMany(MEM_CONTEXT_TEMP())->list[0], NULL, "1 allocation");
MEM_CONTEXT_TEMP_RESET(1); MEM_CONTEXT_TEMP_RESET(1);
TEST_RESULT_PTR(MEM_CONTEXT_TEMP()->allocList, NULL, "nothing allocated"); TEST_RESULT_BOOL(MEM_CONTEXT_TEMP()->allocInitialized, false, "nothing allocated");
} }
MEM_CONTEXT_TEMP_END(); MEM_CONTEXT_TEMP_END();
} }
@ -316,7 +390,7 @@ testRun(void)
TEST_RESULT_PTR(memContextCurrent(), memContextTop(), "context is now 'TOP'"); TEST_RESULT_PTR(memContextCurrent(), memContextTop(), "context is now 'TOP'");
MEM_CONTEXT_NEW_BEGIN(memContextTestName) MEM_CONTEXT_NEW_BEGIN(test-new-block, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX)
{ {
memContext = MEM_CONTEXT_NEW(); memContext = MEM_CONTEXT_NEW();
TEST_RESULT_PTR(memContext, memContextCurrent(), "new mem context is current"); TEST_RESULT_PTR(memContext, memContextCurrent(), "new mem context is current");
@ -336,7 +410,7 @@ testRun(void)
TRY_BEGIN() TRY_BEGIN()
{ {
MEM_CONTEXT_NEW_BEGIN(memContextTestName) MEM_CONTEXT_NEW_BEGIN(test-new-failed-block, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX)
{ {
memContext = MEM_CONTEXT_NEW(); memContext = MEM_CONTEXT_NEW();
TEST_RESULT_Z(memContext->name, memContextTestName, "check mem context name"); TEST_RESULT_Z(memContext->name, memContextTestName, "check mem context name");
@ -365,36 +439,36 @@ testRun(void)
void *mem = NULL; void *mem = NULL;
void *mem2 = NULL; void *mem2 = NULL;
MEM_CONTEXT_NEW_BEGIN("outer") MEM_CONTEXT_NEW_BEGIN("outer", .childQty = MEM_CONTEXT_QTY_MAX)
{ {
MEM_CONTEXT_TEMP_BEGIN() MEM_CONTEXT_TEMP_BEGIN()
{ {
memContextNewP("not-to-be-moved"); memContextNewP("not-to-be-moved", .childQty = MEM_CONTEXT_QTY_MAX);
memContextKeep(); memContextKeep();
MEM_CONTEXT_NEW_BEGIN("inner") MEM_CONTEXT_NEW_BEGIN("inner", .allocQty = MEM_CONTEXT_QTY_MAX)
{ {
memContext = MEM_CONTEXT_NEW(); memContext = MEM_CONTEXT_NEW();
mem = memNew(sizeof(int)); mem = memNew(sizeof(int));
} }
MEM_CONTEXT_NEW_END(); MEM_CONTEXT_NEW_END();
TEST_RESULT_PTR(MEM_CONTEXT_ALLOC_BUFFER(memContext->allocList[0]), mem, "check memory allocation"); TEST_RESULT_PTR(MEM_CONTEXT_ALLOC_BUFFER(memContextAllocMany(memContext)->list[0]), mem, "check memory allocation");
TEST_RESULT_PTR(memContextCurrent()->contextChildList[1], memContext, "check memory context"); TEST_RESULT_PTR(memContextChildMany(memContextCurrent())->list[1], memContext, "check memory context");
// Null out the mem context in the parent so the move will fail // Null out the mem context in the parent so the move will fail
memContextCurrent()->contextChildList[1] = NULL; memContextChildMany(memContextCurrent())->list[1] = NULL;
TEST_ERROR( TEST_ERROR(
memContextMove(memContext, memContextPrior()), AssertError, memContextMove(memContext, memContextPrior()), AssertError,
"assertion 'this->contextParent->contextChildList[this->contextParentIdx] == this' failed"); "assertion 'memContextChildMany(this->contextParent)->list[this->contextParentIdx] == this' failed");
// Set it back so the move will succeed // Set it back so the move will succeed
memContextCurrent()->contextChildList[1] = memContext; memContextChildMany(memContextCurrent())->list[1] = memContext;
TEST_RESULT_VOID(memContextMove(memContext, memContextPrior()), "move context"); TEST_RESULT_VOID(memContextMove(memContext, memContextPrior()), "move context");
TEST_RESULT_VOID(memContextMove(memContext, memContextPrior()), "move context again"); TEST_RESULT_VOID(memContextMove(memContext, memContextPrior()), "move context again");
// Move another context // Move another context
MEM_CONTEXT_NEW_BEGIN("inner2") MEM_CONTEXT_NEW_BEGIN("inner2", .allocQty = MEM_CONTEXT_QTY_MAX)
{ {
memContext2 = MEM_CONTEXT_NEW(); memContext2 = MEM_CONTEXT_NEW();
mem2 = memNew(sizeof(int)); mem2 = memNew(sizeof(int));
@ -405,13 +479,36 @@ testRun(void)
} }
MEM_CONTEXT_TEMP_END(); MEM_CONTEXT_TEMP_END();
TEST_RESULT_PTR(MEM_CONTEXT_ALLOC_BUFFER(memContext->allocList[0]), mem, "check memory allocation"); TEST_RESULT_PTR(MEM_CONTEXT_ALLOC_BUFFER(memContextAllocMany(memContext)->list[0]), mem, "check memory allocation");
TEST_RESULT_PTR(memContextCurrent()->contextChildList[1], memContext, "check memory context"); TEST_RESULT_PTR(memContextChildMany(memContextCurrent())->list[1], memContext, "check memory context");
TEST_RESULT_PTR(MEM_CONTEXT_ALLOC_BUFFER(memContext2->allocList[0]), mem2, "check memory allocation 2"); TEST_RESULT_PTR(MEM_CONTEXT_ALLOC_BUFFER(memContextAllocMany(memContext2)->list[0]), mem2, "check memory allocation 2");
TEST_RESULT_PTR(memContextCurrent()->contextChildList[2], memContext2, "check memory context 2"); TEST_RESULT_PTR(memContextChildMany(memContextCurrent())->list[2], memContext2, "check memory context 2");
} }
MEM_CONTEXT_NEW_END(); MEM_CONTEXT_NEW_END();
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("outer and inner contexts allow one child");
MemContext *memContextParent1;
TEST_ASSIGN(memContextParent1, memContextNewP("parent1", .childQty = 1), "new parent1");
TEST_RESULT_VOID(memContextKeep(), "keep parent1");
TEST_RESULT_VOID(memContextSwitch(memContextParent1), "switch to parent1");
MemContext *memContextChild;
TEST_ASSIGN(memContextChild, memContextNewP("child", .allocQty = 0), "new child");
TEST_RESULT_VOID(memContextKeep(), "keep child");
TEST_RESULT_VOID(memContextSwitch(memContextTop()), "switch to top");
MemContext *memContextParent2;
TEST_ASSIGN(memContextParent2, memContextNewP("parent2", .childQty = 1), "new parent2");
TEST_RESULT_VOID(memContextKeep(), "keep parent2");
TEST_RESULT_VOID(memContextMove(memContextChild, memContextParent2), "move");
TEST_RESULT_PTR(memContextChildOne(memContextParent1)->context, NULL, "check parent1");
TEST_RESULT_PTR(memContextChildOne(memContextParent2)->context, memContextChild, "check parent2");
} }
memContextFree(memContextTop()); memContextFree(memContextTop());

View File

@ -49,7 +49,7 @@ testObjectNew(void)
{ {
TestObject *this = NULL; TestObject *this = NULL;
OBJ_NEW_BEGIN(STRINGIFY(TestObject)) OBJ_NEW_BEGIN(TestObject)
{ {
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();
@ -69,7 +69,7 @@ testObjectContextNew(void)
{ {
TestObjectContext *this = NULL; TestObjectContext *this = NULL;
OBJ_NEW_BEGIN(STRINGIFY(TestObjectContext)) OBJ_NEW_BEGIN(TestObjectContext)
{ {
this = OBJ_NEW_ALLOC(); this = OBJ_NEW_ALLOC();

View File

@ -41,7 +41,8 @@ testRun(void)
TEST_RESULT_VOID(CHECK_SIZE(555), "valid size"); TEST_RESULT_VOID(CHECK_SIZE(555), "valid size");
TEST_ERROR(CHECK_SIZE(STRING_SIZE_MAX + 1), AssertError, "string size must be <= 1073741824 bytes"); TEST_ERROR(CHECK_SIZE(STRING_SIZE_MAX + 1), AssertError, "string size must be <= 1073741824 bytes");
String *string = strNewZ("static string"); String *string = NULL;
TEST_ASSIGN(string, strNewZ("static string"), "new");
TEST_RESULT_BOOL(string->pub.buffer == (char *)(string + 1), true, "string has fixed buffer"); TEST_RESULT_BOOL(string->pub.buffer == (char *)(string + 1), true, "string has fixed buffer");
TEST_RESULT_STR_Z(string, "static string", "new with static string"); TEST_RESULT_STR_Z(string, "static string", "new with static string");
TEST_RESULT_UINT(strSize(string), 13, "check size"); TEST_RESULT_UINT(strSize(string), 13, "check size");
@ -89,6 +90,15 @@ testRun(void)
TEST_TITLE("strNewEncode()"); TEST_TITLE("strNewEncode()");
TEST_RESULT_STR_Z(strNewEncode(encodeBase64, BUFSTRDEF("zz")), "eno=", "encode base64"); TEST_RESULT_STR_Z(strNewEncode(encodeBase64, BUFSTRDEF("zz")), "eno=", "encode base64");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("fixed string large enough to need separate allocation");
char *compare = memNew(MEM_CONTEXT_ALLOC_EXTRA_MAX + 1);
memset(compare, 'A', MEM_CONTEXT_ALLOC_EXTRA_MAX);
compare[MEM_CONTEXT_ALLOC_EXTRA_MAX] = '\0';
TEST_RESULT_STR_Z(strNewZN(compare, MEM_CONTEXT_ALLOC_EXTRA_MAX), compare, "compare");
} }
// ***************************************************************************************************************************** // *****************************************************************************************************************************

View File

@ -16,7 +16,7 @@ testRun(void)
{ {
// Ensure type sizes are as expected // Ensure type sizes are as expected
TEST_RESULT_UINT(sizeof(VariantBoolPub), 8, "check VariantBoolPub size"); TEST_RESULT_UINT(sizeof(VariantBoolPub), 8, "check VariantBoolPub size");
TEST_RESULT_UINT(sizeof(VariantBool), TEST_64BIT() ? 16 : 12, "check VariantBool size"); TEST_RESULT_UINT(sizeof(VariantBool), 8, "check VariantBool size");
// ------------------------------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------------------------------
Variant *boolean = varNewBool(false); Variant *boolean = varNewBool(false);
@ -57,7 +57,7 @@ testRun(void)
{ {
// Ensure type sizes are as expected // Ensure type sizes are as expected
TEST_RESULT_UINT(sizeof(VariantIntPub), 8, "check VariantIntPub size"); TEST_RESULT_UINT(sizeof(VariantIntPub), 8, "check VariantIntPub size");
TEST_RESULT_UINT(sizeof(VariantInt), TEST_64BIT() ? 16 : 12, "check VariantInt size"); TEST_RESULT_UINT(sizeof(VariantInt), 8, "check VariantInt size");
// ------------------------------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------------------------------
Variant *integer = varNewInt(44); Variant *integer = varNewInt(44);
@ -96,7 +96,7 @@ testRun(void)
{ {
// Ensure type sizes are as expected // Ensure type sizes are as expected
TEST_RESULT_UINT(sizeof(VariantInt64Pub), TEST_64BIT() ? 16 : 12, "check VariantInt64Pub size"); TEST_RESULT_UINT(sizeof(VariantInt64Pub), TEST_64BIT() ? 16 : 12, "check VariantInt64Pub size");
TEST_RESULT_UINT(sizeof(VariantInt64), TEST_64BIT() ? 24 : 16, "check VariantInt64 size"); TEST_RESULT_UINT(sizeof(VariantInt64), TEST_64BIT() ? 16 : 12, "check VariantInt64 size");
// ------------------------------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------------------------------
Variant *integer = varNewInt64(44); Variant *integer = varNewInt64(44);
@ -137,7 +137,7 @@ testRun(void)
{ {
// Ensure type sizes are as expected // Ensure type sizes are as expected
TEST_RESULT_UINT(sizeof(VariantUIntPub), 8, "check VariantUIntPub size"); TEST_RESULT_UINT(sizeof(VariantUIntPub), 8, "check VariantUIntPub size");
TEST_RESULT_UINT(sizeof(VariantUInt), TEST_64BIT() ? 16 : 12, "check VariantUInt size"); TEST_RESULT_UINT(sizeof(VariantUInt), 8, "check VariantUInt size");
// ------------------------------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------------------------------
Variant *unsignedint = varNewUInt(787); Variant *unsignedint = varNewUInt(787);
@ -178,7 +178,7 @@ testRun(void)
{ {
// Ensure type sizes are as expected // Ensure type sizes are as expected
TEST_RESULT_UINT(sizeof(VariantUInt64Pub), TEST_64BIT() ? 16 : 12, "check VariantUInt64Pub size"); TEST_RESULT_UINT(sizeof(VariantUInt64Pub), TEST_64BIT() ? 16 : 12, "check VariantUInt64Pub size");
TEST_RESULT_UINT(sizeof(VariantUInt64), TEST_64BIT() ? 24 : 16, "check VariantUInt64 size"); TEST_RESULT_UINT(sizeof(VariantUInt64), TEST_64BIT() ? 16 : 12, "check VariantUInt64 size");
// ------------------------------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------------------------------
Variant *uint64 = varNewUInt64(44); Variant *uint64 = varNewUInt64(44);
@ -220,7 +220,7 @@ testRun(void)
if (testBegin("keyValue")) if (testBegin("keyValue"))
{ {
// Ensure type sizes are as expected // Ensure type sizes are as expected
TEST_RESULT_UINT(sizeof(VariantKeyValue), TEST_64BIT() ? 24 : 12, "check VariantKeyValue size"); TEST_RESULT_UINT(sizeof(VariantKeyValue), TEST_64BIT() ? 16 : 8, "check VariantKeyValue size");
// ------------------------------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------------------------------
TEST_ERROR(varKv(VARINT(66)), AssertError, "assertion 'varType(this) == varTypeKeyValue' failed"); TEST_ERROR(varKv(VARINT(66)), AssertError, "assertion 'varType(this) == varTypeKeyValue' failed");
@ -254,7 +254,7 @@ testRun(void)
{ {
// Ensure type sizes are as expected // Ensure type sizes are as expected
TEST_RESULT_UINT(sizeof(VariantStringPub), TEST_64BIT() ? 16 : 8, "check VariantStringPub size"); TEST_RESULT_UINT(sizeof(VariantStringPub), TEST_64BIT() ? 16 : 8, "check VariantStringPub size");
TEST_RESULT_UINT(sizeof(VariantString), TEST_64BIT() ? 24 : 12, "check VariantString size"); TEST_RESULT_UINT(sizeof(VariantString), TEST_64BIT() ? 16 : 8, "check VariantString size");
// ------------------------------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------------------------------
TEST_RESULT_STR(varStr(varNewStr(NULL)), NULL, "new null str"); TEST_RESULT_STR(varStr(varNewStr(NULL)), NULL, "new null str");
@ -310,13 +310,22 @@ testRun(void)
TEST_RESULT_PTR(varDup(NULL), NULL, "dup NULL"); TEST_RESULT_PTR(varDup(NULL), NULL, "dup NULL");
TEST_RESULT_BOOL(varEq(VARSTRDEF("expect-equal"), VARSTRDEF("expect-equal")), true, "string, string eq"); TEST_RESULT_BOOL(varEq(VARSTRDEF("expect-equal"), VARSTRDEF("expect-equal")), true, "string, string eq");
TEST_RESULT_BOOL(varEq(VARSTRDEF("Y"), VARSTRDEF("X")), false, "string, string not eq"); TEST_RESULT_BOOL(varEq(VARSTRDEF("Y"), VARSTRDEF("X")), false, "string, string not eq");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("string large enough to need separate allocation");
char *compare = memNew(MEM_CONTEXT_ALLOC_EXTRA_MAX + 1);
memset(compare, 'A', MEM_CONTEXT_ALLOC_EXTRA_MAX);
compare[MEM_CONTEXT_ALLOC_EXTRA_MAX] = '\0';
TEST_RESULT_STR_Z(varStr(varNewStrZ(compare)), compare, "compare");
} }
// ***************************************************************************************************************************** // *****************************************************************************************************************************
if (testBegin("VariantList")) if (testBegin("VariantList"))
{ {
// Ensure type sizes are as expected // Ensure type sizes are as expected
TEST_RESULT_UINT(sizeof(VariantVariantList), TEST_64BIT() ? 24 : 12, "check VariantVariantList size"); TEST_RESULT_UINT(sizeof(VariantVariantList), TEST_64BIT() ? 16 : 8, "check VariantVariantList size");
// ------------------------------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------------------------------
TEST_ERROR(varVarLst(VARINT(66)), AssertError, "assertion 'varType(this) == varTypeVariantList' failed"); TEST_ERROR(varVarLst(VARINT(66)), AssertError, "assertion 'varType(this) == varTypeVariantList' failed");

View File

@ -949,7 +949,7 @@ testRun(void)
Manifest *manifest = NULL; Manifest *manifest = NULL;
OBJ_NEW_BEGIN(Manifest) OBJ_NEW_BEGIN(Manifest, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
manifest = manifestNewInternal(); manifest = manifestNewInternal();
manifest->pub.data.backupOptionOnline = true; manifest->pub.data.backupOptionOnline = true;
@ -1035,7 +1035,7 @@ testRun(void)
Manifest *manifest = NULL; Manifest *manifest = NULL;
OBJ_NEW_BEGIN(Manifest) OBJ_NEW_BEGIN(Manifest, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
manifest = manifestNewInternal(); manifest = manifestNewInternal();
manifest->pub.info = infoNew(NULL); manifest->pub.info = infoNew(NULL);
@ -1072,7 +1072,7 @@ testRun(void)
Manifest *manifestPrior = NULL; Manifest *manifestPrior = NULL;
OBJ_NEW_BEGIN(Manifest) OBJ_NEW_BEGIN(Manifest, .childQty = MEM_CONTEXT_QTY_MAX)
{ {
manifestPrior = manifestNewInternal(); manifestPrior = manifestNewInternal();
manifestPrior->pub.data.backupLabel = strNewZ("20190101-010101F"); manifestPrior->pub.data.backupLabel = strNewZ("20190101-010101F");

View File

@ -118,7 +118,7 @@ testIoRateNew(uint64_t bytesPerSec)
{ {
IoFilter *this = NULL; IoFilter *this = NULL;
OBJ_NEW_BEGIN(TestIoRate) OBJ_NEW_BEGIN(TestIoRate, .childQty = MEM_CONTEXT_QTY_MAX, .allocQty = MEM_CONTEXT_QTY_MAX)
{ {
TestIoRate *driver = OBJ_NEW_ALLOC(); TestIoRate *driver = OBJ_NEW_ALLOC();

View File

@ -263,7 +263,7 @@ testRun(void)
// ------------------------------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("build manifest"); TEST_TITLE("build manifest");
MemContext *testContext = memContextNewP("test"); MemContext *testContext = memContextNewP("test", .childQty = MEM_CONTEXT_QTY_MAX);
memContextKeep(); memContextKeep();
Manifest *manifest = NULL; Manifest *manifest = NULL;
TimeMSec timeBegin = timeMSec(); TimeMSec timeBegin = timeMSec();
@ -276,7 +276,7 @@ testRun(void)
MEM_CONTEXT_END(); MEM_CONTEXT_END();
TEST_LOG_FMT("completed in %ums", (unsigned int)(timeMSec() - timeBegin)); TEST_LOG_FMT("completed in %ums", (unsigned int)(timeMSec() - timeBegin));
TEST_LOG_FMT("memory used %zu", memContextSize(testContext)); // TEST_LOG_FMT("memory used %zu", memContextSize(testContext));
TEST_RESULT_UINT(manifestFileTotal(manifest), driver.fileTotal, " check file total"); TEST_RESULT_UINT(manifestFileTotal(manifest), driver.fileTotal, " check file total");
@ -295,7 +295,7 @@ testRun(void)
// ------------------------------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("load manifest"); TEST_TITLE("load manifest");
testContext = memContextNewP("test"); testContext = memContextNewP("test", .childQty = MEM_CONTEXT_QTY_MAX);
memContextKeep(); memContextKeep();
timeBegin = timeMSec(); timeBegin = timeMSec();
@ -306,7 +306,7 @@ testRun(void)
MEM_CONTEXT_END(); MEM_CONTEXT_END();
TEST_LOG_FMT("completed in %ums", (unsigned int)(timeMSec() - timeBegin)); TEST_LOG_FMT("completed in %ums", (unsigned int)(timeMSec() - timeBegin));
TEST_LOG_FMT("memory used %zu", memContextSize(testContext)); // TEST_LOG_FMT("memory used %zu", memContextSize(testContext));
TEST_RESULT_UINT(manifestFileTotal(manifest), driver.fileTotal, " check file total"); TEST_RESULT_UINT(manifestFileTotal(manifest), driver.fileTotal, " check file total");

View File

@ -281,7 +281,7 @@ testRun(void)
IoWrite *write = ioFdWriteNewOpen(STRDEF("invalid"), 0, 0); IoWrite *write = ioFdWriteNewOpen(STRDEF("invalid"), 0, 0);
OBJ_NEW_BEGIN(ProtocolClient) OBJ_NEW_BEGIN(ProtocolClient, .childQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1)
{ {
protocolHelperClient.client = OBJ_NEW_ALLOC(); protocolHelperClient.client = OBJ_NEW_ALLOC();
*protocolHelperClient.client = (ProtocolClient){ *protocolHelperClient.client = (ProtocolClient){
@ -290,7 +290,7 @@ testRun(void)
} }
OBJ_NEW_END(); OBJ_NEW_END();
OBJ_NEW_BEGIN(Exec) OBJ_NEW_BEGIN(Exec, .childQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1)
{ {
protocolHelperClient.exec = OBJ_NEW_ALLOC(); protocolHelperClient.exec = OBJ_NEW_ALLOC();
*protocolHelperClient.exec = (Exec){.name = strNewZ("test"), .command = strNewZ("test"), .processId = INT_MAX}; *protocolHelperClient.exec = (Exec){.name = strNewZ("test"), .command = strNewZ("test"), .processId = INT_MAX};