1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-07-17 01:12:23 +02:00

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.
This commit is contained in:
David Steele
2021-10-07 19:43:28 -04:00
parent 208641ac7f
commit b7e17d80ea
39 changed files with 283 additions and 230 deletions

View File

@ -38,7 +38,8 @@ bldDefineRender(const String *const define, const String *const value)
__attribute__((always_inline)) static inline String * __attribute__((always_inline)) static inline String *
bldHeader(const char *const module, const char *const description) bldHeader(const char *const module, const char *const description)
{ {
return strNewFmt( return strCatFmt(
strNew(),
COMMENT_BLOCK_BEGIN "\n" COMMENT_BLOCK_BEGIN "\n"
"%s\n" "%s\n"
"\n" "\n"

View File

@ -32,7 +32,7 @@ Build enum from a string
static String * static String *
bldEnum(const char *const prefix, const String *const value) 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); const char *const valuePtr = strZ(value);
bool upper = true; bool upper = true;
@ -82,7 +82,8 @@ Render config.auto.h
static void static void
bldCfgRenderConfigAutoH(const Storage *const storageRepo, const BldCfg bldCfg) bldCfgRenderConfigAutoH(const Storage *const storageRepo, const BldCfg bldCfg)
{ {
String *config = strNewFmt( String *config = strCatFmt(
strNew(),
"%s" "%s"
"#ifndef CONFIG_CONFIG_AUTO_H\n" "#ifndef CONFIG_CONFIG_AUTO_H\n"
"#define CONFIG_CONFIG_AUTO_H\n", "#define CONFIG_CONFIG_AUTO_H\n",

View File

@ -48,7 +48,8 @@ Render error.auto.h
static void static void
bldErrRenderErrorAutoH(const Storage *const storageRepo, const BldErr bldErr) bldErrRenderErrorAutoH(const Storage *const storageRepo, const BldErr bldErr)
{ {
String *error = strNewFmt( String *error = strCatFmt(
strNew(),
"%s" "%s"
"#ifndef COMMON_ERROR_AUTO_H\n" "#ifndef COMMON_ERROR_AUTO_H\n"
"#define COMMON_ERROR_AUTO_H\n", "#define COMMON_ERROR_AUTO_H\n",

View File

@ -291,7 +291,8 @@ bldHlpRenderHelpAutoC(const Storage *const storageRepo, const BldCfg bldCfg, con
// Convert buffer to bytes // Convert buffer to bytes
const Buffer *const buffer = bldHlpRenderHelpAutoCCmp(bldCfg, bldHlp); const Buffer *const buffer = bldHlpRenderHelpAutoCCmp(bldCfg, bldHlp);
String *const help = strNewFmt( String *const help = strCatFmt(
strNew(),
"%s" "%s"
"static const unsigned char helpData[%zu] =\n" "static const unsigned char helpData[%zu] =\n"
"{\n", "{\n",

View File

@ -1023,7 +1023,7 @@ cmdArchiveGetAsync(void)
{ {
LOG_WARN_FMT("[%s] %s", errorTypeName(checkResult.errorType), strZ(checkResult.errorMessage)); 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)) if (!strLstEmpty(checkResult.warnList))
strCatFmt(message, "\n%s", strZ(strLstJoin(checkResult.warnList, "\n"))); strCatFmt(message, "\n%s", strZ(strLstJoin(checkResult.warnList, "\n")));

View File

@ -140,7 +140,7 @@ archivePushFile(
} }
// Set archive destination initially to the archive file, this will be updated later for wal segments // 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 // Assume that all repos need a copy of the archive file
bool destinationCopyAny = true; bool destinationCopyAny = true;

View File

@ -70,7 +70,7 @@ backupRegExp(BackupRegExpParam param)
String *result = NULL; String *result = NULL;
// Start the expression with the anchor, date/time regexp and full backup indicator // 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 // Add the diff and/or incr expressions if requested
if (param.differential || param.incremental) if (param.differential || param.incremental)

View File

@ -172,7 +172,7 @@ cmdBegin(void)
MEM_CONTEXT_TEMP_BEGIN() MEM_CONTEXT_TEMP_BEGIN()
{ {
// Basic info on command start // 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 // 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. // 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))); LOG_DETAIL_FMT("statistics: %s", strZ(jsonFromKv(statKv)));
// Basic info on command end // 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) if (errorMessage == NULL)
{ {

View File

@ -238,7 +238,7 @@ helpRender(const Buffer *const helpData)
FUNCTION_LOG_PARAM(BUFFER, helpData); FUNCTION_LOG_PARAM(BUFFER, helpData);
FUNCTION_LOG_END(); FUNCTION_LOG_END();
String *result = strNewZ(PROJECT_NAME " " PROJECT_VERSION); String *result = strCatZ(strNew(), PROJECT_NAME " " PROJECT_VERSION);
MEM_CONTEXT_TEMP_BEGIN() MEM_CONTEXT_TEMP_BEGIN()
{ {
@ -441,7 +441,8 @@ helpRender(const Buffer *const helpData)
ConfigOption optionId = varUInt(varLstGet(optionList, optionIdx)); 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 // 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); ASSERT(strSize(summary) > 1);
if (!isupper(strZ(summary)[1]) && !isdigit(strZ(summary)[1])) if (!isupper(strZ(summary)[1]) && !isdigit(strZ(summary)[1]))

View File

@ -1069,7 +1069,7 @@ formatTextDb(
} }
} }
String *resultCurrent = strNewZ("\n db (current)"); String *resultCurrent = strCatZ(strNew(), "\n db (current)");
bool displayCurrent = false; bool displayCurrent = false;
for (unsigned int dbGrpIdx = backupDbGrpIdxMin; dbGrpIdx < backupDbGrpIdxMax; dbGrpIdx++) for (unsigned int dbGrpIdx = backupDbGrpIdxMin; dbGrpIdx < backupDbGrpIdxMax; dbGrpIdx++)

View File

@ -1675,7 +1675,7 @@ restoreRecoveryConf(unsigned int pgVersion, const String *restoreLabel)
MEM_CONTEXT_TEMP_BEGIN() 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 // Output all recovery options
KeyValue *optionKv = restoreRecoveryOption(pgVersion); KeyValue *optionKv = restoreRecoveryOption(pgVersion);
@ -2073,7 +2073,7 @@ restoreJobResult(const Manifest *manifest, ProtocolParallelJob *job, RegExp *zer
bool zeroed = restoreFileZeroed(file->name, zeroExp); bool zeroed = restoreFileZeroed(file->name, zeroExp);
bool copy = pckReadBoolP(protocolParallelJobResult(job)); bool copy = pckReadBoolP(protocolParallelJobResult(job));
String *log = strNewZ("restore"); String *log = strCatZ(strNew(), "restore");
// Note if file was zeroed (i.e. selective restore) // Note if file was zeroed (i.e. selective restore)
if (zeroed) if (zeroed)
@ -2285,8 +2285,9 @@ cmdRestore(void)
restoreManifestValidate(jobData.manifest, backupData.backupSet); restoreManifestValidate(jobData.manifest, backupData.backupSet);
// Log the backup set to restore. If the backup was online then append the time recovery will start from. // Log the backup set to restore. If the backup was online then append the time recovery will start from.
String *const message = strNewFmt( String *const message = strCatFmt(
"repo%u: restore backup set %s", cfgOptionGroupIdxToKey(cfgOptGrpRepo, backupData.repoIdx), strZ(backupData.backupSet)); strNew(), "repo%u: restore backup set %s", cfgOptionGroupIdxToKey(cfgOptGrpRepo, backupData.repoIdx),
strZ(backupData.backupSet));
if (manifestData(jobData.manifest)->backupOptionOnline) if (manifestData(jobData.manifest)->backupOptionOnline)
{ {

View File

@ -227,7 +227,7 @@ verifyInfoFile(const String *pathFileName, bool keepFile, const String *cipherPa
CATCH_ANY() CATCH_ANY()
{ {
result.errorCode = errorCode(); result.errorCode = errorCode();
String *errorMsg = strNewZ(errorMessage()); String *errorMsg = strCatZ(strNew(), errorMessage());
if (result.errorCode == errorTypeCode(&ChecksumError)) if (result.errorCode == errorTypeCode(&ChecksumError))
strCat(errorMsg, strNewFmt(" %s", strZ(pathFileName))); strCat(errorMsg, strNewFmt(" %s", strZ(pathFileName)));
@ -1252,7 +1252,7 @@ verifyRender(List *archiveIdResultList, List *backupResultList)
ASSERT(archiveIdResultList != NULL); ASSERT(archiveIdResultList != NULL);
ASSERT(backupResultList != NULL); ASSERT(backupResultList != NULL);
String *result = strNewZ("Results:"); String *result = strCatZ(strNew(), "Results:");
// Render archive results // Render archive results
if (lstEmpty(archiveIdResultList)) if (lstEmpty(archiveIdResultList))

View File

@ -88,7 +88,8 @@ exitErrorDetail(void)
FUNCTION_TEST_VOID(); FUNCTION_TEST_VOID();
FUNCTION_TEST_RETURN( FUNCTION_TEST_RETURN(
strNewFmt( strCatFmt(
strNew(),
"--------------------------------------------------------------------\n" "--------------------------------------------------------------------\n"
"If SUBMITTING AN ISSUE please provide the following information:\n" "If SUBMITTING AN ISSUE please provide the following information:\n"
"\n" "\n"
@ -148,7 +149,7 @@ exitSafe(int result, bool error, SignalType signalType)
// On process terminate // On process terminate
if (result == errorTypeCode(&TermError)) if (result == errorTypeCode(&TermError))
{ {
errorMessage = strNewZ("terminated on signal "); errorMessage = strCatZ(strNew(), "terminated on signal ");
// Terminate from a child // Terminate from a child
if (signalType == signalTypeNone) if (signalType == signalTypeNone)

View File

@ -95,7 +95,7 @@ httpHeaderAdd(HttpHeader *this, const String *key, const String *value)
{ {
MEM_CONTEXT_TEMP_BEGIN() MEM_CONTEXT_TEMP_BEGIN()
{ {
String *valueAppend = strDup(varStr(valueVar)); String *const valueAppend = strCat(strNew(), varStr(valueVar));
strCatZ(valueAppend, ", "); strCatZ(valueAppend, ", ");
strCatZ(valueAppend, strZ(value)); strCatZ(valueAppend, strZ(value));
@ -177,7 +177,7 @@ httpHeaderRedact(const HttpHeader *this, const String *key)
String * String *
httpHeaderToLog(const HttpHeader *this) httpHeaderToLog(const HttpHeader *this)
{ {
String *result = strNewZ("{"); String *result = strCatZ(strNew(), "{");
const StringList *keyList = httpHeaderList(this); const StringList *keyList = httpHeaderList(this);
for (unsigned int keyIdx = 0; keyIdx < strLstSize(keyList); keyIdx++) for (unsigned int keyIdx = 0; keyIdx < strLstSize(keyList); keyIdx++)

View File

@ -291,7 +291,7 @@ httpQueryRender(const HttpQuery *this, HttpQueryRenderParam param)
String * String *
httpQueryToLog(const HttpQuery *this) httpQueryToLog(const HttpQuery *this)
{ {
String *result = strNewZ("{"); String *result = strCatZ(strNew(), "{");
const StringList *keyList = httpQueryList(this); const StringList *keyList = httpQueryList(this);
for (unsigned int keyIdx = 0; keyIdx < strLstSize(keyList); keyIdx++) for (unsigned int keyIdx = 0; keyIdx < strLstSize(keyList); keyIdx++)

View File

@ -96,7 +96,8 @@ httpRequestProcess(HttpRequest *this, bool waitForResponse, bool contentCache)
// Format the request and user agent // Format the request and user agent
String *requestStr = String *requestStr =
strNewFmt( strCatFmt(
strNew(),
"%s %s%s%s " HTTP_VERSION CRLF_Z HTTP_HEADER_USER_AGENT ":" PROJECT_NAME "/" PROJECT_VERSION CRLF_Z, "%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 ? "" : "?", strZ(httpRequestVerb(this)), strZ(httpRequestPath(this)), httpRequestQuery(this) == NULL ? "" : "?",
httpRequestQuery(this) == NULL ? "" : strZ(httpQueryRenderP(httpRequestQuery(this)))); httpRequestQuery(this) == NULL ? "" : strZ(httpQueryRenderP(httpRequestQuery(this))));
@ -242,7 +243,7 @@ httpRequestError(const HttpRequest *this, HttpResponse *response)
ASSERT(response != NULL); ASSERT(response != NULL);
// Error code // 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 // Add reason when present
if (strSize(httpResponseReason(response)) > 0) if (strSize(httpResponseReason(response)) > 0)

View File

@ -787,7 +787,7 @@ jsonFromKvInternal(const KeyValue *kv)
ASSERT(kv != NULL); ASSERT(kv != NULL);
String *result = strNewZ("{"); String *result = strCatZ(strNew(), "{");
MEM_CONTEXT_TEMP_BEGIN() MEM_CONTEXT_TEMP_BEGIN()
{ {

View File

@ -39,6 +39,21 @@ STRING_EXTERN(TRUE_STR, TRUE_Z);
STRING_EXTERN(Y_STR, Y_Z); STRING_EXTERN(Y_STR, Y_Z);
STRING_EXTERN(ZERO_STR, ZERO_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 Maximum size of a string
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
@ -74,15 +89,38 @@ strNew(void)
{ {
.pub = .pub =
{ {
// A zero-length string is not very useful so assume this string is being created for appending and allocate extra space // Set empty so nothing is allocated until needed
.extra = STRING_EXTRA_MIN, .buffer = STR_EMPTY_BUFFER,
}, },
.memContext = memContextCurrent(), .memContext = memContextCurrent(),
}; };
// Allocate and assign string FUNCTION_TEST_RETURN(this);
this->pub.buffer = memNew(STRING_EXTRA_MIN + 1); }
this->pub.buffer[0] = '\0';
/***********************************************************************************************************************************
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); FUNCTION_TEST_RETURN(this);
} }
@ -97,25 +135,12 @@ strNewZ(const char *const string)
ASSERT(string != NULL); ASSERT(string != NULL);
// Check size
size_t stringSize = strlen(string);
CHECK_SIZE(stringSize);
// Create object // Create object
String *this = memNew(sizeof(String)); String *this = strNewFixed(strlen(string));
*this = (String) // Assign string
{ strncpy(this->pub.buffer, string, strSize(this));
.pub = this->pub.buffer[strSize(this)] = '\0';
{
.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);
FUNCTION_TEST_RETURN(this); FUNCTION_TEST_RETURN(this);
} }
@ -144,23 +169,10 @@ strNewBuf(const Buffer *buffer)
ASSERT(buffer != NULL); ASSERT(buffer != NULL);
// Check size
CHECK_SIZE(bufUsed(buffer));
// Create object // Create object
String *this = memNew(sizeof(String)); String *this = strNewFixed(bufUsed(buffer));
*this = (String) // Assign string
{
.pub =
{
.size = (unsigned int)bufUsed(buffer),
},
.memContext = memContextCurrent(),
};
// Allocate and assign string
this->pub.buffer = memNew(strSize(this) + 1);
memcpy(this->pub.buffer, bufPtrConst(buffer), strSize(this)); memcpy(this->pub.buffer, bufPtrConst(buffer), strSize(this));
this->pub.buffer[strSize(this)] = 0; this->pub.buffer[strSize(this)] = 0;
@ -178,24 +190,10 @@ strNewEncode(EncodeType type, const Buffer *buffer)
ASSERT(buffer != NULL); ASSERT(buffer != NULL);
// Check encoded size
size_t size = encodeToStrSize(type, bufUsed(buffer));
CHECK_SIZE(size);
// Create object // Create object
String *this = memNew(sizeof(String)); String *this = strNewFixed(encodeToStrSize(type, bufUsed(buffer)));
*this = (String) // Encode buffer
{
.pub =
{
.size = (unsigned int)size,
},
.memContext = memContextCurrent(),
};
// Allocate and encode buffer
this->pub.buffer = memNew(strSize(this) + 1);
encodeToStr(type, bufPtrConst(buffer), bufUsed(buffer), this->pub.buffer); encodeToStr(type, bufPtrConst(buffer), bufUsed(buffer), this->pub.buffer);
FUNCTION_TEST_RETURN(this); FUNCTION_TEST_RETURN(this);
@ -211,26 +209,13 @@ strNewFmt(const char *format, ...)
ASSERT(format != NULL); ASSERT(format != NULL);
// Create object // Determine how long the allocated string needs to be and create object
String *this = memNew(sizeof(String));
*this = (String)
{
.memContext = memContextCurrent(),
};
// Determine how long the allocated string needs to be
va_list argumentList; va_list argumentList;
va_start(argumentList, format); 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); va_end(argumentList);
// Check size // Format string
CHECK_SIZE(formatSize);
// Allocate and assign string
this->pub.size = (unsigned int)formatSize;
this->pub.buffer = memNew(strSize(this) + 1);
va_start(argumentList, format); va_start(argumentList, format);
vsnprintf(this->pub.buffer, strSize(this) + 1, format, argumentList); vsnprintf(this->pub.buffer, strSize(this) + 1, format, argumentList);
va_end(argumentList); va_end(argumentList);
@ -249,27 +234,13 @@ strNewN(const char *string, size_t size)
ASSERT(string != NULL); ASSERT(string != NULL);
// Check size
CHECK_SIZE(size);
// Create object // Create object
String *this = memNew(sizeof(String)); String *this = strNewFixed(size);
*this = (String) // Assign string
{
.pub =
{
.size = (unsigned int)size,
},
.memContext = memContextCurrent(),
};
// Allocate and assign string
this->pub.buffer = memNew(strSize(this) + 1);
strncpy(this->pub.buffer, string, strSize(this)); strncpy(this->pub.buffer, string, strSize(this));
this->pub.buffer[strSize(this)] = 0; this->pub.buffer[strSize(this)] = 0;
// Return buffer
FUNCTION_TEST_RETURN(this); FUNCTION_TEST_RETURN(this);
} }
@ -351,6 +322,9 @@ strResize(String *this, size_t requested)
if (requested > this->pub.extra) if (requested > this->pub.extra)
{ {
// Fixed size strings may not be resized
CHECK(!STR_IS_FIXED_BUFFER());
// Check size // Check size
CHECK_SIZE(strSize(this) + requested); CHECK_SIZE(strSize(this) + requested);
@ -363,7 +337,10 @@ strResize(String *this, size_t requested)
MEM_CONTEXT_BEGIN(this->memContext) 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(); MEM_CONTEXT_END();
} }
@ -400,13 +377,16 @@ strCatZ(String *this, const char *cat)
// Determine length of string to append // Determine length of string to append
size_t sizeGrow = strlen(cat); size_t sizeGrow = strlen(cat);
// Ensure there is enough space to grow the string if (sizeGrow != 0)
strResize(this, sizeGrow); {
// Ensure there is enough space to grow the string
strResize(this, sizeGrow);
// Append the string // Append the string
strcpy(this->pub.buffer + strSize(this), cat); strcpy(this->pub.buffer + strSize(this), cat);
this->pub.size += (unsigned int)sizeGrow; this->pub.size += (unsigned int)sizeGrow;
this->pub.extra -= (unsigned int)sizeGrow; this->pub.extra -= (unsigned int)sizeGrow;
}
FUNCTION_TEST_RETURN(this); FUNCTION_TEST_RETURN(this);
} }
@ -421,22 +401,41 @@ strCatZN(String *this, const char *cat, size_t size)
FUNCTION_TEST_END(); FUNCTION_TEST_END();
ASSERT(this != NULL); ASSERT(this != NULL);
ASSERT(cat != NULL);
// Ensure there is enough space to grow the string if (size != 0)
strResize(this, size); {
ASSERT(cat != NULL);
// Append the string // Ensure there is enough space to grow the string
strncpy(this->pub.buffer + strSize(this), cat, size); strResize(this, size);
this->pub.buffer[strSize(this) + size] = '\0';
// Update size/extra // Append the string
this->pub.size += (unsigned int)size; strncpy(this->pub.buffer + strSize(this), cat, size);
this->pub.extra -= (unsigned int)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); 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 * String *
strCatChr(String *this, char cat) strCatChr(String *this, char cat)
@ -473,16 +472,20 @@ strCatEncode(String *this, EncodeType type, const Buffer *buffer)
ASSERT(this != NULL); ASSERT(this != NULL);
ASSERT(buffer != NULL); ASSERT(buffer != NULL);
// Ensure there is enough space to grow the string
size_t encodeSize = encodeToStrSize(type, bufUsed(buffer)); size_t encodeSize = encodeToStrSize(type, bufUsed(buffer));
strResize(this, encodeSize);
// Append the encoded string if (encodeSize != 0)
encodeToStr(type, bufPtrConst(buffer), bufUsed(buffer), this->pub.buffer + strSize(this)); {
// Ensure there is enough space to grow the string
strResize(this, encodeSize);
// Update size/extra // Append the encoded string
this->pub.size += (unsigned int)encodeSize; encodeToStr(type, bufPtrConst(buffer), bufUsed(buffer), this->pub.buffer + strSize(this));
this->pub.extra -= (unsigned int)encodeSize;
// Update size/extra
this->pub.size += (unsigned int)encodeSize;
this->pub.extra -= (unsigned int)encodeSize;
}
FUNCTION_TEST_RETURN(this); FUNCTION_TEST_RETURN(this);
} }
@ -505,16 +508,19 @@ strCatFmt(String *this, const char *format, ...)
size_t sizeGrow = (size_t)vsnprintf(NULL, 0, format, argumentList); size_t sizeGrow = (size_t)vsnprintf(NULL, 0, format, argumentList);
va_end(argumentList); va_end(argumentList);
// Ensure there is enough space to grow the string if (sizeGrow != 0)
strResize(this, sizeGrow); {
// Ensure there is enough space to grow the string
strResize(this, sizeGrow);
// Append the formatted string // Append the formatted string
va_start(argumentList, format); va_start(argumentList, format);
vsnprintf(this->pub.buffer + strSize(this), sizeGrow + 1, format, argumentList); vsnprintf(this->pub.buffer + strSize(this), sizeGrow + 1, format, argumentList);
va_end(argumentList); va_end(argumentList);
this->pub.size += (unsigned int)sizeGrow; this->pub.size += (unsigned int)sizeGrow;
this->pub.extra -= (unsigned int)sizeGrow; this->pub.extra -= (unsigned int)sizeGrow;
}
FUNCTION_TEST_RETURN(this); FUNCTION_TEST_RETURN(this);
} }
@ -695,9 +701,8 @@ strUpper(String *this)
ASSERT(this != NULL); ASSERT(this != NULL);
if (strSize(this) > 0) for (size_t idx = 0; idx < strSize(this); idx++)
for (size_t idx = 0; idx <= strSize(this); idx++) this->pub.buffer[idx] = (char)toupper(this->pub.buffer[idx]);
this->pub.buffer[idx] = (char)toupper(this->pub.buffer[idx]);
FUNCTION_TEST_RETURN(this); FUNCTION_TEST_RETURN(this);
} }
@ -712,9 +717,8 @@ strLower(String *this)
ASSERT(this != NULL); ASSERT(this != NULL);
if (strSize(this) > 0) for (size_t idx = 0; idx < strSize(this); idx++)
for (size_t idx = 0; idx <= strSize(this); idx++) this->pub.buffer[idx] = (char)tolower(this->pub.buffer[idx]);
this->pub.buffer[idx] = (char)tolower(this->pub.buffer[idx]);
FUNCTION_TEST_RETURN(this); FUNCTION_TEST_RETURN(this);
} }
@ -947,20 +951,13 @@ strTrim(String *this)
if (begin != this->pub.buffer || newSize < strSize(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; this->pub.size = (unsigned int)newSize;
// Move the substr to the beginning of the buffer // Move the substr to the beginning of the buffer
memmove(this->pub.buffer, begin, strSize(this)); memmove(this->pub.buffer, begin, strSize(this));
this->pub.buffer[strSize(this)] = 0; 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) if (strSize(this) > 0)
{ {
// Reset the size to end at the index // 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.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); FUNCTION_TEST_RETURN(this);
@ -1095,7 +1085,9 @@ strFree(String *this)
{ {
MEM_CONTEXT_BEGIN(this->memContext) MEM_CONTEXT_BEGIN(this->memContext)
{ {
memFree(this->pub.buffer); if (!STR_IS_EMPTY_BUFFER() && !STR_IS_FIXED_BUFFER())
memFree(this->pub.buffer);
memFree(this); memFree(this);
} }
MEM_CONTEXT_END(); MEM_CONTEXT_END();

View File

@ -41,31 +41,38 @@ typedef struct String String;
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Constructors 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); 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); 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 // 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
// but only the data before the NULL character will be used as a string. // will be copied but only the data before the NULL character will be used as a string.
String *strNewBuf(const Buffer *buffer); 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); 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); 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))); 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 // Create a new fixed length string from a zero-terminated string with a specific length. The string may or may not be
// nomenclature since we're not concerned about the end of the string. // 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); 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); 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 *strCat(String *this, const String *cat);
String *strCatZ(String *this, const char *cat); String *strCatZ(String *this, const char *cat);
// Append a buffer
String *strCatBuf(String *this, const Buffer *buffer);
// Append a character // Append a character
String *strCatChr(String *this, char cat); 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. // N is <= the end of the string being concatenated.
String *strCatZN(String *this, const char *cat, size_t size); 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 // 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); int strChr(const String *this, char chr);

View File

@ -808,19 +808,36 @@ varNewStr(const String *data)
FUNCTION_TEST_PARAM(STRING, data); FUNCTION_TEST_PARAM(STRING, data);
FUNCTION_TEST_END(); FUNCTION_TEST_END();
// Allocate memory for the variant and set the type and data // Allocate memory for the variant
VariantString *this = memNew(sizeof(VariantString)); VariantString *this = memNew(sizeof(VariantString) + (data != NULL ? sizeof(StringPub) + strSize(data) + 1 : 0));
*this = (VariantString) *this = (VariantString)
{ {
.pub = .pub =
{ {
.type = varTypeString, .type = varTypeString,
.data = strDup(data),
}, },
.memContext = memContextCurrent(), .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); FUNCTION_TEST_RETURN((Variant *)this);
} }
@ -831,7 +848,7 @@ varNewStrZ(const char *data)
FUNCTION_TEST_PARAM(STRINGZ, data); FUNCTION_TEST_PARAM(STRINGZ, data);
FUNCTION_TEST_END(); 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: case varTypeString:
memContext = ((VariantString *)this)->memContext; memContext = ((VariantString *)this)->memContext;
strFree(((VariantString *)this)->pub.data);
break; break;
case varTypeUInt: case varTypeUInt:

View File

@ -333,7 +333,8 @@ cfgLoadLogFile(void)
MEM_CONTEXT_TEMP_BEGIN() MEM_CONTEXT_TEMP_BEGIN()
{ {
// Construct log filename prefix // Construct log filename prefix
String *logFile = strNewFmt( String *logFile = strCatFmt(
strNew(),
"%s/%s-%s", strZ(cfgOptionStr(cfgOptLogPath)), "%s/%s-%s", strZ(cfgOptionStr(cfgOptLogPath)),
cfgOptionTest(cfgOptStanza) ? strZ(cfgOptionStr(cfgOptStanza)): "all", cfgCommandName()); cfgOptionTest(cfgOptStanza) ? strZ(cfgOptionStr(cfgOptStanza)): "all", cfgCommandName());

View File

@ -515,7 +515,7 @@ cfgParseCommandRoleName(const ConfigCommand commandId, const ConfigCommandRole c
FUNCTION_TEST_PARAM(STRING, separator); FUNCTION_TEST_PARAM(STRING, separator);
FUNCTION_TEST_END(); FUNCTION_TEST_END();
String *result = strNewZ(cfgParseCommandName(commandId)); String *result = strCatZ(strNew(), cfgParseCommandName(commandId));
if (commandRoleId != cfgCmdRoleMain) if (commandRoleId != cfgCmdRoleMain)
strCatFmt(result, "%s%s", strZ(separator), strZ(cfgParseCommandRoleStr(commandRoleId))); 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 // Convert the contents of the file buffer to the config string object
if (buffer != NULL) if (buffer != NULL)
result = strNewBuf(buffer); result = strCatBuf(strNew(), buffer);
else if (strEq(configFileName, optConfigDefaultCurrent)) 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 // 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)); buffer = storageGetP(storageNewReadP(storage, origConfigDefault, .ignoreMissing = !configRequired));
if (buffer != NULL) if (buffer != NULL)
result = strNewBuf(buffer); result = strCatBuf(strNew(), buffer);
} }
} }

View File

@ -277,7 +277,8 @@ dbBackupStartQuery(unsigned int pgVersion, bool startFast)
FUNCTION_TEST_END(); FUNCTION_TEST_END();
// Build query to return start lsn and WAL segment name // Build query to return start lsn and WAL segment name
String *result = strNewFmt( String *result = strCatFmt(
strNew(),
"select lsn::text as lsn,\n" "select lsn::text as lsn,\n"
" pg_catalog.pg_%sfile_name(lsn)::text as wal_segment_name\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", " from pg_catalog.pg_start_backup('" PROJECT_NAME " backup started at ' || current_timestamp",
@ -372,7 +373,8 @@ dbBackupStopQuery(unsigned int pgVersion)
FUNCTION_TEST_END(); FUNCTION_TEST_END();
// Build query to return start lsn and WAL segment name // Build query to return start lsn and WAL segment name
String *result = strNewFmt( String *result = strCatFmt(
strNew(),
"select lsn::text as lsn,\n" "select lsn::text as lsn,\n"
" pg_catalog.pg_%sfile_name(lsn)::text as wal_segment_name", " pg_catalog.pg_%sfile_name(lsn)::text as wal_segment_name",
strZ(pgWalName(pgVersion))); strZ(pgWalName(pgVersion)));
@ -510,7 +512,8 @@ dbReplayWait(Db *this, const String *targetLsn, TimeMSec timeout)
do do
{ {
// Build the query // Build the query
String *query = strNewFmt( String *query = strCatFmt(
strNew(),
"select replayLsn::text,\n" "select replayLsn::text,\n"
" (replayLsn > '%s')::bool as targetReached", " (replayLsn > '%s')::bool as targetReached",
strZ(targetLsn)); strZ(targetLsn));

View File

@ -506,7 +506,7 @@ infoLoad(const String *error, InfoLoadCallback *callbackFunction, void *callback
if (loadErrorType == NULL) if (loadErrorType == NULL)
{ {
loadErrorType = errorType(); loadErrorType = errorType();
loadErrorMessage = strNewFmt("%s:", strZ(error)); loadErrorMessage = strCatFmt(strNew(), "%s:", strZ(error));
} }
// Else if the error type is different // Else if the error type is different
else if (loadErrorType != errorType()) else if (loadErrorType != errorType())

View File

@ -2847,7 +2847,7 @@ manifestTargetPath(const Manifest *this, const ManifestTarget *target)
MEM_CONTEXT_TEMP_BEGIN() MEM_CONTEXT_TEMP_BEGIN()
{ {
String *pgPath = strPath(manifestPathPg(target->name)); String *pgPath = strCat(strNew(), strPath(manifestPathPg(target->name)));
if (strSize(pgPath) != 0) if (strSize(pgPath) != 0)
strCatZ(pgPath, "/"); strCatZ(pgPath, "/");

View File

@ -101,7 +101,7 @@ pgClientEscape(const String *string)
ASSERT(string != NULL); ASSERT(string != NULL);
String *result = strNewZ("'"); String *result = strCatZ(strNew(), "'");
// Iterate all characters in the string // Iterate all characters in the string
for (unsigned stringIdx = 0; stringIdx < strSize(string); stringIdx++) for (unsigned stringIdx = 0; stringIdx < strSize(string); stringIdx++)
@ -134,7 +134,7 @@ pgClientOpen(PgClient *this)
MEM_CONTEXT_TEMP_BEGIN() MEM_CONTEXT_TEMP_BEGIN()
{ {
// Base connection string // 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 // Add user if specified
if (this->user != NULL) if (this->user != NULL)

View File

@ -209,7 +209,7 @@ protocolServerProcess(
if (errType == NULL) if (errType == NULL)
{ {
errType = errorType(); errType = errorType();
errMessage = strNewZ(errorMessage()); errMessage = strCatZ(strNew(), errorMessage());
errMessageFirst = strNewZ(errorMessage()); errMessageFirst = strNewZ(errorMessage());
errStackTrace = strNewZ(errorStackTrace()); errStackTrace = strNewZ(errorStackTrace());
} }

View File

@ -168,7 +168,7 @@ storageGcsAuthJwt(StorageGcs *this, time_t timeBegin)
FUNCTION_TEST_END(); FUNCTION_TEST_END();
// Static header with dot delimiter // Static header with dot delimiter
String *result = strNewZ("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9."); String *result = strCatZ(strNew(), "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.");
MEM_CONTEXT_TEMP_BEGIN() MEM_CONTEXT_TEMP_BEGIN()
{ {
@ -382,7 +382,7 @@ storageGcsRequestAsync(StorageGcs *this, const String *verb, StorageGcsRequestAs
MEM_CONTEXT_TEMP_BEGIN() MEM_CONTEXT_TEMP_BEGIN()
{ {
// Generate path // 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) if (!param.noBucket)
strCatFmt(path, "/%s/o", strZ(this->bucket)); strCatFmt(path, "/%s/o", strZ(this->bucket));

View File

@ -306,9 +306,9 @@ storageRepoPathExpression(const String *expression, const String *path)
{ {
// Construct the base path // Construct the base path
if (storageHelper.stanza != NULL) if (storageHelper.stanza != NULL)
result = strNewFmt(STORAGE_PATH_ARCHIVE "/%s", strZ(storageHelper.stanza)); result = strCatFmt(strNew(), STORAGE_PATH_ARCHIVE "/%s", strZ(storageHelper.stanza));
else 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 a subpath should be appended, determine if it is WAL path, else just append the subpath
if (path != NULL) if (path != NULL)
@ -326,9 +326,9 @@ storageRepoPathExpression(const String *expression, const String *path)
{ {
// Construct the base path // Construct the base path
if (storageHelper.stanza != NULL) if (storageHelper.stanza != NULL)
result = strNewFmt(STORAGE_PATH_BACKUP "/%s", strZ(storageHelper.stanza)); result = strCatFmt(strNew(), STORAGE_PATH_BACKUP "/%s", strZ(storageHelper.stanza));
else else
result = strNewZ(STORAGE_PATH_BACKUP); result = strCatZ(strNew(), STORAGE_PATH_BACKUP);
// Append subpath if provided // Append subpath if provided
if (path != NULL) if (path != NULL)

View File

@ -174,8 +174,8 @@ storageS3Auth(
const StringList *headerList = strLstSort(strLstDup(httpHeaderList(httpHeader)), sortOrderAsc); const StringList *headerList = strLstSort(strLstDup(httpHeaderList(httpHeader)), sortOrderAsc);
String *signedHeaders = NULL; String *signedHeaders = NULL;
String *canonicalRequest = strNewFmt( String *canonicalRequest = strCatFmt(
"%s\n%s\n%s\n", strZ(verb), strZ(path), query == NULL ? "" : strZ(httpQueryRenderP(query))); 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++) 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))); strCatFmt(canonicalRequest, "%s:%s\n", strZ(headerKeyLower), strZ(httpHeaderGet(httpHeader, headerKey)));
if (signedHeaders == NULL) if (signedHeaders == NULL)
signedHeaders = strDup(headerKeyLower); signedHeaders = strCat(strNew(), headerKeyLower);
else else
strCatFmt(signedHeaders, ";%s", strZ(headerKeyLower)); strCatFmt(signedHeaders, ";%s", strZ(headerKeyLower));
} }

View File

@ -349,7 +349,7 @@ hrnLogReplace(void)
} }
// Build replacement string. If versioned then append the version number. // 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) if (logReplace->version)
{ {

View File

@ -188,7 +188,7 @@ testStorageGet(const Storage *const storage, const char *const file, const char
ASSERT(storage != NULL); ASSERT(storage != NULL);
ASSERT(file != NULL); ASSERT(file != NULL);
String *fileFull = storagePathP(storage, STR(file)); String *fileFull = strCat(strNew(), storagePathP(storage, STR(file)));
// Add compression extension if one exists // Add compression extension if one exists
compressExtCat(fileFull, param.compressType); compressExtCat(fileFull, param.compressType);
@ -433,7 +433,7 @@ hrnStoragePut(
ASSERT(file != NULL); ASSERT(file != NULL);
// Add compression extension to file name // Add compression extension to file name
String *fileStr = strNewZ(file); String *fileStr = strCatZ(strNew(), file);
compressExtCat(fileStr, param.compressType); compressExtCat(fileStr, param.compressType);
// Create file // Create file

View File

@ -876,7 +876,7 @@ testRun(void)
TEST_TITLE("encrypted/compressed file in backup"); TEST_TITLE("encrypted/compressed file in backup");
// Create a compressed encrypted repo 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( HRN_STORAGE_PUT_Z(
storageRepoWrite(), strZ(filePathName), fileContents, .compressType = compressTypeGz, .cipherType = cipherTypeAes256Cbc, storageRepoWrite(), strZ(filePathName), fileContents, .compressType = compressTypeGz, .cipherType = cipherTypeAes256Cbc,
.cipherPass = "pass"); .cipherPass = "pass");

View File

@ -370,7 +370,7 @@ testRun(void)
// ------------------------------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("compressExtCat()"); TEST_TITLE("compressExtCat()");
String *file = strNewZ("file"); String *file = strCatZ(strNew(), "file");
TEST_RESULT_VOID(compressExtCat(file, compressTypeGz), "cat gz ext"); TEST_RESULT_VOID(compressExtCat(file, compressTypeGz), "cat gz ext");
TEST_RESULT_STR_Z(file, "file.gz", " check gz ext"); TEST_RESULT_STR_Z(file, "file.gz", " check gz ext");

View File

@ -42,6 +42,7 @@ testRun(void)
TEST_ERROR(CHECK_SIZE(STRING_SIZE_MAX + 1), AssertError, "string size must be <= 1073741824 bytes"); TEST_ERROR(CHECK_SIZE(STRING_SIZE_MAX + 1), AssertError, "string size must be <= 1073741824 bytes");
String *string = strNewZ("static string"); 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_STR_Z(string, "static string", "new with static string");
TEST_RESULT_UINT(strSize(string), 13, "check size"); TEST_RESULT_UINT(strSize(string), 13, "check size");
TEST_RESULT_BOOL(strEmpty(string), false, "is not empty"); TEST_RESULT_BOOL(strEmpty(string), false, "is not empty");
@ -77,8 +78,10 @@ testRun(void)
TEST_TITLE("empty string is allocated extra space"); TEST_TITLE("empty string is allocated extra space");
TEST_ASSIGN(string, strNew(), "new empty string"); TEST_ASSIGN(string, strNew(), "new empty string");
TEST_RESULT_UINT(string->pub.size, 0, " check size"); TEST_RESULT_UINT(string->pub.size, 0, "check size");
TEST_RESULT_UINT(string->pub.extra, 64, " check extra"); 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()"); TEST_TITLE("strNewEncode()");
@ -122,23 +125,34 @@ testRun(void)
// ***************************************************************************************************************************** // *****************************************************************************************************************************
if (testBegin("strCat*()")) if (testBegin("strCat*()"))
{ {
String *string = strNewZ("XXXX"); String *string = strCatZ(strNew(), "XXX");
String *string2 = strNewZ("ZZZZ"); 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_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_UINT(string->pub.extra, 55, "check extra");
TEST_RESULT_STR_Z(strCatChr(string, '!'), "XXXXYYYY00777!", "cat chr"); TEST_RESULT_STR_Z(strCatFmt(string, "%05d", 777), "XXXXYYYY?00777", "cat formatted string");
TEST_RESULT_UINT(string->pub.extra, 54, "check extra"); 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( TEST_RESULT_STR_Z(
strCatZN(string, "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*", 55), strCatZN(string, "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$*", 55),
"XXXXYYYY00777!$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$", "cat chr"); "XXXXYYYY?00777!$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$", "cat chr");
TEST_RESULT_UINT(string->pub.extra, 34, "check extra"); TEST_RESULT_UINT(string->pub.extra, 35, "check extra");
TEST_RESULT_STR_Z( TEST_RESULT_STR_Z(
strCatEncode(string, encodeBase64, BUFSTRDEF("zzzzz")), strCatEncode(string, encodeBase64, BUFSTRDEF("zzzzz")),
"XXXXYYYY00777!$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$enp6eno=", "cat encode"); "XXXXYYYY?00777!$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$enp6eno=", "cat encode");
TEST_RESULT_UINT(string->pub.extra, 26, "check extra"); 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"); 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("abcd"), 'i'), -1, "i not found");
TEST_RESULT_INT(strChr(STRDEF(""), 'x'), -1, "empty string - x 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( TEST_ERROR(
strTrunc(val, (int)(strSize(val) + 1)), AssertError, strTrunc(val, (int)(strSize(val) + 1)), AssertError,
"assertion 'idx >= 0 && (size_t)idx <= strSize(this)' failed"); "assertion 'idx >= 0 && (size_t)idx <= strSize(this)' failed");

View File

@ -221,7 +221,7 @@ testRun(void)
{ {
CHECK(TEST_SCALE <= 10000); CHECK(TEST_SCALE <= 10000);
String *iniStr = strNewZ("[section1]\n"); String *iniStr = strCatZ(strNew(), "[section1]\n");
unsigned int iniMax = 100000 * (unsigned int)TEST_SCALE; unsigned int iniMax = 100000 * (unsigned int)TEST_SCALE;
for (unsigned int keyIdx = 0; keyIdx < iniMax; keyIdx++) for (unsigned int keyIdx = 0; keyIdx < iniMax; keyIdx++)

View File

@ -40,7 +40,7 @@ typedef struct TestRequestParam
static void static void
testRequest(IoWrite *write, const char *verb, const char *path, TestRequestParam param) 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 // When SAS spit out the query and merge in the SAS key
if (driver->sasKey != NULL) if (driver->sasKey != NULL)
@ -125,7 +125,7 @@ testResponse(IoWrite *write, TestResponseParam param)
param.code = param.code == 0 ? 200 : param.code; param.code = param.code == 0 ? 200 : param.code;
// Output header and 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 // Add reason for some codes
switch (param.code) switch (param.code)

View File

@ -82,7 +82,8 @@ typedef struct TestRequestParam
static void static void
testRequest(IoWrite *write, const char *verb, TestRequestParam param) 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 // Add object
if (param.object != NULL) if (param.object != NULL)
@ -140,7 +141,7 @@ testResponse(IoWrite *write, TestResponseParam param)
param.code = param.code == 0 ? 200 : param.code; param.code = param.code == 0 ? 200 : param.code;
// Output header and 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 // Add reason for some codes
switch (param.code) 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 char *const preamble = "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=";
const String *const jwt = storageGcsAuthJwt(((StorageGcs *)storageDriver(storage)), time(NULL)); 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" "POST /token HTTP/1.1\r\n"
"user-agent:" PROJECT_NAME "/" PROJECT_VERSION "\r\n" "user-agent:" PROJECT_NAME "/" PROJECT_VERSION "\r\n"
"content-length:%zu\r\n" "content-length:%zu\r\n"

View File

@ -51,7 +51,7 @@ testRequest(IoWrite *write, Storage *s3, const char *verb, const char *path, Tes
} }
// Add request // 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 // Add authorization header when s3 service
if (s3 != NULL) if (s3 != NULL)
@ -142,7 +142,7 @@ testResponse(IoWrite *write, TestResponseParam param)
param.code = param.code == 0 ? 200 : param.code; param.code = param.code == 0 ? 200 : param.code;
// Output header and 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 // Add reason for some codes
switch (param.code) switch (param.code)