improves cxBufferPutString() and adds cxBufferAppendString() - resolves #779

Fri, 12 Dec 2025 15:07:38 +0100

author
Mike Becker <universe@uap-core.de>
date
Fri, 12 Dec 2025 15:07:38 +0100
changeset 1576
7e19168d49d1
parent 1575
dde0c67a449b
child 1577
b5e6b91a98c3

improves cxBufferPutString() and adds cxBufferAppendString() - resolves #779

CHANGELOG file | annotate | diff | comparison | revisions
docs/Writerside/topics/about.md file | annotate | diff | comparison | revisions
docs/Writerside/topics/buffer.h.md file | annotate | diff | comparison | revisions
src/Makefile file | annotate | diff | comparison | revisions
src/buffer.c file | annotate | diff | comparison | revisions
src/cx/buffer.h file | annotate | diff | comparison | revisions
tests/Makefile file | annotate | diff | comparison | revisions
tests/test_buffer.c file | annotate | diff | comparison | revisions
--- 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
--- 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
--- 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.
--- 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 $<
 
--- 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(
--- 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 <code>cxBufferWrite(str, 1, strlen(str), buffer)</code>.
+ * 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.
--- 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 $<
 
--- 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);

mercurial