1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-07-15 01:04:37 +02:00

Add cvtZSubNTo*() functions.

These functions allow conversion from substrings without needing to create a String or a temporary buffer.

httpDateToTime() no longer requires a temp mem context. Also improve handling of month search to avoid an allocation.

httpUriDecode() no longer requires a temp mem context.

jsonReadStr() no longer requires a temp mem context.

pgLsnFromWalSegment() no longer requires a temp mem context.

pgVersionFromStr() no longer requires a temp mem context. Also do a bit of refactoring.

storageGcsCvtTime() no longer leaks six Strings per call.

storageS3CvtTime() no longer leaks six Strings per call.
This commit is contained in:
David Steele
2022-04-28 09:50:23 -04:00
parent 6e18235be8
commit bc46d4e37b
12 changed files with 273 additions and 202 deletions

View File

@ -492,8 +492,7 @@ removeExpiredArchive(InfoBackup *infoBackup, bool timeBasedFullRetention, unsign
if (strCmp(archiveId, strLstGet(listArchiveDisk, archiveIdx)) != 0)
continue;
StringList *archiveSplit = strLstNewSplitZ(archiveId, "-");
unsigned int archivePgId = cvtZToUInt(strZ(strLstGet(archiveSplit, 1)));
const unsigned int archivePgId = cvtZToUInt(strrchr(strZ(archiveId), '-') + 1);
// From the global list of backups to retain, create a list of backups, oldest to newest, associated with
// this archiveId (e.g. 9.4-1), e.g. If globalBackupRetention has 4F, 3F, 2F, 1F then

View File

@ -120,12 +120,12 @@ getEpoch(const String *targetTime)
// Strip off the date and time and put the remainder into another string
String *datetime = strSubN(targetTime, 0, 19);
int dtYear = cvtZToInt(strZ(strSubN(datetime, 0, 4)));
int dtMonth = cvtZToInt(strZ(strSubN(datetime, 5, 2)));
int dtDay = cvtZToInt(strZ(strSubN(datetime, 8, 2)));
int dtHour = cvtZToInt(strZ(strSubN(datetime, 11, 2)));
int dtMinute = cvtZToInt(strZ(strSubN(datetime, 14, 2)));
int dtSecond = cvtZToInt(strZ(strSubN(datetime, 17, 2)));
int dtYear = cvtZSubNToInt(strZ(datetime), 0, 4);
int dtMonth = cvtZSubNToInt(strZ(datetime), 5, 2);
int dtDay = cvtZSubNToInt(strZ(datetime), 8, 2);
int dtHour = cvtZSubNToInt(strZ(datetime), 11, 2);
int dtMinute = cvtZSubNToInt(strZ(datetime), 14, 2);
int dtSecond = cvtZSubNToInt(strZ(datetime), 17, 2);
// Confirm date and time parts are valid
datePartsValid(dtYear, dtMonth, dtDay);
@ -145,13 +145,13 @@ getEpoch(const String *targetTime)
String *timezoneOffset = strSub(timeTargetZone, (size_t)idxSign);
// Include the sign with the hour
int tzHour = cvtZToInt(strZ(strSubN(timezoneOffset, 0, 3)));
int tzHour = cvtZSubNToInt(strZ(timezoneOffset), 0, 3);
int tzMinute = 0;
// If minutes are included in timezone offset then extract the minutes based on whether a colon separates them from
// the hour
if (strSize(timezoneOffset) > 3)
tzMinute = cvtZToInt(strZ(strSubN(timezoneOffset, 3 + (strChr(timezoneOffset, ':') == -1 ? 0 : 1), 2)));
tzMinute = cvtZSubNToInt(strZ(timezoneOffset), 3 + (strChr(timezoneOffset, ':') == -1 ? 0 : 1), 2);
result = epochFromParts(dtYear, dtMonth, dtDay, dtHour, dtMinute, dtSecond, tzOffsetSeconds(tzHour, tzMinute));
}

View File

@ -18,38 +18,32 @@ static const char *const httpCommonMonthList[] =
static const char *const httpCommonDayList[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
time_t
httpDateToTime(const String *lastModified)
httpDateToTime(const String *const lastModified)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRING, lastModified);
FUNCTION_TEST_END();
time_t result = 0;
MEM_CONTEXT_TEMP_BEGIN()
{
// Find the month
const char *month = strZ(strSubN(lastModified, 8, 3));
const char *const month = strZ(lastModified) + 8;
unsigned int monthIdx = 0;
for (; monthIdx < LENGTH_OF(httpCommonMonthList); monthIdx++)
{
if (strcmp(month, httpCommonMonthList[monthIdx]) == 0)
if (strncmp(month, httpCommonMonthList[monthIdx], 3) == 0)
break;
}
if (monthIdx == LENGTH_OF(httpCommonMonthList))
THROW_FMT(FormatError, "invalid month '%s'", month);
THROW_FMT(FormatError, "invalid month '%.3s'", month);
// Convert to time_t
result = epochFromParts(
cvtZToInt(strZ(strSubN(lastModified, 12, 4))), (int)monthIdx + 1, cvtZToInt(strZ(strSubN(lastModified, 5, 2))),
cvtZToInt(strZ(strSubN(lastModified, 17, 2))), cvtZToInt(strZ(strSubN(lastModified, 20, 2))),
cvtZToInt(strZ(strSubN(lastModified, 23, 2))), 0);
}
MEM_CONTEXT_TEMP_END();
FUNCTION_TEST_RETURN(TIME, result);
FUNCTION_TEST_RETURN(
TIME,
epochFromParts(
cvtZSubNToInt(strZ(lastModified), 12, 4), (int)monthIdx + 1, cvtZSubNToInt(strZ(lastModified), 5, 2),
cvtZSubNToInt(strZ(lastModified), 17, 2), cvtZSubNToInt(strZ(lastModified), 20, 2),
cvtZSubNToInt(strZ(lastModified), 23, 2), 0));
}
String *
@ -72,7 +66,7 @@ httpDateFromTime(const time_t time)
/**********************************************************************************************************************************/
String *
httpUriDecode(const String *uri)
httpUriDecode(const String *const uri)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRING, uri);
@ -85,8 +79,6 @@ httpUriDecode(const String *uri)
{
result = strNew();
MEM_CONTEXT_TEMP_BEGIN()
{
// Iterate all characters in the string
for (unsigned uriIdx = 0; uriIdx < strSize(uri); uriIdx++)
{
@ -100,7 +92,7 @@ httpUriDecode(const String *uri)
THROW_FMT(FormatError, "invalid escape sequence length in '%s'", strZ(uri));
// Convert hex digits
uriChar = (char)cvtZToUIntBase(strZ(strSubN(uri, uriIdx + 1, 2)), 16);
uriChar = (char)cvtZSubNToUIntBase(strZ(uri), uriIdx + 1, 2, 16);
// Skip to next character or escape
uriIdx += 2;
@ -109,8 +101,6 @@ httpUriDecode(const String *uri)
strCatChr(result, uriChar);
}
}
MEM_CONTEXT_TEMP_END();
}
FUNCTION_TEST_RETURN(STRING, result);
}

View File

@ -265,7 +265,7 @@ httpResponseNew(HttpSession *session, const String *verb, bool contentCache)
if (spacePos != 3)
THROW_FMT(FormatError, "response status '%s' must have a space after the status code", strZ(status));
this->pub.code = cvtZToUInt(strZ(strSubN(status, 0, (size_t)spacePos)));
this->pub.code = cvtZSubNToUInt(strZ(status), 0, (size_t)spacePos);
// Read reason phrase. A missing reason phrase will be represented as an empty string.
MEM_CONTEXT_OBJ_BEGIN(this)

View File

@ -248,6 +248,26 @@ cvtZToInt(const char *value)
FUNCTION_TEST_RETURN(INT, cvtZToIntBase(value, 10));
}
int
cvtZSubNToIntBase(const char *const value, const size_t offset, const size_t size, const int base)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRINGZ, value);
FUNCTION_TEST_PARAM(SIZE, offset);
FUNCTION_TEST_PARAM(SIZE, size);
FUNCTION_TEST_PARAM(INT, base);
FUNCTION_TEST_END();
ASSERT(value != NULL);
char buffer[CVT_BASE10_BUFFER_SIZE + 1];
ASSERT(size <= CVT_BASE10_BUFFER_SIZE);
strncpy(buffer, value + offset, size);
buffer[size] = '\0';
FUNCTION_TEST_RETURN(INT, cvtZToIntBase(buffer, base));
}
/**********************************************************************************************************************************/
size_t
cvtInt64ToZ(int64_t value, char *buffer, size_t bufferSize)
@ -292,6 +312,26 @@ cvtZToInt64(const char *value)
FUNCTION_TEST_RETURN(INT64, cvtZToInt64Base(value, 10));
}
int64_t
cvtZSubNToInt64Base(const char *const value, const size_t offset, const size_t size, const int base)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRINGZ, value);
FUNCTION_TEST_PARAM(SIZE, offset);
FUNCTION_TEST_PARAM(SIZE, size);
FUNCTION_TEST_PARAM(INT, base);
FUNCTION_TEST_END();
ASSERT(value != NULL);
char buffer[CVT_BASE10_BUFFER_SIZE + 1];
ASSERT(size <= CVT_BASE10_BUFFER_SIZE);
strncpy(buffer, value + offset, size);
buffer[size] = '\0';
FUNCTION_TEST_RETURN(INT64, cvtZToInt64Base(buffer, base));
}
/**********************************************************************************************************************************/
size_t
cvtModeToZ(mode_t value, char *buffer, size_t bufferSize)
@ -434,6 +474,26 @@ cvtZToUInt(const char *value)
FUNCTION_TEST_RETURN(UINT, cvtZToUIntBase(value, 10));
}
unsigned int
cvtZSubNToUIntBase(const char *const value, const size_t offset, const size_t size, const int base)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRINGZ, value);
FUNCTION_TEST_PARAM(SIZE, offset);
FUNCTION_TEST_PARAM(SIZE, size);
FUNCTION_TEST_PARAM(INT, base);
FUNCTION_TEST_END();
ASSERT(value != NULL);
char buffer[CVT_BASE10_BUFFER_SIZE + 1];
ASSERT(size <= CVT_BASE10_BUFFER_SIZE);
strncpy(buffer, value + offset, size);
buffer[size] = '\0';
FUNCTION_TEST_RETURN(UINT, cvtZToUIntBase(buffer, base));
}
/**********************************************************************************************************************************/
size_t
cvtUInt64ToZ(uint64_t value, char *buffer, size_t bufferSize)
@ -484,6 +544,26 @@ cvtZToUInt64(const char *value)
FUNCTION_TEST_RETURN(UINT64, cvtZToUInt64Base(value, 10));
}
uint64_t
cvtZSubNToUInt64Base(const char *const value, const size_t offset, const size_t size, const int base)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRINGZ, value);
FUNCTION_TEST_PARAM(SIZE, offset);
FUNCTION_TEST_PARAM(SIZE, size);
FUNCTION_TEST_PARAM(INT, base);
FUNCTION_TEST_END();
ASSERT(value != NULL);
char buffer[CVT_BASE10_BUFFER_SIZE + 1];
ASSERT(size <= CVT_BASE10_BUFFER_SIZE);
strncpy(buffer, value + offset, size);
buffer[size] = '\0';
FUNCTION_TEST_RETURN(UINT64, cvtZToUInt64Base(buffer, base));
}
/**********************************************************************************************************************************/
void
cvtUInt64ToVarInt128(uint64_t value, uint8_t *const buffer, size_t *const bufferPos, const size_t bufferSize)

View File

@ -31,11 +31,26 @@ double cvtZToDouble(const char *value);
size_t cvtIntToZ(int value, char *buffer, size_t bufferSize);
int cvtZToInt(const char *value);
int cvtZToIntBase(const char *value, int base);
int cvtZSubNToIntBase(const char *value, size_t offset, size_t size, int base);
__attribute__((always_inline)) static inline int
cvtZSubNToInt(const char *const value, const size_t offset, const size_t size)
{
return cvtZSubNToIntBase(value, offset, size, 10);
}
// Convert int64 to zero-terminated string and vice versa
size_t cvtInt64ToZ(int64_t value, char *buffer, size_t bufferSize);
int64_t cvtZToInt64(const char *value);
int64_t cvtZToInt64Base(const char *value, int base);
int64_t cvtZSubNToInt64Base(const char *value, size_t offset, size_t size, int base);
__attribute__((always_inline)) static inline int64_t
cvtZSubNToInt64(const char *const value, const size_t offset, const size_t size)
{
return cvtZSubNToInt64Base(value, offset, size, 10);
}
// Convert int32/64 to uint32/64 using zigzag encoding and vice versa. Zigzag encoding places the sign in the least significant bit
// so that signed and unsigned values alternate, e.g. 0 = 0, -1 = 1, 1 = 2, -2 = 3, 2 = 4, -3 = 5, 3 = 6, etc. This moves as many
@ -80,11 +95,25 @@ size_t cvtTimeToZ(time_t value, char *buffer, size_t bufferSize);
size_t cvtUIntToZ(unsigned int value, char *buffer, size_t bufferSize);
unsigned int cvtZToUInt(const char *value);
unsigned int cvtZToUIntBase(const char *value, int base);
unsigned int cvtZSubNToUIntBase(const char *value, size_t offset, size_t size, int base);
__attribute__((always_inline)) static inline unsigned int
cvtZSubNToUInt(const char *const value, const size_t offset, const size_t size)
{
return cvtZSubNToUIntBase(value, offset, size, 10);
}
// Convert uint64 to zero-terminated string and vice versa
size_t cvtUInt64ToZ(uint64_t value, char *buffer, size_t bufferSize);
uint64_t cvtZToUInt64(const char *value);
uint64_t cvtZToUInt64Base(const char *value, int base);
uint64_t cvtZSubNToUInt64Base(const char* value, size_t offset, size_t size, int base);
__attribute__((always_inline)) static inline uint64_t
cvtZSubNToUInt64(const char *const value, const size_t offset, const size_t size)
{
return cvtZSubNToUInt64Base(value, offset, size, 10);
}
// Convert uint64 to base-128 varint and vice versa
void cvtUInt64ToVarInt128(uint64_t value, uint8_t *buffer, size_t *bufferPos, size_t bufferSize);

View File

@ -415,22 +415,19 @@ jsonReadNumber(JsonRead *const this)
THROW_FMT(JsonFormatError, "invalid number at: %s", this->json);
const size_t size = digits + intSigned;
char working[CVT_BASE10_BUFFER_SIZE];
strncpy(working, this->json, size);
working[size] = '\0';
this->json += size;
// Return result
if (intSigned)
{
FUNCTION_TEST_RETURN_TYPE(
JsonReadNumberResult, (JsonReadNumberResult){.type = jsonNumberTypeI64, .value = {.i64 = cvtZToInt64(working)}});
JsonReadNumberResult,
(JsonReadNumberResult){.type = jsonNumberTypeI64, .value = {.i64 = cvtZSubNToInt64(this->json - size, 0, size)}});
}
FUNCTION_TEST_RETURN_TYPE(
JsonReadNumberResult, (JsonReadNumberResult){.type = jsonNumberTypeU64, .value = {.u64 = cvtZToUInt64(working)}});
JsonReadNumberResult,
(JsonReadNumberResult){.type = jsonNumberTypeU64, .value = {.u64 = cvtZSubNToUInt64(this->json - size, 0, size)}});
}
/**********************************************************************************************************************************/
@ -962,8 +959,6 @@ jsonReadStr(JsonRead *const this)
String *const result = strNew();
MEM_CONTEXT_TEMP_BEGIN()
{
// Skip the beginning "
ASSERT(*this->json == '"');
this->json++;
@ -1029,7 +1024,7 @@ jsonReadStr(JsonRead *const this)
// Decode char
this->json += 2;
strCatChr(result, (char)cvtZToUIntBase(strZ(strNewZN(this->json, 2)), 16));
strCatChr(result, (char)cvtZSubNToUIntBase(this->json, 0, 2, 16));
this->json++;
break;
@ -1060,8 +1055,6 @@ jsonReadStr(JsonRead *const this)
// Advance the character array pointer to the next element after the string
this->json++;
}
MEM_CONTEXT_TEMP_END();
FUNCTION_TEST_RETURN(STRING, result);
}

View File

@ -509,17 +509,10 @@ pgLsnFromWalSegment(const String *const walSegment, const unsigned int walSegmen
ASSERT(strSize(walSegment) == 24);
ASSERT(walSegmentSize > 0);
uint64_t result;
MEM_CONTEXT_TEMP_BEGIN()
{
result =
(cvtZToUInt64Base(strZ(strSubN(walSegment, 8, 8)), 16) << 32) +
(cvtZToUInt64Base(strZ(strSubN(walSegment, 16, 8)), 16) * walSegmentSize);
}
MEM_CONTEXT_TEMP_END();
FUNCTION_TEST_RETURN(UINT64, result);
FUNCTION_TEST_RETURN(
UINT64,
(cvtZSubNToUInt64Base(strZ(walSegment), 8, 8, 16) << 32) +
(cvtZSubNToUInt64Base(strZ(walSegment), 16, 8, 16) * walSegmentSize));
}
/**********************************************************************************************************************************/
@ -533,12 +526,7 @@ pgTimelineFromWalSegment(const String *const walSegment)
ASSERT(walSegment != NULL);
ASSERT(strSize(walSegment) == 24);
char buffer[9];
strncpy(buffer, strZ(walSegment), sizeof(buffer) - 1);
buffer[sizeof(buffer) - 1] = '\0';
FUNCTION_TEST_RETURN(UINT32, cvtZToUIntBase(buffer, 16));
FUNCTION_TEST_RETURN(UINT32, cvtZSubNToUIntBase(strZ(walSegment), 0, 8, 16));
}
/**********************************************************************************************************************************/
@ -646,7 +634,7 @@ pgXactPath(unsigned int pgVersion)
/**********************************************************************************************************************************/
unsigned int
pgVersionFromStr(const String *version)
pgVersionFromStr(const String *const version)
{
FUNCTION_LOG_BEGIN(logLevelTrace);
FUNCTION_LOG_PARAM(STRING, version);
@ -654,33 +642,19 @@ pgVersionFromStr(const String *version)
ASSERT(version != NULL);
unsigned int result = 0;
MEM_CONTEXT_TEMP_BEGIN()
{
// If format is not number.number (9.4) or number only (10) then error
// If format not number.number (9.4) or number only (10) then error. No check for valid/supported PG version is on purpose.
if (!regExpMatchOne(STRDEF("^[0-9]+[.]*[0-9]+$"), version))
THROW_FMT(AssertError, "version %s format is invalid", strZ(version));
// If there is a dot set the major and minor versions, else just the major
int idxStart = strChr(version, '.');
unsigned int major;
unsigned int minor = 0;
// If there is no dot then only the major version is needed
const int idxStart = strChr(version, '.');
if (idxStart != -1)
{
major = cvtZToUInt(strZ(strSubN(version, 0, (size_t)idxStart)));
minor = cvtZToUInt(strZ(strSub(version, (size_t)idxStart + 1)));
}
else
major = cvtZToUInt(strZ(version));
if (idxStart == -1)
FUNCTION_LOG_RETURN(UINT, cvtZToUInt(strZ(version)) * 10000);
// No check to see if valid/supported PG version is on purpose
result = major * 10000 + minor * 100;
}
MEM_CONTEXT_TEMP_END();
FUNCTION_LOG_RETURN(UINT, result);
// Major and minor version are needed
FUNCTION_LOG_RETURN(
UINT, cvtZSubNToUInt(strZ(version), 0, (size_t)idxStart) * 10000 + cvtZToUInt(strZ(version) + (size_t)idxStart + 1) * 100);
}
String *

View File

@ -488,7 +488,7 @@ General function for listing files to be used by other list routines
// Helper to convert YYYY-MM-DDTHH:MM:SS.MSECZ format to time_t. This format is very nearly ISO-8601 except for the inclusion of
// milliseconds, which are discarded here.
static time_t
storageGcsCvtTime(const String *time)
storageGcsCvtTime(const String *const time)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRING, time);
@ -497,9 +497,8 @@ storageGcsCvtTime(const String *time)
FUNCTION_TEST_RETURN(
TIME,
epochFromParts(
cvtZToInt(strZ(strSubN(time, 0, 4))), cvtZToInt(strZ(strSubN(time, 5, 2))),
cvtZToInt(strZ(strSubN(time, 8, 2))), cvtZToInt(strZ(strSubN(time, 11, 2))),
cvtZToInt(strZ(strSubN(time, 14, 2))), cvtZToInt(strZ(strSubN(time, 17, 2))), 0));
cvtZSubNToInt(strZ(time), 0, 4), cvtZSubNToInt(strZ(time), 5, 2), cvtZSubNToInt(strZ(time), 8, 2),
cvtZSubNToInt(strZ(time), 11, 2), cvtZSubNToInt(strZ(time), 14, 2), cvtZSubNToInt(strZ(time), 17, 2), 0));
}
static void

View File

@ -241,7 +241,7 @@ Convert YYYY-MM-DDTHH:MM:SS.MSECZ format to time_t. This format is very nearly I
which are discarded here.
***********************************************************************************************************************************/
static time_t
storageS3CvtTime(const String *time)
storageS3CvtTime(const String *const time)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRING, time);
@ -250,9 +250,8 @@ storageS3CvtTime(const String *time)
FUNCTION_TEST_RETURN(
TIME,
epochFromParts(
cvtZToInt(strZ(strSubN(time, 0, 4))), cvtZToInt(strZ(strSubN(time, 5, 2))),
cvtZToInt(strZ(strSubN(time, 8, 2))), cvtZToInt(strZ(strSubN(time, 11, 2))),
cvtZToInt(strZ(strSubN(time, 14, 2))), cvtZToInt(strZ(strSubN(time, 17, 2))), 0));
cvtZSubNToInt(strZ(time), 0, 4), cvtZSubNToInt(strZ(time), 5, 2), cvtZSubNToInt(strZ(time), 8, 2),
cvtZSubNToInt(strZ(time), 11, 2), cvtZSubNToInt(strZ(time), 14, 2), cvtZSubNToInt(strZ(time), 17, 2), 0));
}
/***********************************************************************************************************************************

View File

@ -65,7 +65,7 @@ testBackupValidateCallback(void *callbackData, const StorageInfo *info)
if (bundle)
{
const uint64_t bundleId = cvtZToUInt64(strZ(strSub(info->name, sizeof("bundle"))));
const uint64_t bundleId = cvtZToUInt64(strZ(info->name) + sizeof("bundle"));
for (unsigned int fileIdx = 0; fileIdx < manifestFileTotal(data->manifest); fileIdx++)
{

View File

@ -73,8 +73,10 @@ testRun(void)
cvtZToInt("-9223372036854775807"), FormatError, "unable to convert base 10 string '-9223372036854775807' to int");
TEST_RESULT_INT(cvtZToIntBase("-FF", 16), -255, "convert string to int");
TEST_RESULT_INT(cvtZSubNToIntBase("XFFX", 1, 2, 16), 255, "convert string to int");
TEST_RESULT_INT(cvtZToInt("0"), 0, "convert string to int");
TEST_RESULT_INT(cvtZToInt("1234567890"), 1234567890, "convert string to int");
TEST_RESULT_INT(cvtZSubNToInt("X1234567890X", 1, 10), 1234567890, "convert string to int");
TEST_RESULT_INT(cvtZToInt("-1234567890"), -1234567890, "convert string to int");
}
@ -155,7 +157,9 @@ testRun(void)
TEST_ERROR(cvtZToUInt("5000000000"), FormatError, "unable to convert base 10 string '5000000000' to unsigned int");
TEST_RESULT_UINT(cvtZToUIntBase("FF", 16), 255, "convert string to unsigned int");
TEST_RESULT_UINT(cvtZSubNToUIntBase("XFFX", 1, 2, 16), 255, "convert string to unsigned int");
TEST_RESULT_UINT(cvtZToUInt("3333333333"), 3333333333U, "convert string to unsigned int");
TEST_RESULT_UINT(cvtZSubNToUInt("X3333333333X", 1, 10), 3333333333U, "convert string to unsigned int");
}
// *****************************************************************************************************************************
@ -178,9 +182,11 @@ testRun(void)
cvtZToInt64("9223372036854775808"), FormatError, "unable to convert base 10 string '9223372036854775808' to int64");
TEST_RESULT_INT(cvtZToInt64Base("-FF", 16), -255, "convert string to int64");
TEST_RESULT_INT(cvtZSubNToInt64Base("X-FFX", 1, 3, 16), -255, "convert string to int64");
TEST_RESULT_INT(cvtZToInt64("0"), 0, "convert string to int64");
TEST_RESULT_INT(cvtZToInt64("9223372036854775807"), 9223372036854775807, "convert string to int64");
TEST_RESULT_INT(cvtZToInt64("-9223372036854775807"), -9223372036854775807, "convert string to int64");
TEST_RESULT_INT(cvtZSubNToInt64("X-9223372036854775807X", 1, 20), -9223372036854775807, "convert string to int64");
}
// *****************************************************************************************************************************
@ -199,7 +205,9 @@ testRun(void)
TEST_ERROR(cvtZToUInt64("-1"), FormatError, "unable to convert base 10 string '-1' to uint64");
TEST_RESULT_UINT(cvtZToUInt64Base("FF", 16), 255, "convert string to uint64");
TEST_RESULT_UINT(cvtZSubNToUInt64Base("XFFX", 1, 2, 16), 255, "convert string to uint64");
TEST_RESULT_UINT(cvtZToUInt64("18446744073709551615"), 18446744073709551615U, "convert string to uint64");
TEST_RESULT_UINT(cvtZSubNToUInt64("X18446744073709551615X", 1, 20), 18446744073709551615U, "convert string to uint64");
}
// *****************************************************************************************************************************