From b7e17d80ea02d70e56327828310c578af51795b5 Mon Sep 17 00:00:00 2001 From: David Steele Date: Thu, 7 Oct 2021 19:43:28 -0400 Subject: [PATCH] More efficient memory allocation for Strings and String Variants. The vast majority of Strings are never modified so for most cases allocate memory for the string with the object. This results in one allocation in most cases instead of two. Use strNew() if strCat*() functions are needed. Update varNewStr() in the same way since String Variants can never be modified. This results in one allocation in all cases instead of three. Also update varNewStrZ() to use STR() instead of strNewZ() to save two more allocations. --- src/build/common/render.h | 3 +- src/build/config/render.c | 5 +- src/build/error/render.c | 5 +- src/build/help/render.c | 3 +- src/command/archive/get/get.c | 2 +- src/command/archive/push/file.c | 2 +- src/command/backup/common.c | 2 +- src/command/command.c | 4 +- src/command/help/help.c | 5 +- src/command/info/info.c | 2 +- src/command/restore/restore.c | 9 +- src/command/verify/verify.c | 4 +- src/common/exit.c | 5 +- src/common/io/http/header.c | 4 +- src/common/io/http/query.c | 2 +- src/common/io/http/request.c | 5 +- src/common/type/json.c | 2 +- src/common/type/string.c | 274 ++++++++++++------------ src/common/type/string.h | 37 +++- src/common/type/variant.c | 26 ++- src/config/load.c | 3 +- src/config/parse.c | 6 +- src/db/db.c | 9 +- src/info/info.c | 2 +- src/info/manifest.c | 2 +- src/postgres/client.c | 4 +- src/protocol/server.c | 2 +- src/storage/gcs/storage.c | 4 +- src/storage/helper.c | 8 +- src/storage/s3/storage.c | 6 +- test/src/common/harnessLog.c | 2 +- test/src/common/harnessStorage.c | 4 +- test/src/module/command/verifyTest.c | 2 +- test/src/module/common/compressTest.c | 2 +- test/src/module/common/typeStringTest.c | 38 ++-- test/src/module/performance/typeTest.c | 2 +- test/src/module/storage/azureTest.c | 4 +- test/src/module/storage/gcsTest.c | 8 +- test/src/module/storage/s3Test.c | 4 +- 39 files changed, 283 insertions(+), 230 deletions(-) diff --git a/src/build/common/render.h b/src/build/common/render.h index 6d615bac6..654d1c0d5 100644 --- a/src/build/common/render.h +++ b/src/build/common/render.h @@ -38,7 +38,8 @@ bldDefineRender(const String *const define, const String *const value) __attribute__((always_inline)) static inline String * bldHeader(const char *const module, const char *const description) { - return strNewFmt( + return strCatFmt( + strNew(), COMMENT_BLOCK_BEGIN "\n" "%s\n" "\n" diff --git a/src/build/config/render.c b/src/build/config/render.c index 0f12daebb..ac1f1b796 100644 --- a/src/build/config/render.c +++ b/src/build/config/render.c @@ -32,7 +32,7 @@ Build enum from a string static String * bldEnum(const char *const prefix, const String *const value) { - String *const result = strNewZ(prefix); + String *const result = strCatZ(strNew(), prefix); const char *const valuePtr = strZ(value); bool upper = true; @@ -82,7 +82,8 @@ Render config.auto.h static void bldCfgRenderConfigAutoH(const Storage *const storageRepo, const BldCfg bldCfg) { - String *config = strNewFmt( + String *config = strCatFmt( + strNew(), "%s" "#ifndef CONFIG_CONFIG_AUTO_H\n" "#define CONFIG_CONFIG_AUTO_H\n", diff --git a/src/build/error/render.c b/src/build/error/render.c index 667a61108..1fbcd137d 100644 --- a/src/build/error/render.c +++ b/src/build/error/render.c @@ -48,7 +48,8 @@ Render error.auto.h static void bldErrRenderErrorAutoH(const Storage *const storageRepo, const BldErr bldErr) { - String *error = strNewFmt( + String *error = strCatFmt( + strNew(), "%s" "#ifndef COMMON_ERROR_AUTO_H\n" "#define COMMON_ERROR_AUTO_H\n", @@ -122,7 +123,7 @@ bldErrRenderErrorAutoC(const Storage *const storageRepo, const BldErr bldErr) } strCatZ( - error, + error, " NULL,\n" "};\n"); diff --git a/src/build/help/render.c b/src/build/help/render.c index 1a148b645..e5a4fb47a 100644 --- a/src/build/help/render.c +++ b/src/build/help/render.c @@ -291,7 +291,8 @@ bldHlpRenderHelpAutoC(const Storage *const storageRepo, const BldCfg bldCfg, con // Convert buffer to bytes const Buffer *const buffer = bldHlpRenderHelpAutoCCmp(bldCfg, bldHlp); - String *const help = strNewFmt( + String *const help = strCatFmt( + strNew(), "%s" "static const unsigned char helpData[%zu] =\n" "{\n", diff --git a/src/command/archive/get/get.c b/src/command/archive/get/get.c index 0d3c144d4..f3c06e8fd 100644 --- a/src/command/archive/get/get.c +++ b/src/command/archive/get/get.c @@ -1023,7 +1023,7 @@ cmdArchiveGetAsync(void) { LOG_WARN_FMT("[%s] %s", errorTypeName(checkResult.errorType), strZ(checkResult.errorMessage)); - String *message = strDup(checkResult.errorMessage); + String *message = strCat(strNew(), checkResult.errorMessage); if (!strLstEmpty(checkResult.warnList)) strCatFmt(message, "\n%s", strZ(strLstJoin(checkResult.warnList, "\n"))); diff --git a/src/command/archive/push/file.c b/src/command/archive/push/file.c index 464407d63..54012cdcf 100644 --- a/src/command/archive/push/file.c +++ b/src/command/archive/push/file.c @@ -140,7 +140,7 @@ archivePushFile( } // Set archive destination initially to the archive file, this will be updated later for wal segments - String *archiveDestination = strDup(archiveFile); + String *archiveDestination = strCat(strNew(), archiveFile); // Assume that all repos need a copy of the archive file bool destinationCopyAny = true; diff --git a/src/command/backup/common.c b/src/command/backup/common.c index eec1b4665..e8c8c002f 100644 --- a/src/command/backup/common.c +++ b/src/command/backup/common.c @@ -70,7 +70,7 @@ backupRegExp(BackupRegExpParam param) String *result = NULL; // Start the expression with the anchor, date/time regexp and full backup indicator - result = strNewZ("^" DATE_TIME_REGEX "F"); + result = strCatZ(strNew(), "^" DATE_TIME_REGEX "F"); // Add the diff and/or incr expressions if requested if (param.differential || param.incremental) diff --git a/src/command/command.c b/src/command/command.c index da96388f2..c93826bff 100644 --- a/src/command/command.c +++ b/src/command/command.c @@ -172,7 +172,7 @@ cmdBegin(void) MEM_CONTEXT_TEMP_BEGIN() { // Basic info on command start - String *info = strNewFmt("%s command begin", strZ(cfgCommandRoleName())); + String *info = strCatFmt(strNew(), "%s command begin", strZ(cfgCommandRoleName())); // Free the old option string if it exists. This is needed when more than one command is run in a row so an option // string gets created for the new command. @@ -213,7 +213,7 @@ cmdEnd(int code, const String *errorMessage) LOG_DETAIL_FMT("statistics: %s", strZ(jsonFromKv(statKv))); // Basic info on command end - String *info = strNewFmt("%s command end: ", strZ(cfgCommandRoleName())); + String *info = strCatFmt(strNew(), "%s command end: ", strZ(cfgCommandRoleName())); if (errorMessage == NULL) { diff --git a/src/command/help/help.c b/src/command/help/help.c index d53a43f0f..6075372bb 100644 --- a/src/command/help/help.c +++ b/src/command/help/help.c @@ -238,7 +238,7 @@ helpRender(const Buffer *const helpData) FUNCTION_LOG_PARAM(BUFFER, helpData); FUNCTION_LOG_END(); - String *result = strNewZ(PROJECT_NAME " " PROJECT_VERSION); + String *result = strCatZ(strNew(), PROJECT_NAME " " PROJECT_VERSION); MEM_CONTEXT_TEMP_BEGIN() { @@ -441,7 +441,8 @@ helpRender(const Buffer *const helpData) ConfigOption optionId = varUInt(varLstGet(optionList, optionIdx)); // Get option summary and lower-case first letter if it does not appear to be part of an acronym - String *summary = strNewN(strZ(optionData[optionId].summary), strSize(optionData[optionId].summary) - 1); + String *summary = strCatN( + strNew(), optionData[optionId].summary, strSize(optionData[optionId].summary) - 1); ASSERT(strSize(summary) > 1); if (!isupper(strZ(summary)[1]) && !isdigit(strZ(summary)[1])) diff --git a/src/command/info/info.c b/src/command/info/info.c index d56d99ede..eac0ca007 100644 --- a/src/command/info/info.c +++ b/src/command/info/info.c @@ -1069,7 +1069,7 @@ formatTextDb( } } - String *resultCurrent = strNewZ("\n db (current)"); + String *resultCurrent = strCatZ(strNew(), "\n db (current)"); bool displayCurrent = false; for (unsigned int dbGrpIdx = backupDbGrpIdxMin; dbGrpIdx < backupDbGrpIdxMax; dbGrpIdx++) diff --git a/src/command/restore/restore.c b/src/command/restore/restore.c index 702d4b84a..fff865acc 100644 --- a/src/command/restore/restore.c +++ b/src/command/restore/restore.c @@ -1675,7 +1675,7 @@ restoreRecoveryConf(unsigned int pgVersion, const String *restoreLabel) MEM_CONTEXT_TEMP_BEGIN() { - result = strNewFmt("# Recovery settings generated by " PROJECT_NAME " restore on %s\n", strZ(restoreLabel)); + result = strCatFmt(strNew(), "# Recovery settings generated by " PROJECT_NAME " restore on %s\n", strZ(restoreLabel)); // Output all recovery options KeyValue *optionKv = restoreRecoveryOption(pgVersion); @@ -2073,7 +2073,7 @@ restoreJobResult(const Manifest *manifest, ProtocolParallelJob *job, RegExp *zer bool zeroed = restoreFileZeroed(file->name, zeroExp); bool copy = pckReadBoolP(protocolParallelJobResult(job)); - String *log = strNewZ("restore"); + String *log = strCatZ(strNew(), "restore"); // Note if file was zeroed (i.e. selective restore) if (zeroed) @@ -2285,8 +2285,9 @@ cmdRestore(void) restoreManifestValidate(jobData.manifest, backupData.backupSet); // Log the backup set to restore. If the backup was online then append the time recovery will start from. - String *const message = strNewFmt( - "repo%u: restore backup set %s", cfgOptionGroupIdxToKey(cfgOptGrpRepo, backupData.repoIdx), strZ(backupData.backupSet)); + String *const message = strCatFmt( + strNew(), "repo%u: restore backup set %s", cfgOptionGroupIdxToKey(cfgOptGrpRepo, backupData.repoIdx), + strZ(backupData.backupSet)); if (manifestData(jobData.manifest)->backupOptionOnline) { diff --git a/src/command/verify/verify.c b/src/command/verify/verify.c index 58a7a7152..eb786b8e3 100644 --- a/src/command/verify/verify.c +++ b/src/command/verify/verify.c @@ -227,7 +227,7 @@ verifyInfoFile(const String *pathFileName, bool keepFile, const String *cipherPa CATCH_ANY() { result.errorCode = errorCode(); - String *errorMsg = strNewZ(errorMessage()); + String *errorMsg = strCatZ(strNew(), errorMessage()); if (result.errorCode == errorTypeCode(&ChecksumError)) strCat(errorMsg, strNewFmt(" %s", strZ(pathFileName))); @@ -1252,7 +1252,7 @@ verifyRender(List *archiveIdResultList, List *backupResultList) ASSERT(archiveIdResultList != NULL); ASSERT(backupResultList != NULL); - String *result = strNewZ("Results:"); + String *result = strCatZ(strNew(), "Results:"); // Render archive results if (lstEmpty(archiveIdResultList)) diff --git a/src/common/exit.c b/src/common/exit.c index 2ed0dd7e1..1783d6f57 100644 --- a/src/common/exit.c +++ b/src/common/exit.c @@ -88,7 +88,8 @@ exitErrorDetail(void) FUNCTION_TEST_VOID(); FUNCTION_TEST_RETURN( - strNewFmt( + strCatFmt( + strNew(), "--------------------------------------------------------------------\n" "If SUBMITTING AN ISSUE please provide the following information:\n" "\n" @@ -148,7 +149,7 @@ exitSafe(int result, bool error, SignalType signalType) // On process terminate if (result == errorTypeCode(&TermError)) { - errorMessage = strNewZ("terminated on signal "); + errorMessage = strCatZ(strNew(), "terminated on signal "); // Terminate from a child if (signalType == signalTypeNone) diff --git a/src/common/io/http/header.c b/src/common/io/http/header.c index d6645186f..49d97cc6d 100644 --- a/src/common/io/http/header.c +++ b/src/common/io/http/header.c @@ -95,7 +95,7 @@ httpHeaderAdd(HttpHeader *this, const String *key, const String *value) { MEM_CONTEXT_TEMP_BEGIN() { - String *valueAppend = strDup(varStr(valueVar)); + String *const valueAppend = strCat(strNew(), varStr(valueVar)); strCatZ(valueAppend, ", "); strCatZ(valueAppend, strZ(value)); @@ -177,7 +177,7 @@ httpHeaderRedact(const HttpHeader *this, const String *key) String * httpHeaderToLog(const HttpHeader *this) { - String *result = strNewZ("{"); + String *result = strCatZ(strNew(), "{"); const StringList *keyList = httpHeaderList(this); for (unsigned int keyIdx = 0; keyIdx < strLstSize(keyList); keyIdx++) diff --git a/src/common/io/http/query.c b/src/common/io/http/query.c index c2db63ae2..9764973e4 100644 --- a/src/common/io/http/query.c +++ b/src/common/io/http/query.c @@ -291,7 +291,7 @@ httpQueryRender(const HttpQuery *this, HttpQueryRenderParam param) String * httpQueryToLog(const HttpQuery *this) { - String *result = strNewZ("{"); + String *result = strCatZ(strNew(), "{"); const StringList *keyList = httpQueryList(this); for (unsigned int keyIdx = 0; keyIdx < strLstSize(keyList); keyIdx++) diff --git a/src/common/io/http/request.c b/src/common/io/http/request.c index ed0107b6b..ba68c75e6 100644 --- a/src/common/io/http/request.c +++ b/src/common/io/http/request.c @@ -96,7 +96,8 @@ httpRequestProcess(HttpRequest *this, bool waitForResponse, bool contentCache) // Format the request and user agent String *requestStr = - strNewFmt( + strCatFmt( + strNew(), "%s %s%s%s " HTTP_VERSION CRLF_Z HTTP_HEADER_USER_AGENT ":" PROJECT_NAME "/" PROJECT_VERSION CRLF_Z, strZ(httpRequestVerb(this)), strZ(httpRequestPath(this)), httpRequestQuery(this) == NULL ? "" : "?", httpRequestQuery(this) == NULL ? "" : strZ(httpQueryRenderP(httpRequestQuery(this)))); @@ -242,7 +243,7 @@ httpRequestError(const HttpRequest *this, HttpResponse *response) ASSERT(response != NULL); // Error code - String *error = strNewFmt("HTTP request failed with %u", httpResponseCode(response)); + String *error = strCatFmt(strNew(), "HTTP request failed with %u", httpResponseCode(response)); // Add reason when present if (strSize(httpResponseReason(response)) > 0) diff --git a/src/common/type/json.c b/src/common/type/json.c index 8dd6cc1c1..52a9baa10 100644 --- a/src/common/type/json.c +++ b/src/common/type/json.c @@ -787,7 +787,7 @@ jsonFromKvInternal(const KeyValue *kv) ASSERT(kv != NULL); - String *result = strNewZ("{"); + String *result = strCatZ(strNew(), "{"); MEM_CONTEXT_TEMP_BEGIN() { diff --git a/src/common/type/string.c b/src/common/type/string.c index 37b75cfed..fd514364c 100644 --- a/src/common/type/string.c +++ b/src/common/type/string.c @@ -39,6 +39,21 @@ STRING_EXTERN(TRUE_STR, TRUE_Z); STRING_EXTERN(Y_STR, Y_Z); STRING_EXTERN(ZERO_STR, ZERO_Z); +/*********************************************************************************************************************************** +Buffer macros +***********************************************************************************************************************************/ +// Fixed size buffer allocated at the end of the object allocation +#define STR_FIXED_BUFFER (char *)(this + 1) + +// Is the string using the fixed size buffer? +#define STR_IS_FIXED_BUFFER() (this->pub.buffer == STR_FIXED_BUFFER) + +// Empty buffer +#define STR_EMPTY_BUFFER (EMPTY_STR->pub.buffer) + +// Is the string using the empty buffer? +#define STR_IS_EMPTY_BUFFER() (this->pub.buffer == STR_EMPTY_BUFFER) + /*********************************************************************************************************************************** Maximum size of a string ***********************************************************************************************************************************/ @@ -74,15 +89,38 @@ strNew(void) { .pub = { - // A zero-length string is not very useful so assume this string is being created for appending and allocate extra space - .extra = STRING_EXTRA_MIN, + // Set empty so nothing is allocated until needed + .buffer = STR_EMPTY_BUFFER, }, .memContext = memContextCurrent(), }; - // Allocate and assign string - this->pub.buffer = memNew(STRING_EXTRA_MIN + 1); - this->pub.buffer[0] = '\0'; + FUNCTION_TEST_RETURN(this); +} + +/*********************************************************************************************************************************** +Create fixed size String +***********************************************************************************************************************************/ +static String * +strNewFixed(const size_t size) +{ + FUNCTION_TEST_BEGIN(); + FUNCTION_TEST_PARAM(SIZE, size); + FUNCTION_TEST_END(); + + CHECK_SIZE(size); + + String *this = memNew(sizeof(String) + size + 1); + + *this = (String) + { + .pub = + { + .size = (unsigned int)size, + .buffer = STR_FIXED_BUFFER, + }, + .memContext = memContextCurrent(), + }; FUNCTION_TEST_RETURN(this); } @@ -97,25 +135,12 @@ strNewZ(const char *const string) ASSERT(string != NULL); - // Check size - size_t stringSize = strlen(string); - CHECK_SIZE(stringSize); - // Create object - String *this = memNew(sizeof(String)); + String *this = strNewFixed(strlen(string)); - *this = (String) - { - .pub = - { - .size = (unsigned int)stringSize, - }, - .memContext = memContextCurrent(), - }; - - // Allocate and assign string - this->pub.buffer = memNew(strSize(this) + this->pub.extra + 1); - strcpy(this->pub.buffer, string); + // Assign string + strncpy(this->pub.buffer, string, strSize(this)); + this->pub.buffer[strSize(this)] = '\0'; FUNCTION_TEST_RETURN(this); } @@ -144,23 +169,10 @@ strNewBuf(const Buffer *buffer) ASSERT(buffer != NULL); - // Check size - CHECK_SIZE(bufUsed(buffer)); - // Create object - String *this = memNew(sizeof(String)); + String *this = strNewFixed(bufUsed(buffer)); - *this = (String) - { - .pub = - { - .size = (unsigned int)bufUsed(buffer), - }, - .memContext = memContextCurrent(), - }; - - // Allocate and assign string - this->pub.buffer = memNew(strSize(this) + 1); + // Assign string memcpy(this->pub.buffer, bufPtrConst(buffer), strSize(this)); this->pub.buffer[strSize(this)] = 0; @@ -178,24 +190,10 @@ strNewEncode(EncodeType type, const Buffer *buffer) ASSERT(buffer != NULL); - // Check encoded size - size_t size = encodeToStrSize(type, bufUsed(buffer)); - CHECK_SIZE(size); - // Create object - String *this = memNew(sizeof(String)); + String *this = strNewFixed(encodeToStrSize(type, bufUsed(buffer))); - *this = (String) - { - .pub = - { - .size = (unsigned int)size, - }, - .memContext = memContextCurrent(), - }; - - // Allocate and encode buffer - this->pub.buffer = memNew(strSize(this) + 1); + // Encode buffer encodeToStr(type, bufPtrConst(buffer), bufUsed(buffer), this->pub.buffer); FUNCTION_TEST_RETURN(this); @@ -211,26 +209,13 @@ strNewFmt(const char *format, ...) ASSERT(format != NULL); - // Create object - String *this = memNew(sizeof(String)); - - *this = (String) - { - .memContext = memContextCurrent(), - }; - - // Determine how long the allocated string needs to be + // Determine how long the allocated string needs to be and create object va_list argumentList; va_start(argumentList, format); - size_t formatSize = (size_t)vsnprintf(NULL, 0, format, argumentList); + String *this = strNewFixed((size_t)vsnprintf(NULL, 0, format, argumentList)); va_end(argumentList); - // Check size - CHECK_SIZE(formatSize); - - // Allocate and assign string - this->pub.size = (unsigned int)formatSize; - this->pub.buffer = memNew(strSize(this) + 1); + // Format string va_start(argumentList, format); vsnprintf(this->pub.buffer, strSize(this) + 1, format, argumentList); va_end(argumentList); @@ -249,27 +234,13 @@ strNewN(const char *string, size_t size) ASSERT(string != NULL); - // Check size - CHECK_SIZE(size); - // Create object - String *this = memNew(sizeof(String)); + String *this = strNewFixed(size); - *this = (String) - { - .pub = - { - .size = (unsigned int)size, - }, - .memContext = memContextCurrent(), - }; - - // Allocate and assign string - this->pub.buffer = memNew(strSize(this) + 1); + // Assign string strncpy(this->pub.buffer, string, strSize(this)); this->pub.buffer[strSize(this)] = 0; - // Return buffer FUNCTION_TEST_RETURN(this); } @@ -351,6 +322,9 @@ strResize(String *this, size_t requested) if (requested > this->pub.extra) { + // Fixed size strings may not be resized + CHECK(!STR_IS_FIXED_BUFFER()); + // Check size CHECK_SIZE(strSize(this) + requested); @@ -363,7 +337,10 @@ strResize(String *this, size_t requested) MEM_CONTEXT_BEGIN(this->memContext) { - this->pub.buffer = memResize(this->pub.buffer, strSize(this) + this->pub.extra + 1); + if (STR_IS_EMPTY_BUFFER()) + this->pub.buffer = memNew(strSize(this) + this->pub.extra + 1); + else + this->pub.buffer = memResize(this->pub.buffer, strSize(this) + this->pub.extra + 1); } MEM_CONTEXT_END(); } @@ -400,13 +377,16 @@ strCatZ(String *this, const char *cat) // Determine length of string to append size_t sizeGrow = strlen(cat); - // Ensure there is enough space to grow the string - strResize(this, sizeGrow); + if (sizeGrow != 0) + { + // Ensure there is enough space to grow the string + strResize(this, sizeGrow); - // Append the string - strcpy(this->pub.buffer + strSize(this), cat); - this->pub.size += (unsigned int)sizeGrow; - this->pub.extra -= (unsigned int)sizeGrow; + // Append the string + strcpy(this->pub.buffer + strSize(this), cat); + this->pub.size += (unsigned int)sizeGrow; + this->pub.extra -= (unsigned int)sizeGrow; + } FUNCTION_TEST_RETURN(this); } @@ -421,22 +401,41 @@ strCatZN(String *this, const char *cat, size_t size) FUNCTION_TEST_END(); ASSERT(this != NULL); - ASSERT(cat != NULL); - // Ensure there is enough space to grow the string - strResize(this, size); + if (size != 0) + { + ASSERT(cat != NULL); - // Append the string - strncpy(this->pub.buffer + strSize(this), cat, size); - this->pub.buffer[strSize(this) + size] = '\0'; + // Ensure there is enough space to grow the string + strResize(this, size); - // Update size/extra - this->pub.size += (unsigned int)size; - this->pub.extra -= (unsigned int)size; + // Append the string + strncpy(this->pub.buffer + strSize(this), cat, size); + this->pub.buffer[strSize(this) + size] = '\0'; + + // Update size/extra + this->pub.size += (unsigned int)size; + this->pub.extra -= (unsigned int)size; + } FUNCTION_TEST_RETURN(this); } +/**********************************************************************************************************************************/ +String * +strCatBuf(String *const this, const Buffer *const buffer) +{ + FUNCTION_TEST_BEGIN(); + FUNCTION_TEST_PARAM(STRING, this); + FUNCTION_TEST_PARAM(BUFFER, buffer); + FUNCTION_TEST_END(); + + ASSERT(this != NULL); + ASSERT(buffer != NULL); + + FUNCTION_TEST_RETURN(strCatZN(this, (char *)bufPtrConst(buffer), bufUsed(buffer))); +} + /**********************************************************************************************************************************/ String * strCatChr(String *this, char cat) @@ -473,16 +472,20 @@ strCatEncode(String *this, EncodeType type, const Buffer *buffer) ASSERT(this != NULL); ASSERT(buffer != NULL); - // Ensure there is enough space to grow the string size_t encodeSize = encodeToStrSize(type, bufUsed(buffer)); - strResize(this, encodeSize); - // Append the encoded string - encodeToStr(type, bufPtrConst(buffer), bufUsed(buffer), this->pub.buffer + strSize(this)); + if (encodeSize != 0) + { + // Ensure there is enough space to grow the string + strResize(this, encodeSize); - // Update size/extra - this->pub.size += (unsigned int)encodeSize; - this->pub.extra -= (unsigned int)encodeSize; + // Append the encoded string + encodeToStr(type, bufPtrConst(buffer), bufUsed(buffer), this->pub.buffer + strSize(this)); + + // Update size/extra + this->pub.size += (unsigned int)encodeSize; + this->pub.extra -= (unsigned int)encodeSize; + } FUNCTION_TEST_RETURN(this); } @@ -505,16 +508,19 @@ strCatFmt(String *this, const char *format, ...) size_t sizeGrow = (size_t)vsnprintf(NULL, 0, format, argumentList); va_end(argumentList); - // Ensure there is enough space to grow the string - strResize(this, sizeGrow); + if (sizeGrow != 0) + { + // Ensure there is enough space to grow the string + strResize(this, sizeGrow); - // Append the formatted string - va_start(argumentList, format); - vsnprintf(this->pub.buffer + strSize(this), sizeGrow + 1, format, argumentList); - va_end(argumentList); + // Append the formatted string + va_start(argumentList, format); + vsnprintf(this->pub.buffer + strSize(this), sizeGrow + 1, format, argumentList); + va_end(argumentList); - this->pub.size += (unsigned int)sizeGrow; - this->pub.extra -= (unsigned int)sizeGrow; + this->pub.size += (unsigned int)sizeGrow; + this->pub.extra -= (unsigned int)sizeGrow; + } FUNCTION_TEST_RETURN(this); } @@ -695,9 +701,8 @@ strUpper(String *this) ASSERT(this != NULL); - if (strSize(this) > 0) - for (size_t idx = 0; idx <= strSize(this); idx++) - this->pub.buffer[idx] = (char)toupper(this->pub.buffer[idx]); + for (size_t idx = 0; idx < strSize(this); idx++) + this->pub.buffer[idx] = (char)toupper(this->pub.buffer[idx]); FUNCTION_TEST_RETURN(this); } @@ -712,9 +717,8 @@ strLower(String *this) ASSERT(this != NULL); - if (strSize(this) > 0) - for (size_t idx = 0; idx <= strSize(this); idx++) - this->pub.buffer[idx] = (char)tolower(this->pub.buffer[idx]); + for (size_t idx = 0; idx < strSize(this); idx++) + this->pub.buffer[idx] = (char)tolower(this->pub.buffer[idx]); FUNCTION_TEST_RETURN(this); } @@ -947,20 +951,13 @@ strTrim(String *this) if (begin != this->pub.buffer || newSize < strSize(this)) { - // Calculate new size + // Calculate new size and extra + this->pub.extra = (unsigned int)(strSize(this) - newSize); this->pub.size = (unsigned int)newSize; // Move the substr to the beginning of the buffer memmove(this->pub.buffer, begin, strSize(this)); this->pub.buffer[strSize(this)] = 0; - this->pub.extra = 0; - - MEM_CONTEXT_BEGIN(this->memContext) - { - // Resize the buffer - this->pub.buffer = memResize(this->pub.buffer, strSize(this) + 1); - } - MEM_CONTEXT_END(); } } @@ -1005,16 +1002,9 @@ strTrunc(String *this, int idx) if (strSize(this) > 0) { // Reset the size to end at the index - this->pub.size = (unsigned int)(idx); + this->pub.extra = (unsigned int)(strSize(this) - (size_t)idx); + this->pub.size = (unsigned int)idx; this->pub.buffer[strSize(this)] = 0; - this->pub.extra = 0; - - MEM_CONTEXT_BEGIN(this->memContext) - { - // Resize the buffer - this->pub.buffer = memResize(this->pub.buffer, strSize(this) + 1); - } - MEM_CONTEXT_END(); } FUNCTION_TEST_RETURN(this); @@ -1095,7 +1085,9 @@ strFree(String *this) { MEM_CONTEXT_BEGIN(this->memContext) { - memFree(this->pub.buffer); + if (!STR_IS_EMPTY_BUFFER() && !STR_IS_FIXED_BUFFER()) + memFree(this->pub.buffer); + memFree(this); } MEM_CONTEXT_END(); diff --git a/src/common/type/string.h b/src/common/type/string.h index b373a5c6e..b4b3d479e 100644 --- a/src/common/type/string.h +++ b/src/common/type/string.h @@ -41,31 +41,38 @@ typedef struct String String; /*********************************************************************************************************************************** Constructors + +Only strings created with strNew() can use strCat*() functions. If a string needs to be concatenated then start with, .e.g.: + +String *example = strCatZ(strNew(), "example"); + +This syntax signals that the string will be modified so the string is allocated separately from the object so it can be resized when +needed. Most strings never need to be modified and can be stored more efficiently by allocating their memory with the object. ***********************************************************************************************************************************/ -// Create a new empty string +// Create a new empty string for concatenation String *strNew(void); -// Create a new string from a zero-terminated string +// Create a new fixed length string from a zero-terminated string String *strNewZ(const char *const string); -// Create a new string from a buffer. If the buffer has a NULL character this may not work as expected. All the data will be copied -// but only the data before the NULL character will be used as a string. +// Create a new fixed length string from a buffer. If the buffer has a NULL character this may not work as expected. All the data +// will be copied but only the data before the NULL character will be used as a string. String *strNewBuf(const Buffer *buffer); -// Create a new string by converting the double value +// Create a new fixed length string by converting the double value String *strNewDbl(double value); -// Create a new string encoded with the specified type (e.g. encodeBase64) from a buffer +// Create a new fixed length string encoded with the specified type (e.g. encodeBase64) from a buffer String *strNewEncode(EncodeType type, const Buffer *buffer); -// Create a new string from a format string with parameters (i.e. sprintf) +// Create a new fixed length string from a format string with parameters (i.e. sprintf) String *strNewFmt(const char *format, ...) __attribute__((format(printf, 1, 2))); -// Create a new string from a string with a specific length. The string may or may not be zero-terminated but we'll use that -// nomenclature since we're not concerned about the end of the string. +// Create a new fixed length string from a zero-terminated string with a specific length. The string may or may not be +// zero-terminated but we'll use that nomenclature since we're not concerned about the end of the string. String *strNewN(const char *string, size_t size); -// Duplicate a string +// Create a new fixed length string from a string String *strDup(const String *this); /*********************************************************************************************************************************** @@ -110,6 +117,9 @@ bool strBeginsWithZ(const String *this, const char *beginsWith); String *strCat(String *this, const String *cat); String *strCatZ(String *this, const char *cat); +// Append a buffer +String *strCatBuf(String *this, const Buffer *buffer); + // Append a character String *strCatChr(String *this, char cat); @@ -123,6 +133,13 @@ String *strCatFmt(String *this, const char *format, ...) __attribute__((format(p // N is <= the end of the string being concatenated. String *strCatZN(String *this, const char *cat, size_t size); +__attribute__((always_inline)) static inline String * +strCatN(String *this, const String *const cat, const size_t size) +{ + ASSERT_INLINE(cat != NULL); + return strCatZN(this, strZ(cat), size); +} + // Return the index to the location of the first occurrence of a character within a string, else -1 int strChr(const String *this, char chr); diff --git a/src/common/type/variant.c b/src/common/type/variant.c index 7c5022158..1690b89bb 100644 --- a/src/common/type/variant.c +++ b/src/common/type/variant.c @@ -808,19 +808,36 @@ varNewStr(const String *data) FUNCTION_TEST_PARAM(STRING, data); FUNCTION_TEST_END(); - // Allocate memory for the variant and set the type and data - VariantString *this = memNew(sizeof(VariantString)); + // Allocate memory for the variant + VariantString *this = memNew(sizeof(VariantString) + (data != NULL ? sizeof(StringPub) + strSize(data) + 1 : 0)); *this = (VariantString) { .pub = { .type = varTypeString, - .data = strDup(data), }, .memContext = memContextCurrent(), }; + if (data != NULL) + { + // Point the String after the VariantString struct + StringPub *const pubData = (StringPub *)(this + 1); + this->pub.data = (String *)pubData; + + // Assign the string size and buffer (after StringPub struct) + *pubData = (StringPub) + { + .size = (unsigned int)strSize(data), + .buffer = (char *)(pubData + 1), + }; + + // Assign the string + strncpy(pubData->buffer, strZ(data), strSize(data)); + pubData->buffer[strSize(data)] = '\0'; + } + FUNCTION_TEST_RETURN((Variant *)this); } @@ -831,7 +848,7 @@ varNewStrZ(const char *data) FUNCTION_TEST_PARAM(STRINGZ, data); FUNCTION_TEST_END(); - FUNCTION_TEST_RETURN(varNewStr(data == NULL ? NULL : strNewZ(data))); + FUNCTION_TEST_RETURN(varNewStr(data == NULL ? NULL : STR(data))); } /**********************************************************************************************************************************/ @@ -1031,7 +1048,6 @@ varFree(Variant *this) case varTypeString: memContext = ((VariantString *)this)->memContext; - strFree(((VariantString *)this)->pub.data); break; case varTypeUInt: diff --git a/src/config/load.c b/src/config/load.c index 53bde9ea9..07ab05504 100644 --- a/src/config/load.c +++ b/src/config/load.c @@ -333,7 +333,8 @@ cfgLoadLogFile(void) MEM_CONTEXT_TEMP_BEGIN() { // Construct log filename prefix - String *logFile = strNewFmt( + String *logFile = strCatFmt( + strNew(), "%s/%s-%s", strZ(cfgOptionStr(cfgOptLogPath)), cfgOptionTest(cfgOptStanza) ? strZ(cfgOptionStr(cfgOptStanza)): "all", cfgCommandName()); diff --git a/src/config/parse.c b/src/config/parse.c index 3bbea6b4d..28c524c55 100644 --- a/src/config/parse.c +++ b/src/config/parse.c @@ -515,7 +515,7 @@ cfgParseCommandRoleName(const ConfigCommand commandId, const ConfigCommandRole c FUNCTION_TEST_PARAM(STRING, separator); FUNCTION_TEST_END(); - String *result = strNewZ(cfgParseCommandName(commandId)); + String *result = strCatZ(strNew(), cfgParseCommandName(commandId)); if (commandRoleId != cfgCmdRoleMain) strCatFmt(result, "%s%s", strZ(separator), strZ(cfgParseCommandRoleStr(commandRoleId))); @@ -1097,14 +1097,14 @@ cfgFileLoad( // NOTE: Pas // Convert the contents of the file buffer to the config string object if (buffer != NULL) - result = strNewBuf(buffer); + result = strCatBuf(strNew(), buffer); else if (strEq(configFileName, optConfigDefaultCurrent)) { // If config is current default and it was not found, attempt to load the config file from the old default location buffer = storageGetP(storageNewReadP(storage, origConfigDefault, .ignoreMissing = !configRequired)); if (buffer != NULL) - result = strNewBuf(buffer); + result = strCatBuf(strNew(), buffer); } } diff --git a/src/db/db.c b/src/db/db.c index 9871c24d3..bc1865e8a 100644 --- a/src/db/db.c +++ b/src/db/db.c @@ -277,7 +277,8 @@ dbBackupStartQuery(unsigned int pgVersion, bool startFast) FUNCTION_TEST_END(); // Build query to return start lsn and WAL segment name - String *result = strNewFmt( + String *result = strCatFmt( + strNew(), "select lsn::text as lsn,\n" " pg_catalog.pg_%sfile_name(lsn)::text as wal_segment_name\n" " from pg_catalog.pg_start_backup('" PROJECT_NAME " backup started at ' || current_timestamp", @@ -372,7 +373,8 @@ dbBackupStopQuery(unsigned int pgVersion) FUNCTION_TEST_END(); // Build query to return start lsn and WAL segment name - String *result = strNewFmt( + String *result = strCatFmt( + strNew(), "select lsn::text as lsn,\n" " pg_catalog.pg_%sfile_name(lsn)::text as wal_segment_name", strZ(pgWalName(pgVersion))); @@ -510,7 +512,8 @@ dbReplayWait(Db *this, const String *targetLsn, TimeMSec timeout) do { // Build the query - String *query = strNewFmt( + String *query = strCatFmt( + strNew(), "select replayLsn::text,\n" " (replayLsn > '%s')::bool as targetReached", strZ(targetLsn)); diff --git a/src/info/info.c b/src/info/info.c index ea4161562..533ef1682 100644 --- a/src/info/info.c +++ b/src/info/info.c @@ -506,7 +506,7 @@ infoLoad(const String *error, InfoLoadCallback *callbackFunction, void *callback if (loadErrorType == NULL) { loadErrorType = errorType(); - loadErrorMessage = strNewFmt("%s:", strZ(error)); + loadErrorMessage = strCatFmt(strNew(), "%s:", strZ(error)); } // Else if the error type is different else if (loadErrorType != errorType()) diff --git a/src/info/manifest.c b/src/info/manifest.c index 0a211480a..939c75311 100644 --- a/src/info/manifest.c +++ b/src/info/manifest.c @@ -2847,7 +2847,7 @@ manifestTargetPath(const Manifest *this, const ManifestTarget *target) MEM_CONTEXT_TEMP_BEGIN() { - String *pgPath = strPath(manifestPathPg(target->name)); + String *pgPath = strCat(strNew(), strPath(manifestPathPg(target->name))); if (strSize(pgPath) != 0) strCatZ(pgPath, "/"); diff --git a/src/postgres/client.c b/src/postgres/client.c index e61b1995c..e6797a3f3 100644 --- a/src/postgres/client.c +++ b/src/postgres/client.c @@ -101,7 +101,7 @@ pgClientEscape(const String *string) ASSERT(string != NULL); - String *result = strNewZ("'"); + String *result = strCatZ(strNew(), "'"); // Iterate all characters in the string for (unsigned stringIdx = 0; stringIdx < strSize(string); stringIdx++) @@ -134,7 +134,7 @@ pgClientOpen(PgClient *this) MEM_CONTEXT_TEMP_BEGIN() { // Base connection string - String *connInfo = strNewFmt("dbname=%s port=%u", strZ(pgClientEscape(this->database)), this->port); + String *connInfo = strCatFmt(strNew(), "dbname=%s port=%u", strZ(pgClientEscape(this->database)), this->port); // Add user if specified if (this->user != NULL) diff --git a/src/protocol/server.c b/src/protocol/server.c index 03364b569..6497a8a8b 100644 --- a/src/protocol/server.c +++ b/src/protocol/server.c @@ -209,7 +209,7 @@ protocolServerProcess( if (errType == NULL) { errType = errorType(); - errMessage = strNewZ(errorMessage()); + errMessage = strCatZ(strNew(), errorMessage()); errMessageFirst = strNewZ(errorMessage()); errStackTrace = strNewZ(errorStackTrace()); } diff --git a/src/storage/gcs/storage.c b/src/storage/gcs/storage.c index 836ef50c2..f2310bdb5 100644 --- a/src/storage/gcs/storage.c +++ b/src/storage/gcs/storage.c @@ -168,7 +168,7 @@ storageGcsAuthJwt(StorageGcs *this, time_t timeBegin) FUNCTION_TEST_END(); // Static header with dot delimiter - String *result = strNewZ("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9."); + String *result = strCatZ(strNew(), "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9."); MEM_CONTEXT_TEMP_BEGIN() { @@ -382,7 +382,7 @@ storageGcsRequestAsync(StorageGcs *this, const String *verb, StorageGcsRequestAs MEM_CONTEXT_TEMP_BEGIN() { // Generate path - String *path = strNewFmt("%s/storage/v1/b", param.upload ? "/upload" : ""); + String *path = strCatFmt(strNew(), "%s/storage/v1/b", param.upload ? "/upload" : ""); if (!param.noBucket) strCatFmt(path, "/%s/o", strZ(this->bucket)); diff --git a/src/storage/helper.c b/src/storage/helper.c index c9e177c4d..bf94efe24 100644 --- a/src/storage/helper.c +++ b/src/storage/helper.c @@ -306,9 +306,9 @@ storageRepoPathExpression(const String *expression, const String *path) { // Construct the base path if (storageHelper.stanza != NULL) - result = strNewFmt(STORAGE_PATH_ARCHIVE "/%s", strZ(storageHelper.stanza)); + result = strCatFmt(strNew(), STORAGE_PATH_ARCHIVE "/%s", strZ(storageHelper.stanza)); else - result = strNewZ(STORAGE_PATH_ARCHIVE); + result = strCatZ(strNew(), STORAGE_PATH_ARCHIVE); // If a subpath should be appended, determine if it is WAL path, else just append the subpath if (path != NULL) @@ -326,9 +326,9 @@ storageRepoPathExpression(const String *expression, const String *path) { // Construct the base path if (storageHelper.stanza != NULL) - result = strNewFmt(STORAGE_PATH_BACKUP "/%s", strZ(storageHelper.stanza)); + result = strCatFmt(strNew(), STORAGE_PATH_BACKUP "/%s", strZ(storageHelper.stanza)); else - result = strNewZ(STORAGE_PATH_BACKUP); + result = strCatZ(strNew(), STORAGE_PATH_BACKUP); // Append subpath if provided if (path != NULL) diff --git a/src/storage/s3/storage.c b/src/storage/s3/storage.c index f463c4549..20d9c7ca2 100644 --- a/src/storage/s3/storage.c +++ b/src/storage/s3/storage.c @@ -174,8 +174,8 @@ storageS3Auth( const StringList *headerList = strLstSort(strLstDup(httpHeaderList(httpHeader)), sortOrderAsc); String *signedHeaders = NULL; - String *canonicalRequest = strNewFmt( - "%s\n%s\n%s\n", strZ(verb), strZ(path), query == NULL ? "" : strZ(httpQueryRenderP(query))); + String *canonicalRequest = strCatFmt( + strNew(), "%s\n%s\n%s\n", strZ(verb), strZ(path), query == NULL ? "" : strZ(httpQueryRenderP(query))); for (unsigned int headerIdx = 0; headerIdx < strLstSize(headerList); headerIdx++) { @@ -189,7 +189,7 @@ storageS3Auth( strCatFmt(canonicalRequest, "%s:%s\n", strZ(headerKeyLower), strZ(httpHeaderGet(httpHeader, headerKey))); if (signedHeaders == NULL) - signedHeaders = strDup(headerKeyLower); + signedHeaders = strCat(strNew(), headerKeyLower); else strCatFmt(signedHeaders, ";%s", strZ(headerKeyLower)); } diff --git a/test/src/common/harnessLog.c b/test/src/common/harnessLog.c index 0fe6b0a06..9636cfe95 100644 --- a/test/src/common/harnessLog.c +++ b/test/src/common/harnessLog.c @@ -349,7 +349,7 @@ hrnLogReplace(void) } // Build replacement string. If versioned then append the version number. - String *replace = strNewFmt("[%s", strZ(logReplace->replacement)); + String *replace = strCatFmt(strNew(), "[%s", strZ(logReplace->replacement)); if (logReplace->version) { diff --git a/test/src/common/harnessStorage.c b/test/src/common/harnessStorage.c index 183e8c565..f8f9f77d2 100644 --- a/test/src/common/harnessStorage.c +++ b/test/src/common/harnessStorage.c @@ -188,7 +188,7 @@ testStorageGet(const Storage *const storage, const char *const file, const char ASSERT(storage != NULL); ASSERT(file != NULL); - String *fileFull = storagePathP(storage, STR(file)); + String *fileFull = strCat(strNew(), storagePathP(storage, STR(file))); // Add compression extension if one exists compressExtCat(fileFull, param.compressType); @@ -433,7 +433,7 @@ hrnStoragePut( ASSERT(file != NULL); // Add compression extension to file name - String *fileStr = strNewZ(file); + String *fileStr = strCatZ(strNew(), file); compressExtCat(fileStr, param.compressType); // Create file diff --git a/test/src/module/command/verifyTest.c b/test/src/module/command/verifyTest.c index 39ac79ac7..3f18dcac4 100644 --- a/test/src/module/command/verifyTest.c +++ b/test/src/module/command/verifyTest.c @@ -876,7 +876,7 @@ testRun(void) TEST_TITLE("encrypted/compressed file in backup"); // Create a compressed encrypted repo file in backup - filePathName = strNewZ(STORAGE_REPO_BACKUP "/testfile"); + filePathName = strCatZ(strNew(), STORAGE_REPO_BACKUP "/testfile"); HRN_STORAGE_PUT_Z( storageRepoWrite(), strZ(filePathName), fileContents, .compressType = compressTypeGz, .cipherType = cipherTypeAes256Cbc, .cipherPass = "pass"); diff --git a/test/src/module/common/compressTest.c b/test/src/module/common/compressTest.c index fbc94e27e..dd9ee7394 100644 --- a/test/src/module/common/compressTest.c +++ b/test/src/module/common/compressTest.c @@ -370,7 +370,7 @@ testRun(void) // ------------------------------------------------------------------------------------------------------------------------- TEST_TITLE("compressExtCat()"); - String *file = strNewZ("file"); + String *file = strCatZ(strNew(), "file"); TEST_RESULT_VOID(compressExtCat(file, compressTypeGz), "cat gz ext"); TEST_RESULT_STR_Z(file, "file.gz", " check gz ext"); diff --git a/test/src/module/common/typeStringTest.c b/test/src/module/common/typeStringTest.c index ece1091bd..88a87cb79 100644 --- a/test/src/module/common/typeStringTest.c +++ b/test/src/module/common/typeStringTest.c @@ -42,6 +42,7 @@ testRun(void) TEST_ERROR(CHECK_SIZE(STRING_SIZE_MAX + 1), AssertError, "string size must be <= 1073741824 bytes"); String *string = strNewZ("static string"); + TEST_RESULT_BOOL(string->pub.buffer == (char *)(string + 1), true, "string has fixed buffer"); TEST_RESULT_STR_Z(string, "static string", "new with static string"); TEST_RESULT_UINT(strSize(string), 13, "check size"); TEST_RESULT_BOOL(strEmpty(string), false, "is not empty"); @@ -77,8 +78,10 @@ testRun(void) TEST_TITLE("empty string is allocated extra space"); TEST_ASSIGN(string, strNew(), "new empty string"); - TEST_RESULT_UINT(string->pub.size, 0, " check size"); - TEST_RESULT_UINT(string->pub.extra, 64, " check extra"); + TEST_RESULT_UINT(string->pub.size, 0, "check size"); + TEST_RESULT_UINT(string->pub.extra, 0, "check extra"); + TEST_RESULT_PTR(string->pub.buffer, EMPTY_STR->pub.buffer, "check buffer"); + TEST_RESULT_VOID(strFree(string), "free string"); // ------------------------------------------------------------------------------------------------------------------------- TEST_TITLE("strNewEncode()"); @@ -122,23 +125,34 @@ testRun(void) // ***************************************************************************************************************************** if (testBegin("strCat*()")) { - String *string = strNewZ("XXXX"); + String *string = strCatZ(strNew(), "XXX"); String *string2 = strNewZ("ZZZZ"); - TEST_RESULT_STR_Z(strCat(string, STRDEF("YYYY")), "XXXXYYYY", "cat string"); + TEST_RESULT_STR_Z(strCatN(string, STRDEF("XX"), 1), "XXXX", "cat N chars"); TEST_RESULT_UINT(string->pub.extra, 60, "check extra"); - TEST_RESULT_STR_Z(strCatFmt(string, "%05d", 777), "XXXXYYYY00777", "cat formatted string"); + TEST_RESULT_STR_Z(strCatZ(string, ""), "XXXX", "cat empty string"); + TEST_RESULT_UINT(string->pub.extra, 60, "check extra"); + TEST_RESULT_STR_Z(strCatEncode(string, encodeBase64, BUFSTRDEF("")), "XXXX", "cat empty encode"); + TEST_RESULT_UINT(string->pub.extra, 60, "check extra"); + TEST_RESULT_STR_Z(strCat(string, STRDEF("YYYY")), "XXXXYYYY", "cat string"); + TEST_RESULT_UINT(string->pub.extra, 56, "check extra"); + TEST_RESULT_STR_Z(strCatZN(string, NULL, 0), "XXXXYYYY", "cat 0"); + TEST_RESULT_UINT(string->pub.extra, 56, "check extra"); + TEST_RESULT_STR_Z(strCatBuf(string, BUFSTRDEF("?")), "XXXXYYYY?", "cat buf"); TEST_RESULT_UINT(string->pub.extra, 55, "check extra"); - TEST_RESULT_STR_Z(strCatChr(string, '!'), "XXXXYYYY00777!", "cat chr"); - TEST_RESULT_UINT(string->pub.extra, 54, "check extra"); + TEST_RESULT_STR_Z(strCatFmt(string, "%05d", 777), "XXXXYYYY?00777", "cat formatted string"); + TEST_RESULT_UINT(string->pub.extra, 50, "check extra"); + TEST_RESULT_STR_Z(strCatChr(string, '!'), "XXXXYYYY?00777!", "cat chr"); + TEST_RESULT_UINT(string->pub.extra, 49, "check extra"); TEST_RESULT_STR_Z( strCatZN(string, "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*", 55), - "XXXXYYYY00777!$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$", "cat chr"); - TEST_RESULT_UINT(string->pub.extra, 34, "check extra"); + "XXXXYYYY?00777!$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$", "cat chr"); + TEST_RESULT_UINT(string->pub.extra, 35, "check extra"); TEST_RESULT_STR_Z( strCatEncode(string, encodeBase64, BUFSTRDEF("zzzzz")), - "XXXXYYYY00777!$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$enp6eno=", "cat encode"); - TEST_RESULT_UINT(string->pub.extra, 26, "check extra"); + "XXXXYYYY?00777!$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$enp6eno=", "cat encode"); + TEST_RESULT_UINT(string->pub.extra, 27, "check extra"); + TEST_RESULT_VOID(strFree(string), "free string"); TEST_RESULT_STR_Z(string2, "ZZZZ", "check unaltered string"); } @@ -262,7 +276,7 @@ testRun(void) TEST_RESULT_INT(strChr(STRDEF("abcd"), 'i'), -1, "i not found"); TEST_RESULT_INT(strChr(STRDEF(""), 'x'), -1, "empty string - x not found"); - String *val = strNewZ("abcdef"); + String *val = strCatZ(strNew(), "abcdef"); TEST_ERROR( strTrunc(val, (int)(strSize(val) + 1)), AssertError, "assertion 'idx >= 0 && (size_t)idx <= strSize(this)' failed"); diff --git a/test/src/module/performance/typeTest.c b/test/src/module/performance/typeTest.c index 384a70a99..42d0794f2 100644 --- a/test/src/module/performance/typeTest.c +++ b/test/src/module/performance/typeTest.c @@ -221,7 +221,7 @@ testRun(void) { CHECK(TEST_SCALE <= 10000); - String *iniStr = strNewZ("[section1]\n"); + String *iniStr = strCatZ(strNew(), "[section1]\n"); unsigned int iniMax = 100000 * (unsigned int)TEST_SCALE; for (unsigned int keyIdx = 0; keyIdx < iniMax; keyIdx++) diff --git a/test/src/module/storage/azureTest.c b/test/src/module/storage/azureTest.c index c00af046a..49d1419a8 100644 --- a/test/src/module/storage/azureTest.c +++ b/test/src/module/storage/azureTest.c @@ -40,7 +40,7 @@ typedef struct TestRequestParam static void testRequest(IoWrite *write, const char *verb, const char *path, TestRequestParam param) { - String *request = strNewFmt("%s /" TEST_ACCOUNT "/" TEST_CONTAINER, verb); + String *request = strCatFmt(strNew(), "%s /" TEST_ACCOUNT "/" TEST_CONTAINER, verb); // When SAS spit out the query and merge in the SAS key if (driver->sasKey != NULL) @@ -125,7 +125,7 @@ testResponse(IoWrite *write, TestResponseParam param) param.code = param.code == 0 ? 200 : param.code; // Output header and code - String *response = strNewFmt("HTTP/1.1 %u ", param.code); + String *response = strCatFmt(strNew(), "HTTP/1.1 %u ", param.code); // Add reason for some codes switch (param.code) diff --git a/test/src/module/storage/gcsTest.c b/test/src/module/storage/gcsTest.c index 84a3265cc..b9f33cba5 100644 --- a/test/src/module/storage/gcsTest.c +++ b/test/src/module/storage/gcsTest.c @@ -82,7 +82,8 @@ typedef struct TestRequestParam static void testRequest(IoWrite *write, const char *verb, TestRequestParam param) { - String *request = strNewFmt("%s %s/storage/v1/b%s", verb, param.upload ? "/upload" : "", param.noBucket ? "" : "/bucket/o"); + String *request = strCatFmt( + strNew(), "%s %s/storage/v1/b%s", verb, param.upload ? "/upload" : "", param.noBucket ? "" : "/bucket/o"); // Add object if (param.object != NULL) @@ -140,7 +141,7 @@ testResponse(IoWrite *write, TestResponseParam param) param.code = param.code == 0 ? 200 : param.code; // Output header and code - String *response = strNewFmt("HTTP/1.1 %u ", param.code); + String *response = strCatFmt(strNew(), "HTTP/1.1 %u ", param.code); // Add reason for some codes switch (param.code) @@ -335,7 +336,8 @@ testRun(void) const char *const preamble = "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion="; const String *const jwt = storageGcsAuthJwt(((StorageGcs *)storageDriver(storage)), time(NULL)); - String *const authRequest = strNewFmt( + String *const authRequest = strCatFmt( + strNew(), "POST /token HTTP/1.1\r\n" "user-agent:" PROJECT_NAME "/" PROJECT_VERSION "\r\n" "content-length:%zu\r\n" diff --git a/test/src/module/storage/s3Test.c b/test/src/module/storage/s3Test.c index d6b0d5733..e6e623c03 100644 --- a/test/src/module/storage/s3Test.c +++ b/test/src/module/storage/s3Test.c @@ -51,7 +51,7 @@ testRequest(IoWrite *write, Storage *s3, const char *verb, const char *path, Tes } // Add request - String *request = strNewFmt("%s %s HTTP/1.1\r\nuser-agent:" PROJECT_NAME "/" PROJECT_VERSION "\r\n", verb, path); + String *request = strCatFmt(strNew(), "%s %s HTTP/1.1\r\nuser-agent:" PROJECT_NAME "/" PROJECT_VERSION "\r\n", verb, path); // Add authorization header when s3 service if (s3 != NULL) @@ -142,7 +142,7 @@ testResponse(IoWrite *write, TestResponseParam param) param.code = param.code == 0 ? 200 : param.code; // Output header and code - String *response = strNewFmt("HTTP/%s %u ", param.http == NULL ? "1.1" : param.http, param.code); + String *response = strCatFmt(strNew(), "HTTP/%s %u ", param.http == NULL ? "1.1" : param.http, param.code); // Add reason for some codes switch (param.code)