From 420d1cf804562fe9f96faa0b84d623cb30b3dff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Fri, 9 Jan 2026 15:19:46 +0800 Subject: [PATCH] FFstrbuf: updates `ffStrbufSet*` that they won't reserve extra space --- src/common/FFstrbuf.h | 91 ++++++++++++++----------------- src/common/impl/FFstrbuf.c | 62 ++++++++++++++++++++- tests/strbuf.c | 108 +++++++++++++++++++++++++++++++++++-- 3 files changed, 204 insertions(+), 57 deletions(-) diff --git a/src/common/FFstrbuf.h b/src/common/FFstrbuf.h index a4bfcde4c..7e82cdd4d 100644 --- a/src/common/FFstrbuf.h +++ b/src/common/FFstrbuf.h @@ -24,6 +24,8 @@ #define FASTFETCH_STRBUF_DEFAULT_ALLOC 32 +// static string (allocated == 0), chars points to a string literal +// dynamic string (allocated > 0), chars points to a heap allocated buffer typedef struct FFstrbuf { uint32_t allocated; @@ -55,6 +57,10 @@ void ffStrbufPrependC(FFstrbuf* strbuf, char c); void ffStrbufInsertNC(FFstrbuf* strbuf, uint32_t index, uint32_t num, char c); +// Clear the content of strbuf and set new value +// NOTE: Unlike ffStrbufAppend*, ffStrbufSet* functions may NOT reserve extra space +void ffStrbufSet(FFstrbuf* strbuf, const FFstrbuf* value); +void ffStrbufSetNS(FFstrbuf* strbuf, uint32_t length, const char* value); FF_C_PRINTF(2, 3) void ffStrbufSetF(FFstrbuf* strbuf, const char* format, ...); void ffStrbufTrimLeft(FFstrbuf* strbuf, char c); @@ -148,7 +154,7 @@ FF_C_NODISCARD static inline FFstrbuf ffStrbufCreateA(uint32_t allocate) static inline void ffStrbufInitCopy(FFstrbuf* __restrict strbuf, const FFstrbuf* __restrict src) { if (src->allocated == 0) // static string - memcpy(strbuf, src, sizeof(FFstrbuf)); + *strbuf = *src; else { ffStrbufInitA(strbuf, src->allocated); @@ -168,9 +174,7 @@ static inline void ffStrbufInitMove(FFstrbuf* strbuf, FFstrbuf* src) { if (src) { - strbuf->allocated = src->allocated; - strbuf->chars = src->chars; - strbuf->length = src->length; + *strbuf = *src; ffStrbufInit(src); } else @@ -220,19 +224,10 @@ static inline void ffStrbufInitMoveS(FFstrbuf* strbuf, char* heapStr) static inline void ffStrbufDestroy(FFstrbuf* strbuf) { - extern char* CHAR_NULL_PTR; + if(strbuf->allocated > 0) + free(strbuf->chars); - if(strbuf->allocated == 0) - { - strbuf->length = 0; - strbuf->chars = CHAR_NULL_PTR; - return; - } - - //Avoid free-after-use. These 3 assignments are cheap so don't remove them - strbuf->allocated = strbuf->length = 0; - free(strbuf->chars); - strbuf->chars = CHAR_NULL_PTR; + ffStrbufInit(strbuf); } FF_C_NODISCARD static inline uint32_t ffStrbufGetFree(const FFstrbuf* strbuf) @@ -249,6 +244,30 @@ static inline void ffStrbufRecalculateLength(FFstrbuf* strbuf) strbuf->length = (uint32_t) strlen(strbuf->chars); } +static inline void ffStrbufSetS(FFstrbuf* strbuf, const char* value) +{ + assert(strbuf != NULL); + + if (value == NULL) + ffStrbufClear(strbuf); + else + ffStrbufSetNS(strbuf, (uint32_t) strlen(value), value); +} + +static inline bool ffStrbufSetJsonVal(FFstrbuf* strbuf, yyjson_val* jsonVal) +{ + assert(strbuf != NULL); + + if (yyjson_is_str(jsonVal)) + { + ffStrbufSetNS(strbuf, (uint32_t) unsafe_yyjson_get_len(jsonVal), unsafe_yyjson_get_str(jsonVal)); + return true; + } + + ffStrbufClear(strbuf); + return false; +} + static inline void ffStrbufAppendS(FFstrbuf* strbuf, const char* value) { if(value == NULL) @@ -266,42 +285,10 @@ static inline bool ffStrbufAppendJsonVal(FFstrbuf* strbuf, yyjson_val* jsonVal) return false; } -static inline void ffStrbufSetS(FFstrbuf* strbuf, const char* value) -{ - ffStrbufClear(strbuf); - - if(value != NULL) - ffStrbufAppendNS(strbuf, (uint32_t) strlen(value), value); -} - -static inline void ffStrbufSetNS(FFstrbuf* strbuf, uint32_t length, const char* value) -{ - ffStrbufClear(strbuf); - ffStrbufAppendNS(strbuf, length, value); -} - -static inline void ffStrbufSet(FFstrbuf* strbuf, const FFstrbuf* value) -{ - assert(value && value != strbuf); - if (strbuf->allocated == 0 && value->allocated == 0) - { - memcpy(strbuf, value, sizeof(FFstrbuf)); - return; - } - ffStrbufSetNS(strbuf, value->length, value->chars); -} - -static inline bool ffStrbufSetJsonVal(FFstrbuf* strbuf, yyjson_val* jsonVal) -{ - ffStrbufClear(strbuf); - return ffStrbufAppendJsonVal(strbuf, jsonVal); -} - static inline void ffStrbufInit(FFstrbuf* strbuf) { extern char* CHAR_NULL_PTR; - strbuf->allocated = 0; - strbuf->length = 0; + strbuf->allocated = strbuf->length = 0; strbuf->chars = CHAR_NULL_PTR; } @@ -332,10 +319,12 @@ FF_C_NODISCARD static inline FFstrbuf ffStrbufCreateStatic(const char* str) static inline void ffStrbufSetStatic(FFstrbuf* strbuf, const char* value) { if(strbuf->allocated > 0) - ffStrbufDestroy(strbuf); + free(strbuf->chars); if(value != NULL) ffStrbufInitStatic(strbuf, value); + else + ffStrbufInit(strbuf); } static inline void ffStrbufInitNS(FFstrbuf* strbuf, uint32_t length, const char* str) @@ -392,7 +381,7 @@ static inline void ffStrbufPrependS(FFstrbuf* strbuf, const char* value) ffStrbufPrependNS(strbuf, (uint32_t) strlen(value), value); } -static inline int ffStrbufComp(const FFstrbuf* strbuf, const FFstrbuf* comp) +static inline FF_C_NODISCARD int ffStrbufComp(const FFstrbuf* strbuf, const FFstrbuf* comp) { uint32_t length = strbuf->length > comp->length ? comp->length : strbuf->length; return memcmp(strbuf->chars, comp->chars, length + 1); diff --git a/src/common/impl/FFstrbuf.c b/src/common/impl/FFstrbuf.c index d65902108..1f59bf1a1 100644 --- a/src/common/impl/FFstrbuf.c +++ b/src/common/impl/FFstrbuf.c @@ -72,6 +72,7 @@ void ffStrbufEnsureFree(FFstrbuf* strbuf, uint32_t free) strbuf->allocated = allocate; } +// Ensure that at least `free` bytes are available in the buffer besides the current length // for an empty buffer, free + 1 length memory will be allocated(+1 for the NUL) void ffStrbufEnsureFixedLengthFree(FFstrbuf* strbuf, uint32_t free) { @@ -165,7 +166,7 @@ void ffStrbufAppendVF(FFstrbuf* strbuf, const char* format, va_list arguments) uint32_t free = ffStrbufGetFree(strbuf); int written = vsnprintf(strbuf->chars + strbuf->length, strbuf->allocated > 0 ? free + 1 : 0, format, arguments); - if(written > 0 && strbuf->length + (uint32_t) written > free) + if(written > 0 && (uint32_t) written > free) { ffStrbufEnsureFree(strbuf, (uint32_t) written); written = vsnprintf(strbuf->chars + strbuf->length, (uint32_t) written + 1, format, copy); @@ -237,6 +238,55 @@ void ffStrbufPrependC(FFstrbuf* strbuf, char c) strbuf->length += 1; } +void ffStrbufSetNS(FFstrbuf* strbuf, uint32_t length, const char* value) +{ + assert(strbuf != NULL); + + if (length == 0) + { + ffStrbufClear(strbuf); + return; + } + + assert(value != NULL); + + if (strbuf->allocated < length + 1) + { + if (strbuf->allocated > 0) + free(strbuf->chars); + strbuf->allocated = length + 1; + strbuf->chars = malloc(sizeof(char) * strbuf->allocated); + } + + memcpy(strbuf->chars, value, length); + strbuf->length = length; + strbuf->chars[length] = '\0'; +} + +void ffStrbufSet(FFstrbuf* strbuf, const FFstrbuf* value) +{ + assert(value && value != strbuf); + + if (value->length == 0) + { + ffStrbufClear(strbuf); + return; + } + + if (value->allocated == 0) // static string + { + if (strbuf->allocated != 0) + { + free(strbuf->chars); + strbuf->allocated = 0; + } + strbuf->chars = value->chars; + strbuf->length = value->length; + return; + } + ffStrbufSetNS(strbuf, value->length, value->chars); +} + void ffStrbufTrimLeft(FFstrbuf* strbuf, char c) { if(strbuf->length == 0) @@ -398,7 +448,15 @@ void ffStrbufReplaceAllC(FFstrbuf* strbuf, char find, char replace) return; ffStrbufEnsureFree(strbuf, 0); - for (char *current_pos = strchr(strbuf->chars, find); current_pos; current_pos = strchr(current_pos + 1, find)) + for ( + char *current_pos = memchr(strbuf->chars, find, strbuf->length); + current_pos; + current_pos = memchr( + current_pos + 1, + find, + strbuf->length - (uint32_t)(current_pos + 1 - strbuf->chars) + ) + ) *current_pos = replace; } diff --git a/tests/strbuf.c b/tests/strbuf.c index 7e8058018..aa891698c 100644 --- a/tests/strbuf.c +++ b/tests/strbuf.c @@ -629,17 +629,28 @@ int main(void) ffStrbufSetStatic(&strbuf, "abcdef"); FF_STRBUF_AUTO_DESTROY newStr = ffStrbufCreateS("123456"); ffStrbufSet(&newStr, &strbuf); - VERIFY(newStr.allocated > 0); - VERIFY(newStr.chars != strbuf.chars); + VERIFY(newStr.allocated == 0); + VERIFY(newStr.chars == strbuf.chars); VERIFY(ffStrbufEqualS(&newStr, "abcdef")); } + { + ffStrbufClear(&strbuf); + FF_STRBUF_AUTO_DESTROY newStr = ffStrbufCreateS("123456"); + uint32_t oldAlloc = newStr.allocated; + VERIFY(oldAlloc > newStr.length); + ffStrbufSet(&newStr, &strbuf); + VERIFY(newStr.allocated == oldAlloc); + VERIFY(newStr.chars != strbuf.chars); + VERIFY(ffStrbufEqualS(&newStr, "")); + } + { ffStrbufSetStatic(&strbuf, "abcdefghijkl"); FF_STRBUF_AUTO_DESTROY newStr = ffStrbufCreateS("123456"); ffStrbufSet(&newStr, &strbuf); - VERIFY(newStr.allocated > 0); - VERIFY(newStr.chars != strbuf.chars); + VERIFY(newStr.allocated == 0); + VERIFY(newStr.chars == strbuf.chars); VERIFY(ffStrbufEqualS(&newStr, "abcdefghijkl")); } @@ -1024,6 +1035,95 @@ int main(void) ffStrbufDestroy(&strbuf); } + //setS + ffStrbufInitStatic(&strbuf, "STATIC"); + ffStrbufSetS(&strbuf, "DYNAMIC"); + VERIFY(ffStrbufEqualS(&strbuf, "DYNAMIC")); + VERIFY(strbuf.allocated > 0); + ffStrbufDestroy(&strbuf); + + ffStrbufInit(&strbuf); + ffStrbufSetS(&strbuf, ""); + VERIFY(ffStrbufEqualS(&strbuf, "")); + VERIFY(strbuf.allocated == 0); + ffStrbufDestroy(&strbuf); + + ffStrbufInitStatic(&strbuf, "STATIC"); + ffStrbufSetS(&strbuf, ""); + VERIFY(ffStrbufEqualS(&strbuf, "")); + VERIFY(strbuf.allocated == 0); + ffStrbufDestroy(&strbuf); + + ffStrbufInitStatic(&strbuf, "STATIC"); + ffStrbufSetStatic(&strbuf, ""); + VERIFY(ffStrbufEqualS(&strbuf, "")); + VERIFY(strbuf.allocated == 0); + ffStrbufDestroy(&strbuf); + + ffStrbufInitS(&strbuf, "DYNAMIC"); + ffStrbufSetStatic(&strbuf, ""); + VERIFY(ffStrbufEqualS(&strbuf, "")); + VERIFY(strbuf.allocated == 0); + ffStrbufDestroy(&strbuf); + + ffStrbufInitS(&strbuf, "DYNAMIC"); + ffStrbufSetS(&strbuf, "ANOTHER DYNAMIC"); + VERIFY(ffStrbufEqualS(&strbuf, "ANOTHER DYNAMIC")); + VERIFY(strbuf.allocated > 0); + ffStrbufDestroy(&strbuf); + + ffStrbufInit(&strbuf); + ffStrbufSetStatic(&strbuf, ""); + VERIFY(ffStrbufEqualS(&strbuf, "")); + VERIFY(strbuf.allocated == 0); + ffStrbufDestroy(&strbuf); + + //set + ffStrbufInitStatic(&strbuf, "STATIC"); + { + FF_STRBUF_AUTO_DESTROY other = ffStrbufCreateS("DYNAMIC"); + ffStrbufSet(&strbuf, &other); + } + VERIFY(ffStrbufEqualS(&strbuf, "DYNAMIC")); + VERIFY(strbuf.allocated > 0); + ffStrbufDestroy(&strbuf); + + ffStrbufInit(&strbuf); + { + FF_STRBUF_AUTO_DESTROY other = ffStrbufCreateS(""); + ffStrbufSet(&strbuf, &other); + } + VERIFY(ffStrbufEqualS(&strbuf, "")); + VERIFY(strbuf.allocated == 0); + ffStrbufDestroy(&strbuf); + + ffStrbufInitStatic(&strbuf, "STATIC"); + { + FF_STRBUF_AUTO_DESTROY other = ffStrbufCreateS(""); + ffStrbufSet(&strbuf, &other); + } + VERIFY(ffStrbufEqualS(&strbuf, "")); + VERIFY(strbuf.allocated == 0); + ffStrbufDestroy(&strbuf); + + ffStrbufInitS(&strbuf, "DYNAMIC"); + { + FF_STRBUF_AUTO_DESTROY other = ffStrbufCreateS(""); + ffStrbufSet(&strbuf, &other); + } + VERIFY(ffStrbufEqualS(&strbuf, "")); + VERIFY(strbuf.allocated > 0); + ffStrbufDestroy(&strbuf); + + ffStrbufInitS(&strbuf, "DYNAMIC"); + { + FF_STRBUF_AUTO_DESTROY other = ffStrbufCreateStatic("STATIC"); + ffStrbufSet(&strbuf, &other); + } + VERIFY(ffStrbufEqualS(&strbuf, "STATIC")); + VERIFY(strbuf.allocated == 0); + ffStrbufDestroy(&strbuf); + //Success puts("\e[32mAll tests passed!" FASTFETCH_TEXT_MODIFIER_RESET); }