fix #566 by changing the spec (pro-move 8) default tip

Thu, 16 Jan 2025 18:56:44 +0100

author
Mike Becker <universe@uap-core.de>
date
Thu, 16 Jan 2025 18:56:44 +0100
changeset 1129
cc60b7d07912
parent 1128
3672b89e606c

fix #566 by changing the spec (pro-move 8)

src/cx/buffer.h file | annotate | diff | comparison | revisions
tests/test_buffer.c file | annotate | diff | comparison | revisions
--- a/src/cx/buffer.h	Thu Jan 16 18:56:17 2025 +0100
+++ b/src/cx/buffer.h	Thu Jan 16 18:56:44 2025 +0100
@@ -484,14 +484,26 @@
  * the target until the target signals that it cannot take more data by
  * returning zero via the respective write function. In that case, the remaining
  * data in this buffer is shifted to the beginning of this buffer so that the
- * newly available space can be used to append as much data as possible. This
- * function only stops writing more elements, when the flush target and this
+ * newly available space can be used to append as much data as possible.
+ *
+ * This function only stops writing more elements, when the flush target and this
  * buffer are both incapable of taking more data or all data has been written.
- * If number of items that shall be written is larger than the buffer can hold,
- * the first items from @c ptr are directly relayed to the flush target, if
- * possible.
- * The number returned by this function is only the number of elements from
- * @c ptr that could be written to either the flush target or the buffer.
+ *
+ * If, after flushing, the number of items that shall be written still exceeds
+ * the capacity or flush threshold, this function tries to write all items directly
+ * to the flush target, if possible.
+ *
+ * The number returned by this function is the number of elements from
+ * @c ptr that could be written to either the flush target or the buffer
+ * (so it does not include the number of items that had been already in the buffer
+ * in were flushed during the process).
+ *
+ * @attention
+ * When @p size is larger than one and the contents of the buffer are not aligned
+ * with @p size, flushing stops after all complete items have been flushed, leaving
+ * the mis-aligned part in the buffer.
+ * Afterward, this function refuses to write any data to the buffer, until the
+ * mis-alignment has been resolved (e.g. by manually flushing with cxBufferFlush()).
  *
  * @note The signature is compatible with the fwrite() family of functions.
  *
--- a/tests/test_buffer.c	Thu Jan 16 18:56:17 2025 +0100
+++ b/tests/test_buffer.c	Thu Jan 16 18:56:44 2025 +0100
@@ -1180,6 +1180,93 @@
     cxBufferDestroy(&target);
 }
 
+CX_TEST(test_buffer_write_flush_multibyte) {
+    // this test case tests that flushing works correctly even when the
+    // contents in the buffer are currently not aligned with the item size
+    CxBuffer buf, target;
+    cxBufferInit(&target, NULL, 8, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND);
+    cxBufferInit(&buf, NULL, 8, cxDefaultAllocator, CX_BUFFER_DEFAULT);
+    memset(buf.space, 0, 8);
+    cxBufferPutString(&buf, "pre");
+    CX_TEST_DO {
+        CxBufferFlushConfig flush;
+        flush.threshold = 0;
+        flush.blksize = 4; // test blksize that is not aligned
+        flush.blkmax = 2; // test with two blocks
+        flush.target = &target;
+        flush.wfunc = cxBufferWriteFunc;
+        CX_TEST_ASSERT(0 == cxBufferEnableFlushing(&buf, flush));
+        // first case: string fits after flush
+        size_t written = cxBufferWrite("foobar", 3, 2, &buf);
+        CX_TEST_ASSERT(written == 2);
+        CX_TEST_ASSERT(buf.pos == 6);
+        CX_TEST_ASSERT(buf.size == 6);
+        CX_TEST_ASSERT(target.pos == 3);
+        CX_TEST_ASSERT(target.size == 3);
+        CX_TEST_ASSERT(0 == memcmp(buf.space, "foobar", 6));
+        CX_TEST_ASSERT(0 == memcmp(target.space, "pre", 3));
+        // second case: string does not fit, data is relayed, but only two blocks!
+        written = cxBufferWrite("bazfooBAR", 3, 3, &buf);
+        CX_TEST_ASSERT(written == 3);
+        CX_TEST_ASSERT(buf.pos == 3);
+        CX_TEST_ASSERT(buf.size == 3);
+        CX_TEST_ASSERT(target.pos == 15);
+        CX_TEST_ASSERT(target.size == 15);
+        CX_TEST_ASSERT(0 == memcmp(buf.space, "BAR", 3));
+        CX_TEST_ASSERT(0 == memcmp(target.space, "prefoobarbazfoo", 15));
+        // third case: everything can be relayed, block size is large enough
+        buf.flush->blkmax = 3;
+        written = cxBufferWrite("abcdef012", 3, 3, &buf);
+        CX_TEST_ASSERT(written == 3);
+        CX_TEST_ASSERT(buf.pos == 0);
+        CX_TEST_ASSERT(buf.size == 0);
+        CX_TEST_ASSERT(target.pos == 27);
+        CX_TEST_ASSERT(target.size == 27);
+        CX_TEST_ASSERT(0 == memcmp(target.space, "prefoobarbazfooBARabcdef012", 27));
+    }
+    cxBufferDestroy(&buf);
+    cxBufferDestroy(&target);
+}
+
+CX_TEST(test_buffer_write_flush_misaligned) {
+    // this test case tests that flushing works correctly even when the
+    // contents in the buffer are currently not aligned with the item size
+    CxBuffer buf, target;
+    cxBufferInit(&target, NULL, 8, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND);
+    cxBufferInit(&buf, NULL, 8, cxDefaultAllocator, CX_BUFFER_DEFAULT);
+    memset(buf.space, 0, 8);
+    cxBufferPutString(&buf, "prep");
+    CX_TEST_DO {
+        CxBufferFlushConfig flush;
+        flush.threshold = 0;
+        flush.blksize = 32;
+        flush.blkmax = 1;
+        flush.target = &target;
+        flush.wfunc = cxBufferWriteFunc;
+        CX_TEST_ASSERT(0 == cxBufferEnableFlushing(&buf, flush));
+        // first case: string fits after flush
+        size_t written = cxBufferWrite("foobar", 3, 2, &buf);
+        CX_TEST_ASSERT(written == 2);
+        CX_TEST_ASSERT(buf.pos == 7);
+        CX_TEST_ASSERT(buf.size == 7);
+        CX_TEST_ASSERT(target.pos == 3);
+        CX_TEST_ASSERT(target.size == 3);
+        CX_TEST_ASSERT(0 == memcmp(buf.space, "pfoobar", 7));
+        CX_TEST_ASSERT(0 == memcmp(target.space, "pre", 3));
+        // second case: string does not fit, relaying not possible due to misalignment
+        written = cxBufferWrite("bazfoobar", 3, 3, &buf);
+        CX_TEST_ASSERT(written == 0);
+        CX_TEST_ASSERT(buf.pos == 1);
+        CX_TEST_ASSERT(buf.size == 1);
+        CX_TEST_ASSERT(target.pos == 9);
+        CX_TEST_ASSERT(target.size == 9);
+        CX_TEST_ASSERT(0 == memcmp(buf.space, "r", 1));
+        CX_TEST_ASSERT(0 == memcmp(target.space, "prepfooba", 9));
+    }
+    cxBufferDestroy(&buf);
+    cxBufferDestroy(&target);
+}
+
 CX_TEST(test_buffer_flush) {
     CxBuffer buf, target;
     cxBufferInit(&target, NULL, 8, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND);
@@ -1377,6 +1464,8 @@
     cx_test_register(suite, test_buffer_write_flush_at_capacity);
     cx_test_register(suite, test_buffer_write_flush_at_threshold);
     cx_test_register(suite, test_buffer_write_flush_rate_limited_and_buffer_too_small);
+    cx_test_register(suite, test_buffer_write_flush_multibyte);
+    cx_test_register(suite, test_buffer_write_flush_misaligned);
     cx_test_register(suite, test_buffer_flush);
     cx_test_register(suite, test_buffer_get);
     cx_test_register(suite, test_buffer_get_eof);

mercurial