diff -r eb48f716b31c -r e10457d74fe1 src/ucx/cx/allocator.h
--- a/src/ucx/cx/allocator.h	Mon Feb 10 17:44:51 2025 +0100
+++ b/src/ucx/cx/allocator.h	Sun Mar 02 18:10:52 2025 +0100
@@ -26,7 +26,7 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 /**
- * \file allocator.h
+ * @file allocator.h
  * Interface for custom allocators.
  */
 
@@ -58,16 +58,15 @@
             void *data,
             void *mem,
             size_t n
-    )
-    __attribute__((__warn_unused_result__));
+    );
 
     /**
      * The allocator's calloc() implementation.
      */
     void *(*calloc)(
             void *data,
-            size_t nelem,
-            size_t n
+            size_t nmemb,
+            size_t size
     );
 
     /**
@@ -76,8 +75,7 @@
     void (*free)(
             void *data,
             void *mem
-    )
-    __attribute__((__nonnull__));
+    );
 } cx_allocator_class;
 
 /**
@@ -102,27 +100,28 @@
 /**
  * A default allocator using standard library malloc() etc.
  */
-extern CxAllocator *cxDefaultAllocator;
+cx_attr_export
+extern const CxAllocator * const cxDefaultAllocator;
 
 /**
  * Function pointer type for destructor functions.
  *
  * A destructor function deallocates possible contents and MAY free the memory
- * pointed to by \p memory. Read the documentation of the respective function
- * pointer to learn if a destructor SHALL, MAY, or MUST NOT free the memory in that
- * particular implementation.
+ * pointed to by @p memory. Read the documentation of the respective function
+ * pointer to learn if a destructor SHALL, MAY, or MUST NOT free the memory in
+ * that particular implementation.
  *
  * @param memory a pointer to the object to destruct
   */
-typedef void (*cx_destructor_func)(void *memory) __attribute__((__nonnull__));
+typedef void (*cx_destructor_func)(void *memory);
 
 /**
  * Function pointer type for destructor functions.
  *
  * A destructor function deallocates possible contents and MAY free the memory
- * pointed to by \p memory. Read the documentation of the respective function
- * pointer to learn if a destructor SHALL, MAY, or MUST NOT free the memory in that
- * particular implementation.
+ * pointed to by @p memory. Read the documentation of the respective function
+ * pointer to learn if a destructor SHALL, MAY, or MUST NOT free the memory in
+ * that particular implementation.
  *
  * @param data an optional pointer to custom data
  * @param memory a pointer to the object to destruct
@@ -130,108 +129,290 @@
 typedef void (*cx_destructor_func2)(
         void *data,
         void *memory
-) __attribute__((__nonnull__(2)));
+);
 
 /**
- * Re-allocate a previously allocated block and changes the pointer in-place, if necessary.
+ * Reallocate a previously allocated block and changes the pointer in-place,
+ * if necessary.
  *
- * \par Error handling
- * \c errno will be set by realloc() on failure.
+ * @par Error handling
+ * @c errno will be set by realloc() on failure.
  *
  * @param mem pointer to the pointer to allocated block
  * @param n the new size in bytes
- * @return zero on success, non-zero on failure
+ * @retval zero success
+ * @retval non-zero failure
+ * @see cx_reallocatearray()
  */
-int cx_reallocate(
+cx_attr_nonnull
+cx_attr_nodiscard
+cx_attr_export
+int cx_reallocate_(
         void **mem,
         size_t n
-)
-__attribute__((__nonnull__));
+);
+
+/**
+ * Reallocate a previously allocated block and changes the pointer in-place,
+ * if necessary.
+ *
+ * The size is calculated by multiplying @p nemb and @p size.
+ *
+ * @par Error handling
+ * @c errno will be set by realloc() on failure or when the multiplication of
+ * @p nmemb and @p size overflows.
+ *
+ * @param mem pointer to the pointer to allocated block
+ * @param nmemb the number of elements
+ * @param size the size of each element
+ * @retval zero success
+ * @retval non-zero failure
+ * @see cx_reallocate()
+ */
+cx_attr_nonnull
+cx_attr_nodiscard
+cx_attr_export
+int cx_reallocatearray_(
+        void **mem,
+        size_t nmemb,
+        size_t size
+);
 
 /**
- * Allocate \p n bytes of memory.
+ * Reallocate a previously allocated block and changes the pointer in-place,
+ * if necessary.
+ *
+ * @par Error handling
+ * @c errno will be set by realloc() on failure.
+ *
+ * @param mem (@c void**) pointer to the pointer to allocated block
+ * @param n (@c size_t) the new size in bytes
+ * @retval zero success
+ * @retval non-zero failure
+ * @see cx_reallocatearray()
+ */
+#define cx_reallocate(mem, n) cx_reallocate_((void**)(mem), n)
+
+/**
+ * Reallocate a previously allocated block and changes the pointer in-place,
+ * if necessary.
+ *
+ * The size is calculated by multiplying @p nemb and @p size.
+ *
+ * @par Error handling
+ * @c errno will be set by realloc() on failure or when the multiplication of
+ * @p nmemb and @p size overflows.
+ *
+ * @param mem (@c void**) pointer to the pointer to allocated block
+ * @param nmemb (@c size_t) the number of elements
+ * @param size (@c size_t) the size of each element
+ * @retval zero success
+ * @retval non-zero failure
+ */
+#define cx_reallocatearray(mem, nmemb, size) \
+    cx_reallocatearray_((void**)(mem), nmemb, size)
+
+/**
+ * Free a block allocated by this allocator.
+ *
+ * @note Freeing a block of a different allocator is undefined.
+ *
+ * @param allocator the allocator
+ * @param mem a pointer to the block to free
+ */
+cx_attr_nonnull_arg(1)
+cx_attr_export
+void cxFree(
+        const CxAllocator *allocator,
+        void *mem
+);
+
+/**
+ * Allocate @p n bytes of memory.
  *
  * @param allocator the allocator
  * @param n the number of bytes
  * @return a pointer to the allocated memory
  */
+cx_attr_nodiscard
+cx_attr_nonnull
+cx_attr_malloc
+cx_attr_dealloc_ucx
+cx_attr_allocsize(2)
+cx_attr_export
 void *cxMalloc(
-        CxAllocator const *allocator,
+        const CxAllocator *allocator,
         size_t n
-)
-__attribute__((__malloc__))
-__attribute__((__alloc_size__(2)));
+);
 
 /**
- * Re-allocate the previously allocated block in \p mem, making the new block \p n bytes long.
- * This function may return the same pointer that was passed to it, if moving the memory
- * was not necessary.
+ * Reallocate the previously allocated block in @p mem, making the new block
+ * @p n bytes long.
+ * This function may return the same pointer that was passed to it, if moving
+ * the memory was not necessary.
  *
- * \note Re-allocating a block allocated by a different allocator is undefined.
+ * @note Re-allocating a block allocated by a different allocator is undefined.
  *
  * @param allocator the allocator
  * @param mem pointer to the previously allocated block
  * @param n the new size in bytes
- * @return a pointer to the re-allocated memory
+ * @return a pointer to the reallocated memory
  */
+cx_attr_nodiscard
+cx_attr_nonnull_arg(1)
+cx_attr_dealloc_ucx
+cx_attr_allocsize(3)
+cx_attr_export
 void *cxRealloc(
-        CxAllocator const *allocator,
+        const CxAllocator *allocator,
         void *mem,
         size_t n
-)
-__attribute__((__warn_unused_result__))
-__attribute__((__alloc_size__(3)));
+);
 
 /**
- * Re-allocate a previously allocated block and changes the pointer in-place, if necessary.
- * This function acts like cxRealloc() using the pointer pointed to by \p mem.
+ * Reallocate the previously allocated block in @p mem, making the new block
+ * @p n bytes long.
+ * This function may return the same pointer that was passed to it, if moving
+ * the memory was not necessary.
+ *
+ * The size is calculated by multiplying @p nemb and @p size.
+ * If that multiplication overflows, this function returns @c NULL and @c errno
+ * will be set.
+ *
+ * @note Re-allocating a block allocated by a different allocator is undefined.
  *
- * \note Re-allocating a block allocated by a different allocator is undefined.
+ * @param allocator the allocator
+ * @param mem pointer to the previously allocated block
+ * @param nmemb the number of elements
+ * @param size the size of each element
+ * @return a pointer to the reallocated memory
+ */
+cx_attr_nodiscard
+cx_attr_nonnull_arg(1)
+cx_attr_dealloc_ucx
+cx_attr_allocsize(3, 4)
+cx_attr_export
+void *cxReallocArray(
+        const CxAllocator *allocator,
+        void *mem,
+        size_t nmemb,
+        size_t size
+);
+
+/**
+ * Reallocate a previously allocated block and changes the pointer in-place,
+ * if necessary.
+ * This function acts like cxRealloc() using the pointer pointed to by @p mem.
  *
- * \par Error handling
- * \c errno will be set, if the underlying realloc function does so.
+ * @note Re-allocating a block allocated by a different allocator is undefined.
+ *
+ * @par Error handling
+ * @c errno will be set, if the underlying realloc function does so.
  *
  * @param allocator the allocator
  * @param mem pointer to the pointer to allocated block
  * @param n the new size in bytes
- * @return zero on success, non-zero on failure
+ * @retval zero success
+ * @retval non-zero failure
  */
-int cxReallocate(
-        CxAllocator const *allocator,
+cx_attr_nodiscard
+cx_attr_nonnull
+cx_attr_export
+int cxReallocate_(
+        const CxAllocator *allocator,
         void **mem,
         size_t n
-)
-__attribute__((__nonnull__));
+);
+
+/**
+ * Reallocate a previously allocated block and changes the pointer in-place,
+ * if necessary.
+ * This function acts like cxRealloc() using the pointer pointed to by @p mem.
+ *
+ * @note Re-allocating a block allocated by a different allocator is undefined.
+ *
+ * @par Error handling
+ * @c errno will be set, if the underlying realloc function does so.
+ *
+ * @param allocator (@c CxAllocator*) the allocator
+ * @param mem (@c void**) pointer to the pointer to allocated block
+ * @param n (@c size_t) the new size in bytes
+ * @retval zero success
+ * @retval non-zero failure
+ */
+#define cxReallocate(allocator, mem, n) \
+    cxReallocate_(allocator, (void**)(mem), n)
 
 /**
- * Allocate \p nelem elements of \p n bytes each, all initialized to zero.
+ * Reallocate a previously allocated block and changes the pointer in-place,
+ * if necessary.
+ * This function acts like cxReallocArray() using the pointer pointed to
+ * by @p mem.
+ *
+ * @note Re-allocating a block allocated by a different allocator is undefined.
+ *
+ * @par Error handling
+ * @c errno will be set, if the underlying realloc function does so or the
+ * multiplication of @p nmemb and @p size overflows.
  *
  * @param allocator the allocator
- * @param nelem the number of elements
- * @param n the size of each element in bytes
+ * @param mem pointer to the pointer to allocated block
+ * @param nmemb the number of elements
+ * @param size the size of each element
+ * @retval zero success
+ * @retval non-zero on failure
+ */
+cx_attr_nodiscard
+cx_attr_nonnull
+cx_attr_export
+int cxReallocateArray_(
+        const CxAllocator *allocator,
+        void **mem,
+        size_t nmemb,
+        size_t size
+);
+
+/**
+ * Reallocate a previously allocated block and changes the pointer in-place,
+ * if necessary.
+ * This function acts like cxReallocArray() using the pointer pointed to
+ * by @p mem.
+ *
+ * @note Re-allocating a block allocated by a different allocator is undefined.
+ *
+ * @par Error handling
+ * @c errno will be set, if the underlying realloc function does so or the
+ * multiplication of @p nmemb and @p size overflows.
+ *
+ * @param allocator (@c CxAllocator*) the allocator
+ * @param mem (@c void**) pointer to the pointer to allocated block
+ * @param nmemb (@c size_t) the number of elements
+ * @param size (@c size_t) the size of each element
+ * @retval zero success
+ * @retval non-zero failure
+ */
+#define cxReallocateArray(allocator, mem, nmemb, size) \
+        cxReallocateArray_(allocator, (void**) (mem), nmemb, size)
+
+/**
+ * Allocate @p nmemb elements of @p n bytes each, all initialized to zero.
+ *
+ * @param allocator the allocator
+ * @param nmemb the number of elements
+ * @param size the size of each element in bytes
  * @return a pointer to the allocated memory
  */
+cx_attr_nonnull_arg(1)
+cx_attr_nodiscard
+cx_attr_malloc
+cx_attr_dealloc_ucx
+cx_attr_allocsize(2, 3)
+cx_attr_export
 void *cxCalloc(
-        CxAllocator const *allocator,
-        size_t nelem,
-        size_t n
-)
-__attribute__((__malloc__))
-__attribute__((__alloc_size__(2, 3)));
-
-/**
- * Free a block allocated by this allocator.
- *
- * \note Freeing a block of a different allocator is undefined.
- *
- * @param allocator the allocator
- * @param mem a pointer to the block to free
- */
-void cxFree(
-        CxAllocator const *allocator,
-        void *mem
-)
-__attribute__((__nonnull__));
+        const CxAllocator *allocator,
+        size_t nmemb,
+        size_t size
+);
 
 #ifdef __cplusplus
 } // extern "C"