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

Add KeyValue object.

This commit is contained in:
David Steele 2017-12-22 22:05:37 -05:00
parent 2add6cef95
commit b2a64b1f43
10 changed files with 471 additions and 18 deletions

View File

@ -42,11 +42,7 @@
</release-item>
<release-item>
<p>Add <code>String</code> object to simplify string handling.</p>
</release-item>
<release-item>
<p>Add <code>List</code> and <code>StringList</code> objects to simplify list handling.</p>
<p>Add <code>Buffer</code>, <code>KeyValue</code>, <code>List</code>, <code>String</code>, <code>StringList</code>, <code>Variant</code>, and <code>VariantList</code> objects.</p>
</release-item>
<release-item>
@ -61,6 +57,10 @@
<p>Minor changes to <code>Manifest</code> module, mostly for test reproducibility.</p>
</release-item>
<release-item>
<p>Improve <code>MemContext</code> module. Add temporary context blocks and refactor allocation arrays to include allocation size.</p>
</release-item>
<release-item>
<p>Replace <code>cfgCommandTotal()</code>/<code>cfgOptionTotal()</code> functions with constants. The constants are applicable in more cases and allow the compiler to optimize certain loops more efficiently.</p>
</release-item>
@ -72,18 +72,6 @@
<release-item>
<p>Refactor code to make valgrind happy.</p>
</release-item>
<release-item>
<p>Improve <code>MemContext</code> module. Add temporary context blocks and refactor allocation arrays to include allocation size.</p>
</release-item>
<release-item>
<p>Add <code>Buffer</code> object.</p>
</release-item>
<release-item>
<p>Add <code>Variant</code> and <code>VariantList</code> objects.</p>
</release-item>
</release-development-list>
</release-core-list>

View File

@ -7,6 +7,7 @@ pgbackrest: \
common/errorType.o \
common/memContext.o \
common/type/buffer.o \
common/type/keyValue.o \
common/type/list.o \
common/type/string.o \
common/type/stringList.o \
@ -22,6 +23,7 @@ pgbackrest: \
common/errorType.o \
common/memContext.o \
common/type/buffer.o \
common/type/keyValue.o \
common/type/list.o \
common/type/string.o \
common/type/stringList.o \

View File

@ -14,6 +14,11 @@ Include NULL
***********************************************************************************************************************************/
#include <stdio.h>
/***********************************************************************************************************************************
Include limits for data types
***********************************************************************************************************************************/
#include <limits.h>
/***********************************************************************************************************************************
Define standard integer types for portability
***********************************************************************************************************************************/
@ -35,5 +40,7 @@ Include all header files in type directory for convenience
#include "common/type/list.h"
#include "common/type/string.h"
#include "common/type/stringList.h"
#include "common/type/variant.h"
#include "common/type/variantList.h"
#endif

255
src/common/type/keyValue.c Normal file
View File

@ -0,0 +1,255 @@
/***********************************************************************************************************************************
Key Value Handler
***********************************************************************************************************************************/
#include "common/memContext.h"
#include "common/type.h"
#include "common/type/keyValue.h"
#include "common/type/variantList.h"
/***********************************************************************************************************************************
Constant to indicate key not found
***********************************************************************************************************************************/
#define KEY_NOT_FOUND UINT_MAX
/***********************************************************************************************************************************
Contains information about the key value store
***********************************************************************************************************************************/
struct KeyValue
{
MemContext *memContext; // Mem context for the store
List *list; // List of keys/values
VariantList *keyList; // List of keys
};
/***********************************************************************************************************************************
Contains information about an individual key/value pair
***********************************************************************************************************************************/
typedef struct KeyValuePair
{
Variant *key; // The key
Variant *value; // The value (this may be NULL)
} KeyValuePair;
/***********************************************************************************************************************************
Create a new key/value store
***********************************************************************************************************************************/
KeyValue *
kvNew()
{
KeyValue *this = NULL;
MEM_CONTEXT_NEW_BEGIN("keyValue")
{
// Allocate state and set context
this = memNew(sizeof(KeyValue));
this->memContext = MEM_CONTEXT_NEW();
// Initialize list
this->list = lstNew(sizeof(KeyValuePair));
this->keyList = varLstNew();
}
MEM_CONTEXT_NEW_END();
return this;
}
/***********************************************************************************************************************************
Duplicate key/value store
***********************************************************************************************************************************/
KeyValue *
kvDup(const KeyValue *source)
{
KeyValue *this = kvNew();
// Duplicate all key/values
for (unsigned int listIdx = 0; listIdx < lstSize(source->list); listIdx++)
{
const KeyValuePair *sourcePair = (const KeyValuePair *)lstGet(source->list, listIdx);
// Copy the pair
KeyValuePair pair;
pair.key = varDup(sourcePair->key);
pair.value = varDup(sourcePair->value);
// Add to the list
lstAdd(this->list, &pair);
}
this->keyList = varLstDup(source->keyList);
return this;
}
/***********************************************************************************************************************************
Get key index if it exists
***********************************************************************************************************************************/
static unsigned int
kvGetIdx(const KeyValue *this, const Variant *key)
{
// Search for the key
unsigned int listIdx = 0;
for (; listIdx < lstSize(this->list); listIdx++)
{
const KeyValuePair *pair = (const KeyValuePair *)lstGet(this->list, listIdx);
// Break if the key matches
if (varEq(key, pair->key))
break;
}
// If key was not found
if (listIdx == lstSize(this->list))
return KEY_NOT_FOUND;
return listIdx;
}
/***********************************************************************************************************************************
Get list of keys
***********************************************************************************************************************************/
const VariantList *
kvKeyList(const KeyValue *this)
{
return this->keyList;
}
/***********************************************************************************************************************************
Internal put key/value pair
Handles the common logic for the external put functions. The correct mem context should be set before calling this function.
***********************************************************************************************************************************/
static void
kvPutInternal(KeyValue *this, const Variant *key, Variant *value)
{
// Find the key
unsigned int listIdx = kvGetIdx(this, key);
// If the key was not found then add it
if (listIdx == KEY_NOT_FOUND)
{
// Copy the pair
KeyValuePair pair;
pair.key = varDup(key);
pair.value = value;
// Add to the list
lstAdd(this->list, &pair);
// Add to the key list
varLstAdd(this->keyList, varDup(key));
}
// Else update it
else
{
KeyValuePair *pair = (KeyValuePair *)lstGet(this->list, listIdx);
if (pair->value != NULL)
varFree(pair->value);
pair->value = value;
}
}
/***********************************************************************************************************************************
Put key/value pair
***********************************************************************************************************************************/
KeyValue *
kvPut(KeyValue *this, const Variant *key, const Variant *value)
{
MEM_CONTEXT_BEGIN(this->memContext)
{
kvPutInternal(this, key, varDup(value));
}
MEM_CONTEXT_END();
return this;
}
/***********************************************************************************************************************************
Add value to key -- if the key does not exist then this works the same as kvPut()
***********************************************************************************************************************************/
KeyValue *
kvAdd(KeyValue *this, const Variant *key, const Variant *value)
{
MEM_CONTEXT_BEGIN(this->memContext)
{
// Find the key
unsigned int listIdx = kvGetIdx(this, key);
// If the key was not found then add it
if (listIdx == KEY_NOT_FOUND)
{
kvPutInternal(this, key, varDup(value));
}
// Else create or add to the variant list
else
{
KeyValuePair *pair = (KeyValuePair *)lstGet(this->list, listIdx);
if (pair->value == NULL)
pair->value = varDup(value);
else if (varType(pair->value) != varTypeVariantList)
{
Variant *valueList = varNewVarLstEmpty();
varLstAdd(varVarLst(valueList), pair->value);
varLstAdd(varVarLst(valueList), varDup(value));
pair->value = valueList;
}
else
varLstAdd(varVarLst(pair->value), varDup(value));
}
}
MEM_CONTEXT_END();
return this;
}
/***********************************************************************************************************************************
Put key/value store
If this is called on an existing key it will replace the key with an empty kev/value store, even if the key already contains a
key/value store.
***********************************************************************************************************************************/
KeyValue *
kvPutKv(KeyValue *this, const Variant *key)
{
KeyValue *result = NULL;
MEM_CONTEXT_BEGIN(this->memContext)
{
Variant *keyValue = varNewKv();
result = varKv(keyValue);
kvPutInternal(this, key, keyValue);
}
MEM_CONTEXT_END();
return result;
}
/***********************************************************************************************************************************
Get a value using the key
***********************************************************************************************************************************/
const Variant *
kvGet(const KeyValue *this, const Variant *key)
{
Variant *result = NULL;
// Find the key
unsigned int listIdx = kvGetIdx(this, key);
if (listIdx != KEY_NOT_FOUND)
result = ((KeyValuePair *)lstGet(this->list, listIdx))->value;
return result;
}
/***********************************************************************************************************************************
Free the string
***********************************************************************************************************************************/
void
kvFree(KeyValue *this)
{
memContextFree(this->memContext);
}

View File

@ -0,0 +1,26 @@
/***********************************************************************************************************************************
Key Value Handler
***********************************************************************************************************************************/
#ifndef COMMON_TYPE_KEYVALUE_H
#define COMMON_TYPE_KEYVALUE_H
#include "common/type/variant.h"
/***********************************************************************************************************************************
Key value object
***********************************************************************************************************************************/
typedef struct KeyValue KeyValue;
/***********************************************************************************************************************************
Functions
***********************************************************************************************************************************/
KeyValue *kvNew();
KeyValue *kvDup(const KeyValue *source);
KeyValue *kvAdd(KeyValue *this, const Variant *key, const Variant *value);
const VariantList *kvKeyList(const KeyValue *this);
KeyValue *kvPut(KeyValue *this, const Variant *key, const Variant *value);
KeyValue *kvPutKv(KeyValue *this, const Variant *key);
const Variant *kvGet(const KeyValue *this, const Variant *key);
void kvFree(KeyValue *this);
#endif

View File

@ -88,6 +88,13 @@ varDup(const Variant *this)
break;
}
case varTypeKeyValue:
{
KeyValue *data = kvDup(varKv(this));
result = varNewInternal(varTypeKeyValue, (void *)&data, sizeof(data));
break;
}
case varTypeString:
{
result = varNewStr(varStr(this));
@ -336,6 +343,38 @@ varInt(const Variant *this)
return *((int *)varData(this));
}
/***********************************************************************************************************************************
New key/value variant
***********************************************************************************************************************************/
Variant *
varNewKv()
{
// Create the variant
KeyValue *data = kvNew();
return varNewInternal(varTypeKeyValue, (void *)&data, sizeof(data));
}
/***********************************************************************************************************************************
Return key/value
***********************************************************************************************************************************/
KeyValue *
varKv(const Variant *this)
{
KeyValue *result = NULL;
if (this != NULL)
{
// Only valid for key/value
if (this->type != varTypeKeyValue)
THROW(AssertError, "variant type is not 'KeyValue'");
// Get the string
result = *((KeyValue **)varData(this));
}
return result;
}
/***********************************************************************************************************************************
Return int regardless of variant type
***********************************************************************************************************************************/
@ -481,6 +520,12 @@ varFree(Variant *this)
{
switch (this->type)
{
case varTypeKeyValue:
{
kvFree(varKv(this));
break;
}
case varTypeString:
{
strFree(varStr(this));

View File

@ -27,6 +27,7 @@ typedef struct Variant Variant;
/***********************************************************************************************************************************
Functions
***********************************************************************************************************************************/
#include "common/type/keyValue.h"
#include "common/type/string.h"
#include "common/type/variantList.h"
@ -42,6 +43,9 @@ Variant *varNewInt(int data);
int varInt(const Variant *this);
int varIntForce(const Variant *this);
Variant *varNewKv();
KeyValue *varKv(const Variant *this);
Variant *varNewStr(const String *data);
Variant *varNewStrZ(const char *data);
String *varStr(const Variant *this);

View File

@ -173,7 +173,7 @@ my $oTestDef =
},
{
&TESTDEF_NAME => 'type-variant',
&TESTDEF_TOTAL => 5,
&TESTDEF_TOTAL => 6,
&TESTDEF_C => true,
&TESTDEF_COVERAGE =>
@ -191,6 +191,16 @@ my $oTestDef =
'common/type/variantList' => TESTDEF_COVERAGE_FULL,
},
},
{
&TESTDEF_NAME => 'type-key-value',
&TESTDEF_TOTAL => 2,
&TESTDEF_C => true,
&TESTDEF_COVERAGE =>
{
'common/type/keyValue' => TESTDEF_COVERAGE_FULL,
},
},
{
&TESTDEF_NAME => 'encode',
&TESTDEF_TOTAL => 1,

View File

@ -0,0 +1,91 @@
/***********************************************************************************************************************************
Test Key Value Data Type
***********************************************************************************************************************************/
/***********************************************************************************************************************************
Test Run
***********************************************************************************************************************************/
void testRun()
{
// -----------------------------------------------------------------------------------------------------------------------------
if (testBegin("kvNew() and kvFree()"))
{
KeyValue *store = NULL;
TEST_ASSIGN(store, kvNew(), "new store");
TEST_RESULT_PTR_NE(store->memContext, NULL, "mem context set");
TEST_RESULT_PTR_NE(store->list, NULL, "list set");
TEST_RESULT_INT(lstSize(store->list), 0, "list empty");
TEST_RESULT_VOID(kvFree(store), "free store");
}
// -----------------------------------------------------------------------------------------------------------------------------
if (testBegin("kvPut(), kvAdd(), kvKeyList(), kvGet(), and kvDup()"))
{
KeyValue *store = NULL;
TEST_ASSIGN(store, kvNew(), "new store");
// Set various data types
// -------------------------------------------------------------------------------------------------------------------------
TEST_RESULT_PTR(kvPut(store, varNewStr(strNew("str-key")), varNewStr(strNew("str-value"))), store, "put string/string");
TEST_RESULT_PTR(kvPut(store, varNewInt(42), varNewInt(57)), store, "put int/int");
TEST_RESULT_PTR(kvPut(store, varNewStr(strNew("str-key-int")), varNewInt(99)), store, "put string/int");
TEST_RESULT_PTR(kvPut(store, varNewInt(78), NULL), store, "put int/null");
// Get the types and make sure they have the correct value
// -------------------------------------------------------------------------------------------------------------------------
TEST_RESULT_STR(strPtr(varStr(kvGet(store, varNewStr(strNew("str-key"))))), "str-value", "get string/string");
TEST_RESULT_INT(varInt(kvGet(store, varNewInt(42))), 57, "get int/int");
TEST_RESULT_INT(varInt(kvGet(store, varNewStr(strNew("str-key-int")))), 99, "get string/int");
TEST_RESULT_PTR(kvGet(store, varNewInt(78)), NULL, "get int/null");
// Check that a null value can be changed to non-null
// -------------------------------------------------------------------------------------------------------------------------
TEST_RESULT_PTR(kvPut(store, varNewInt(78), varNewInt(66)), store, "update int/null to int/int");
TEST_RESULT_INT(varInt(kvGet(store, varNewInt(78))), 66, "get int/int");
// Check that a value can be changed
// -------------------------------------------------------------------------------------------------------------------------
TEST_RESULT_PTR(kvPut(store, varNewInt(78), varNewBool(false)), store, "update int/int to int/bool");
TEST_RESULT_INT(varBool(kvGet(store, varNewInt(78))), false, "get int/bool");
// Use add to create variant list
// -------------------------------------------------------------------------------------------------------------------------
TEST_RESULT_PTR(kvAdd(store, varNewInt(99), NULL), store, "add int/null");
TEST_RESULT_PTR(kvAdd(store, varNewInt(99), varNewInt(1)), store, "add int/int");
TEST_RESULT_PTR(kvAdd(store, varNewInt(99), varNewInt(2)), store, "add int/int");
TEST_RESULT_PTR(kvAdd(store, varNewInt(99), varNewInt(3)), store, "add int/int");
TEST_RESULT_INT(varInt(varLstGet(varVarLst(kvGet(store, varNewInt(99))), 0)), 1, "get int/int");
TEST_RESULT_INT(varInt(varLstGet(varVarLst(kvGet(store, varNewInt(99))), 1)), 2, "get int/int");
TEST_RESULT_INT(varInt(varLstGet(varVarLst(kvGet(store, varNewInt(99))), 2)), 3, "get int/int");
// Check item in key list
// -------------------------------------------------------------------------------------------------------------------------
TEST_RESULT_INT(varInt(varLstGet(kvKeyList(store), 1)), 42, "key list");
// Create a new kv and add it to this kv
// -------------------------------------------------------------------------------------------------------------------------
KeyValue *storeSub = kvPutKv(store, varNewStr(strNew("kv-key")));
kvPut(storeSub, varNewStr(strNew("str-sub-key")), varNewStr(strNew("str-sub-value")));
TEST_RESULT_STR(
strPtr(varStr(kvGet(varKv(kvGet(store, varNewStr(strNew("kv-key")))), varNewStr(strNew("str-sub-key"))))),
"str-sub-value", "get string/kv");
// Duplicate the kv
// -------------------------------------------------------------------------------------------------------------------------
KeyValue *storeDup = kvDup(store);
TEST_RESULT_INT(varBool(kvGet(store, varNewInt(78))), false, "get int/bool");
TEST_RESULT_INT(varInt(varLstGet(varVarLst(kvGet(store, varNewInt(99))), 2)), 3, "get int/int");
TEST_RESULT_STR(
strPtr(varStr(kvGet(varKv(kvGet(storeDup, varNewStr(strNew("kv-key")))), varNewStr(strNew("str-sub-key"))))),
"str-sub-value", "get string/kv");
TEST_RESULT_VOID(kvFree(storeDup), "free dup store");
TEST_RESULT_VOID(kvFree(store), "free store");
}
}

View File

@ -106,6 +106,31 @@ void testRun()
TEST_RESULT_BOOL(varEq(varNewInt(444), varNewInt(123)), false, "int, int not eq");
}
// -----------------------------------------------------------------------------------------------------------------------------
if (testBegin("keyValue"))
{
TEST_ERROR(varKv(varNewInt(66)), AssertError, "variant type is not 'KeyValue'");
// -------------------------------------------------------------------------------------------------------------------------
Variant *keyValue = NULL;
TEST_ASSIGN(keyValue, varNewKv(), "new");
TEST_RESULT_PTR(kvPut(varKv(keyValue), varNewInt(44), varNewInt(55)), varKv(keyValue), " put int/int");
TEST_RESULT_INT(varInt(kvGet(varKv(keyValue), varNewInt(44))), 55, " get int/int");
// -------------------------------------------------------------------------------------------------------------------------
Variant *keyValueDup = NULL;
TEST_ASSIGN(keyValueDup, varDup(keyValue), "duplicate");
TEST_RESULT_INT(varInt(kvGet(varKv(keyValueDup), varNewInt(44))), 55, " get int/int");
varFree(keyValue);
varFree(keyValueDup);
// -------------------------------------------------------------------------------------------------------------------------
TEST_ERROR(varEq(varNewKv(), varNewKv()), AssertError, "unable to test equality for KeyValue");
}
// -----------------------------------------------------------------------------------------------------------------------------
if (testBegin("String"))
{