ucx/cx/buffer.h

changeset 11
0aa8cbd7912e
parent 0
1a157da63d7c
child 16
04c9f8d8f03b
--- a/ucx/cx/buffer.h	Fri Jan 03 21:40:57 2025 +0100
+++ b/ucx/cx/buffer.h	Sat Jan 04 13:03:01 2025 +0100
@@ -49,7 +49,7 @@
 #include "common.h"
 #include "allocator.h"
 
-#ifdef    __cplusplus
+#ifdef __cplusplus
 extern "C" {
 #endif
 
@@ -60,14 +60,34 @@
 
 /**
  * If this flag is enabled, the buffer will automatically free its contents when destroyed.
+ *
+ * Do NOT set this flag together with #CX_BUFFER_COPY_ON_WRITE. It will be automatically
+ * set when the copy-on-write operations is performed.
  */
 #define CX_BUFFER_FREE_CONTENTS 0x01
 
 /**
- * If this flag is enabled, the buffer will automatically extends its capacity.
+ * If this flag is enabled, the buffer will automatically extend its capacity.
  */
 #define CX_BUFFER_AUTO_EXTEND 0x02
 
+/**
+ * If this flag is enabled, the buffer will allocate new memory when written to.
+ *
+ * The current contents of the buffer will be copied to the new memory and the flag
+ * will be cleared while the #CX_BUFFER_FREE_CONTENTS flag will be set automatically.
+ */
+#define CX_BUFFER_COPY_ON_WRITE 0x04
+
+/**
+ * If this flag is enabled, the buffer will copy its contents to a new memory area on reallocation.
+ *
+ * After performing the copy, the flag is automatically cleared.
+ * This flag has no effect on buffers which do not have #CX_BUFFER_AUTO_EXTEND set, which is why
+ * buffers automatically admit the auto-extend flag when initialized with copy-on-extend enabled.
+ */
+#define CX_BUFFER_COPY_ON_EXTEND 0x08
+
 /** Structure for the UCX buffer data. */
 typedef struct {
     /** A pointer to the buffer contents. */
@@ -82,7 +102,7 @@
         unsigned char *bytes;
     };
     /** The allocator to use for automatic memory management. */
-    CxAllocator const *allocator;
+    const CxAllocator *allocator;
     /** Current position of the buffer. */
     size_t pos;
     /** Current capacity (i.e. maximum size) of the buffer. */
@@ -128,6 +148,7 @@
      * @see #CX_BUFFER_DEFAULT
      * @see #CX_BUFFER_FREE_CONTENTS
      * @see #CX_BUFFER_AUTO_EXTEND
+     * @see #CX_BUFFER_COPY_ON_WRITE
      */
     int flags;
 } cx_buffer_s;
@@ -140,47 +161,40 @@
 /**
  * Initializes a fresh buffer.
  *
+ * You may also provide a read-only \p space, in which case
+ * you will need to cast the pointer, and you should set the
+ * #CX_BUFFER_COPY_ON_WRITE flag.
+ *
+ * You need to set the size manually after initialization, if
+ * you provide \p space which already contains data.
+ *
+ * When you specify stack memory as \p space and decide to use
+ * the auto-extension feature, you \em must use the
+ * #CX_BUFFER_COPY_ON_EXTEND flag, instead of the
+ * #CX_BUFFER_AUTO_EXTEND flag.
+ *
  * \note You may provide \c NULL as argument for \p space.
  * Then this function will allocate the space and enforce
- * the #CX_BUFFER_FREE_CONTENTS flag.
+ * the #CX_BUFFER_FREE_CONTENTS flag. In that case, specifying
+ * copy-on-write should be avoided, because the allocated
+ * space will be leaking after the copy-on-write operation.
  *
  * @param buffer the buffer to initialize
  * @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 default heap allocator will be used.
+ * memory management
+ * (if \c NULL, a default stdlib allocator will be used)
  * @param flags buffer features (see cx_buffer_s.flags)
  * @return zero on success, non-zero if a required allocation failed
  */
-__attribute__((__nonnull__(1)))
+cx_attr_nonnull_arg(1)
 int cxBufferInit(
         CxBuffer *buffer,
         void *space,
         size_t capacity,
-        CxAllocator const *allocator,
-        int flags
-);
-
-/**
- * Allocates and initializes a fresh buffer.
- *
- * \note You may provide \c NULL as argument for \p space.
- * 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 default heap allocator will be used.
- * @param flags buffer features (see cx_buffer_s.flags)
- * @return a pointer to the buffer on success, \c NULL if a required allocation failed
- */
-CxBuffer *cxBufferCreate(
-        void *space,
-        size_t capacity,
-        CxAllocator const *allocator,
+        const CxAllocator *allocator,
         int flags
 );
 
@@ -193,7 +207,7 @@
  * @param buffer the buffer which contents shall be destroyed
  * @see cxBufferInit()
  */
-__attribute__((__nonnull__))
+cx_attr_nonnull
 void cxBufferDestroy(CxBuffer *buffer);
 
 /**
@@ -202,13 +216,49 @@
  * If the #CX_BUFFER_FREE_CONTENTS feature is enabled, this function also destroys
  * the contents. If you \em only want to destroy the contents, use cxBufferDestroy().
  *
+ * \remark As with all free() functions, this accepts \c NULL arguments in which
+ * case it does nothing.
+ *
  * @param buffer the buffer to deallocate
  * @see cxBufferCreate()
  */
-__attribute__((__nonnull__))
 void cxBufferFree(CxBuffer *buffer);
 
 /**
+ * Allocates and initializes a fresh buffer.
+ *
+ * You may also provide a read-only \p space, in which case
+ * you will need to cast the pointer, and you should set the
+ * #CX_BUFFER_COPY_ON_WRITE flag.
+ * When you specify stack memory as \p space and decide to use
+ * the auto-extension feature, you \em must use the
+ * #CX_BUFFER_COPY_ON_EXTEND flag, instead of the
+ * #CX_BUFFER_AUTO_EXTEND flag.
+ *
+ * \note You may provide \c NULL as argument for \p space.
+ * 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, a default stdlib allocator will be used)
+ * @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
+CxBuffer *cxBufferCreate(
+        void *space,
+        size_t capacity,
+        const CxAllocator *allocator,
+        int flags
+);
+
+/**
  * Shifts the contents of the buffer by the given offset.
  *
  * If the offset is positive, the contents are shifted to the right.
@@ -239,9 +289,9 @@
  *
  * @param buffer the buffer
  * @param shift the shift offset (negative means left shift)
- * @return 0 on success, non-zero if a required auto-extension fails
+ * @return 0 on success, non-zero if a required auto-extension or copy-on-write fails
  */
-__attribute__((__nonnull__))
+cx_attr_nonnull
 int cxBufferShift(
         CxBuffer *buffer,
         off_t shift
@@ -253,10 +303,10 @@
  *
  * @param buffer the buffer
  * @param shift the shift offset
- * @return 0 on success, non-zero if a required auto-extension fails
+ * @return 0 on success, non-zero if a required auto-extension or copy-on-write fails
  * @see cxBufferShift()
  */
-__attribute__((__nonnull__))
+cx_attr_nonnull
 int cxBufferShiftRight(
         CxBuffer *buffer,
         size_t shift
@@ -266,15 +316,12 @@
  * Shifts the buffer to the left.
  * See cxBufferShift() for details.
  *
- * \note Since a left shift cannot fail due to memory allocation problems, this
- * function always returns zero.
- *
  * @param buffer the buffer
  * @param shift the positive shift offset
- * @return always zero
+ * @return usually zero, except the buffer uses copy-on-write and the allocation fails
  * @see cxBufferShift()
  */
-__attribute__((__nonnull__))
+cx_attr_nonnull
 int cxBufferShiftLeft(
         CxBuffer *buffer,
         size_t shift
@@ -300,7 +347,7 @@
  * @return 0 on success, non-zero if the position is invalid
  *
  */
-__attribute__((__nonnull__))
+cx_attr_nonnull
 int cxBufferSeek(
         CxBuffer *buffer,
         off_t offset,
@@ -313,10 +360,13 @@
  * The data is deleted by zeroing it with a call to memset().
  * If you do not need that, you can use the faster cxBufferReset().
  *
+ * \note If the #CX_BUFFER_COPY_ON_WRITE flag is set, this function
+ * will not erase the data and behave exactly as cxBufferReset().
+ *
  * @param buffer the buffer to be cleared
  * @see cxBufferReset()
  */
-__attribute__((__nonnull__))
+cx_attr_nonnull
 void cxBufferClear(CxBuffer *buffer);
 
 /**
@@ -328,18 +378,19 @@
  * @param buffer the buffer to be cleared
  * @see cxBufferClear()
  */
-__attribute__((__nonnull__))
+cx_attr_nonnull
 void cxBufferReset(CxBuffer *buffer);
 
 /**
  * Tests, if the buffer position has exceeded the buffer size.
  *
  * @param buffer the buffer to test
- * @return non-zero, if the current buffer position has exceeded the last
+ * @return true, if the current buffer position has exceeded the last
  * byte of the buffer's contents.
  */
-__attribute__((__nonnull__))
-int cxBufferEof(CxBuffer const *buffer);
+cx_attr_nonnull
+cx_attr_nodiscard
+bool cxBufferEof(const CxBuffer *buffer);
 
 
 /**
@@ -351,7 +402,7 @@
  * @param capacity the minimum required capacity for this buffer
  * @return 0 on success or a non-zero value on failure
  */
-__attribute__((__nonnull__))
+cx_attr_nonnull
 int cxBufferMinimumCapacity(
         CxBuffer *buffer,
         size_t capacity
@@ -383,9 +434,35 @@
  * @param buffer the CxBuffer to write to
  * @return the total count of elements written
  */
-__attribute__((__nonnull__))
+cx_attr_nonnull
 size_t cxBufferWrite(
-        void const *ptr,
+        const void *ptr,
+        size_t size,
+        size_t nitems,
+        CxBuffer *buffer
+);
+
+/**
+ * Appends data to a CxBuffer.
+ *
+ * The data is always appended to current data within the buffer,
+ * regardless of the current position.
+ * This is especially useful when the buffer is primarily meant for reading
+ * while additional data is added to the buffer occasionally.
+ * Consequently, the position of the buffer is unchanged after this operation.
+ *
+ * \note The signature is compatible with the fwrite() family of functions.
+ *
+ * @param ptr a pointer to the memory area containing the bytes to be written
+ * @param size the length of one element
+ * @param nitems the element count
+ * @param buffer the CxBuffer to write to
+ * @return the total count of elements written
+ * @see cxBufferWrite()
+ */
+cx_attr_nonnull
+size_t cxBufferAppend(
+        const void *ptr,
         size_t size,
         size_t nitems,
         CxBuffer *buffer
@@ -404,7 +481,7 @@
  * @param buffer the CxBuffer to read from
  * @return the total number of elements read
  */
-__attribute__((__nonnull__))
+cx_attr_nonnull
 size_t cxBufferRead(
         void *ptr,
         size_t size,
@@ -417,30 +494,46 @@
  *
  * 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 buffer extension fails, \c EOF is returned.
+ * the buffer capacity is extended by cxBufferMinimumCapacity(). If the feature
+ * is disabled or buffer extension fails, \c EOF is returned.
  *
  * On successful write, the position of the buffer is increased.
  *
  * @param buffer the buffer to write to
  * @param c the character to write
- * @return the byte that has bean written or \c EOF when the end of the stream is
+ * @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
  */
-__attribute__((__nonnull__))
+cx_attr_nonnull
 int cxBufferPut(
         CxBuffer *buffer,
         int c
 );
 
 /**
+ * Writes a terminating zero to a buffer.
+ *
+ * On successful write, \em neither the position \em nor the size of the buffer is
+ * increased.
+ *
+ * The purpose of this function is to have the written data ready to be used as
+ * a C string.
+ *
+ * @param buffer the buffer to write to
+ * @return zero, if the terminator could be written, non-zero otherwise
+ */
+cx_attr_nonnull
+int cxBufferTerminate(CxBuffer *buffer);
+
+/**
  * Writes a string to a buffer.
  *
  * @param buffer the buffer
  * @param str the zero-terminated string
  * @return the number of bytes written
  */
-__attribute__((__nonnull__))
+cx_attr_nonnull
+cx_attr_cstr_arg(2)
 size_t cxBufferPutString(
         CxBuffer *buffer,
         const char *str
@@ -454,7 +547,7 @@
  * @param buffer the buffer to read from
  * @return the character or \c EOF, if the end of the buffer is reached
  */
-__attribute__((__nonnull__))
+cx_attr_nonnull
 int cxBufferGet(CxBuffer *buffer);
 
 #ifdef __cplusplus

mercurial