1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-03-03 14:52:21 +02:00

Add uint64 variant type and supporting conversion functions.

Contributed by Cynthia Shang.
Reviewed by Stephen Frost.
This commit is contained in:
Cynthia Shang 2018-07-12 15:23:18 -04:00 committed by David Steele
parent 0e331b12ba
commit 0e6b927a17
8 changed files with 296 additions and 15 deletions

View File

@ -23,6 +23,15 @@
<p>Improve performance of string to int conversion. Use <code>strtoll()</code> instead of <code>sprintf()</code> for conversion. Also use available integer min/max constants rather than hard-coded values.</p>
</release-item>
<release-item>
<release-item-contributor-list>
<release-item-contributor id="shang.cynthia"/>
<release-item-reviewer id="frost.stephen"/>
</release-item-contributor-list>
<p>Add <code>uint64</code> variant type and supporting conversion functions.</p>
</release-item>
</release-development-list>
</release-core-list>

View File

@ -63,6 +63,31 @@ cvtZToInt64Internal(const char *value, const char *type)
FUNCTION_TEST_RESULT(INT64, result);
}
/***********************************************************************************************************************************
Convert zero-terminated string to uint64 and validate result
***********************************************************************************************************************************/
static uint64_t
cvtZToUInt64Internal(const char *value, const char *type)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(CHARP, value);
FUNCTION_TEST_PARAM(CHARP, type);
FUNCTION_TEST_ASSERT(value != NULL);
FUNCTION_TEST_ASSERT(type != NULL);
FUNCTION_TEST_END();
// Convert from string
errno = 0;
char *endPtr = NULL;
uint64_t result = strtoull(value, &endPtr, 10);
// Validate the result
cvtZToIntValid(errno, value, endPtr, type);
FUNCTION_TEST_RESULT(UINT64, result);
}
/***********************************************************************************************************************************
Convert uint64 to zero-terminated string
***********************************************************************************************************************************/
@ -289,7 +314,7 @@ cvtUIntToZ(unsigned int value, char *buffer, size_t bufferSize)
}
/***********************************************************************************************************************************
Convert uint64 to zero-terminated string
Convert uint64 to zero-terminated string and visa versa
***********************************************************************************************************************************/
size_t
cvtUInt64ToZ(uint64_t value, char *buffer, size_t bufferSize)
@ -309,3 +334,21 @@ cvtUInt64ToZ(uint64_t value, char *buffer, size_t bufferSize)
FUNCTION_TEST_RESULT(SIZE, result);
}
uint64_t
cvtZToUInt64(const char *value)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(CHARP, value);
FUNCTION_TEST_ASSERT(value != NULL);
FUNCTION_TEST_END();
uint64_t result = cvtZToUInt64Internal(value, "uint64");
// Don't allow negative numbers even though strtoull() does
if (*value == '-')
THROW_FMT(FormatError, "unable to convert string '%s' to uint64", value);
FUNCTION_TEST_RESULT(UINT64, result);
}

View File

@ -26,6 +26,7 @@ size_t cvtSizeToZ(size_t value, char *buffer, size_t bufferSize);
size_t cvtUIntToZ(unsigned int value, char *buffer, size_t bufferSize);
size_t cvtUInt64ToZ(uint64_t value, char *buffer, size_t bufferSize);
uint64_t cvtZToUInt64(const char *value);
size_t cvtBoolToZ(bool value, char *buffer, size_t bufferSize);

View File

@ -1,7 +1,9 @@
/***********************************************************************************************************************************
Variant Data Type
***********************************************************************************************************************************/
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -34,6 +36,7 @@ static const char *variantTypeName[] =
"KeyValue", // varTypeKeyValue
"String", // varTypeString
"VariantList", // varTypeVariantList
"uint64", // varTypeUInt64
};
/***********************************************************************************************************************************
@ -117,6 +120,12 @@ varDup(const Variant *this)
break;
}
case varTypeUInt64:
{
result = varNewUInt64(varUInt64(this));
break;
}
case varTypeKeyValue:
{
KeyValue *data = kvDup(varKv(this));
@ -186,6 +195,12 @@ varEq(const Variant *this1, const Variant *this2)
break;
}
case varTypeUInt64:
{
result = varUInt64(this1) == varUInt64(this2);
break;
}
case varTypeString:
{
result = strEq(varStr(this1), varStr(this2));
@ -278,6 +293,10 @@ varBoolForce(const Variant *this)
result = varInt64(this) != 0;
break;
case varTypeUInt64:
result = varUInt64(this) != 0;
break;
case varTypeString:
{
// List of false/true boolean string values. Note that false/true values must be equal.
@ -306,7 +325,7 @@ varBoolForce(const Variant *this)
}
default:
THROW_FMT(FormatError, "unable to force %s to %s", variantTypeName[this->type], variantTypeName[varTypeBool]);
THROW_FMT(AssertError, "unable to force %s to %s", variantTypeName[this->type], variantTypeName[varTypeBool]);
}
FUNCTION_TEST_RESULT(BOOL, result);
@ -382,6 +401,12 @@ varDblForce(const Variant *this)
break;
}
case varTypeUInt64:
{
result = (double)varUInt64(this);
break;
}
case varTypeString:
{
result = cvtZToDouble(strPtr(varStr(this)));
@ -389,7 +414,7 @@ varDblForce(const Variant *this)
}
default:
THROW_FMT(FormatError, "unable to force %s to %s", variantTypeName[this->type], variantTypeName[varTypeDouble]);
THROW_FMT(AssertError, "unable to force %s to %s", variantTypeName[this->type], variantTypeName[varTypeDouble]);
}
FUNCTION_TEST_RESULT(DOUBLE, result);
@ -460,7 +485,21 @@ varIntForce(const Variant *this)
// Make sure the value fits into a normal 32-bit int range since 32-bit platforms are supported
if (resultTest > INT32_MAX || resultTest < INT32_MIN)
THROW_FMT(
AssertError, "unable to convert %s %" PRId64 " to %s", variantTypeName[this->type], resultTest,
FormatError, "unable to convert %s %" PRId64 " to %s", variantTypeName[this->type], resultTest,
variantTypeName[varTypeInt]);
result = (int)resultTest;
break;
}
case varTypeUInt64:
{
uint64_t resultTest = varUInt64(this);
// Make sure the value fits into a normal 32-bit int range
if (resultTest > INT32_MAX)
THROW_FMT(
FormatError, "unable to convert %s %" PRIu64 " to %s", variantTypeName[this->type], resultTest,
variantTypeName[varTypeInt]);
result = (int)resultTest;
@ -474,7 +513,7 @@ varIntForce(const Variant *this)
}
default:
THROW_FMT(FormatError, "unable to force %s to %s", variantTypeName[this->type], variantTypeName[varTypeInt]);
THROW_FMT(AssertError, "unable to force %s to %s", variantTypeName[this->type], variantTypeName[varTypeInt]);
}
FUNCTION_TEST_RESULT(INT, result);
@ -544,6 +583,23 @@ varInt64Force(const Variant *this)
break;
}
case varTypeUInt64:
{
uint64_t resultTest = varUInt64(this);
// If max number of unsigned 64-bit integer is greater than max 64-bit signed integer can hold, then error
if (resultTest <= INT64_MAX)
result = (int64_t)resultTest;
else
{
THROW_FMT(
FormatError, "unable to convert %s %" PRIu64 " to %s", variantTypeName[this->type], resultTest,
variantTypeName[varTypeInt64]);
}
break;
}
case varTypeString:
{
result = cvtZToInt64(strPtr(varStr(this)));
@ -551,12 +607,117 @@ varInt64Force(const Variant *this)
}
default:
THROW_FMT(FormatError, "unable to force %s to %s", variantTypeName[this->type], variantTypeName[varTypeInt64]);
THROW_FMT(AssertError, "unable to force %s to %s", variantTypeName[this->type], variantTypeName[varTypeInt64]);
}
FUNCTION_TEST_RESULT(INT64, result);
}
/***********************************************************************************************************************************
New uint64 variant
***********************************************************************************************************************************/
Variant *
varNewUInt64(uint64_t data)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(UINT64, data);
FUNCTION_TEST_END();
FUNCTION_TEST_RESULT(VARIANT, varNewInternal(varTypeUInt64, (void *)&data, sizeof(data)));
}
/***********************************************************************************************************************************
Return int64
***********************************************************************************************************************************/
uint64_t
varUInt64(const Variant *this)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(VARIANT, this);
FUNCTION_TEST_ASSERT(this != NULL);
FUNCTION_TEST_END();
ASSERT(this->type == varTypeUInt64);
FUNCTION_TEST_RESULT(UINT64, *((uint64_t *)varData(this)));
}
/***********************************************************************************************************************************
Return uint64 regardless of variant type
***********************************************************************************************************************************/
uint64_t
varUInt64Force(const Variant *this)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(VARIANT, this);
FUNCTION_TEST_ASSERT(this != NULL);
FUNCTION_TEST_END();
uint64_t result = 0;
switch (this->type)
{
case varTypeBool:
{
result = varBool(this);
break;
}
case varTypeInt:
{
int resultTest = varInt(this);
// If integer is a negative number, throw an error since the resulting conversion would be a different number
if (resultTest >= 0)
result = (uint64_t)resultTest;
else
{
THROW_FMT(
FormatError, "unable to convert %s %d to %s", variantTypeName[this->type], resultTest,
variantTypeName[varTypeUInt64]);
}
break;
}
case varTypeInt64:
{
int64_t resultTest = varInt64(this);
// If integer is a negative number, throw an error since the resulting conversion would be out of bounds
if (resultTest >= 0)
result = (uint64_t)resultTest;
else
{
THROW_FMT(
FormatError, "unable to convert %s %" PRId64 " to %s", variantTypeName[this->type], resultTest,
variantTypeName[varTypeUInt64]);
}
break;
}
case varTypeUInt64:
{
result = varUInt64(this);
break;
}
case varTypeString:
{
result = cvtZToUInt64(strPtr(varStr(this)));
break;
}
default:
THROW_FMT(AssertError, "unable to force %s to %s", variantTypeName[this->type], variantTypeName[varTypeUInt64]);
}
FUNCTION_TEST_RESULT(UINT64, result);
}
/***********************************************************************************************************************************
New key/value variant
***********************************************************************************************************************************/
@ -703,6 +864,12 @@ varStrForce(const Variant *this)
break;
}
case varTypeUInt64:
{
result = strNewFmt("%" PRIu64, varUInt64(this));
break;
}
case varTypeString:
{
result = strDup(varStr(this));
@ -801,6 +968,7 @@ varToLog(const Variant *this, char *buffer, size_t bufferSize)
case varTypeDouble:
case varTypeInt:
case varTypeInt64:
case varTypeUInt64:
{
string = varStrForce(this);
break;
@ -854,6 +1022,7 @@ varFree(Variant *this)
case varTypeDouble:
case varTypeInt:
case varTypeInt64:
case varTypeUInt64:
break;
}

View File

@ -23,6 +23,7 @@ typedef enum
varTypeKeyValue,
varTypeString,
varTypeVariantList,
varTypeUInt64,
} VariantType;
#include "common/type/keyValue.h"
@ -56,6 +57,10 @@ Variant *varNewStrZ(const char *data);
String *varStr(const Variant *this);
String *varStrForce(const Variant *this);
Variant *varNewUInt64(uint64_t data);
uint64_t varUInt64(const Variant *this);
uint64_t varUInt64Force(const Variant *this);
Variant *varNewVarLst(const VariantList *data);
VariantList *varVarLst(const Variant *this);

View File

@ -180,7 +180,7 @@ unit:
# ----------------------------------------------------------------------------------------------------------------------------
- name: type-variant
total: 8
total: 9
coverage:
common/type/variant: full

View File

@ -120,14 +120,21 @@ testRun()
}
// *****************************************************************************************************************************
if (testBegin("UInt64ToZ()"))
if (testBegin("UInt64ToZ() and cvtZToUInt64()"))
{
char buffer[STACK_TRACE_PARAM_MAX];
TEST_ERROR(cvtUInt64ToZ(9999, buffer, 4), AssertError, "buffer overflow");
TEST_RESULT_INT(cvtUInt64ToZ(0xFFFFFFFFFFFFFFFF, buffer, STACK_TRACE_PARAM_MAX), 20, "convert int64 to string");
TEST_RESULT_INT(cvtUInt64ToZ(0xFFFFFFFFFFFFFFFF, buffer, STACK_TRACE_PARAM_MAX), 20, "convert uint64 to string");
TEST_RESULT_STR(buffer, "18446744073709551615", " check buffer");
TEST_ERROR(cvtZToUInt64("FEF"), FormatError, "unable to convert string 'FEF' to uint64");
TEST_ERROR(cvtZToUInt64(" 10"), FormatError, "unable to convert string ' 10' to uint64"); // number but leading space
TEST_ERROR(cvtZToUInt64("10 "), FormatError, "unable to convert string '10 ' to uint64"); // number but trailing space
TEST_ERROR(cvtZToUInt64("-1"), FormatError, "unable to convert string '-1' to uint64"); // number but trailing space
TEST_RESULT_DOUBLE(cvtZToUInt64("18446744073709551615"), 18446744073709551615U, "convert string to uint64");
}
FUNCTION_HARNESS_RESULT_VOID();

View File

@ -29,8 +29,9 @@ testRun()
TEST_RESULT_BOOL(varBoolForce(varNewBool(false)), false, "force bool to bool");
TEST_RESULT_BOOL(varBoolForce(varNewInt(1)), true, "force int to bool");
TEST_RESULT_BOOL(varBoolForce(varNewInt64(false)), false, "force int64 to bool");
TEST_RESULT_BOOL(varBoolForce(varNewUInt64(12)), true, "force uint64 to bool");
TEST_ERROR(varBoolForce(varNewVarLst(varLstNew())), FormatError, "unable to force VariantList to bool");
TEST_ERROR(varBoolForce(varNewVarLst(varLstNew())), AssertError, "unable to force VariantList to bool");
// -------------------------------------------------------------------------------------------------------------------------
TEST_RESULT_VOID(varFree(NULL), "ok to free null variant");
@ -60,11 +61,14 @@ testRun()
TEST_RESULT_DOUBLE(varDblForce(varNewBool(false)), 0, "force bool to double");
TEST_RESULT_DOUBLE(varDblForce(varNewInt(123)), 123, "force int to double");
TEST_RESULT_DOUBLE(varDblForce(varNewInt64(999999999999)), 999999999999, "force int64 to double");
TEST_RESULT_DOUBLE(varDblForce(varNewUInt64(9223372036854775807U)), 9223372036854775807U, "force uint64 to double");
TEST_RESULT_DOUBLE(varDblForce(varNewStr(strNew("879.01"))), 879.01, "force String to double");
TEST_RESULT_DOUBLE(varDblForce(varNewStr(strNew("0"))), 0, "force String to double");
TEST_RESULT_DOUBLE(
varDblForce(varNewUInt64(UINT64_MAX)), 18446744073709551616.0, "force max uint64 to double (it will be rounded)");
TEST_ERROR(varDblForce(varNewStr(strNew("AAA"))), FormatError, "unable to convert string 'AAA' to double");
TEST_ERROR(varDblForce(varNewVarLst(varLstNew())), FormatError, "unable to force VariantList to double");
TEST_ERROR(varDblForce(varNewVarLst(varLstNew())), AssertError, "unable to force VariantList to double");
// -------------------------------------------------------------------------------------------------------------------------
TEST_RESULT_DOUBLE(varDbl(varDup(varNewDbl(3.1415))), 3.1415, "dup double");
@ -84,10 +88,12 @@ testRun()
// -------------------------------------------------------------------------------------------------------------------------
TEST_RESULT_INT(varIntForce(varNewBool(true)), 1, "force bool to int");
TEST_ERROR(varIntForce(varNewVarLst(varLstNew())), FormatError, "unable to force VariantList to int");
TEST_ERROR(varIntForce(varNewVarLst(varLstNew())), AssertError, "unable to force VariantList to int");
TEST_RESULT_INT(varIntForce(varNewInt64(999)), 999, "force int64 to int");
TEST_ERROR(varIntForce(varNewInt64(2147483648)), AssertError, "unable to convert int64 2147483648 to int");
TEST_ERROR(varIntForce(varNewInt64(-2147483649)), AssertError, "unable to convert int64 -2147483649 to int");
TEST_ERROR(varIntForce(varNewInt64(2147483648)), FormatError, "unable to convert int64 2147483648 to int");
TEST_ERROR(varIntForce(varNewInt64(-2147483649)), FormatError, "unable to convert int64 -2147483649 to int");
TEST_RESULT_INT(varIntForce(varNewUInt64(12345)), 12345, "force uint64 to int");
TEST_ERROR(varIntForce(varNewUInt64(2147483648)), FormatError, "unable to convert uint64 2147483648 to int");
// -------------------------------------------------------------------------------------------------------------------------
TEST_ERROR(varInt(varNewStrZ("string")), AssertError, "assertion 'this->type == varTypeInt' failed");
@ -116,11 +122,14 @@ testRun()
TEST_RESULT_INT(varInt64Force(varNewBool(true)), 1, "force bool to int64");
TEST_RESULT_INT(varInt64Force(varNewInt(2147483647)), 2147483647, "force int to int64");
TEST_RESULT_INT(varInt64Force(varNewStrZ("9223372036854775807")), 9223372036854775807L, "force str to int64");
TEST_RESULT_INT(varInt64Force(varNewUInt64(9223372036854775807U)), 9223372036854775807L, "force uint64 to int64");
TEST_ERROR(
varInt64Force(varNewStrZ("9223372036854775808")), FormatError,
"unable to convert string '9223372036854775808' to int64");
TEST_ERROR(varInt64Force(varNewVarLst(varLstNew())), FormatError, "unable to force VariantList to int64");
TEST_ERROR(varInt64Force(varNewVarLst(varLstNew())), AssertError, "unable to force VariantList to int64");
TEST_ERROR(varInt64Force(varNewUInt64(9223372036854775808U)), FormatError,
"unable to convert uint64 9223372036854775808 to int64");
// -------------------------------------------------------------------------------------------------------------------------
TEST_ERROR(varInt64(varNewStrZ("string")), AssertError, "assertion 'this->type == varTypeInt64' failed");
@ -136,6 +145,43 @@ testRun()
TEST_RESULT_BOOL(varEq(varNewInt64(444), varNewInt64(123)), false, "int64, int64 not eq");
}
// -----------------------------------------------------------------------------------------------------------------------------
if (testBegin("uint64"))
{
Variant *uint64 = varNewUInt64(44);
TEST_RESULT_DOUBLE(varUInt64(uint64), 44, "uint64 variant");
TEST_RESULT_DOUBLE(varUInt64Force(uint64), 44, "force uint64 to uint64");
varFree(uint64);
// -------------------------------------------------------------------------------------------------------------------------
TEST_RESULT_DOUBLE(varUInt64Force(varNewBool(true)), 1, "force bool to uint64");
TEST_RESULT_DOUBLE(varUInt64Force(varNewInt(2147483647)), 2147483647, "force int to uint64");
TEST_RESULT_DOUBLE(varUInt64Force(varNewInt64(2147483647)), 2147483647, "force int64 to uint64");
TEST_RESULT_DOUBLE(varUInt64Force(varNewStrZ("18446744073709551615")), 18446744073709551615U, "force str to uint64");
TEST_RESULT_DOUBLE(varUInt64Force(varNewUInt64(18446744073709551615U)), 18446744073709551615U, "force uint64 to uint64");
TEST_ERROR(
varUInt64Force(varNewStrZ("18446744073709551616")), FormatError,
"unable to convert string '18446744073709551616' to uint64"); // string value is out of bounds for uint64
TEST_ERROR(varUInt64Force(varNewStrZ(" 16")), FormatError,"unable to convert string ' 16' to uint64");
TEST_ERROR(varUInt64Force(varNewVarLst(varLstNew())), AssertError, "unable to force VariantList to uint64");
TEST_ERROR(varUInt64Force(varNewInt64(-1)), FormatError, "unable to convert int64 -1 to uint64");
TEST_ERROR(varUInt64Force(varNewInt(-1)), FormatError, "unable to convert int -1 to uint64");
// -------------------------------------------------------------------------------------------------------------------------
TEST_ERROR(varUInt64(varNewStrZ("string")), AssertError, "assertion 'this->type == varTypeUInt64' failed");
// -------------------------------------------------------------------------------------------------------------------------
TEST_RESULT_DOUBLE(varUInt64(varDup(varNewUInt64(88976))), 88976, "dup uint64");
// -------------------------------------------------------------------------------------------------------------------------
TEST_RESULT_BOOL(varEq(NULL, NULL), true, "null, null eq");
TEST_RESULT_BOOL(varEq(NULL, varNewUInt64(123)), false, "null, uint64 not eq");
TEST_RESULT_BOOL(varEq(varNewUInt64(9223372036854775807L), varNewUInt64(9223372036854775807L)), true, "uint64, uint64 eq");
TEST_RESULT_BOOL(varEq(varNewUInt64(444), varNewUInt64(123)), false, "uint64, uint64 not eq");
}
// -----------------------------------------------------------------------------------------------------------------------------
if (testBegin("keyValue"))
{
@ -198,6 +244,7 @@ testRun()
TEST_RESULT_STR(strPtr(varStrForce(varNewDbl((double)999999999.123456))), "999999999.123456", "force double to string");
TEST_RESULT_STR(strPtr(varStrForce(varNewBool(true))), "true", "force bool to string");
TEST_RESULT_STR(strPtr(varStrForce(varNewBool(false))), "false", "force bool to string");
TEST_RESULT_STR(strPtr(varStrForce(varNewUInt64(18446744073709551615U))), "18446744073709551615", "force uint64 to string");
TEST_ERROR(varStrForce(varNewKv()), FormatError, "unable to force KeyValue to String");