1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2024-12-12 10:04:14 +02:00

Refactor List, StringList, and VariantList for performance.

Introduce a standard pattern for exposing public struct members (as documented in CODING.md) and use it to inline lstSize() which should improve the performance of iterating large lists.

Since many functions in these modules are just thin wrappers of other functions, inline where appropriate.

Remove strLstExistsZ() and strLstInsertZ() since they were only used in tests, where the String version of the function is sufficient.

Move strLstNewSplitSizeZ() to command/help/help.c and remove strLstNewSplitSize(). This function has only ever been used by help and does not seem widely applicable.
This commit is contained in:
David Steele 2021-04-07 12:50:33 -04:00
parent 904738a5f1
commit 79a2d02c9c
16 changed files with 349 additions and 548 deletions

View File

@ -205,6 +205,36 @@ Don't use a macro when a function could be used instead. Macros make it hard to
Object-oriented programming is used extensively. The object pointer is always referred to as `this`.
An object can expose internal struct members by defining a public struct that contains the members to be exposed and using inline functions to get/set the members.
The header file:
```c
/***********************************************************************************************************************************
Getters/setters
***********************************************************************************************************************************/
typedef struct ListPub
{
unsigned int listSize; // List size
} ListPub;
// List size
__attribute__((always_inline)) static inline unsigned int
lstSize(const List *this)
{
ASSERT_INLINE(this != NULL);
return ((const ListPub *)this)->listSize;
}
```
And the C file:
```c
struct List
{
ListPub pub; // Publicly accessible variables
...
};
```
The public struct must be the first member of the private struct. The naming convention for the public struct is to add `Pub` to the end of the private struct name.
### Variadic Functions
Variadic functions can take a variable number of parameters. While the `printf()` pattern is variadic, it is not very flexible in terms of optional parameters given in any order.

View File

@ -266,6 +266,40 @@ switch (int)
<title>Objects</title>
<p>Object-oriented programming is used extensively. The object pointer is always referred to as <id>this</id>.</p>
<p>An object can expose internal struct members by defining a public struct that contains the members to be exposed and using inline functions to get/set the members.</p>
<p>The header file:</p>
<code-block type="c">
/***********************************************************************************************************************************
Getters/setters
***********************************************************************************************************************************/
typedef struct ListPub
{
unsigned int listSize; // List size
} ListPub;
// List size
__attribute__((always_inline)) static inline unsigned int
lstSize(const List *this)
{
ASSERT_INLINE(this != NULL);
return ((const ListPub *)this)->listSize;
}
</code-block>
<p>And the C file:</p>
<code-block type="c">
struct List
{
ListPub pub; // Publicly accessible variables
...
};
</code-block>
<p>The public struct must be the first member of the private struct. The naming convention for the public struct is to add <id>Pub</id> to the end of the private struct name.</p>
</section>
<section id="variadic-functions">

View File

@ -26,6 +26,75 @@ Define the console width - use a fixed with of 80 since this should be safe on v
***********************************************************************************************************************************/
#define CONSOLE_WIDTH 80
/***********************************************************************************************************************************
Helper function to split a string into a string list based on a delimiter and max size per item. In other words each item in the
list will be no longer than size even if multiple delimiters are skipped. This is useful for breaking up text on spaces, for
example.
***********************************************************************************************************************************/
static StringList *
helpRenderSplitSize(const String *string, const char *delimiter, size_t size)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRING, string);
FUNCTION_TEST_PARAM(STRINGZ, delimiter);
FUNCTION_TEST_PARAM(SIZE, size);
FUNCTION_TEST_END();
ASSERT(string != NULL);
ASSERT(delimiter != NULL);
ASSERT(size > 0);
// Create the list
StringList *this = strLstNew();
// Base points to the beginning of the string that is being searched
const char *stringBase = strZ(string);
// Match points to the next delimiter match that has been found
const char *stringMatchLast = NULL;
const char *stringMatch = NULL;
MEM_CONTEXT_BEGIN(lstMemContext((List *)this))
{
do
{
// Find a delimiter match
stringMatch = strstr(stringMatchLast == NULL ? stringBase : stringMatchLast, delimiter);
// 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);
strLstAdd(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
{
if (stringMatchLast != NULL && strlen(stringBase) - strlen(delimiter) >= size)
{
strLstAdd(this, strNewN(stringBase, (size_t)((stringMatchLast - strlen(delimiter)) - stringBase)));
stringBase = stringMatchLast;
}
strLstAdd(this, strNew(stringBase));
}
}
while (stringMatch != NULL);
}
MEM_CONTEXT_END();
FUNCTION_TEST_RETURN(this);
}
/***********************************************************************************************************************************
Helper function for helpRender() to make output look good on a console
***********************************************************************************************************************************/
@ -55,7 +124,7 @@ helpRenderText(const String *text, size_t indent, bool indentFirst, size_t lengt
strCat(result, LF_STR);
// Split the paragraph into lines that don't exceed the line length
StringList *partList = strLstNewSplitSizeZ(strLstGet(lineList, lineIdx), " ", length - indent);
StringList *partList = helpRenderSplitSize(strLstGet(lineList, lineIdx), " ", length - indent);
for (unsigned int partIdx = 0; partIdx < strLstSize(partList); partIdx++)
{

View File

@ -17,9 +17,9 @@ Object type
***********************************************************************************************************************************/
struct List
{
ListPub pub; // Publicly accessible variables
MemContext *memContext;
size_t itemSize;
unsigned int listSize;
unsigned int listSizeMax;
SortOrder sortOrder;
unsigned char *listAlloc; // Pointer to memory allocated for the list
@ -59,21 +59,6 @@ lstNew(size_t itemSize, ListParam param)
FUNCTION_TEST_RETURN(this);
}
/**********************************************************************************************************************************/
void *
lstAdd(List *this, const void *item)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(LIST, this);
FUNCTION_TEST_PARAM_P(VOID, item);
FUNCTION_TEST_END();
ASSERT(this != NULL);
ASSERT(item != NULL);
FUNCTION_TEST_RETURN(lstInsert(this, lstSize(this), item));
}
/**********************************************************************************************************************************/
List *
lstClear(List *this)
@ -92,7 +77,7 @@ lstClear(List *this)
}
MEM_CONTEXT_END();
this->listSize = 0;
this->pub.listSize = 0;
this->listSizeMax = 0;
}
@ -137,8 +122,8 @@ lstGet(const List *this, unsigned int listIdx)
ASSERT(this != NULL);
// Ensure list index is in range
if (listIdx >= this->listSize)
THROW_FMT(AssertError, "cannot get index %u from list with %u value(s)", listIdx, this->listSize);
if (listIdx >= lstSize(this))
THROW_FMT(AssertError, "cannot get index %u from list with %u value(s)", listIdx, lstSize(this));
// Return pointer to list item
FUNCTION_TEST_RETURN(this->list + (listIdx * this->itemSize));
@ -154,26 +139,11 @@ lstGetLast(const List *this)
ASSERT(this != NULL);
// Ensure there are items in the list
if (this->listSize == 0)
if (lstSize(this) == 0)
THROW(AssertError, "cannot get last from list with no values");
// Return pointer to list item
FUNCTION_TEST_RETURN(lstGet(this, this->listSize - 1));
}
/**********************************************************************************************************************************/
bool
lstExists(const List *this, const void *item)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(LIST, this);
FUNCTION_TEST_PARAM_P(VOID, item);
FUNCTION_TEST_END();
ASSERT(this != NULL);
ASSERT(item != NULL);
FUNCTION_TEST_RETURN(lstFind(this, item) != NULL);
FUNCTION_TEST_RETURN(lstGet(this, lstSize(this) - 1));
}
/**********************************************************************************************************************************/
@ -190,13 +160,13 @@ lstFind(const List *this, const void *item)
ASSERT(item != NULL);
if (this->sortOrder == sortOrderAsc)
FUNCTION_TEST_RETURN(bsearch(item, this->list, this->listSize, this->itemSize, this->comparator));
FUNCTION_TEST_RETURN(bsearch(item, this->list, lstSize(this), this->itemSize, this->comparator));
else if (this->sortOrder == sortOrderDesc)
{
// Assign the list for the descending comparator to use
comparatorDescList = this;
FUNCTION_TEST_RETURN(bsearch(item, this->list, this->listSize, this->itemSize, lstComparatorDesc));
FUNCTION_TEST_RETURN(bsearch(item, this->list, lstSize(this), this->itemSize, lstComparatorDesc));
}
// Fall back on an iterative search
@ -260,7 +230,7 @@ lstIdx(const List *this, const void *item)
size_t result = (size_t)((unsigned char * const)item - this->list) / this->itemSize;
// Item pointers should always be in range
ASSERT(result < this->listSize);
ASSERT(result < lstSize(this));
FUNCTION_TEST_RETURN((unsigned int)result);
}
@ -279,7 +249,7 @@ lstInsert(List *this, unsigned int listIdx, const void *item)
ASSERT(item != NULL);
// If list size = max then allocate more space
if (this->listSize == this->listSizeMax)
if (lstSize(this) == this->listSizeMax)
{
MEM_CONTEXT_BEGIN(this->memContext)
{
@ -303,9 +273,9 @@ lstInsert(List *this, unsigned int listIdx, const void *item)
// Else if there is space before the beginning of the list then move the list down
else if (
(this->list != this->listAlloc) &&
(this->listSize + ((uintptr_t)(this->list - this->listAlloc) / this->itemSize) == this->listSizeMax))
(lstSize(this) + ((uintptr_t)(this->list - this->listAlloc) / this->itemSize) == this->listSizeMax))
{
memmove(this->listAlloc, this->list, this->listSize * this->itemSize);
memmove(this->listAlloc, this->list, lstSize(this) * this->itemSize);
this->list = this->listAlloc;
}
@ -319,7 +289,7 @@ lstInsert(List *this, unsigned int listIdx, const void *item)
// Copy item into the list
this->sortOrder = sortOrderNone;
memcpy(itemPtr, item, this->itemSize);
this->listSize++;
this->pub.listSize++;
FUNCTION_TEST_RETURN(itemPtr);
}
@ -337,7 +307,7 @@ lstRemoveIdx(List *this, unsigned int listIdx)
ASSERT(listIdx <= lstSize(this));
// Decrement the list size
this->listSize--;
this->pub.listSize--;
// If this is the first item then move the list pointer up to avoid moving all the items
if (listIdx == 0)
@ -386,10 +356,10 @@ lstRemoveLast(List *this)
ASSERT(this != NULL);
if (this->listSize == 0)
if (lstSize(this) == 0)
THROW(AssertError, "cannot remove last from list with no values");
FUNCTION_TEST_RETURN(lstRemoveIdx(this, this->listSize - 1));
FUNCTION_TEST_RETURN(lstRemoveIdx(this, lstSize(this) - 1));
}
/**********************************************************************************************************************************/
@ -405,19 +375,6 @@ lstMemContext(const List *this)
FUNCTION_TEST_RETURN(this->memContext);
}
/**********************************************************************************************************************************/
unsigned int
lstSize(const List *this)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(LIST, this);
FUNCTION_TEST_END();
ASSERT(this != NULL);
FUNCTION_TEST_RETURN(this->listSize);
}
/**********************************************************************************************************************************/
List *
lstSort(List *this, SortOrder sortOrder)
@ -433,7 +390,7 @@ lstSort(List *this, SortOrder sortOrder)
switch (sortOrder)
{
case sortOrderAsc:
qsort(this->list, this->listSize, this->itemSize, this->comparator);
qsort(this->list, lstSize(this), this->itemSize, this->comparator);
break;
case sortOrderDesc:
@ -441,7 +398,7 @@ lstSort(List *this, SortOrder sortOrder)
// Assign the list that will be sorted for the comparator function to use
comparatorDescList = this;
qsort(this->list, this->listSize, this->itemSize, lstComparatorDesc);
qsort(this->list, lstSize(this), this->itemSize, lstComparatorDesc);
break;
}
@ -475,5 +432,5 @@ lstComparatorSet(List *this, ListComparator *comparator)
String *
lstToLog(const List *this)
{
return strNewFmt("{size: %u}", this->listSize);
return strNewFmt("{size: %u}", lstSize(this));
}

View File

@ -63,12 +63,35 @@ typedef struct ListParam
List *lstNew(size_t itemSize, ListParam param);
/***********************************************************************************************************************************
Getters/Setters
***********************************************************************************************************************************/
typedef struct ListPub
{
unsigned int listSize; // List size
} ListPub;
// Set a new comparator
List *lstComparatorSet(List *this, ListComparator *comparator);
// List size
__attribute__((always_inline)) static inline unsigned int
lstSize(const List *this)
{
ASSERT_INLINE(this != NULL);
return ((const ListPub *)this)->listSize;
}
// Is the list empty?
__attribute__((always_inline)) static inline bool
lstEmpty(const List *this)
{
return lstSize(this) == 0;
}
/***********************************************************************************************************************************
Functions
***********************************************************************************************************************************/
// Add an item to the end of the list
void *lstAdd(List *this, const void *item);
// Clear items from a list
List *lstClear(List *this);
@ -76,20 +99,31 @@ List *lstClear(List *this);
void *lstGet(const List *this, unsigned int listIdx);
void *lstGetLast(const List *this);
// Does an item exist in the list?
bool lstExists(const List *this, const void *item);
// Find an item in the list
void *lstFind(const List *this, const void *item);
void *lstFindDefault(const List *this, const void *item, void *itemDefault);
unsigned int lstFindIdx(const List *this, const void *item);
// Does an item exist in the list?
__attribute__((always_inline)) static inline bool
lstExists(const List *this, const void *item)
{
return lstFind(this, item) != NULL;
}
// Get the index of a list item
unsigned int lstIdx(const List *this, const void *item);
// Insert an item into the list
void *lstInsert(List *this, unsigned int listIdx, const void *item);
// Add an item to the end of the list
__attribute__((always_inline)) static inline void *
lstAdd(List *this, const void *item)
{
return lstInsert(this, lstSize(this), item);
}
// Memory context for this list
MemContext *lstMemContext(const List *this);
@ -101,25 +135,9 @@ bool lstRemove(List *this, const void *item);
List *lstRemoveIdx(List *this, unsigned int listIdx);
List *lstRemoveLast(List *this);
// Return list size
unsigned int lstSize(const List *this);
// Is the list empty?
__attribute__((always_inline)) static inline bool
lstEmpty(const List *this)
{
return lstSize(this) == 0;
}
// List sort
List *lstSort(List *this, SortOrder sortOrder);
/***********************************************************************************************************************************
Getters/Setters
***********************************************************************************************************************************/
// Set a new comparator
List *lstComparatorSet(List *this, ListComparator *comparator);
/***********************************************************************************************************************************
Destructor
***********************************************************************************************************************************/

View File

@ -12,64 +12,25 @@ String List Handler
#include "common/memContext.h"
#include "common/type/stringList.h"
/***********************************************************************************************************************************
Wrapper for lstNew*()
***********************************************************************************************************************************/
StringList *
strLstNew(void)
{
FUNCTION_TEST_VOID();
FUNCTION_TEST_RETURN((StringList *)lstNewP(sizeof(String *), .comparator = lstComparatorStr));
}
/***********************************************************************************************************************************
Internal add -- the string must have been created in the list's mem context before being passed
***********************************************************************************************************************************/
static String *
__attribute__((always_inline)) static inline String *
strLstAddInternal(StringList *this, String *string)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRING_LIST, this);
FUNCTION_TEST_PARAM(STRING, string);
FUNCTION_TEST_END();
ASSERT(this != NULL);
FUNCTION_TEST_RETURN(*(String **)lstAdd((List *)this, &string));
return *(String **)lstAdd((List *)this, &string);
}
/***********************************************************************************************************************************
Internal insert -- the string must have been created in the list's mem context before being passed
***********************************************************************************************************************************/
static String *
__attribute__((always_inline)) static inline String *
strLstInsertInternal(StringList *this, unsigned int listIdx, String *string)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRING_LIST, this);
FUNCTION_TEST_PARAM(UINT, listIdx);
FUNCTION_TEST_PARAM(STRING, string);
FUNCTION_TEST_END();
ASSERT(this != NULL);
FUNCTION_TEST_RETURN(*(String **)lstInsert((List *)this, listIdx, &string));
return *(String **)lstInsert((List *)this, listIdx, &string);
}
/**********************************************************************************************************************************/
StringList *
strLstNewSplit(const String *string, const String *delimiter)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRING, string);
FUNCTION_TEST_PARAM(STRING, delimiter);
FUNCTION_TEST_END();
ASSERT(string != NULL);
ASSERT(delimiter != NULL);
FUNCTION_TEST_RETURN(strLstNewSplitZ(string, strZ(delimiter)));
}
StringList *
strLstNewSplitZ(const String *string, const char *delimiter)
{
@ -114,85 +75,6 @@ strLstNewSplitZ(const String *string, const char *delimiter)
FUNCTION_TEST_RETURN(this);
}
/**********************************************************************************************************************************/
StringList *
strLstNewSplitSize(const String *string, const String *delimiter, size_t size)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRING, string);
FUNCTION_TEST_PARAM(STRING, delimiter);
FUNCTION_TEST_PARAM(SIZE, size);
FUNCTION_TEST_END();
ASSERT(string != NULL);
ASSERT(delimiter != NULL);
FUNCTION_TEST_RETURN(strLstNewSplitSizeZ(string, strZ(delimiter), size));
}
StringList *
strLstNewSplitSizeZ(const String *string, const char *delimiter, size_t size)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRING, string);
FUNCTION_TEST_PARAM(STRINGZ, delimiter);
FUNCTION_TEST_PARAM(SIZE, size);
FUNCTION_TEST_END();
ASSERT(string != NULL);
ASSERT(delimiter != NULL);
// Create the list
StringList *this = strLstNew();
// Base points to the beginning of the string that is being searched
const char *stringBase = strZ(string);
// Match points to the next delimiter match that has been found
const char *stringMatchLast = NULL;
const char *stringMatch = NULL;
MEM_CONTEXT_BEGIN(lstMemContext((List *)this))
{
do
{
// Find a delimiter match
stringMatch = strstr(stringMatchLast == NULL ? stringBase : stringMatchLast, delimiter);
// 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
{
if (stringMatchLast != NULL && strlen(stringBase) - strlen(delimiter) >= size)
{
strLstAddInternal(this, strNewN(stringBase, (size_t)((stringMatchLast - strlen(delimiter)) - stringBase)));
stringBase = stringMatchLast;
}
strLstAddInternal(this, strNew(stringBase));
}
}
while (stringMatch != NULL);
}
MEM_CONTEXT_END();
FUNCTION_TEST_RETURN(this);
}
/**********************************************************************************************************************************/
StringList *
strLstNewVarLst(const VariantList *sourceList)
@ -247,35 +129,6 @@ strLstDup(const StringList *sourceList)
FUNCTION_TEST_RETURN(this);
}
/**********************************************************************************************************************************/
bool
strLstExists(const StringList *this, const String *string)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRING_LIST, this);
FUNCTION_TEST_PARAM(STRING, string);
FUNCTION_TEST_END();
ASSERT(this != NULL);
FUNCTION_TEST_RETURN(lstExists((List *)this, &string));
}
bool
strLstExistsZ(const StringList *this, const char *cstring)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRING_LIST, this);
FUNCTION_TEST_PARAM(STRINGZ, cstring);
FUNCTION_TEST_END();
ASSERT(this != NULL);
const String *string = cstring == NULL ? NULL : STR(cstring);
FUNCTION_TEST_RETURN(lstExists((List *)this, &string));
}
/**********************************************************************************************************************************/
String *
strLstAdd(StringList *this, const String *string)
@ -337,20 +190,6 @@ strLstAddZ(StringList *this, const char *string)
FUNCTION_TEST_RETURN(result);
}
/**********************************************************************************************************************************/
String *
strLstGet(const StringList *this, unsigned int listIdx)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRING_LIST, this);
FUNCTION_TEST_PARAM(UINT, listIdx);
FUNCTION_TEST_END();
ASSERT(this != NULL);
FUNCTION_TEST_RETURN(*(String **)lstGet((List *)this, listIdx));
}
/**********************************************************************************************************************************/
String *
strLstInsert(StringList *this, unsigned int listIdx, const String *string)
@ -374,43 +213,6 @@ strLstInsert(StringList *this, unsigned int listIdx, const String *string)
FUNCTION_TEST_RETURN(result);
}
String *
strLstInsertZ(StringList *this, unsigned int listIdx, const char *string)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRING_LIST, this);
FUNCTION_TEST_PARAM(UINT, listIdx);
FUNCTION_TEST_PARAM(STRINGZ, string);
FUNCTION_TEST_END();
ASSERT(this != NULL);
String *result = NULL;
MEM_CONTEXT_BEGIN(lstMemContext((List *)this))
{
result = strLstInsertInternal(this, listIdx, strNew(string));
}
MEM_CONTEXT_END();
FUNCTION_TEST_RETURN(result);
}
/**********************************************************************************************************************************/
String *
strLstJoin(const StringList *this, const char *separator)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRING_LIST, this);
FUNCTION_TEST_PARAM(STRINGZ, separator);
FUNCTION_TEST_END();
ASSERT(this != NULL);
ASSERT(separator != NULL);
FUNCTION_TEST_RETURN(strLstJoinQuote(this, separator, ""));
}
/**********************************************************************************************************************************/
String *
strLstJoinQuote(const StringList *this, const char *separator, const char *quote)
@ -502,22 +304,6 @@ strLstMergeAnti(const StringList *this, const StringList *anti)
FUNCTION_TEST_RETURN(result);
}
/**********************************************************************************************************************************/
StringList *
strLstMove(StringList *this, MemContext *parentNew)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRING_LIST, this);
FUNCTION_TEST_PARAM(MEM_CONTEXT, parentNew);
FUNCTION_TEST_END();
ASSERT(parentNew != NULL);
lstMove((List *)this, parentNew);
FUNCTION_TEST_RETURN(this);
}
/**********************************************************************************************************************************/
const char **
strLstPtr(const StringList *this)
@ -543,93 +329,9 @@ strLstPtr(const StringList *this)
FUNCTION_TEST_RETURN(list);
}
/**********************************************************************************************************************************/
bool
strLstRemove(StringList *this, const String *item)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRING_LIST, this);
FUNCTION_TEST_PARAM(STRING, item);
FUNCTION_TEST_END();
ASSERT(this != NULL);
ASSERT(item != NULL);
FUNCTION_TEST_RETURN(lstRemove((List *)this, &item));
}
StringList *
strLstRemoveIdx(StringList *this, unsigned int listIdx)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRING_LIST, this);
FUNCTION_TEST_PARAM(UINT, listIdx);
FUNCTION_TEST_END();
ASSERT(this != NULL);
FUNCTION_TEST_RETURN((StringList *)lstRemoveIdx((List *)this, listIdx));
}
/**********************************************************************************************************************************/
unsigned int
strLstSize(const StringList *this)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRING_LIST, this);
FUNCTION_TEST_END();
ASSERT(this != NULL);
FUNCTION_TEST_RETURN(lstSize((List *)this));
}
/**********************************************************************************************************************************/
StringList *
strLstSort(StringList *this, SortOrder sortOrder)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRING_LIST, this);
FUNCTION_TEST_PARAM(ENUM, sortOrder);
FUNCTION_TEST_END();
ASSERT(this != NULL);
lstSort((List *)this, sortOrder);
FUNCTION_TEST_RETURN(this);
}
/**********************************************************************************************************************************/
StringList *
strLstComparatorSet(StringList *this, ListComparator *comparator)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRING_LIST, this);
FUNCTION_TEST_PARAM(FUNCTIONP, comparator);
FUNCTION_TEST_END();
lstComparatorSet((List *)this, comparator);
FUNCTION_TEST_RETURN(this);
}
/**********************************************************************************************************************************/
String *
strLstToLog(const StringList *this)
{
return strNewFmt("{[%s]}", strZ(strLstJoinQuote(this, ", ", "\"")));
}
/**********************************************************************************************************************************/
void
strLstFree(StringList *this)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRING_LIST, this);
FUNCTION_TEST_END();
lstFree((List *)this);
FUNCTION_TEST_RETURN_VOID();
}

View File

@ -17,16 +17,21 @@ typedef struct StringList StringList;
/***********************************************************************************************************************************
Constructors
***********************************************************************************************************************************/
StringList *strLstNew(void);
// Create empty StringList
__attribute__((always_inline)) static inline StringList *
strLstNew(void)
{
return (StringList *)lstNewP(sizeof(String *), .comparator = lstComparatorStr);
}
// Split a string into a string list based on a delimiter
StringList *strLstNewSplit(const String *string, const String *delimiter);
StringList *strLstNewSplitZ(const String *string, const char *delimiter);
// Split a string into a string list based on a delimiter and max size per item. In other words each item in the list will be no
// longer than size even if multiple delimiters are skipped. This is useful for breaking up text on spaces, for example.
StringList *strLstNewSplitSize(const String *string, const String *delimiter, size_t size);
StringList *strLstNewSplitSizeZ(const String *string, const char *delimiter, size_t size);
__attribute__((always_inline)) static inline StringList *
strLstNewSplit(const String *string, const String *delimiter)
{
return strLstNewSplitZ(string, strZ(delimiter));
}
// New string list from a variant list -- all variants in the list must be type string
StringList *strLstNewVarLst(const VariantList *sourceList);
@ -34,6 +39,30 @@ StringList *strLstNewVarLst(const VariantList *sourceList);
// Duplicate a string list
StringList *strLstDup(const StringList *sourceList);
/***********************************************************************************************************************************
Getters/Setters
***********************************************************************************************************************************/
// Set a new comparator
__attribute__((always_inline)) static inline StringList *
strLstComparatorSet(StringList *this, ListComparator *comparator)
{
return (StringList *)lstComparatorSet((List *)this, comparator);
}
// List size
__attribute__((always_inline)) static inline unsigned int
strLstSize(const StringList *this)
{
return lstSize((List *)this);
}
// Is the list empty?
__attribute__((always_inline)) static inline bool
strLstEmpty(const StringList *this)
{
return strLstSize(this) == 0;
}
/***********************************************************************************************************************************
Functions
***********************************************************************************************************************************/
@ -43,60 +72,75 @@ String *strLstAddZ(StringList *this, const char *string);
String *strLstAddIfMissing(StringList *this, const String *string);
// Does the specified string exist in the list?
bool strLstExists(const StringList *this, const String *string);
bool strLstExistsZ(const StringList *this, const char *cstring);
__attribute__((always_inline)) static inline bool
strLstExists(const StringList *this, const String *string)
{
return lstExists((List *)this, &string);
}
// Insert into the list
String *strLstInsert(StringList *this, unsigned int listIdx, const String *string);
String *strLstInsertZ(StringList *this, unsigned int listIdx, const char *string);
// Get a string by index
String *strLstGet(const StringList *this, unsigned int listIdx);
// Join a list of strings into a single string using the specified separator
String *strLstJoin(const StringList *this, const char *separator);
__attribute__((always_inline)) static inline String *
strLstGet(const StringList *this, unsigned int listIdx)
{
return *(String **)lstGet((List *)this, listIdx);
}
// Join a list of strings into a single string using the specified separator and quote with specified quote character
String *strLstJoinQuote(const StringList *this, const char *separator, const char *quote);
// Join a list of strings into a single string using the specified separator
__attribute__((always_inline)) static inline String *
strLstJoin(const StringList *this, const char *separator)
{
return strLstJoinQuote(this, separator, "");
}
// Return all items in this list which are not in the anti list. The input lists must *both* be sorted ascending or the results will
// be undefined.
StringList *strLstMergeAnti(const StringList *this, const StringList *anti);
// Move to a new parent mem context
StringList *strLstMove(StringList *this, MemContext *parentNew);
__attribute__((always_inline)) static inline StringList *
strLstMove(StringList *this, MemContext *parentNew)
{
return (StringList *)lstMove((List *)this, parentNew);
}
// Return a null-terminated 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.
const char **strLstPtr(const StringList *this);
// Remove an item from the list
bool strLstRemove(StringList *this, const String *item);
StringList *strLstRemoveIdx(StringList *this, unsigned int listIdx);
// List size
unsigned int strLstSize(const StringList *this);
// Is the list empty?
__attribute__((always_inline)) static inline bool
strLstEmpty(const StringList *this)
strLstRemove(StringList *this, const String *item)
{
return strLstSize(this) == 0;
return lstRemove((List *)this, &item);
}
__attribute__((always_inline)) static inline StringList *
strLstRemoveIdx(StringList *this, unsigned int listIdx)
{
return (StringList *)lstRemoveIdx((List *)this, listIdx);
}
// List sort
StringList *strLstSort(StringList *this, SortOrder sortOrder);
/***********************************************************************************************************************************
Getters/Setters
***********************************************************************************************************************************/
// Set a new comparator
StringList *strLstComparatorSet(StringList *this, ListComparator *comparator);
__attribute__((always_inline)) static inline StringList *
strLstSort(StringList *this, SortOrder sortOrder)
{
return (StringList *)lstSort((List *)this, sortOrder);
}
/***********************************************************************************************************************************
Destructor
***********************************************************************************************************************************/
void strLstFree(StringList *this);
__attribute__((always_inline)) static inline void
strLstFree(StringList *this)
{
lstFree((List *)this);
}
/***********************************************************************************************************************************
Macros for function logging

View File

@ -12,16 +12,6 @@ Variant List Handler
#include "common/type/list.h"
#include "common/type/variantList.h"
/***********************************************************************************************************************************
Wrapper for lstNewP()
***********************************************************************************************************************************/
VariantList *
varLstNew(void)
{
FUNCTION_TEST_VOID();
FUNCTION_TEST_RETURN((VariantList *)lstNewP(sizeof(Variant *)));
}
/**********************************************************************************************************************************/
VariantList *
varLstNewStrLst(const StringList *stringList)
@ -63,75 +53,3 @@ varLstDup(const VariantList *source)
FUNCTION_TEST_RETURN(result);
}
/**********************************************************************************************************************************/
VariantList *
varLstAdd(VariantList *this, Variant *data)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(VARIANT_LIST, this);
FUNCTION_TEST_PARAM(VARIANT, data);
FUNCTION_TEST_END();
ASSERT(this != NULL);
lstAdd((List *)this, &data);
FUNCTION_TEST_RETURN(this);
}
/**********************************************************************************************************************************/
Variant *
varLstGet(const VariantList *this, unsigned int listIdx)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(VARIANT_LIST, this);
FUNCTION_TEST_PARAM(UINT, listIdx);
FUNCTION_TEST_END();
ASSERT(this != NULL);
FUNCTION_TEST_RETURN(*(Variant **)lstGet((List *)this, listIdx));
}
/**********************************************************************************************************************************/
unsigned int
varLstSize(const VariantList *this)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(VARIANT_LIST, this);
FUNCTION_TEST_END();
ASSERT(this != NULL);
FUNCTION_TEST_RETURN(lstSize((List *)this));
}
/**********************************************************************************************************************************/
VariantList *
varLstMove(VariantList *this, MemContext *parentNew)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(VARIANT_LIST, this);
FUNCTION_TEST_PARAM(MEM_CONTEXT, parentNew);
FUNCTION_TEST_END();
ASSERT(parentNew != NULL);
lstMove((List *)this, parentNew);
FUNCTION_TEST_RETURN(this);
}
/**********************************************************************************************************************************/
void
varLstFree(VariantList *this)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(VARIANT_LIST, this);
FUNCTION_TEST_END();
lstFree((List *)this);
FUNCTION_TEST_RETURN_VOID();
}

View File

@ -15,7 +15,12 @@ typedef struct VariantList VariantList;
/***********************************************************************************************************************************
Constructors
***********************************************************************************************************************************/
VariantList *varLstNew(void);
// Create empty VariantList
__attribute__((always_inline)) static inline VariantList *
varLstNew(void)
{
return (VariantList *)lstNewP(sizeof(Variant *));
}
// Create VariantList from StringList
VariantList *varLstNewStrLst(const StringList *stringList);
@ -24,19 +29,14 @@ VariantList *varLstNewStrLst(const StringList *stringList);
VariantList *varLstDup(const VariantList *source);
/***********************************************************************************************************************************
Functions
Getters/Setters
***********************************************************************************************************************************/
// Add to list
VariantList *varLstAdd(VariantList *this, Variant *data);
// Get by index
Variant *varLstGet(const VariantList *this, unsigned int listIdx);
// Move to new parent mem context
VariantList *varLstMove(VariantList *this, MemContext *parentNew);
// List size
unsigned int varLstSize(const VariantList *this);
__attribute__((always_inline)) static inline unsigned int
varLstSize(const VariantList *this)
{
return lstSize((List *)this);
}
// Is the list empty?
__attribute__((always_inline)) static inline bool
@ -45,10 +45,39 @@ varLstEmpty(const VariantList *this)
return varLstSize(this) == 0;
}
/***********************************************************************************************************************************
Functions
***********************************************************************************************************************************/
// Add to list
__attribute__((always_inline)) static inline VariantList *
varLstAdd(VariantList *this, Variant *data)
{
lstAdd((List *)this, &data);
return this;
}
// Get by index
__attribute__((always_inline)) static inline Variant *
varLstGet(const VariantList *this, const unsigned int listIdx)
{
return *(Variant **)lstGet((List *)this, listIdx);
}
// Move to new parent mem context
__attribute__((always_inline)) static inline VariantList *
varLstMove(VariantList *this, MemContext *parentNew)
{
return (VariantList *)lstMove((List *)this, parentNew);
}
/***********************************************************************************************************************************
Destructor
***********************************************************************************************************************************/
void varLstFree(VariantList *this);
__attribute__((always_inline)) static inline void
varLstFree(VariantList *this)
{
lstFree((List *)this);
}
/***********************************************************************************************************************************
Macros for function logging

View File

@ -122,7 +122,7 @@ unit:
# ----------------------------------------------------------------------------------------------------------------------------
- name: type-string
total: 26
total: 25
feature: string
coverage:
@ -730,7 +730,7 @@ unit:
# ----------------------------------------------------------------------------------------------------------------------------
- name: help
total: 4
total: 5
coverage:
- command/help/help

View File

@ -72,7 +72,7 @@ harnessCfgLoadRole(ConfigCommand commandId, ConfigCommandRole commandRoleId, con
// Set job retry to 0 if it is valid
if (cfgParseOptionValid(commandId, commandRoleId, cfgOptJobRetry))
strLstInsertZ(argList, 0, "--" CFGOPT_JOB_RETRY "=0");
strLstInsert(argList, 0, STRDEF("--" CFGOPT_JOB_RETRY "=0"));
// Set log path if valid
if (cfgParseOptionValid(commandId, commandRoleId, cfgOptLogPath))

View File

@ -192,7 +192,8 @@ testRun(void)
TEST_RESULT_VOID(expireBackup(infoBackup, full1, 0), "expire backup with both manifest files");
TEST_RESULT_BOOL(
(strLstSize(storageListP(storageTest, full1Path)) && strLstExistsZ(storageListP(storageTest, full1Path), "bogus")),
(strLstSize(storageListP(storageTest, full1Path)) &&
strLstExists(storageListP(storageTest, full1Path), STRDEF("bogus"))),
true, "full1 - only manifest files removed");
TEST_RESULT_VOID(expireBackup(infoBackup, full2, 0), "expire backup with no manifest - does not error");

View File

@ -47,6 +47,23 @@ testRun(void)
"Use 'pgbackrest help [command]' for more information.\n",
helpVersion));
// *****************************************************************************************************************************
if (testBegin("helpRenderSplitSize()"))
{
TEST_RESULT_STR_Z(strLstJoin(helpRenderSplitSize(STRDEF("abc def"), " ", 3), "-"), "abc-def", "two items");
TEST_RESULT_STR_Z(strLstJoin(helpRenderSplitSize(STRDEF("abc def"), " ", 4), "-"), "abc-def", "one items");
TEST_RESULT_STR_Z(strLstJoin(helpRenderSplitSize(STRDEF("abc def ghi"), " ", 4), "-"), "abc-def-ghi", "three items");
TEST_RESULT_STR_Z(strLstJoin(helpRenderSplitSize(STRDEF("abc def ghi"), " ", 8), "-"), "abc def-ghi", "three items");
TEST_RESULT_STR_Z(strLstJoin(helpRenderSplitSize(STRDEF("abc def "), " ", 4), "-"), "abc-def ", "two items");
TEST_RESULT_STR_Z(
strLstJoin(helpRenderSplitSize(STRDEF("this is a short sentence"), " ", 10), "\n"),
"this is a\n"
"short\n"
"sentence",
"empty list");
}
// *****************************************************************************************************************************
if (testBegin("helpRenderText()"))
{

View File

@ -35,7 +35,7 @@ testRun(void)
List *list = lstNewP(sizeof(void *));
TEST_RESULT_UINT(list->itemSize, sizeof(void *), "item size");
TEST_RESULT_UINT(list->listSize, 0, "list size");
TEST_RESULT_UINT(list->pub.listSize, 0, "list size");
TEST_RESULT_UINT(list->listSizeMax, 0, "list size max");
TEST_RESULT_PTR(lstMemContext(list), list->memContext, "list mem context");
TEST_RESULT_VOID(lstClear(list), "clear list");
@ -130,7 +130,7 @@ testRun(void)
TEST_RESULT_VOID(lstAdd(list, &item), "add item %d", item);
}
TEST_RESULT_INT(list->listSize, list->listSizeMax, "size equals max size");
TEST_RESULT_UINT(lstSize(list), list->listSizeMax, "size equals max size");
// Remove last item
TEST_RESULT_VOID(lstRemoveLast(list), "remove last item");

View File

@ -341,8 +341,8 @@ testRun(void)
TEST_ASSIGN(list, strLstNew(), "new list");
TEST_RESULT_VOID(strLstAddIfMissing(list, STRDEF("item1")), "add item 1");
TEST_RESULT_UINT(strLstSize(list), 1, "check size");
TEST_RESULT_BOOL(strLstExistsZ(list, "item1"), true, "check exists");
TEST_RESULT_BOOL(strLstExistsZ(list, NULL), false, "check null exists");
TEST_RESULT_BOOL(strLstExists(list, STRDEF("item1")), true, "check exists");
TEST_RESULT_BOOL(strLstExists(list, NULL), false, "check null exists");
TEST_RESULT_VOID(strLstAddIfMissing(list, STRDEF("item1")), "add item 1 again");
TEST_RESULT_UINT(strLstSize(list), 1, "check size");
TEST_RESULT_BOOL(strLstEmpty(list), false, " not empty");
@ -361,24 +361,6 @@ testRun(void)
TEST_RESULT_STR_Z(strLstJoin(strLstNewSplit(STRDEF("item1, item2"), STRDEF(", ")), ", "), "item1, item2", "two items");
}
// *****************************************************************************************************************************
if (testBegin("strLstNewSplitSize()"))
{
TEST_RESULT_STR_Z(strLstJoin(strLstNewSplitSize(STRDEF(""), STRDEF(" "), 0), ", "), "", "empty list");
TEST_RESULT_STR_Z(strLstJoin(strLstNewSplitSizeZ(STRDEF("abc def"), " ", 3), "-"), "abc-def", "two items");
TEST_RESULT_STR_Z(strLstJoin(strLstNewSplitSizeZ(STRDEF("abc def"), " ", 4), "-"), "abc-def", "one items");
TEST_RESULT_STR_Z(strLstJoin(strLstNewSplitSizeZ(STRDEF("abc def ghi"), " ", 4), "-"), "abc-def-ghi", "three items");
TEST_RESULT_STR_Z(strLstJoin(strLstNewSplitSizeZ(STRDEF("abc def ghi"), " ", 8), "-"), "abc def-ghi", "three items");
TEST_RESULT_STR_Z(strLstJoin(strLstNewSplitSizeZ(STRDEF("abc def "), " ", 4), "-"), "abc-def ", "two items");
TEST_RESULT_STR_Z(
strLstJoin(strLstNewSplitSize(STRDEF("this is a short sentence"), STRDEF(" "), 10), "\n"),
"this is a\n"
"short\n"
"sentence",
"empty list");
}
// *****************************************************************************************************************************
if (testBegin("strLstNewVarLst()"))
{
@ -430,7 +412,7 @@ testRun(void)
}
// *****************************************************************************************************************************
if (testBegin("strLstExists() and strLstExistsZ()"))
if (testBegin("strLstExists()"))
{
StringList *list = strLstNew();
strLstAddZ(list, "A");
@ -438,8 +420,6 @@ testRun(void)
TEST_RESULT_BOOL(strLstExists(list, STRDEF("B")), false, "string does not exist");
TEST_RESULT_BOOL(strLstExists(list, STRDEF("C")), true, "string exists");
TEST_RESULT_BOOL(strLstExistsZ(list, "B"), false, "string does not exist");
TEST_RESULT_BOOL(strLstExistsZ(list, "C"), true, "string exists");
}
// *****************************************************************************************************************************
@ -531,11 +511,11 @@ testRun(void)
TEST_RESULT_STR_Z(strLstToLog(list), "{[]}", "format empty list");
strLstInsertZ(list, 0, "item3");
strLstInsert(list, 0, STRDEF("item3"));
TEST_RESULT_STR_Z(strLstToLog(list), "{[\"item3\"]}", "format 1 item list");
strLstInsert(list, 0, STRDEF("item1"));
strLstInsertZ(list, 1, "item2");
strLstInsert(list, 1, STRDEF("item2"));
TEST_RESULT_STR_Z(strLstToLog(list), "{[\"item1\", \"item2\", \"item3\"]}", "format 3 item list");
}

View File

@ -174,8 +174,9 @@ testRun(void)
TEST_RESULT_UINT(backupDataPtr->backupInfoSizeDelta, 163866, " backup delta");
TEST_RESULT_STR_Z(backupDataPtr->backupPrior, "20161219-212741F", " backup prior exists");
TEST_RESULT_BOOL(
(strLstSize(backupDataPtr->backupReference) == 1 && strLstExistsZ(backupDataPtr->backupReference, "20161219-212741F")), true,
" backup reference exists");
(strLstSize(backupDataPtr->backupReference) == 1 &&
strLstExists(backupDataPtr->backupReference, STRDEF("20161219-212741F"))),
true, " backup reference exists");
TEST_RESULT_PTR(infoBackupDataByLabel(infoBackup, STRDEF("20161219-12345")), NULL, " backup label does not exist");
backupData = infoBackupData(infoBackup, 2);
@ -185,8 +186,9 @@ testRun(void)
TEST_RESULT_STR_Z(backupData.backupType, "incr", " backup type incr");
TEST_RESULT_STR_Z(backupData.backupPrior, "20161219-212741F", " backup prior exists");
TEST_RESULT_BOOL(
(strLstSize(backupData.backupReference) == 2 && strLstExistsZ(backupData.backupReference, "20161219-212741F") &&
strLstExistsZ(backupData.backupReference, "20161219-212741F_20161219-212803D")), true, " backup reference exists");
(strLstSize(backupData.backupReference) == 2 && strLstExists(backupData.backupReference, STRDEF("20161219-212741F")) &&
strLstExists(backupData.backupReference, STRDEF("20161219-212741F_20161219-212803D"))),
true, " backup reference exists");
TEST_RESULT_BOOL(backupData.optionArchiveCheck, true, " option archive check");
TEST_RESULT_BOOL(backupData.optionArchiveCopy, false, " option archive copy");
TEST_RESULT_BOOL(backupData.optionBackupStandby, false, " option backup standby");