--- a/tests/test_buffer.c Wed Nov 26 23:22:03 2025 +0100 +++ b/tests/test_buffer.c Wed Nov 26 23:35:25 2025 +0100 @@ -1478,7 +1478,7 @@ CX_TEST_ASSERT(target.size == 8); CX_TEST_ASSERT(0 == memcmp(buf.space, "arxyz123", 8)); CX_TEST_ASSERT(0 == memcmp(target.space, "prepfoob", 8)); - // final test, cannot write anything more + // final test - cannot write anything more written = cxBufferWrite("baz", 1, 3, &buf); CX_TEST_ASSERT(written == 0); CX_TEST_ASSERT(buf.pos == 8); @@ -1492,6 +1492,98 @@ cxBufferDestroy(&target); } +CX_TEST(test_buffer_write_flush_multibyte_target_full) { + CxBuffer buf, target; + cxBufferInit(&target, NULL, 12, cxDefaultAllocator, CX_BUFFER_DEFAULT); + cxBufferInit(&buf, NULL, 8, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND); + CX_TEST_DO { + CxBufferFlushConfig flush; + flush.threshold = 12; + flush.blksize = 8; + flush.blkmax = 2; + flush.target = ⌖ + flush.wfunc = cxBufferWriteFunc; + CX_TEST_ASSERT(0 == cxBufferEnableFlushing(&buf, flush)); + cxBufferPutString(&buf, "preparation"); + size_t written = cxBufferWrite("teststring", 2, 5, &buf); + CX_TEST_ASSERT(written == 5); + CX_TEST_ASSERT(buf.pos == 11); + CX_TEST_ASSERT(buf.size == 11); + CX_TEST_ASSERT(target.pos == 10); + CX_TEST_ASSERT(target.size == 10); + CX_TEST_ASSERT(0 == memcmp(buf.space, "nteststring", 11)); + CX_TEST_ASSERT(0 == memcmp(target.space, "preparatio", 10)); + // pop the misaligned byte from the buffer + cxBufferPop(&buf, 1, 1); + // write three more items, but only one fits into the target and one more into the buffer + written = cxBufferWrite("123456", 2, 3, &buf); + CX_TEST_ASSERT(written == 2); + CX_TEST_ASSERT(buf.pos == 12); + CX_TEST_ASSERT(buf.size == 12); + CX_TEST_ASSERT(target.pos == 12); + CX_TEST_ASSERT(target.size == 12); + CX_TEST_ASSERT(0 == memcmp(buf.space, "eststrin1234", 12)); + CX_TEST_ASSERT(0 == memcmp(target.space, "preparationt", 12)); + } + cxBufferDestroy(&buf); + cxBufferDestroy(&target); +} + +CX_TEST(test_buffer_write_flush_at_threshold_target_full) { + CxBuffer buf, target; + // target does NOT auto-extend and can get completely full + cxBufferInit(&target, NULL, 8, cxDefaultAllocator, CX_BUFFER_DEFAULT); + // source may auto-extend but flushes at a certain threshold + cxBufferInit(&buf, NULL, 8, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND); + cxBufferPutString(&buf, "prep"); + CX_TEST_DO { + CxBufferFlushConfig flush; + flush.threshold = 16; + flush.blksize = 4; + flush.blkmax = 2; + flush.target = ⌖ + flush.wfunc = cxBufferWriteFunc; + CX_TEST_ASSERT(0 == cxBufferEnableFlushing(&buf, flush)); + // step one - adding 6 bytes does not exceed the threshold + size_t written = cxBufferWrite("foobar", 1, 6, &buf); + CX_TEST_ASSERT(written == 6); + CX_TEST_ASSERT(buf.pos == 10); + CX_TEST_ASSERT(buf.size == 10); + CX_TEST_ASSERT(target.pos == 0); + CX_TEST_ASSERT(target.size == 0); + CX_TEST_ASSERT(0 == memcmp(buf.space, "prepfoobar", 10)); + // step two - adding 8 bytes is two too many, two blocks are flushed + written = cxBufferWrite("12345678", 1, 8, &buf); + CX_TEST_ASSERT(written == 8); + CX_TEST_ASSERT(buf.pos == 10); + CX_TEST_ASSERT(buf.size == 10); + CX_TEST_ASSERT(target.pos == 8); + CX_TEST_ASSERT(target.size == 8); + CX_TEST_ASSERT(0 == memcmp(buf.space, "ar12345678", 10)); + CX_TEST_ASSERT(0 == memcmp(target.space, "prepfoob", 8)); + // step three - cannot flush more, but can write 6 more bytes + written = cxBufferWrite("ABCDEFGH", 1, 8, &buf); + CX_TEST_ASSERT(written == 6); + CX_TEST_ASSERT(buf.pos == 16); + CX_TEST_ASSERT(buf.size == 16); + CX_TEST_ASSERT(target.pos == 8); + CX_TEST_ASSERT(target.size == 8); + CX_TEST_ASSERT(0 == memcmp(buf.space, "ar12345678ABCDEF", 16)); + CX_TEST_ASSERT(0 == memcmp(target.space, "prepfoob", 8)); + // final test - cannot write anything more + written = cxBufferWrite("baz", 1, 3, &buf); + CX_TEST_ASSERT(written == 0); + CX_TEST_ASSERT(buf.pos == 16); + CX_TEST_ASSERT(buf.size == 16); + CX_TEST_ASSERT(target.pos == 8); + CX_TEST_ASSERT(target.size == 8); + CX_TEST_ASSERT(0 == memcmp(buf.space, "ar12345678ABCDEF", 16)); + CX_TEST_ASSERT(0 == memcmp(target.space, "prepfoob", 8)); + } + cxBufferDestroy(&buf); + cxBufferDestroy(&target); +} + CX_TEST(test_buffer_flush) { CxBuffer buf, target; cxBufferInit(&target, NULL, 8, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND); @@ -1700,10 +1792,12 @@ cx_test_register(suite, test_buffer_write_only_overwrite); 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_at_threshold_target_full); 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_write_flush_target_full); + cx_test_register(suite, test_buffer_write_flush_multibyte_target_full); cx_test_register(suite, test_buffer_flush); cx_test_register(suite, test_buffer_get); cx_test_register(suite, test_buffer_get_eof);