# HG changeset patch # User Mike Becker # Date 1765548458 -3600 # Node ID 7e19168d49d163076556c94251b10f0bb45f6710 # Parent dde0c67a449bd5017a7d974a465ec2696fe3d247 improves cxBufferPutString() and adds cxBufferAppendString() - resolves #779 diff -r dde0c67a449b -r 7e19168d49d1 CHANGELOG --- a/CHANGELOG Thu Dec 11 23:47:46 2025 +0100 +++ b/CHANGELOG Fri Dec 12 15:07:38 2025 +0100 @@ -5,15 +5,17 @@ * adds cxJsonFromString(), cxJsonToString(), and cxJsonToPrettyString() * adds line continuation support to CxProperties / CxPropertiesConfig * adds cxBufferMaximumCapacity() + * adds cxBufferAppendString() * adds CX_BUFFER_DO_NOT_FREE buffer flag - * changes cxFreeDefault() from a macro to a function so that it can be used as a simple destructor * changes cxBufferReserve() to allow reducing the capacity * changes cxBufferTerminate() to automatically shrink the buffer * changes cxBufferTerminate() so that position and size are equal after successful operation + * changes cxBufferPutString() to accept any kind of string that cx_strcast() supports * changes the members of CxJson and CxJsonValue * changes the return value of cxJsonObjIter() to CxMapIterator * changes CxTree structure so that it now inherits CX_COLLECTION_BASE * changes cxPropertiesLoad() to directly load properties from a file to a CxMap + * changes cxFreeDefault() from a macro to a function so that it can be used as a simple destructor * fixes cxJsonWrite() incorrectly returning non-zero when strings needed to be escaped * fixes cxJsonNext() incorrectly returning CX_JSON_INCOMPLETE_DATA when the input ends with trailing spaces * fixes critical memory leak when using cxMapFree() on a kv-list that is using destructors diff -r dde0c67a449b -r 7e19168d49d1 docs/Writerside/topics/about.md --- a/docs/Writerside/topics/about.md Thu Dec 11 23:47:46 2025 +0100 +++ b/docs/Writerside/topics/about.md Fri Dec 12 15:07:38 2025 +0100 @@ -32,15 +32,17 @@ * adds cxJsonFromString(), cxJsonToString(), and cxJsonToPrettyString() * adds line continuation support to CxProperties / CxPropertiesConfig * adds cxBufferMaximumCapacity() +* adds cxBufferAppendString() * adds CX_BUFFER_DO_NOT_FREE buffer flag -* changes cxFreeDefault() from a macro to a function so that it can be used as a simple destructor * changes cxBufferReserve() to allow reducing the capacity * changes cxBufferTerminate() to automatically shrink the buffer * changes cxBufferTerminate() so that position and size are equal after successful operation +* changes cxBufferPutString() to accept any kind of string that cx_strcast() supports * changes the members of CxJson and CxJsonValue * changes the return value of cxJsonObjIter() to CxMapIterator * changes CxTree structure so that it now inherits CX_COLLECTION_BASE * changes cxPropertiesLoad() to directly load properties from a file to a CxMap +* changes cxFreeDefault() from a macro to a function so that it can be used as a simple destructor * fixes cxJsonWrite() incorrectly returning non-zero when strings needed to be escaped * fixes cxJsonNext() incorrectly returning CX_JSON_INCOMPLETE_DATA when the input ends with trailing spaces * fixes critical memory leak when using cxMapFree() on a kv-list that is using destructors diff -r dde0c67a449b -r 7e19168d49d1 docs/Writerside/topics/buffer.h.md --- a/docs/Writerside/topics/buffer.h.md Thu Dec 11 23:47:46 2025 +0100 +++ b/docs/Writerside/topics/buffer.h.md Fri Dec 12 15:07:38 2025 +0100 @@ -165,12 +165,14 @@ int cxBufferPut(CxBuffer *buffer, int c); -size_t cxBufferPutString(CxBuffer *buffer, const char *str); +size_t cxBufferPutString(CxBuffer *buffer, AnyStr str); int cxBufferTerminate(CxBuffer *buffer); size_t cxBufferAppend(const void *ptr, size_t size, size_t nitems, CxBuffer *buffer); + +size_t cxBufferAppendString(CxBuffer *buffer, AnyStr str); ``` The primary function for writing to a buffer is `cxBufferWrite()` @@ -189,8 +191,8 @@ Just like `putc()` this function returns the written character on success, and `EOF` on failure, but it does _not_ set `errno` on failure. -The function `cxBufferPutString()` is a convenience function, -that uses stdlib `strlen()` to compute the length of `str` and then invokes `cxBufferWrite()`. +The function `cxBufferPutString()` is a convenience function that uses `cx_strcast()` to convert any supported string type to a `cxstring` and then invokes `cxBufferWrite()`. +Similarly, `cxBufferAppendString()` performs the same operation with `cxBufferAppend()`. All the above functions advance the buffer position by the number of bytes written and cause the _size_ of the buffer to grow, if necessary, to contain all written bytes. diff -r dde0c67a449b -r 7e19168d49d1 src/Makefile --- a/src/Makefile Thu Dec 11 23:47:46 2025 +0100 +++ b/src/Makefile Fri Dec 12 15:07:38 2025 +0100 @@ -79,7 +79,7 @@ $(CC) -o $@ $(CFLAGS) -c $< $(build_dir)/buffer$(OBJ_EXT): buffer.c cx/buffer.h cx/common.h \ - cx/allocator.h + cx/allocator.h cx/string.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< diff -r dde0c67a449b -r 7e19168d49d1 src/buffer.c --- a/src/buffer.c Thu Dec 11 23:47:46 2025 +0100 +++ b/src/buffer.c Fri Dec 12 15:07:38 2025 +0100 @@ -299,6 +299,9 @@ size_t nitems, CxBuffer *buffer ) { + // trivial case + if (size == 0 || nitems == 0) return 0; + // optimize for easy case if (size == 1 && (buffer->capacity - buffer->pos) >= nitems) { if (buffer_copy_on_write(buffer)) return 0; @@ -364,6 +367,9 @@ size_t nitems, CxBuffer *buffer ) { + // trivial case + if (size == 0 || nitems == 0) return 0; + size_t pos = buffer->pos; size_t append_pos = buffer->size; buffer->pos = append_pos; @@ -418,11 +424,12 @@ return 0; } -size_t cxBufferPutString( - CxBuffer *buffer, - const char *str -) { - return cxBufferWrite(str, 1, strlen(str), buffer); +size_t cx_buffer_put_string(CxBuffer *buffer, cxstring str) { + return cxBufferWrite(str.ptr, 1, str.length, buffer); +} + +size_t cx_buffer_append_string(CxBuffer *buffer, cxstring str) { + return cxBufferAppend(str.ptr, 1, str.length, buffer); } size_t cxBufferRead( diff -r dde0c67a449b -r 7e19168d49d1 src/cx/buffer.h --- a/src/cx/buffer.h Thu Dec 11 23:47:46 2025 +0100 +++ b/src/cx/buffer.h Fri Dec 12 15:07:38 2025 +0100 @@ -48,6 +48,7 @@ #include "common.h" #include "allocator.h" +#include "string.h" #ifdef __cplusplus extern "C" { @@ -558,16 +559,48 @@ CX_EXPORT int cxBufferTerminate(CxBuffer *buffer); /** - * Writes a string to a buffer. - * - * This is a convenience function for cxBufferWrite(str, 1, strlen(str), buffer). + * Internal function - do not use. * * @param buffer the buffer - * @param str the zero-terminated string + * @param str the string * @return the number of bytes written + * @see cxBufferPutString() + */ +cx_attr_nonnull +CX_EXPORT size_t cx_buffer_put_string(CxBuffer *buffer, cxstring str); + +/** + * Writes a string to a buffer with cxBufferWrite(). + * + * @param buffer (@c CxBuffer*) the buffer + * @param str (any string) the zero-terminated string + * @return (@c size_t) the number of bytes written + * @see cxBufferWrite() + * @see cx_strcast() */ -cx_attr_nonnull cx_attr_cstr_arg(2) -CX_EXPORT size_t cxBufferPutString(CxBuffer *buffer, const char *str); +#define cxBufferPutString(buffer, str) cx_buffer_put_string(buffer, cx_strcast(str)) + +/** + * Internal function - do not use. + * + * @param buffer the buffer + * @param str the string + * @return the number of bytes written + * @see cxBufferPutString() + */ +cx_attr_nonnull +CX_EXPORT size_t cx_buffer_append_string(CxBuffer *buffer, cxstring str); + +/** + * Appends a string to a buffer with cxBufferAppend(). + * + * @param buffer (@c CxBuffer*) the buffer + * @param str (any string) the zero-terminated string + * @return (@c size_t) the number of bytes written + * @see cxBufferAppend() + * @see cx_strcast() + */ +#define cxBufferAppendString(buffer, str) cx_buffer_append_string(buffer, cx_strcast(str)) /** * Gets a character from a buffer. diff -r dde0c67a449b -r 7e19168d49d1 tests/Makefile --- a/tests/Makefile Thu Dec 11 23:47:46 2025 +0100 +++ b/tests/Makefile Fri Dec 12 15:07:38 2025 +0100 @@ -60,7 +60,7 @@ $(TEST_DIR)/test_buffer$(OBJ_EXT): test_buffer.c ../src/cx/test.h \ ../src/cx/common.h util_allocator.h ../src/cx/allocator.h \ - ../src/cx/buffer.h ../src/cx/allocator.h + ../src/cx/buffer.h ../src/cx/allocator.h ../src/cx/string.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -I../src -c $< @@ -139,7 +139,7 @@ $(TEST_DIR)/test_streams$(OBJ_EXT): test_streams.c ../src/cx/test.h \ ../src/cx/common.h ../src/cx/streams.h ../src/cx/buffer.h \ - ../src/cx/allocator.h + ../src/cx/allocator.h ../src/cx/string.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -I../src -c $< diff -r dde0c67a449b -r 7e19168d49d1 tests/test_buffer.c --- a/tests/test_buffer.c Thu Dec 11 23:47:46 2025 +0100 +++ b/tests/test_buffer.c Fri Dec 12 15:07:38 2025 +0100 @@ -956,6 +956,24 @@ cxBufferDestroy(&buf); } +CX_TEST(test_buffer_append_string) { + CxBuffer buf; + cxBufferInit(&buf, NULL, 16, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND); + memcpy(buf.space, "prepXXXX\0\0\0\0\0\0\0\0", 16); + buf.capacity = 8; + buf.size = 6; + buf.pos = 4; + CX_TEST_DO { + size_t written = cxBufferAppendString(&buf, "testing"); + CX_TEST_ASSERT(written == 7); + CX_TEST_ASSERT(buf.size == 13); + CX_TEST_ASSERT(buf.pos == 4); + CX_TEST_ASSERT(buf.capacity >= 13); + CX_TEST_ASSERT(0 == memcmp(buf.space, "prepXXtesting", 13)); + } + cxBufferDestroy(&buf); +} + CX_TEST(test_buffer_put_fit) { CxBuffer buf; cxBufferInit(&buf, NULL, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); @@ -1485,6 +1503,7 @@ cx_test_register(suite, test_buffer_write_multibyte_extend); cx_test_register(suite, test_buffer_write_copy_on_write); cx_test_register(suite, test_buffer_append); + cx_test_register(suite, test_buffer_append_string); cx_test_register(suite, test_buffer_put_fit); cx_test_register(suite, test_buffer_put_discard); cx_test_register(suite, test_buffer_put_extend);