You've already forked pgbackrest
mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2026-05-22 10:15:16 +02:00
Allow MemContext objects to be copied to a new parent.
This makes it easier to create objects and then copy them to another context when they are complete without having to worry about freeing them on error. Update List, StringList, and Buffer to allow moves. Update Ini and Storage to take advantage of moves.
This commit is contained in:
@@ -39,6 +39,10 @@
|
||||
<p>Storage object improvements. Convert all functions to variadic functions. Enforce read-only storage. Add <code>storageLocalWrite()</code> helper function. Add <code>storageExists()</code>, <code>storagePathCreate()</code>, and <code>storageRemove()</code>. Add <code>StorageFile</code> object and <code>storageOpenRead()</code>/<code>storageOpenWrite()</code>. Abstract Posix driver code into a separate module.</p>
|
||||
</release-item>
|
||||
|
||||
<release-item>
|
||||
<p>Allow <code>MemContext</code> objects to be copied to a new parent. This makes it easier to create objects and then copy them to another context when they are complete without having to worry about freeing them on error. Update <code>List</code>, <code>StringList</code>, and <code>Buffer</code> to allow moves. Update <code>Ini</code> and <code>Storage</code> to take advantage of moves.</p>
|
||||
</release-item>
|
||||
|
||||
<release-item>
|
||||
<p>Improve branch coverage in C code.</p>
|
||||
</release-item>
|
||||
|
||||
+1
-3
@@ -110,8 +110,6 @@ iniSectionKeyList(const Ini *this, const String *section)
|
||||
// Get the section
|
||||
KeyValue *sectionKv = varKv(kvGet(this->store, varNewStr(section)));
|
||||
|
||||
memContextSwitch(MEM_CONTEXT_OLD());
|
||||
|
||||
// Return key list of the section exists
|
||||
if (sectionKv != NULL)
|
||||
result = strLstNewVarLst(kvKeyList(sectionKv));
|
||||
@@ -119,7 +117,7 @@ iniSectionKeyList(const Ini *this, const String *section)
|
||||
else
|
||||
result = strLstNew();
|
||||
|
||||
memContextSwitch(MEM_CONTEXT_TEMP());
|
||||
strLstMove(result, MEM_CONTEXT_OLD());
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
|
||||
+91
-37
@@ -4,6 +4,7 @@ Memory Context Manager
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/error.h"
|
||||
#include "common/memContext.h"
|
||||
|
||||
@@ -113,6 +114,55 @@ memFreeInternal(void *buffer)
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Find space for a new mem context
|
||||
***********************************************************************************************************************************/
|
||||
static unsigned int
|
||||
memContextNewIndex(MemContext *memContext, bool allowFree)
|
||||
{
|
||||
// Try to find space for the new context
|
||||
unsigned int contextIdx;
|
||||
|
||||
for (contextIdx = 0; contextIdx < memContext->contextChildListSize; contextIdx++)
|
||||
{
|
||||
if (!memContext->contextChildList[contextIdx] ||
|
||||
(allowFree && memContext->contextChildList[contextIdx]->state == memContextStateFree))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If no space was found then allocate more
|
||||
if (contextIdx == memContext->contextChildListSize)
|
||||
{
|
||||
// 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 = memAllocInternal(sizeof(MemContext *) * MEM_CONTEXT_INITIAL_SIZE, true);
|
||||
|
||||
// Set new list size
|
||||
memContext->contextChildListSize = MEM_CONTEXT_INITIAL_SIZE;
|
||||
}
|
||||
// Else grow the list
|
||||
else
|
||||
{
|
||||
// Calculate new list size
|
||||
unsigned int contextChildListSizeNew = memContext->contextChildListSize * 2;
|
||||
|
||||
// ReAllocate memory before modifying anything else in case there is an error
|
||||
memContext->contextChildList = memReAllocInternal(
|
||||
memContext->contextChildList, sizeof(MemContext *) * memContext->contextChildListSize,
|
||||
sizeof(MemContext *) * contextChildListSizeNew, true);
|
||||
|
||||
// Set new list size
|
||||
memContext->contextChildListSize = contextChildListSizeNew;
|
||||
}
|
||||
}
|
||||
|
||||
return contextIdx;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Create a new memory context
|
||||
***********************************************************************************************************************************/
|
||||
@@ -123,43 +173,8 @@ memContextNew(const char *name)
|
||||
if (strlen(name) == 0 || strlen(name) > MEM_CONTEXT_NAME_SIZE)
|
||||
THROW(AssertError, "context name length must be > 0 and <= %d", MEM_CONTEXT_NAME_SIZE);
|
||||
|
||||
// Try to find space for the new context
|
||||
unsigned int contextIdx;
|
||||
|
||||
for (contextIdx = 0; contextIdx < memContextCurrent()->contextChildListSize; contextIdx++)
|
||||
if (!memContextCurrent()->contextChildList[contextIdx] ||
|
||||
memContextCurrent()->contextChildList[contextIdx]->state == memContextStateFree)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// If no space was found then allocate more
|
||||
if (contextIdx == memContextCurrent()->contextChildListSize)
|
||||
{
|
||||
// If no space has been allocated to the list
|
||||
if (memContextCurrent()->contextChildListSize == 0)
|
||||
{
|
||||
// Allocate memory before modifying anything else in case there is an error
|
||||
memContextCurrent()->contextChildList = memAllocInternal(sizeof(MemContext *) * MEM_CONTEXT_INITIAL_SIZE, true);
|
||||
|
||||
// Set new list size
|
||||
memContextCurrent()->contextChildListSize = MEM_CONTEXT_INITIAL_SIZE;
|
||||
}
|
||||
// Else grow the list
|
||||
else
|
||||
{
|
||||
// Calculate new list size
|
||||
unsigned int contextChildListSizeNew = memContextCurrent()->contextChildListSize * 2;
|
||||
|
||||
// ReAllocate memory before modifying anything else in case there is an error
|
||||
memContextCurrent()->contextChildList = memReAllocInternal(
|
||||
memContextCurrent()->contextChildList, sizeof(MemContext *) * memContextCurrent()->contextChildListSize,
|
||||
sizeof(MemContext *) * contextChildListSizeNew, true);
|
||||
|
||||
// Set new list size
|
||||
memContextCurrent()->contextChildListSize = contextChildListSizeNew;
|
||||
}
|
||||
}
|
||||
// Find space for the new context
|
||||
unsigned int contextIdx = memContextNewIndex(memContextCurrent(), true);
|
||||
|
||||
// If the context has not been allocated yet
|
||||
if (!memContextCurrent()->contextChildList[contextIdx])
|
||||
@@ -330,6 +345,45 @@ memFree(void *buffer)
|
||||
alloc->active = false;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Move a context to a new parent context
|
||||
|
||||
This is generally used to move objects to a new context once they have been successfully created.
|
||||
***********************************************************************************************************************************/
|
||||
void
|
||||
memContextMove(MemContext *this, MemContext *parentNew)
|
||||
{
|
||||
// Only move if a valid mem context is provided
|
||||
if (this != NULL)
|
||||
{
|
||||
// Find context in the old parent and NULL it out
|
||||
MemContext *parentOld = this->contextParent;
|
||||
unsigned int contextIdx;
|
||||
|
||||
for (contextIdx = 0; contextIdx < parentOld->contextChildListSize; contextIdx++)
|
||||
{
|
||||
if (parentOld->contextChildList[contextIdx] == this)
|
||||
{
|
||||
parentOld->contextChildList[contextIdx] = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The memory must be found
|
||||
if (contextIdx == parentOld->contextChildListSize)
|
||||
THROW(AssertError, "unable to find mem context in old parent");
|
||||
|
||||
// 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
|
||||
// index and use it with (what might be) the new pointer.
|
||||
contextIdx = memContextNewIndex(parentNew, false);
|
||||
ASSERT_DEBUG(parentNew->contextChildList[contextIdx] == NULL);
|
||||
parentNew->contextChildList[contextIdx] = this;
|
||||
|
||||
// Assign new parent
|
||||
this->contextParent = parentNew;
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Switch to the specified context and return the old context
|
||||
***********************************************************************************************************************************/
|
||||
|
||||
@@ -63,6 +63,7 @@ TRY_END();
|
||||
Use the MEM_CONTEXT*() macros when possible rather than implement error-handling for every memory context block.
|
||||
***********************************************************************************************************************************/
|
||||
MemContext *memContextNew(const char *name);
|
||||
void memContextMove(MemContext *this, MemContext *parentNew);
|
||||
void memContextCallback(MemContext *this, void (*callbackFunction)(void *), void *callbackArgument);
|
||||
MemContext *memContextSwitch(MemContext *this);
|
||||
void memContextFree(MemContext *this);
|
||||
|
||||
+26
-18
@@ -3,7 +3,6 @@ String Handler
|
||||
***********************************************************************************************************************************/
|
||||
#include <string.h>
|
||||
|
||||
#include "common/memContext.h"
|
||||
#include "common/type/buffer.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
@@ -22,14 +21,20 @@ Create a new buffer
|
||||
Buffer *
|
||||
bufNew(size_t size)
|
||||
{
|
||||
// Create object
|
||||
Buffer *this = memNew(sizeof(Buffer));
|
||||
this->memContext = memContextCurrent();
|
||||
this->size = size;
|
||||
Buffer *this = NULL;
|
||||
|
||||
// Allocate buffer
|
||||
if (size > 0)
|
||||
this->buffer = memNewRaw(this->size);
|
||||
MEM_CONTEXT_NEW_BEGIN("Buffer")
|
||||
{
|
||||
// Create object
|
||||
this = memNew(sizeof(Buffer));
|
||||
this->memContext = MEM_CONTEXT_NEW();
|
||||
this->size = size;
|
||||
|
||||
// Allocate buffer
|
||||
if (size > 0)
|
||||
this->buffer = memNewRaw(this->size);
|
||||
}
|
||||
MEM_CONTEXT_NEW_END();
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -49,6 +54,18 @@ bufNewStr(const String *string)
|
||||
return this;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Move buffer to a new mem context
|
||||
***********************************************************************************************************************************/
|
||||
Buffer *
|
||||
bufMove(Buffer *this, MemContext *parentNew)
|
||||
{
|
||||
if (this != NULL)
|
||||
memContextMove(this->memContext, parentNew);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Return buffer ptr
|
||||
***********************************************************************************************************************************/
|
||||
@@ -113,14 +130,5 @@ void
|
||||
bufFree(Buffer *this)
|
||||
{
|
||||
if (this != NULL)
|
||||
{
|
||||
MEM_CONTEXT_BEGIN(this->memContext)
|
||||
{
|
||||
if (this->buffer != NULL)
|
||||
memFree(this->buffer);
|
||||
|
||||
memFree(this);
|
||||
}
|
||||
MEM_CONTEXT_END();
|
||||
}
|
||||
memContextFree(this->memContext);
|
||||
}
|
||||
|
||||
@@ -4,20 +4,20 @@ Buffer Handler
|
||||
#ifndef COMMON_TYPE_BUFFER_H
|
||||
#define COMMON_TYPE_BUFFER_H
|
||||
|
||||
#include "common/typec.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Buffer object
|
||||
***********************************************************************************************************************************/
|
||||
typedef struct Buffer Buffer;
|
||||
|
||||
#include "common/memContext.h"
|
||||
#include "common/type/string.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Functions
|
||||
***********************************************************************************************************************************/
|
||||
#include "common/type/string.h"
|
||||
|
||||
Buffer *bufNew(size_t size);
|
||||
Buffer *bufNewStr(const String *string);
|
||||
Buffer *bufMove(Buffer *this, MemContext *parentNew);
|
||||
Buffer *bufResize(Buffer *this, size_t size);
|
||||
size_t bufSize(const Buffer *this);
|
||||
unsigned char *bufPtr(const Buffer *this);
|
||||
|
||||
+48
-18
@@ -5,7 +5,6 @@ List Handler
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "common/memContext.h"
|
||||
#include "common/type/list.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
@@ -13,6 +12,7 @@ Contains information about the list
|
||||
***********************************************************************************************************************************/
|
||||
struct List
|
||||
{
|
||||
MemContext *memContext;
|
||||
size_t itemSize;
|
||||
unsigned int listSize;
|
||||
unsigned int listSizeMax;
|
||||
@@ -25,9 +25,16 @@ Create a new list
|
||||
List *
|
||||
lstNew(size_t itemSize)
|
||||
{
|
||||
// Create object
|
||||
List *this = memNew(sizeof(List));
|
||||
this->itemSize = itemSize;
|
||||
List *this = NULL;
|
||||
|
||||
MEM_CONTEXT_NEW_BEGIN("List")
|
||||
{
|
||||
// Create object
|
||||
this = memNew(sizeof(List));
|
||||
this->memContext = MEM_CONTEXT_NEW();
|
||||
this->itemSize = itemSize;
|
||||
}
|
||||
MEM_CONTEXT_NEW_END();
|
||||
|
||||
// Return buffer
|
||||
return this;
|
||||
@@ -42,18 +49,22 @@ lstAdd(List *this, const void *item)
|
||||
// If list size = max then allocate more space
|
||||
if (this->listSize == this->listSizeMax)
|
||||
{
|
||||
// If nothing has been allocated yet
|
||||
if (this->listSizeMax == 0)
|
||||
MEM_CONTEXT_BEGIN(this->memContext)
|
||||
{
|
||||
this->listSizeMax = LIST_INITIAL_SIZE;
|
||||
this->list = memNewRaw(this->listSizeMax * this->itemSize);
|
||||
}
|
||||
// Else the list needs to be extended
|
||||
else
|
||||
{
|
||||
this->listSizeMax *= 2;
|
||||
this->list = memGrowRaw(this->list, this->listSizeMax * this->itemSize);
|
||||
// If nothing has been allocated yet
|
||||
if (this->listSizeMax == 0)
|
||||
{
|
||||
this->listSizeMax = LIST_INITIAL_SIZE;
|
||||
this->list = memNewRaw(this->listSizeMax * this->itemSize);
|
||||
}
|
||||
// Else the list needs to be extended
|
||||
else
|
||||
{
|
||||
this->listSizeMax *= 2;
|
||||
this->list = memGrowRaw(this->list, this->listSizeMax * this->itemSize);
|
||||
}
|
||||
}
|
||||
MEM_CONTEXT_END();
|
||||
}
|
||||
|
||||
memcpy(this->list + (this->listSize * this->itemSize), item, this->itemSize);
|
||||
@@ -77,6 +88,27 @@ lstGet(const List *this, unsigned int listIdx)
|
||||
return this->list + (listIdx * this->itemSize);
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Return the memory context for this list
|
||||
***********************************************************************************************************************************/
|
||||
MemContext *
|
||||
lstMemContext(const List *this)
|
||||
{
|
||||
return this->memContext;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Move the string list
|
||||
***********************************************************************************************************************************/
|
||||
List *
|
||||
lstMove(List *this, MemContext *parentNew)
|
||||
{
|
||||
if (this != NULL)
|
||||
memContextMove(this->memContext, parentNew);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Return list size
|
||||
***********************************************************************************************************************************/
|
||||
@@ -103,8 +135,6 @@ Free the string
|
||||
void
|
||||
lstFree(List *this)
|
||||
{
|
||||
if (this->list != NULL)
|
||||
memFree(this->list);
|
||||
|
||||
memFree(this);
|
||||
if (this != NULL)
|
||||
memContextFree(this->memContext);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ List Handler
|
||||
#ifndef COMMON_TYPE_LIST_H
|
||||
#define COMMON_TYPE_LIST_H
|
||||
|
||||
#include "common/memContext.h"
|
||||
#include "common/type/string.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
@@ -22,6 +23,8 @@ Functions
|
||||
List *lstNew(size_t itemSize);
|
||||
List *lstAdd(List *this, const void *item);
|
||||
void *lstGet(const List *this, unsigned int listIdx);
|
||||
MemContext *lstMemContext(const List *this);
|
||||
List *lstMove(List *this, MemContext *parentNew);
|
||||
unsigned int lstSize(const List *this);
|
||||
List *lstSort(List *this, int (*comparator)(const void *, const void*));
|
||||
void lstFree(List *this);
|
||||
|
||||
@@ -18,6 +18,15 @@ strLstNew()
|
||||
return (StringList *)lstNew(sizeof(String *));
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Internal add -- the string must have been created in the list's mem context before being passed
|
||||
***********************************************************************************************************************************/
|
||||
static StringList *
|
||||
strLstAddInternal(StringList *this, String *string)
|
||||
{
|
||||
return (StringList *)lstAdd((List *)this, &string);
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Split a string into a string list based on a delimiter
|
||||
***********************************************************************************************************************************/
|
||||
@@ -39,22 +48,26 @@ strLstNewSplitZ(const String *string, const char *delimiter)
|
||||
// Match points to the next delimiter match that has been found
|
||||
const char *stringMatch = NULL;
|
||||
|
||||
do
|
||||
MEM_CONTEXT_BEGIN(lstMemContext((List *)this))
|
||||
{
|
||||
// Find a delimiter match
|
||||
stringMatch = strstr(stringBase, delimiter);
|
||||
|
||||
// If a match was found then add the string
|
||||
if (stringMatch != NULL)
|
||||
do
|
||||
{
|
||||
strLstAdd(this, strNewN(stringBase, (size_t)(stringMatch - stringBase)));
|
||||
stringBase = stringMatch + strlen(delimiter);
|
||||
// Find a delimiter match
|
||||
stringMatch = strstr(stringBase, delimiter);
|
||||
|
||||
// If a match was found then add the string
|
||||
if (stringMatch != NULL)
|
||||
{
|
||||
strLstAddInternal(this, strNewN(stringBase, (size_t)(stringMatch - stringBase)));
|
||||
stringBase = stringMatch + strlen(delimiter);
|
||||
}
|
||||
// Else make whatever is left the last string
|
||||
else
|
||||
strLstAddInternal(this, strNew(stringBase));
|
||||
}
|
||||
// Else make whatever is left the last string
|
||||
else
|
||||
strLstAdd(this, strNew(stringBase));
|
||||
while(stringMatch != NULL);
|
||||
}
|
||||
while(stringMatch != NULL);
|
||||
MEM_CONTEXT_END();
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -84,39 +97,43 @@ strLstNewSplitSizeZ(const String *string, const char *delimiter, size_t size)
|
||||
const char *stringMatchLast = NULL;
|
||||
const char *stringMatch = NULL;
|
||||
|
||||
do
|
||||
MEM_CONTEXT_BEGIN(lstMemContext((List *)this))
|
||||
{
|
||||
// Find a delimiter match
|
||||
stringMatch = strstr(stringMatchLast == NULL ? stringBase : stringMatchLast, delimiter);
|
||||
|
||||
// If a match was found then add the string
|
||||
if (stringMatch != NULL)
|
||||
do
|
||||
{
|
||||
if ((size_t)(stringMatch - stringBase) >= size)
|
||||
{
|
||||
if (stringMatchLast != NULL)
|
||||
stringMatch = stringMatchLast - strlen(delimiter);
|
||||
// Find a delimiter match
|
||||
stringMatch = strstr(stringMatchLast == NULL ? stringBase : stringMatchLast, delimiter);
|
||||
|
||||
strLstAdd(this, strNewN(stringBase, (size_t)(stringMatch - stringBase)));
|
||||
stringBase = stringMatch + strlen(delimiter);
|
||||
stringMatchLast = NULL;
|
||||
// If a match was found then add the string
|
||||
if (stringMatch != NULL)
|
||||
{
|
||||
if ((size_t)(stringMatch - stringBase) >= size)
|
||||
{
|
||||
if (stringMatchLast != NULL)
|
||||
stringMatch = stringMatchLast - strlen(delimiter);
|
||||
|
||||
strLstAddInternal(this, strNewN(stringBase, (size_t)(stringMatch - stringBase)));
|
||||
stringBase = stringMatch + strlen(delimiter);
|
||||
stringMatchLast = NULL;
|
||||
}
|
||||
else
|
||||
stringMatchLast = stringMatch + strlen(delimiter);
|
||||
}
|
||||
// Else make whatever is left the last string
|
||||
else
|
||||
stringMatchLast = stringMatch + strlen(delimiter);
|
||||
}
|
||||
// Else make whatever is left the last string
|
||||
else
|
||||
{
|
||||
if (stringMatchLast != NULL && strlen(stringBase) - strlen(delimiter) >= size)
|
||||
{
|
||||
strLstAdd(this, strNewN(stringBase, (size_t)((stringMatchLast - strlen(delimiter)) - stringBase)));
|
||||
stringBase = stringMatchLast;
|
||||
}
|
||||
if (stringMatchLast != NULL && strlen(stringBase) - strlen(delimiter) >= size)
|
||||
{
|
||||
strLstAddInternal(this, strNewN(stringBase, (size_t)((stringMatchLast - strlen(delimiter)) - stringBase)));
|
||||
stringBase = stringMatchLast;
|
||||
}
|
||||
|
||||
strLstAdd(this, strNew(stringBase));
|
||||
strLstAddInternal(this, strNew(stringBase));
|
||||
}
|
||||
}
|
||||
while(stringMatch != NULL);
|
||||
}
|
||||
while(stringMatch != NULL);
|
||||
MEM_CONTEXT_END();
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -131,8 +148,12 @@ strLstNewVarLst(const VariantList *sourceList)
|
||||
StringList *this = strLstNew();
|
||||
|
||||
// Copy variants
|
||||
for (unsigned int listIdx = 0; listIdx < varLstSize(sourceList); listIdx++)
|
||||
strLstAdd(this, varStr(varLstGet(sourceList, listIdx)));
|
||||
MEM_CONTEXT_BEGIN(lstMemContext((List *)this))
|
||||
{
|
||||
for (unsigned int listIdx = 0; listIdx < varLstSize(sourceList); listIdx++)
|
||||
strLstAddInternal(this, strDup(varStr(varLstGet(sourceList, listIdx))));
|
||||
}
|
||||
MEM_CONTEXT_END();
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -146,9 +167,13 @@ strLstDup(const StringList *sourceList)
|
||||
// Create the list
|
||||
StringList *this = strLstNew();
|
||||
|
||||
// Copy variants
|
||||
for (unsigned int listIdx = 0; listIdx < strLstSize(sourceList); listIdx++)
|
||||
strLstAdd(this, strLstGet(sourceList, listIdx));
|
||||
// Copy strings
|
||||
MEM_CONTEXT_BEGIN(lstMemContext((List *)this))
|
||||
{
|
||||
for (unsigned int listIdx = 0; listIdx < strLstSize(sourceList); listIdx++)
|
||||
strLstAddInternal(this, strDup(strLstGet(sourceList, listIdx)));
|
||||
}
|
||||
MEM_CONTEXT_END();
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -159,8 +184,15 @@ Wrapper for lstAdd()
|
||||
StringList *
|
||||
strLstAdd(StringList *this, const String *string)
|
||||
{
|
||||
String *stringCopy = strDup(string);
|
||||
return (StringList *)lstAdd((List *)this, &stringCopy);
|
||||
StringList *result = NULL;
|
||||
|
||||
MEM_CONTEXT_BEGIN(lstMemContext((List *)this))
|
||||
{
|
||||
result = strLstAddInternal(this, strDup(string));
|
||||
}
|
||||
MEM_CONTEXT_END();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
@@ -169,7 +201,15 @@ Add a zero-terminated string to the list
|
||||
StringList *
|
||||
strLstAddZ(StringList *this, const char *string)
|
||||
{
|
||||
return strLstAdd(this, strNew(string));
|
||||
StringList *result = NULL;
|
||||
|
||||
MEM_CONTEXT_BEGIN(lstMemContext((List *)this))
|
||||
{
|
||||
result = strLstAddInternal(this, strNew(string));
|
||||
}
|
||||
MEM_CONTEXT_END();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
@@ -203,6 +243,16 @@ strLstJoin(const StringList *this, const char *separator)
|
||||
return join;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Move the string list
|
||||
***********************************************************************************************************************************/
|
||||
StringList *
|
||||
strLstMove(StringList *this, MemContext *parentNew)
|
||||
{
|
||||
lstMove((List *)this, parentNew);
|
||||
return this;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Return an array of pointers to the zero-terminated strings in this list. DO NOT override const and modify any of the strings in
|
||||
this array, though it is OK to modify the array itself.
|
||||
@@ -273,6 +323,5 @@ Wrapper for lstFree()
|
||||
void
|
||||
strLstFree(StringList *this)
|
||||
{
|
||||
if (this != NULL)
|
||||
lstFree((List *)this);
|
||||
lstFree((List *)this);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,10 @@ String List Handler
|
||||
#ifndef COMMON_TYPE_STRINGLIST_H
|
||||
#define COMMON_TYPE_STRINGLIST_H
|
||||
|
||||
#include "common/type/string.h"
|
||||
/***********************************************************************************************************************************
|
||||
String list type
|
||||
***********************************************************************************************************************************/
|
||||
typedef struct StringList StringList;
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Sort orders
|
||||
@@ -15,16 +18,13 @@ typedef enum
|
||||
sortOrderDesc,
|
||||
} SortOrder;
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
String list type
|
||||
***********************************************************************************************************************************/
|
||||
typedef struct StringList StringList;
|
||||
#include "common/memContext.h"
|
||||
#include "common/type/string.h"
|
||||
#include "common/type/variantList.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Functions
|
||||
***********************************************************************************************************************************/
|
||||
#include "common/type/variantList.h"
|
||||
|
||||
StringList *strLstNew();
|
||||
StringList *strLstNewSplit(const String *string, const String *delimiter);
|
||||
StringList *strLstNewSplitZ(const String *string, const char *delimiter);
|
||||
@@ -37,6 +37,7 @@ StringList *strLstAdd(StringList *this, const String *string);
|
||||
StringList *strLstAddZ(StringList *this, const char *string);
|
||||
String *strLstGet(const StringList *this, unsigned int listIdx);
|
||||
String *strLstJoin(const StringList *this, const char *separator);
|
||||
StringList * strLstMove(StringList *this, MemContext *parentNew);
|
||||
const char **strLstPtr(const StringList *this);
|
||||
unsigned int strLstSize(const StringList *this);
|
||||
StringList *strLstSort(StringList *this, SortOrder sortOrder);
|
||||
|
||||
+59
-68
@@ -54,56 +54,53 @@ Read from storage into a buffer
|
||||
Buffer *
|
||||
storageDriverPosixGet(const StorageFile *file)
|
||||
{
|
||||
Buffer volatile *result = NULL;
|
||||
Buffer *result = NULL;
|
||||
|
||||
TRY_BEGIN()
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
// Create result buffer with buffer size
|
||||
ssize_t actualBytes = 0;
|
||||
size_t totalBytes = 0;
|
||||
|
||||
do
|
||||
TRY_BEGIN()
|
||||
{
|
||||
size_t bufferSize = storageBufferSize(storageFileStorage(file));
|
||||
result = bufNew(bufferSize);
|
||||
|
||||
// Allocate the buffer before first read
|
||||
if (result == NULL)
|
||||
result = bufNew(bufferSize);
|
||||
// Grow the buffer on subsequent reads
|
||||
else
|
||||
bufResize((Buffer *)result, bufSize((Buffer *)result) + bufferSize);
|
||||
// Create result buffer with buffer size
|
||||
ssize_t actualBytes = 0;
|
||||
size_t totalBytes = 0;
|
||||
|
||||
// Read and handle errors
|
||||
actualBytes = read(
|
||||
STORAGE_DATA(file)->handle, bufPtr((Buffer *)result) + totalBytes, bufferSize);
|
||||
do
|
||||
{
|
||||
// Grow the buffer on subsequent reads
|
||||
if (totalBytes != 0)
|
||||
bufResize(result, bufSize(result) + bufferSize);
|
||||
|
||||
// Error occurred during write
|
||||
if (actualBytes == -1)
|
||||
THROW_SYS_ERROR(FileReadError, "unable to read '%s'", strPtr(storageFileName(file)));
|
||||
// Read and handle errors
|
||||
actualBytes = read(
|
||||
STORAGE_DATA(file)->handle, bufPtr(result) + totalBytes, bufferSize);
|
||||
|
||||
// Track total bytes read
|
||||
totalBytes += (size_t)actualBytes;
|
||||
// Error occurred during write
|
||||
if (actualBytes == -1)
|
||||
THROW_SYS_ERROR(FileReadError, "unable to read '%s'", strPtr(storageFileName(file)));
|
||||
|
||||
// Track total bytes read
|
||||
totalBytes += (size_t)actualBytes;
|
||||
}
|
||||
while (actualBytes != 0);
|
||||
|
||||
// Resize buffer to total bytes read
|
||||
bufResize(result, totalBytes);
|
||||
}
|
||||
while (actualBytes != 0);
|
||||
FINALLY()
|
||||
{
|
||||
close(STORAGE_DATA(file)->handle);
|
||||
storageFileFree(file);
|
||||
}
|
||||
TRY_END();
|
||||
|
||||
// Resize buffer to total bytes read
|
||||
bufResize((Buffer *)result, totalBytes);
|
||||
bufMove(result, MEM_CONTEXT_OLD());
|
||||
}
|
||||
CATCH_ANY()
|
||||
{
|
||||
// Free buffer on error if it was allocated
|
||||
bufFree((Buffer *)result);
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
RETHROW();
|
||||
}
|
||||
FINALLY()
|
||||
{
|
||||
close(STORAGE_DATA(file)->handle);
|
||||
storageFileFree(file);
|
||||
}
|
||||
TRY_END();
|
||||
|
||||
return (Buffer *)result;
|
||||
return result;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
@@ -113,9 +110,7 @@ StringList *
|
||||
storageDriverPosixList(const String *path, bool errorOnMissing, const String *expression)
|
||||
{
|
||||
StringList *result = NULL;
|
||||
|
||||
DIR *dir = NULL;
|
||||
RegExp *regExp = NULL;
|
||||
|
||||
TRY_BEGIN()
|
||||
{
|
||||
@@ -130,44 +125,40 @@ storageDriverPosixList(const String *path, bool errorOnMissing, const String *ex
|
||||
}
|
||||
else
|
||||
{
|
||||
// Prepare regexp if an expression was passed
|
||||
if (expression != NULL)
|
||||
regExp = regExpNew(expression);
|
||||
|
||||
// Create the string list now that we know the directory is valid
|
||||
result = strLstNew();
|
||||
|
||||
// Read the directory entries
|
||||
struct dirent *dirEntry = readdir(dir);
|
||||
|
||||
while (dirEntry != NULL)
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
String *entry = strNew(dirEntry->d_name);
|
||||
// Prepare regexp if an expression was passed
|
||||
RegExp *regExp = (expression == NULL) ? NULL : regExpNew(expression);
|
||||
|
||||
// Create the string list now that we know the directory is valid
|
||||
result = strLstNew();
|
||||
|
||||
// Read the directory entries
|
||||
struct dirent *dirEntry = readdir(dir);
|
||||
|
||||
while (dirEntry != NULL)
|
||||
{
|
||||
String *entry = strNew(dirEntry->d_name);
|
||||
|
||||
// Exclude current/parent directory and apply the expression if specified
|
||||
if (!strEqZ(entry, ".") && !strEqZ(entry, "..") && (regExp == NULL || regExpMatch(regExp, entry)))
|
||||
strLstAdd(result, entry);
|
||||
|
||||
// Exclude current/parent directory and apply the expression if specified
|
||||
if (!strEqZ(entry, ".") && !strEqZ(entry, "..") && (regExp == NULL || regExpMatch(regExp, entry)))
|
||||
strLstAdd(result, entry);
|
||||
else
|
||||
strFree(entry);
|
||||
|
||||
dirEntry = readdir(dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
CATCH_ANY()
|
||||
{
|
||||
// Free list on error
|
||||
strLstFree(result);
|
||||
dirEntry = readdir(dir);
|
||||
}
|
||||
|
||||
RETHROW();
|
||||
// Move finished list up to the old context
|
||||
strLstMove(result, MEM_CONTEXT_OLD());
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
}
|
||||
}
|
||||
FINALLY()
|
||||
{
|
||||
if (dir != NULL)
|
||||
closedir(dir);
|
||||
|
||||
if (regExp != NULL)
|
||||
regExpFree(regExp);
|
||||
}
|
||||
TRY_END();
|
||||
|
||||
|
||||
+8
-20
@@ -123,21 +123,15 @@ storageList(const Storage *this, const String *pathExp, StorageListParam param)
|
||||
{
|
||||
StringList *result = NULL;
|
||||
|
||||
String *path = NULL;
|
||||
|
||||
TRY_BEGIN()
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
// Build the path
|
||||
path = storagePathNP(this, pathExp);
|
||||
String *path = storagePathNP(this, pathExp);
|
||||
|
||||
// Call driver function
|
||||
result = storageDriverPosixList(path, param.errorOnMissing, param.expression);
|
||||
// Move list up to the old context
|
||||
result = strLstMove(storageDriverPosixList(path, param.errorOnMissing, param.expression), MEM_CONTEXT_OLD());
|
||||
}
|
||||
FINALLY()
|
||||
{
|
||||
strFree(path);
|
||||
}
|
||||
TRY_END();
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -151,7 +145,7 @@ storageOpenRead(const Storage *this, const String *fileExp, StorageOpenReadParam
|
||||
{
|
||||
StorageFile *result = NULL;
|
||||
|
||||
MEM_CONTEXT_NEW_BEGIN("StorageFileRead")
|
||||
MEM_CONTEXT_NEW_BEGIN("StorageFile")
|
||||
{
|
||||
String *fileName = storagePathNP(this, fileExp);
|
||||
|
||||
@@ -159,13 +153,7 @@ storageOpenRead(const Storage *this, const String *fileExp, StorageOpenReadParam
|
||||
void *data = storageDriverPosixOpenRead(fileName, param.ignoreMissing);
|
||||
|
||||
// Free mem contexts if missing files are ignored
|
||||
if (data == NULL)
|
||||
{
|
||||
memContextSwitch(MEM_CONTEXT_OLD());
|
||||
memContextFree(MEM_CONTEXT_NEW());
|
||||
}
|
||||
// Else create the storage file
|
||||
else
|
||||
if (data != NULL)
|
||||
result = storageFileNew(this, fileName, storageFileTypeRead, data);
|
||||
}
|
||||
MEM_CONTEXT_NEW_END();
|
||||
@@ -183,7 +171,7 @@ storageOpenWrite(const Storage *this, const String *fileExp, StorageOpenWritePar
|
||||
|
||||
StorageFile *result = NULL;
|
||||
|
||||
MEM_CONTEXT_NEW_BEGIN("StorageFileWrite")
|
||||
MEM_CONTEXT_NEW_BEGIN("StorageFile")
|
||||
{
|
||||
String *fileName = storagePathNP(this, fileExp);
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ my $oTestDef =
|
||||
},
|
||||
{
|
||||
&TESTDEF_NAME => 'mem-context',
|
||||
&TESTDEF_TOTAL => 6,
|
||||
&TESTDEF_TOTAL => 7,
|
||||
&TESTDEF_C => true,
|
||||
&TESTDEF_CDEF => '-DNO_MEM_CONTEXT -DNO_LOG',
|
||||
|
||||
|
||||
@@ -91,8 +91,6 @@ testRun()
|
||||
TEST_RESULT_INT(memContextTop()->contextChildListSize, 0, "top context should init with zero children");
|
||||
TEST_RESULT_PTR(memContextTop()->contextChildList, NULL, "top context child list empty");
|
||||
|
||||
// TEST_ERROR(memContextFree(memContextTop()), AssertError, "cannot free top context");
|
||||
|
||||
// Current context should equal top context
|
||||
TEST_RESULT_PTR(memContextCurrent(), memContextTop(), "top context == current context");
|
||||
|
||||
@@ -156,6 +154,11 @@ testRun()
|
||||
TEST_ERROR(
|
||||
memContextFree(memContextTop()->contextChildList[MEM_CONTEXT_INITIAL_SIZE]),
|
||||
AssertError, "cannot free inactive context");
|
||||
|
||||
MemContext *noAllocation = memContextNew("empty");
|
||||
noAllocation->allocListSize = 0;
|
||||
free(noAllocation->allocList);
|
||||
TEST_RESULT_VOID(memContextFree(noAllocation), "free context with no allocations");
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -201,6 +204,11 @@ testRun()
|
||||
TEST_RESULT_INT(expectedTotal, sizeof(size_t), "all bytes are 0xFE in original portion");
|
||||
|
||||
// Free memory
|
||||
TEST_RESULT_VOID(memFree(memContextCurrent()->allocList[MEM_CONTEXT_ALLOC_INITIAL_SIZE].buffer), "free allocation");
|
||||
TEST_ERROR(
|
||||
memFree(memContextCurrent()->allocList[MEM_CONTEXT_ALLOC_INITIAL_SIZE].buffer), AssertError,
|
||||
"unable to find allocation");
|
||||
|
||||
TEST_ERROR(memFree(NULL), AssertError, "unable to find null allocation");
|
||||
TEST_ERROR(memFree((void *)0x01), AssertError, "unable to find allocation");
|
||||
memFree(buffer);
|
||||
@@ -298,5 +306,46 @@ testRun()
|
||||
TEST_RESULT_BOOL(memContext->state == memContextStateFree, true, "new mem context is not active");
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
if (testBegin("memContextMove()"))
|
||||
{
|
||||
TEST_RESULT_VOID(memContextMove(NULL, NULL), "move NULL context");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
MemContext *memContext = NULL;
|
||||
void *mem = NULL;
|
||||
|
||||
MEM_CONTEXT_NEW_BEGIN("outer")
|
||||
{
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
memContextNew("not-to-be-moved");
|
||||
|
||||
MEM_CONTEXT_NEW_BEGIN("inner")
|
||||
{
|
||||
memContext = MEM_CONTEXT_NEW();
|
||||
mem = memNew(sizeof(int));
|
||||
}
|
||||
MEM_CONTEXT_NEW_END();
|
||||
|
||||
TEST_RESULT_PTR(memContext->allocList[0].buffer, mem, "check memory allocation");
|
||||
TEST_RESULT_PTR(memContextCurrent()->contextChildList[1], memContext, "check memory context");
|
||||
|
||||
// Null out the mem context in the parent so the move will fail
|
||||
memContextCurrent()->contextChildList[1] = NULL;
|
||||
TEST_ERROR(memContextMove(memContext, MEM_CONTEXT_OLD()), AssertError, "unable to find mem context in old parent");
|
||||
|
||||
// Set it back so the move will fail
|
||||
memContextCurrent()->contextChildList[1] = memContext;
|
||||
TEST_RESULT_VOID(memContextMove(memContext, MEM_CONTEXT_OLD()), "move context");
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
TEST_RESULT_PTR(memContext->allocList[0].buffer, mem, "check memory allocation");
|
||||
TEST_RESULT_PTR(memContextCurrent()->contextChildList[1], memContext, "check memory context");
|
||||
}
|
||||
MEM_CONTEXT_NEW_END();
|
||||
}
|
||||
|
||||
memContextFree(memContextTop());
|
||||
}
|
||||
|
||||
@@ -9,11 +9,17 @@ void
|
||||
testRun()
|
||||
{
|
||||
// *****************************************************************************************************************************
|
||||
if (testBegin("bufNew(), bufNewStr(), bufSize(), bufPtr(), and bufFree()"))
|
||||
if (testBegin("bufNew(), bufNewStr(), bufMove(), bufSize(), bufPtr(), and bufFree()"))
|
||||
{
|
||||
Buffer *buffer = NULL;
|
||||
|
||||
TEST_ASSIGN(buffer, bufNew(256), "new buffer");
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
TEST_ASSIGN(buffer, bufNew(256), "new buffer");
|
||||
bufMove(buffer, MEM_CONTEXT_OLD());
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
TEST_RESULT_PTR(bufPtr(buffer), buffer->buffer, "buffer pointer");
|
||||
TEST_RESULT_INT(bufSize(buffer), 256, "buffer size");
|
||||
|
||||
@@ -23,6 +29,8 @@ testRun()
|
||||
TEST_RESULT_VOID(bufFree(buffer), "free buffer");
|
||||
TEST_RESULT_VOID(bufFree(bufNew(0)), "free empty buffer");
|
||||
TEST_RESULT_VOID(bufFree(NULL), "free null buffer");
|
||||
|
||||
TEST_RESULT_VOID(bufMove(NULL, NULL), "move null buffer");
|
||||
}
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
|
||||
@@ -27,29 +27,39 @@ void
|
||||
testRun()
|
||||
{
|
||||
// *****************************************************************************************************************************
|
||||
if (testBegin("lstNew() and lstFree()"))
|
||||
if (testBegin("lstNew(), lstMemContext(), and lstFree()"))
|
||||
{
|
||||
List *list = lstNew(sizeof(void *));
|
||||
|
||||
TEST_RESULT_INT(list->itemSize, sizeof(void *), "item size");
|
||||
TEST_RESULT_INT(list->listSize, 0, "list size");
|
||||
TEST_RESULT_INT(list->listSizeMax, 0, "list size max");
|
||||
TEST_RESULT_PTR(lstMemContext(list), list->memContext, "list mem context");
|
||||
|
||||
void *ptr = NULL;
|
||||
TEST_RESULT_PTR(lstAdd(list, &ptr), list, "add item");
|
||||
|
||||
TEST_RESULT_VOID(lstFree(list), "free list");
|
||||
TEST_RESULT_VOID(lstFree(lstNew(1)), "free empty list");
|
||||
TEST_RESULT_VOID(lstFree(NULL), "free null list");
|
||||
}
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
if (testBegin("lstAdd() and lstSize()"))
|
||||
if (testBegin("lstAdd(), lstMove(), and lstSize()"))
|
||||
{
|
||||
List *list = lstNew(sizeof(int));
|
||||
List *list = NULL;
|
||||
|
||||
// Add ints to the list
|
||||
for (int listIdx = 0; listIdx <= LIST_INITIAL_SIZE; listIdx++)
|
||||
TEST_RESULT_PTR(lstAdd(list, &listIdx), list, "add item %d", listIdx);
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
list = lstNew(sizeof(int));
|
||||
|
||||
// Add ints to the list
|
||||
for (int listIdx = 0; listIdx <= LIST_INITIAL_SIZE; listIdx++)
|
||||
TEST_RESULT_PTR(lstAdd(list, &listIdx), list, "add item %d", listIdx);
|
||||
|
||||
lstMove(list, MEM_CONTEXT_OLD());
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
TEST_RESULT_INT(lstSize(list), 9, "list size");
|
||||
|
||||
@@ -61,6 +71,7 @@ testRun()
|
||||
}
|
||||
|
||||
TEST_ERROR(lstGet(list, lstSize(list)), AssertError, "cannot get index 9 from list with 9 value(s)");
|
||||
TEST_RESULT_VOID(lstMove(NULL, NULL), "move null list");
|
||||
}
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
|
||||
@@ -9,21 +9,29 @@ void
|
||||
testRun()
|
||||
{
|
||||
// *****************************************************************************************************************************
|
||||
if (testBegin("strLstNew(), strLstAdd, strLstGet(), strLstSize(), and strLstFree()"))
|
||||
if (testBegin("strLstNew(), strLstAdd, strLstGet(), strLstMove(), strLstSize(), and strLstFree()"))
|
||||
{
|
||||
StringList *list = strLstNew();
|
||||
|
||||
// Add strings to the list
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
for (int listIdx = 0; listIdx <= LIST_INITIAL_SIZE; listIdx++)
|
||||
StringList *list = NULL;
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
if (listIdx == 0)
|
||||
list = strLstNew();
|
||||
|
||||
for (int listIdx = 0; listIdx <= LIST_INITIAL_SIZE; listIdx++)
|
||||
{
|
||||
TEST_RESULT_PTR(strLstAdd(list, NULL), list, "add null item");
|
||||
if (listIdx == 0)
|
||||
{
|
||||
TEST_RESULT_PTR(strLstAdd(list, NULL), list, "add null item");
|
||||
}
|
||||
else
|
||||
TEST_RESULT_PTR(strLstAdd(list, strNewFmt("STR%02d", listIdx)), list, "add item %d", listIdx);
|
||||
}
|
||||
else
|
||||
TEST_RESULT_PTR(strLstAdd(list, strNewFmt("STR%02d", listIdx)), list, "add item %d", listIdx);
|
||||
|
||||
strLstMove(list, MEM_CONTEXT_OLD());
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
TEST_RESULT_INT(strLstSize(list), 9, "list size");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user