diff -r 07b815faa6ac -r f00d03835dd9 src/ucx/cx/buffer.h --- a/src/ucx/cx/buffer.h Tue Dec 30 21:44:49 2025 +0100 +++ b/src/ucx/cx/buffer.h Tue Jan 13 18:09:20 2026 +0100 @@ -48,10 +48,7 @@ #include "common.h" #include "allocator.h" - -#ifdef __cplusplus -extern "C" { -#endif +#include "string.h" /** * No buffer features enabled (all flags cleared). @@ -89,6 +86,13 @@ #define CX_BUFFER_COPY_ON_EXTEND 0x08 /** + * If this flag is enabled, the buffer will never free its contents regardless of #CX_BUFFER_FREE_CONTENTS. + * + * This is useful, for example, when you want to keep a pointer to the data after destroying the buffer. + */ +#define CX_BUFFER_DO_NOT_FREE 0x10 + +/** * Function pointer for cxBufferWrite that is compatible with cx_write_func. * @see cx_write_func */ @@ -99,59 +103,6 @@ */ #define cxBufferReadFunc ((cx_read_func) cxBufferRead) -/** - * 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 the write function. - */ - void *target; - - /** - * The write-function used for flushing. - * If NULL, the flushed content gets discarded. - */ - cx_write_func wfunc; -}; - -/** - * Type alias 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 { @@ -168,16 +119,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. */ size_t capacity; + /** Maximum capacity that this buffer may grow to. */ + size_t max_capacity; /** Current size of the buffer content. */ size_t size; /** @@ -217,35 +164,18 @@ * space will be leaking after the copy-on-write operation. * * @param buffer the buffer to initialize + * @param allocator the allocator this buffer shall use for automatic + * memory management + * (if @c NULL, the cxDefaultAllocator will be used) * @param space pointer to the memory area, or @c NULL to allocate * new memory * @param capacity the capacity of the buffer - * @param allocator the allocator this buffer shall use for automatic - * memory management - * (if @c NULL, the cxDefaultAllocator will be used) * @param flags buffer features (see cx_buffer_s.flags) * @return zero on success, non-zero if a required allocation failed */ -cx_attr_nonnull_arg(1) -CX_EXPORT int cxBufferInit(CxBuffer *buffer, void *space, size_t capacity, - const CxAllocator *allocator, int flags); - -/** - * 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 -CX_EXPORT int cxBufferEnableFlushing(CxBuffer *buffer, CxBufferFlushConfig config); +CX_EXTERN CX_NONNULL_ARG(1) +int cxBufferInit(CxBuffer *buffer, const CxAllocator *allocator, + void *space, size_t capacity, int flags); /** * Destroys the buffer contents. @@ -256,8 +186,8 @@ * @param buffer the buffer which contents shall be destroyed * @see cxBufferInit() */ -cx_attr_nonnull -CX_EXPORT void cxBufferDestroy(CxBuffer *buffer); +CX_EXTERN CX_NONNULL +void cxBufferDestroy(CxBuffer *buffer); /** * Deallocates the buffer. @@ -268,7 +198,8 @@ * @param buffer the buffer to deallocate * @see cxBufferCreate() */ -CX_EXPORT void cxBufferFree(CxBuffer *buffer); +CX_EXTERN +void cxBufferFree(CxBuffer *buffer); /** * Allocates and initializes a fresh buffer. @@ -285,18 +216,18 @@ * Then this function will allocate the space and enforce * the #CX_BUFFER_FREE_CONTENTS flag. * - * @param space pointer to the memory area, or @c NULL to allocate - * new memory - * @param capacity the capacity of the buffer * @param allocator the allocator to use for allocating the structure and the automatic * memory management within the buffer * (if @c NULL, the cxDefaultAllocator will be used) + * @param space pointer to the memory area, or @c NULL to allocate + * new memory + * @param capacity the capacity of the buffer * @param flags buffer features (see cx_buffer_s.flags) * @return a pointer to the buffer on success, @c NULL if a required allocation failed */ -cx_attr_malloc cx_attr_dealloc(cxBufferFree, 1) cx_attr_nodiscard -CX_EXPORT CxBuffer *cxBufferCreate(void *space, size_t capacity, - const CxAllocator *allocator, int flags); +CX_EXTERN CX_MALLOC CX_DEALLOC(cxBufferFree, 1) CX_NODISCARD +CxBuffer *cxBufferCreate(const CxAllocator *allocator, void *space, + size_t capacity, int flags); /** * Shifts the contents of the buffer by the given offset. @@ -334,8 +265,8 @@ * @see cxBufferShiftLeft() * @see cxBufferShiftRight() */ -cx_attr_nonnull -CX_EXPORT int cxBufferShift(CxBuffer *buffer, off_t shift); +CX_EXTERN CX_NONNULL +int cxBufferShift(CxBuffer *buffer, off_t shift); /** * Shifts the buffer to the right. @@ -347,8 +278,8 @@ * @retval non-zero if a required auto-extension or copy-on-write fails * @see cxBufferShift() */ -cx_attr_nonnull -CX_EXPORT int cxBufferShiftRight(CxBuffer *buffer, size_t shift); +CX_EXTERN CX_NONNULL +int cxBufferShiftRight(CxBuffer *buffer, size_t shift); /** * Shifts the buffer to the left. @@ -360,8 +291,8 @@ * @retval non-zero if the buffer uses copy-on-write and the allocation fails * @see cxBufferShift() */ -cx_attr_nonnull -CX_EXPORT int cxBufferShiftLeft(CxBuffer *buffer, size_t shift); +CX_EXTERN CX_NONNULL +int cxBufferShiftLeft(CxBuffer *buffer, size_t shift); /** @@ -384,8 +315,8 @@ * @retval non-zero if the position is invalid * */ -cx_attr_nonnull -CX_EXPORT int cxBufferSeek(CxBuffer *buffer, off_t offset, int whence); +CX_EXTERN CX_NONNULL +int cxBufferSeek(CxBuffer *buffer, off_t offset, int whence); /** * Discards items from the end of the buffer. @@ -398,8 +329,8 @@ * @param nitems the number of items to discard * @return the actual number of discarded items */ -cx_attr_nonnull -CX_EXPORT size_t cxBufferPop(CxBuffer *buffer, size_t size, size_t nitems); +CX_EXTERN CX_NONNULL +size_t cxBufferPop(CxBuffer *buffer, size_t size, size_t nitems); /** * Clears the buffer by resetting the position and deleting the data. @@ -413,8 +344,8 @@ * @param buffer the buffer to be cleared * @see cxBufferReset() */ -cx_attr_nonnull -CX_EXPORT void cxBufferClear(CxBuffer *buffer); +CX_EXTERN CX_NONNULL +void cxBufferClear(CxBuffer *buffer); /** * Resets the buffer by resetting the position and size to zero. @@ -425,8 +356,8 @@ * @param buffer the buffer to be cleared * @see cxBufferClear() */ -cx_attr_nonnull -CX_EXPORT void cxBufferReset(CxBuffer *buffer); +CX_EXTERN CX_NONNULL +void cxBufferReset(CxBuffer *buffer); /** * Tests, if the buffer position has exceeded the buffer size. @@ -436,13 +367,15 @@ * byte of the buffer's contents * @retval false otherwise */ -cx_attr_nonnull cx_attr_nodiscard -CX_EXPORT bool cxBufferEof(const CxBuffer *buffer); +CX_EXTERN CX_NONNULL CX_NODISCARD +bool cxBufferEof(const CxBuffer *buffer); /** * Ensures that the buffer has the required capacity. * * If the current capacity is not sufficient, the buffer will be extended. + * If the current capacity is larger, the buffer is shrunk and superfluous + * content is discarded. * * This function will reserve no more bytes than requested, in contrast to * cxBufferMinimumCapacity(), which may reserve more bytes to improve the @@ -450,13 +383,32 @@ * * @param buffer the buffer * @param capacity the required capacity for this buffer - * @retval zero the capacity was already sufficient or successfully increased + * @retval zero on success * @retval non-zero on allocation failure * @see cxBufferShrink() * @see cxBufferMinimumCapacity() */ -cx_attr_nonnull -CX_EXPORT int cxBufferReserve(CxBuffer *buffer, size_t capacity); +CX_EXTERN CX_NONNULL +int cxBufferReserve(CxBuffer *buffer, size_t capacity); + +/** + * Limits the buffer's capacity. + * + * If the current capacity is already larger, this function fails and returns + * non-zero. + * + * The capacity limit will affect auto-extension features, as well as future + * calls to cxBufferMinimumCapacity() and cxBufferReserve(). + * + * @param buffer the buffer + * @param capacity the maximum allowed capacity for this buffer + * @retval zero the limit is applied + * @retval non-zero the new limit is smaller than the current capacity + * @see cxBufferReserve() + * @see cxBufferMinimumCapacity() + */ +CX_EXTERN CX_NONNULL +int cxBufferMaximumCapacity(CxBuffer *buffer, size_t capacity); /** * Ensures that the buffer has a minimum capacity. @@ -471,11 +423,12 @@ * @param capacity the minimum required capacity for this buffer * @retval zero the capacity was already sufficient or successfully increased * @retval non-zero on allocation failure + * @see cxBufferMaximumCapacity() * @see cxBufferReserve() * @see cxBufferShrink() */ -cx_attr_nonnull -CX_EXPORT int cxBufferMinimumCapacity(CxBuffer *buffer, size_t capacity); +CX_EXTERN CX_NONNULL +int cxBufferMinimumCapacity(CxBuffer *buffer, size_t capacity); /** * Shrinks the capacity of the buffer to fit its current size. @@ -494,39 +447,19 @@ * @see cxBufferReserve() * @see cxBufferMinimumCapacity() */ -cx_attr_nonnull -CX_EXPORT void cxBufferShrink(CxBuffer *buffer, size_t reserve); +CX_EXTERN CX_NONNULL +void cxBufferShrink(CxBuffer *buffer, size_t reserve); /** * 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 - * 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 - * buffer are both incapable of taking more data or all data has been written. + * If auto-extension is enabled, the buffer's capacity is automatically + * increased when it is not large enough to hold all data. + * By default, the capacity grows indefinitely, unless limited with + * cxBufferMaximumCapacity(). + * When auto-extension fails, this function writes no data and returns zero. * - * 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. - * That means it does @em not include the number of items that were already in - * the buffer and were also 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 misaligned part in the buffer. - * Afterward, this function only writes as many items as possible to the buffer. + * The position of the buffer is moved alongside the written data. * * @note The signature is compatible with the fwrite() family of functions. * @@ -538,8 +471,8 @@ * @see cxBufferAppend() * @see cxBufferRead() */ -cx_attr_nonnull -CX_EXPORT size_t cxBufferWrite(const void *ptr, size_t size, +CX_EXTERN CX_NONNULL +size_t cxBufferWrite(const void *ptr, size_t size, size_t nitems, CxBuffer *buffer); /** @@ -561,67 +494,11 @@ * @see cxBufferWrite() * @see cxBufferRead() */ -cx_attr_nonnull -CX_EXPORT size_t cxBufferAppend(const void *ptr, size_t size, +CX_EXTERN CX_NONNULL +size_t cxBufferAppend(const void *ptr, size_t size, size_t nitems, CxBuffer *buffer); /** - * 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 operation 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 makes little 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 -CX_EXPORT size_t cxBufferFlush(CxBuffer *buffer); - -/** * Reads data from a CxBuffer. * * The position of the buffer is increased by the number of bytes read. @@ -636,8 +513,8 @@ * @see cxBufferWrite() * @see cxBufferAppend() */ -cx_attr_nonnull -CX_EXPORT size_t cxBufferRead(void *ptr, size_t size, +CX_EXTERN CX_NONNULL +size_t cxBufferRead(void *ptr, size_t size, size_t nitems, CxBuffer *buffer); /** @@ -645,8 +522,9 @@ * * The least significant byte of the argument is written to the buffer. If the * end of the buffer is reached and #CX_BUFFER_AUTO_EXTEND feature is enabled, - * the buffer capacity is extended by cxBufferMinimumCapacity(). If the feature - * is disabled or the buffer extension fails, @c EOF is returned. + * the buffer capacity is extended, unless a limit set by + * cxBufferMaximumCapacity() is reached. + * If the feature is disabled or the buffer extension fails, @c EOF is returned. * * On successful writing, the position of the buffer is increased. * @@ -655,38 +533,71 @@ * * @param buffer the buffer to write to * @param c the character to write - * @return the byte that has been written or @c EOF when the end of the stream is - * reached, and automatic extension is not enabled or not possible + * @return the byte that has been written or @c EOF when the end of the + * stream is reached, and automatic extension is not enabled or not possible * @see cxBufferTerminate() */ -cx_attr_nonnull -CX_EXPORT int cxBufferPut(CxBuffer *buffer, int c); +CX_EXTERN CX_NONNULL +int cxBufferPut(CxBuffer *buffer, int c); /** * Writes a terminating zero to a buffer at the current position. * - * If successful, sets the size to the current position and advances the position by one. + * If successful, also sets the size to the current position and shrinks the buffer. * * The purpose of this function is to have the written data ready to be used as * a C string with the buffer's size being the length of that string. * * @param buffer the buffer to write to * @return zero, if the terminator could be written, non-zero otherwise + * @see cxBufferShrink() */ -cx_attr_nonnull -CX_EXPORT int cxBufferTerminate(CxBuffer *buffer); +CX_EXTERN CX_NONNULL +int cxBufferTerminate(CxBuffer *buffer); + +/** + * Internal function - do not use. + * + * @param buffer the buffer + * @param str the string + * @return the number of bytes written + * @see cxBufferPutString() + */ +CX_EXTERN CX_NONNULL +size_t cx_buffer_put_string(CxBuffer *buffer, cxstring str); /** - * Writes a string to a buffer. + * Writes a string to a buffer with cxBufferWrite(). * - * This is a convenience function for cxBufferWrite(str, 1, strlen(str), buffer). + * @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() + */ +#define cxBufferPutString(buffer, str) cx_buffer_put_string(buffer, cx_strcast(str)) + +/** + * 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_attr_cstr_arg(2) -CX_EXPORT size_t cxBufferPutString(CxBuffer *buffer, const char *str); +CX_EXTERN CX_NONNULL +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. @@ -696,11 +607,29 @@ * @param buffer the buffer to read from * @return the character or @c EOF, if the end of the buffer is reached */ -cx_attr_nonnull -CX_EXPORT int cxBufferGet(CxBuffer *buffer); +CX_EXTERN CX_NONNULL +int cxBufferGet(CxBuffer *buffer); -#ifdef __cplusplus +/** + * Gets the data in a buffer as a @c cxstring. + * + * @param buffer the buffer + * @return the data in the buffer interpreted as a @c cxstring + */ +CX_NONNULL CX_INLINE +cxstring cx_bstr(CxBuffer *buffer) { + return cx_strn(buffer->space, buffer->size); } -#endif + +/** + * Gets the data in a buffer as a @c cxmutstr. + * + * @param buffer the buffer + * @return the data in the buffer interpreted as a @c cxmutstr + */ +CX_NONNULL CX_INLINE +cxmutstr cx_bstr_m(CxBuffer *buffer) { + return cx_mutstrn(buffer->space, buffer->size); +} #endif // UCX_BUFFER_H