diff --git a/doc/xml/release.xml b/doc/xml/release.xml index 39ebd04ba..f859d5ab4 100644 --- a/doc/xml/release.xml +++ b/doc/xml/release.xml @@ -23,6 +23,15 @@

Improve performance of string to int conversion. Use strtoll() instead of sprintf() for conversion. Also use available integer min/max constants rather than hard-coded values.

+ + + + + + + +

Add uint64 variant type and supporting conversion functions.

+
diff --git a/src/common/type/convert.c b/src/common/type/convert.c index 1c051d2b5..7237b17ba 100644 --- a/src/common/type/convert.c +++ b/src/common/type/convert.c @@ -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); +} diff --git a/src/common/type/convert.h b/src/common/type/convert.h index d5ddabe59..86ac6db8c 100644 --- a/src/common/type/convert.h +++ b/src/common/type/convert.h @@ -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); diff --git a/src/common/type/variant.c b/src/common/type/variant.c index aaa4f4dbc..8766fb141 100644 --- a/src/common/type/variant.c +++ b/src/common/type/variant.c @@ -1,7 +1,9 @@ /*********************************************************************************************************************************** Variant Data Type ***********************************************************************************************************************************/ +#include #include +#include #include #include #include @@ -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; } diff --git a/src/common/type/variant.h b/src/common/type/variant.h index f6610ebe4..4aa50ae44 100644 --- a/src/common/type/variant.h +++ b/src/common/type/variant.h @@ -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); diff --git a/test/define.yaml b/test/define.yaml index 4828ab299..15cdd0c22 100644 --- a/test/define.yaml +++ b/test/define.yaml @@ -180,7 +180,7 @@ unit: # ---------------------------------------------------------------------------------------------------------------------------- - name: type-variant - total: 8 + total: 9 coverage: common/type/variant: full diff --git a/test/src/module/common/typeConvertTest.c b/test/src/module/common/typeConvertTest.c index f61204e26..8bc410677 100644 --- a/test/src/module/common/typeConvertTest.c +++ b/test/src/module/common/typeConvertTest.c @@ -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(); diff --git a/test/src/module/common/typeVariantTest.c b/test/src/module/common/typeVariantTest.c index 30c744c63..059d8360d 100644 --- a/test/src/module/common/typeVariantTest.c +++ b/test/src/module/common/typeVariantTest.c @@ -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");