--- a/src/cx/buffer.h Sun Jan 05 14:03:30 2025 +0100 +++ b/src/cx/buffer.h Sun Jan 05 18:19:42 2025 +0100 @@ -88,6 +88,60 @@ */ #define CX_BUFFER_COPY_ON_EXTEND 0x08 +/** + * Configuration for automatic flushing. + */ +struct cx_buffer_flush_config_s { + /** + * The buffer may not extend beyond this threshold before starting to flush. + * + * Only used when the buffer uses #CX_BUFFER_AUTO_EXTEND. + * The threshold will be the maximum capacity the buffer is extended to + * before flushing. + */ + size_t threshold; + /** + * The block size for the elements to flush. + */ + size_t blksize; + /** + * The maximum number of blocks to flush in one cycle. + * + * @attention while it is guaranteed that cxBufferFlush() will not flush + * more blocks, this is not necessarily the case for cxBufferWrite(). + * After performing a flush cycle, cxBufferWrite() will retry the write + * operation and potentially trigger another flush cycle, until the + * flush target accepts no more data. + */ + size_t blkmax; + + /** + * The target for write function. + */ + void *target; + + /** + * The write-function used for flushing. + * If NULL, the flushed content gets discarded. + */ + cx_write_func wfunc; +}; + +/** + * Type alais for the flush configuration struct. + * + * @code + * struct cx_buffer_flush_config_s { + * size_t threshold; + * size_t blksize; + * size_t blkmax; + * void *target; + * cx_write_func wfunc; + * }; + * @endcode + */ +typedef struct cx_buffer_flush_config_s CxBufferFlushConfig; + /** Structure for the UCX buffer data. */ struct cx_buffer_s { /** A pointer to the buffer contents. */ @@ -103,6 +157,12 @@ }; /** The allocator to use for automatic memory management. */ const CxAllocator *allocator; + /** + * Optional flush configuration + * + * @see cxBufferEnableFlushing() + */ + CxBufferFlushConfig* flush; /** Current position of the buffer. */ size_t pos; /** Current capacity (i.e. maximum size) of the buffer. */ @@ -110,40 +170,6 @@ /** Current size of the buffer content. */ size_t size; /** - * The buffer may not extend beyond this threshold before starting to flush. - * Default is @c SIZE_MAX (flushing disabled when auto extension is enabled). - */ - size_t flush_threshold; - /** - * The block size for the elements to flush. - * Default is 4096 bytes. - */ - size_t flush_blksize; - /** - * The maximum number of blocks to flush in one cycle. - * Zero disables flushing entirely (this is the default). - * Set this to @c SIZE_MAX to flush the entire buffer. - * - * @attention if the maximum number of blocks multiplied with the block size - * is smaller than the expected contents written to this buffer within one write - * operation, multiple flush cycles are performed after that write. - * That means the total number of blocks flushed after one write to this buffer may - * be larger than @c flush_blkmax. - */ - size_t flush_blkmax; - - /** - * The write function used for flushing. - * If NULL, the flushed content gets discarded. - */ - cx_write_func flush_func; - - /** - * The target for @c flush_func. - */ - void *flush_target; - - /** * Flag register for buffer features. * @see #CX_BUFFER_DEFAULT * @see #CX_BUFFER_FREE_CONTENTS @@ -199,6 +225,26 @@ ); /** + * Configures the buffer for flushing. + * + * Flushing can happen automatically when data is written + * to the buffer (see cxBufferWrite()) or manually when + * cxBufferFlush() is called. + * + * @param buffer the buffer + * @param config the flush configuration + * @retval zero success + * @retval non-zero failure + * @see cxBufferFlush() + * @see cxBufferWrite() + */ +cx_attr_nonnull +int cxBufferEnableFlushing( + CxBuffer *buffer, + CxBufferFlushConfig config +); + +/** * Destroys the buffer contents. * * Has no effect if the #CX_BUFFER_FREE_CONTENTS feature is not enabled. @@ -419,6 +465,10 @@ /** * Writes data to a CxBuffer. * + * If automatic flushing is not enabled, the data is simply written into the + * buffer at the current position and the position of the buffer is increased + * by the number of bytes written. + * * If flushing is enabled and the buffer needs to flush, the data is flushed to * 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 @@ -426,16 +476,11 @@ * 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. - * The number returned by this function is the total number of elements that - * could be written during the process. It does not necessarily mean that those - * elements are still in this buffer, because some of them could have also been - * flushed already. - * - * If automatic flushing is not enabled, the data is simply written into the - * buffer at the current position and the position of the buffer is increased - * by the number of bytes written. - * Use cxBufferAppend() if you want to add data to this buffer regardless of - * the position. + * 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. * * @note The signature is compatible with the fwrite() family of functions. * @@ -483,6 +528,62 @@ ); /** + * Performs a single flush-run on the specified buffer. + * + * Does nothing when the position in the buffer is zero. + * Otherwise, the data until the current position minus + * one is considered for flushing. + * Note carefully that flushing will never exceed the + * current @em position, even when the size of the + * buffer is larger than the current position. + * + * One flush run will try to flush @c blkmax many + * blocks of size @c blksize until either the @p buffer + * has no more data to flush or the write function + * used for flushing returns zero. + * + * The buffer is shifted left for that many bytes + * the flush operation has successfully flushed. + * + * @par Example 1 + * Assume you have a buffer with size 340 and you are + * at position 200. The flush configuration is + * @c blkmax=4 and @c blksize=64 . + * Assume that the entire flush operation is successful. + * All 200 bytes on the left hand-side from the current + * position are written. + * That means, the size of the buffer is now 140 and the + * position is zero. + * + * @par Example 2 + * Same as Example 1, but now the @c blkmax is 1. + * The size of the buffer is now 276 and the position is 136. + * + * @par Example 3 + * Same as Example 1, but now assume the flush target + * only accepts 100 bytes before returning zero. + * That means, the flush operations manages to flush + * one complete block and one partial block, ending + * up with a buffer with size 240 and position 100. + * + * @remark Just returns zero when flushing was not enabled with + * cxBufferEnableFlushing(). + * + * @remark When the buffer uses copy-on-write, the memory + * is copied first, before attempting any flush. + * This is, however, considered an erroneous use of the + * buffer, because it does not make much sense to put + * readonly data into an UCX buffer for flushing, instead + * of writing it directly to the target. + * + * @param buffer the buffer + * @return the number of successfully flushed bytes + * @see cxBufferEnableFlushing() + */ +cx_attr_nonnull +size_t cxBufferFlush(CxBuffer *buffer); + +/** * Reads data from a CxBuffer. * * The position of the buffer is increased by the number of bytes read.