diff --git a/src/util/FFstrbuf.c b/src/util/FFstrbuf.c index 6e0db579b..db2e6e737 100644 --- a/src/util/FFstrbuf.c +++ b/src/util/FFstrbuf.c @@ -542,6 +542,74 @@ int64_t ffStrbufToSInt(const FFstrbuf* strbuf, int64_t defaultValue) return str_end == strbuf->chars ? defaultValue : (int64_t)result; } +void ffStrbufAppendSInt(FFstrbuf* strbuf, int64_t value) +{ + ffStrbufEnsureFree(strbuf, 21); // Required by yyjson_write_number + char* start = strbuf->chars + strbuf->length; + + yyjson_val val = {}; + unsafe_yyjson_set_sint(&val, value); + char* end = yyjson_write_number(&val, start); + + assert(end != NULL); + + strbuf->length += (uint32_t)(end - start); +} + +void ffStrbufAppendUInt(FFstrbuf* strbuf, uint64_t value) +{ + ffStrbufEnsureFree(strbuf, 21); // Required by yyjson_write_number + char* start = strbuf->chars + strbuf->length; + + yyjson_val val = {}; + unsafe_yyjson_set_uint(&val, value); + char* end = yyjson_write_number(&val, start); + + assert(end != NULL); + + strbuf->length += (uint32_t)(end - start); +} + +void ffStrbufAppendDouble(FFstrbuf* strbuf, double value, uint8_t precision) +{ + assert(precision <= 15); // yyjson_write_number supports up to 15 digits after the decimal point + + ffStrbufEnsureFree(strbuf, 40); // Required by yyjson_write_number + char* start = strbuf->chars + strbuf->length; + + yyjson_val val = {}; + unsafe_yyjson_set_double(&val, value); + if (precision > 0) unsafe_yyjson_set_fp_to_fixed(&val, (int) precision); + + // Write at most digits after the decimal point; doesn't append trailing zeros + char* end = yyjson_write_number(&val, start); + + assert(end != NULL); + + strbuf->length += (uint32_t)(end - start); + + if (__builtin_expect(value > 1e21 || value < -1e21, false)) + { + // If the value is too large, yyjson_write_number will write it in scientific notation + return; + } + + if (precision > 1) + { + for (char* p = end - 1; *p != '.' && p > start; --p) + --precision; + if (precision > 0) + ffStrbufAppendNC(strbuf, precision, '0'); + } + else if (precision == 0 && end[-1] == '0' && end[-2] == '.') + { + // yyjson always appends a decimal point if value is an integer + // Remove trailing zeros and the decimal point if precision is 0 + strbuf->length -= 2; + strbuf->chars[strbuf->length] = '\0'; + } +} + void ffStrbufUpperCase(FFstrbuf* strbuf) { for (uint32_t i = 0; i < strbuf->length; ++i) diff --git a/src/util/FFstrbuf.h b/src/util/FFstrbuf.h index 39737fed0..3d9c6efe6 100644 --- a/src/util/FFstrbuf.h +++ b/src/util/FFstrbuf.h @@ -103,6 +103,12 @@ bool ffStrbufMatchSeparatedNS(const FFstrbuf* strbuf, uint32_t compLength, const int ffStrbufAppendUtf32CodePoint(FFstrbuf* strbuf, uint32_t codepoint); +void ffStrbufAppendSInt(FFstrbuf* strbuf, int64_t value); +void ffStrbufAppendUInt(FFstrbuf* strbuf, uint64_t value); +// Appends a double value to the string buffer with the specified precision. +// if `precision == 0`, let yyjson decide the precision +void ffStrbufAppendDouble(FFstrbuf* strbuf, double value, uint8_t precision); + FF_C_NODISCARD static inline FFstrbuf ffStrbufCreateA(uint32_t allocate) { FFstrbuf strbuf; diff --git a/tests/strbuf.c b/tests/strbuf.c index 456b9c330..931b8629f 100644 --- a/tests/strbuf.c +++ b/tests/strbuf.c @@ -720,6 +720,96 @@ int main(void) ffStrbufDestroy(&strbuf); } + { + ffStrbufAppendSInt(&strbuf, 1234567890); + VERIFY(ffStrbufEqualS(&strbuf, "1234567890")); + + ffStrbufClear(&strbuf); + ffStrbufAppendSInt(&strbuf, -1234567890); + VERIFY(ffStrbufEqualS(&strbuf, "-1234567890")); + + ffStrbufClear(&strbuf); + ffStrbufAppendSInt(&strbuf, 0); + VERIFY(ffStrbufEqualS(&strbuf, "0")); + + ffStrbufClear(&strbuf); + ffStrbufAppendUInt(&strbuf, 1234567890); + VERIFY(ffStrbufEqualS(&strbuf, "1234567890")); + + ffStrbufClear(&strbuf); + ffStrbufAppendUInt(&strbuf, 0); + VERIFY(ffStrbufEqualS(&strbuf, "0")); + + ffStrbufDestroy(&strbuf); + } + + { + ffStrbufAppendDouble(&strbuf, 120.0, 0); + VERIFY(ffStrbufEqualS(&strbuf, "120")); + + ffStrbufClear(&strbuf); + ffStrbufAppendDouble(&strbuf, 120.0, 5); + VERIFY(ffStrbufEqualS(&strbuf, "120.00000")); + + ffStrbufClear(&strbuf); + ffStrbufAppendDouble(&strbuf, 120.123456789, 5); + VERIFY(ffStrbufEqualS(&strbuf, "120.12346")); + + ffStrbufClear(&strbuf); + ffStrbufAppendDouble(&strbuf, 120.123456789, 0); + VERIFY(ffStrbufEqualS(&strbuf, "120.123456789")); + + ffStrbufClear(&strbuf); + ffStrbufAppendDouble(&strbuf, 120.123, 5); + VERIFY(ffStrbufEqualS(&strbuf, "120.12300")); + + ffStrbufClear(&strbuf); + ffStrbufAppendDouble(&strbuf, -120.0, 0); + VERIFY(ffStrbufEqualS(&strbuf, "-120")); + + ffStrbufClear(&strbuf); + ffStrbufAppendDouble(&strbuf, -120.0, 5); + VERIFY(ffStrbufEqualS(&strbuf, "-120.00000")); + + ffStrbufClear(&strbuf); + ffStrbufAppendDouble(&strbuf, -120.123456789, 5); + VERIFY(ffStrbufEqualS(&strbuf, "-120.12346")); + + ffStrbufClear(&strbuf); + ffStrbufAppendDouble(&strbuf, -120.123456789, 0); + VERIFY(ffStrbufEqualS(&strbuf, "-120.123456789")); + + ffStrbufClear(&strbuf); + ffStrbufAppendDouble(&strbuf, -120.123, 5); + VERIFY(ffStrbufEqualS(&strbuf, "-120.12300")); + + ffStrbufClear(&strbuf); + ffStrbufAppendDouble(&strbuf, 1.2345e50, 1); + VERIFY(ffStrbufEqualS(&strbuf, "1.2345e50")); + + ffStrbufClear(&strbuf); + ffStrbufAppendDouble(&strbuf, -1.2345e50, 1); + VERIFY(ffStrbufEqualS(&strbuf, "-1.2345e50")); + + ffStrbufClear(&strbuf); + ffStrbufAppendDouble(&strbuf, 1.2345e20, 1); + VERIFY(ffStrbufEqualS(&strbuf, "123450000000000000000.0")); + + ffStrbufClear(&strbuf); + ffStrbufAppendDouble(&strbuf, -1.2345e20, 1); + VERIFY(ffStrbufEqualS(&strbuf, "-123450000000000000000.0")); + + ffStrbufClear(&strbuf); + ffStrbufAppendDouble(&strbuf, 0, 0); + VERIFY(ffStrbufEqualS(&strbuf, "0")); + + ffStrbufClear(&strbuf); + ffStrbufAppendDouble(&strbuf, -0, 0); + VERIFY(ffStrbufEqualS(&strbuf, "0")); + + ffStrbufDestroy(&strbuf); + } + //Success puts("\e[32mAll tests passed!" FASTFETCH_TEXT_MODIFIER_RESET); }