Thu, 16 Jan 2025 18:56:44 +0100
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 = ⌖ + 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 = ⌖ + 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);