// Get available user settings
// The first entry should be the default, however it must be checked if
// it is valid for this environment
- CxMempool *mp = cxMempoolCreate(64, NULL);
+ CxMempool *mp = cxMempoolCreateSimple(64);
CxList *usersettings = note_store_get_user_settings(mp->allocator, host, user, NULL);
int settings_valid = 0;
if(usersettings && cxListSize(usersettings) > 0) {
*/
void note_store_reload() {
// load notebook/collection structure
- CxMempool *mp = cxMempoolCreate(64, NULL);
+ CxMempool *mp = cxMempoolCreateSimple(64);
const CxAllocator *a = mp->allocator;
DBUQuery *q = connection->createQuery(connection, NULL);
dbuQuerySetSQL(q, SQL_NOTEBOOK_STRUCTURE);
DavConfig* dav_config_new(xmlDoc *doc) {
- CxMempool *cfg_mp = cxMempoolCreate(128, NULL);
+ CxMempool *cfg_mp = cxMempoolCreateSimple(128);
DavConfig *config = cxMalloc(cfg_mp->allocator, sizeof(DavConfig));
memset(config, 0, sizeof(DavConfig));
config->mp = cfg_mp;
}
int dav_load_prop(DavResource *res, DavPropName *properties, size_t numprop) {
- CxMempool *mp = cxMempoolCreate(64, NULL);
+ CxMempool *mp = cxMempoolCreateSimple(64);
const CxAllocator *a = mp->allocator;
CxList *proplist = cxArrayListCreate(a, NULL, sizeof(DavProperty), numprop);
#include "cx/allocator.h"
#include <errno.h>
+#include <string.h>
static void *cx_malloc_stdlib(
cx_attr_unused void *d,
free(mem);
}
-static cx_allocator_class cx_default_allocator_class = {
+static cx_allocator_class cx_stdlib_allocator_class = {
cx_malloc_stdlib,
cx_realloc_stdlib,
cx_calloc_stdlib,
cx_free_stdlib
};
-struct cx_allocator_s cx_default_allocator = {
- &cx_default_allocator_class,
+struct cx_allocator_s cx_stdlib_allocator = {
+ &cx_stdlib_allocator_class,
NULL
};
-const CxAllocator * const cxDefaultAllocator = &cx_default_allocator;
+const CxAllocator * const cxStdlibAllocator = &cx_stdlib_allocator;
+const CxAllocator * cxDefaultAllocator = cxStdlibAllocator;
int cx_reallocate_(
void **mem,
return allocator->cl->malloc(allocator->data, n);
}
+void *cxZalloc(
+ const CxAllocator *allocator,
+ size_t n
+) {
+ void *mem = allocator->cl->malloc(allocator->data, n);
+ if (mem != NULL) {
+ memset(mem, 0, n);
+ }
+ return mem;
+}
+
void *cxRealloc(
const CxAllocator *allocator,
void *mem,
static void *cx_array_default_realloc(
void *array,
- size_t capacity,
+ cx_attr_unused size_t old_capacity,
+ size_t new_capacity,
size_t elem_size,
cx_attr_unused CxArrayReallocator *alloc
) {
size_t n;
- if (cx_szmul(capacity, elem_size, &n)) {
+ if (cx_szmul(new_capacity, elem_size, &n)) {
errno = EOVERFLOW;
return NULL;
}
- return realloc(array, n);
+ return cxReallocDefault(array, n);
}
CxArrayReallocator cx_array_default_reallocator_impl = {
static void *cx_array_advanced_realloc(
void *array,
- size_t capacity,
+ size_t old_capacity,
+ size_t new_capacity,
size_t elem_size,
cx_attr_unused CxArrayReallocator *alloc
) {
// check for overflow
size_t n;
- if (cx_szmul(capacity, elem_size, &n)) {
+ if (cx_szmul(new_capacity, elem_size, &n)) {
errno = EOVERFLOW;
return NULL;
}
if (array == alloc->ptr2) {
newmem = cxMalloc(al, n);
if (newmem != NULL && array != NULL) {
- memcpy(newmem, array, n);
+ memcpy(newmem, array, old_capacity*elem_size);
}
} else {
newmem = cxRealloc(al, array, n);
// perform reallocation
void *newmem = reallocator->realloc(
- *array, newcap, elem_size, reallocator
+ *array, oldcap, newcap, elem_size, reallocator
);
if (newmem == NULL) {
return 1; // LCOV_EXCL_LINE
// perform reallocation
void *newmem = reallocator->realloc(
- *target, newcap, elem_size, reallocator
+ *target, oldcap, newcap, elem_size, reallocator
);
if (newmem == NULL) {
return 1;
// store some counts
size_t old_size = *size;
+ size_t old_capacity = *capacity;
size_t needed_capacity = old_size + elem_count;
// if we need more than we have, try a reallocation
- if (needed_capacity > *capacity) {
+ if (needed_capacity > old_capacity) {
size_t new_capacity = cx_array_align_capacity(needed_capacity, 16, SIZE_MAX);
void *new_mem = reallocator->realloc(
- *target, new_capacity, elem_size, reallocator
+ *target, old_capacity, new_capacity, elem_size, reallocator
);
if (new_mem == NULL) {
// give it up right away, there is no contract
// decide if we can use the local buffer
if (elem_size > CX_ARRAY_SWAP_SBO_SIZE) {
- tmp = malloc(elem_size);
+ tmp = cxMallocDefault(elem_size);
// we don't want to enforce error handling
if (tmp == NULL) abort();
} else {
// free dynamic memory, if it was needed
if (tmp != sbo_mem) {
- free(tmp);
+ cxFreeDefault(tmp);
}
}
// get a correctly typed pointer to the list
cx_array_list *arl = (cx_array_list *) list;
- // do we need to move some elements?
- if (index < list->collection.size) {
- const char *first_to_move = (const char *) arl->data;
- first_to_move += index * list->collection.elem_size;
- size_t elems_to_move = list->collection.size - index;
- size_t start_of_moved = index + n;
-
- if (cx_array_copy(
- &arl->data,
- &list->collection.size,
- &arl->capacity,
- 0,
- start_of_moved,
- first_to_move,
- list->collection.elem_size,
- elems_to_move,
- &arl->reallocator
- )) {
- // if moving existing elems is unsuccessful, abort
+ // guarantee enough capacity
+ if (arl->capacity < list->collection.size + n) {
+ size_t new_capacity = list->collection.size + n;
+ new_capacity = new_capacity - (new_capacity % 16) + 16;
+ if (cxReallocateArray(
+ list->collection.allocator,
+ &arl->data, new_capacity,
+ list->collection.elem_size)
+ ) {
return 0;
}
+ arl->capacity = new_capacity;
}
- // note that if we had to move the elements, the following operation
- // is guaranteed to succeed, because we have the memory already allocated
- // therefore, it is impossible to leave this function with an invalid array
+ // determine insert position
+ char *arl_data = arl->data;
+ char *insert_pos = arl_data + index * list->collection.elem_size;
- // place the new elements
- if (cx_array_copy(
- &arl->data,
- &list->collection.size,
- &arl->capacity,
- 0,
- index,
- array,
- list->collection.elem_size,
- n,
- &arl->reallocator
- )) {
- // array list implementation is "all or nothing"
- return 0;
- } else {
- return n;
+ // do we need to move some elements?
+ if (index < list->collection.size) {
+ size_t elems_to_move = list->collection.size - index;
+ char *target = insert_pos + n * list->collection.elem_size;
+ memmove(target, insert_pos, elems_to_move * list->collection.elem_size);
}
+
+ // place the new elements, if any
+ if (array != NULL) {
+ memcpy(insert_pos, array, n * list->collection.elem_size);
+ }
+ list->collection.size += n;
+
+ return n;
}
static size_t cx_arl_insert_sorted(
}
}
-static int cx_arl_insert_element(
+static void *cx_arl_insert_element(
struct cx_list_s *list,
size_t index,
const void *element
) {
- return 1 != cx_arl_insert_array(list, index, element, 1);
+ if (cx_arl_insert_array(list, index, element, 1) == 1) {
+ return ((char*)((cx_array_list *) list)->data) + index * list->collection.elem_size;
+ } else {
+ return NULL;
+ }
}
static int cx_arl_insert_iter(
) {
struct cx_list_s *list = iter->src_handle.m;
if (iter->index < list->collection.size) {
- int result = cx_arl_insert_element(
- list,
- iter->index + 1 - prepend,
- elem
- );
- if (result == 0) {
- iter->elem_count++;
- if (prepend != 0) {
- iter->index++;
- iter->elem_handle = ((char *) iter->elem_handle) + list->collection.elem_size;
- }
+ if (cx_arl_insert_element(list,
+ iter->index + 1 - prepend, elem) == NULL) {
+ return 1;
+ }
+ iter->elem_count++;
+ if (prepend != 0) {
+ iter->index++;
+ iter->elem_handle = ((char *) iter->elem_handle) + list->collection.elem_size;
}
- return result;
+ return 0;
} else {
- int result = cx_arl_insert_element(list, list->collection.size, elem);
- if (result == 0) {
- iter->elem_count++;
- iter->index = list->collection.size;
+ if (cx_arl_insert_element(list, list->collection.size, elem) == NULL) {
+ return 1;
}
- return result;
+ iter->elem_count++;
+ iter->index = list->collection.size;
+ return 0;
}
}
CxBuffer *buffer,
CxBufferFlushConfig config
) {
- buffer->flush = malloc(sizeof(CxBufferFlushConfig));
+ buffer->flush = cxMallocDefault(sizeof(CxBufferFlushConfig));
if (buffer->flush == NULL) return -1; // LCOV_EXCL_LINE
memcpy(buffer->flush, &config, sizeof(CxBufferFlushConfig));
return 0;
if (buffer->flags & CX_BUFFER_FREE_CONTENTS) {
cxFree(buffer->allocator, buffer->bytes);
}
- free(buffer->flush);
+ cxFreeDefault(buffer->flush);
memset(buffer, 0, sizeof(CxBuffer));
}
typedef struct cx_allocator_s CxAllocator;
/**
- * A default allocator using standard library malloc() etc.
+ * A pre-defined allocator using standard library malloc() etc.
*/
cx_attr_export
-extern const CxAllocator * const cxDefaultAllocator;
+extern const CxAllocator * const cxStdlibAllocator;
+
+/**
+ * The default allocator that is used by UCX.
+ * Initialized with cxStdlibAllocator, but you may change it.
+ */
+cx_attr_export
+extern const CxAllocator * cxDefaultAllocator;
/**
* Function pointer type for destructor functions.
* Reallocate a previously allocated block and changes the pointer in-place,
* if necessary.
*
+ * @note This will use stdlib reallocate and @em not the cxDefaultAllocator.
+ *
* @par Error handling
* @c errno will be set by realloc() on failure.
*
*
* The size is calculated by multiplying @p nemb and @p size.
*
+ * @note This will use stdlib reallocate and @em not the cxDefaultAllocator.
+ *
* @par Error handling
* @c errno will be set by realloc() on failure or when the multiplication of
* @p nmemb and @p size overflows.
* Reallocate a previously allocated block and changes the pointer in-place,
* if necessary.
*
+ * @note This will use stdlib reallocate and @em not the cxDefaultAllocator.
+ *
* @par Error handling
* @c errno will be set by realloc() on failure.
*
*
* The size is calculated by multiplying @p nemb and @p size.
*
+ * @note This will use stdlib reallocate and @em not the cxDefaultAllocator.
+ *
* @par Error handling
* @c errno will be set by realloc() on failure or when the multiplication of
* @p nmemb and @p size overflows.
#define cx_reallocatearray(mem, nmemb, size) \
cx_reallocatearray_((void**)(mem), nmemb, size)
+/**
+ * Allocates memory and sets every byte to zero.
+ *
+ * @param n (@c size_t) the number of bytes
+ * @return (@c void*) a pointer to the allocated memory
+ */
+#define cx_zalloc(n) calloc(1, n)
+
/**
* Free a block allocated by this allocator.
*
size_t size
);
+/**
+ * Allocate @p n bytes of memory and sets every byte to zero.
+ *
+ * @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 *cxZalloc(
+ const CxAllocator *allocator,
+ size_t n
+);
+
+/**
+ * Convenience macro that invokes cxMalloc() with the cxDefaultAllocator.
+ */
+#define cxMallocDefault(...) cxMalloc(cxDefaultAllocator, __VA_ARGS__)
+/**
+ * Convenience macro that invokes cxZalloc() with the cxDefaultAllocator.
+ */
+#define cxZallocDefault(...) cxZalloc(cxDefaultAllocator, __VA_ARGS__)
+/**
+ * Convenience macro that invokes cxCalloc() with the cxDefaultAllocator.
+ */
+#define cxCallocDefault(...) cxCalloc(cxDefaultAllocator, __VA_ARGS__)
+/**
+ * Convenience macro that invokes cxRealloc() with the cxDefaultAllocator.
+ */
+#define cxReallocDefault(...) cxRealloc(cxDefaultAllocator, __VA_ARGS__)
+/**
+ * Convenience macro that invokes cxReallocate() with the cxDefaultAllocator.
+ */
+#define cxReallocateDefault(...) cxReallocate(cxDefaultAllocator, __VA_ARGS__)
+/**
+ * Convenience macro that invokes cxReallocateArray() with the cxDefaultAllocator.
+ */
+#define cxReallocateArrayDefault(...) cxReallocateArray(cxDefaultAllocator, __VA_ARGS__)
+/**
+ * Convenience macro that invokes cxReallocArray() with the cxDefaultAllocator.
+ */
+#define cxReallocArrayDefault(...) cxReallocArray(cxDefaultAllocator, __VA_ARGS__)
+/**
+ * Convenience macro that invokes cxFree() with the cxDefaultAllocator.
+ */
+#define cxFreeDefault(...) cxFree(cxDefaultAllocator, __VA_ARGS__)
+
#ifdef __cplusplus
} // extern "C"
#endif
* @endcode
*
*
- * The memory for the array is allocated with stdlib malloc().
+ * The memory for the array is allocated with the cxDefaultAllocator.
+ *
* @param array the name of the array
* @param capacity the initial capacity
* @see cx_array_initialize_a()
#define cx_array_initialize(array, capacity) \
array##_capacity = capacity; \
array##_size = 0; \
- array = malloc(sizeof(array[0]) * capacity)
+ array = cxMallocDefault(sizeof(array[0]) * capacity)
/**
* Initializes an array with the given capacity using the specified allocator.
* cxFree(al, myarray); // don't forget to free with same allocator
* @endcode
*
- * The memory for the array is allocated with stdlib malloc().
* @param allocator (@c CxAllocator*) the allocator
* @param array the name of the array
* @param capacity the initial capacity
* or to transport other additional data.
*
* @param array the array to reallocate
- * @param capacity the new capacity (number of elements)
+ * @param old_capacity the old number of elements
+ * @param new_capacity the new number of elements
* @param elem_size the size of each element
* @param alloc a reference to this allocator
* @return a pointer to the reallocated memory or @c NULL on failure
*/
cx_attr_nodiscard
- cx_attr_nonnull_arg(4)
- cx_attr_allocsize(2, 3)
+ cx_attr_nonnull_arg(5)
+ cx_attr_allocsize(3, 4)
void *(*realloc)(
void *array,
- size_t capacity,
+ size_t old_capacity,
+ size_t new_capacity,
size_t elem_size,
struct cx_array_reallocator_s *alloc
);
typedef struct cx_array_reallocator_s CxArrayReallocator;
/**
- * A default stdlib-based array reallocator.
+ * A default array reallocator that is based on the cxDefaultAllocator.
*/
cx_attr_export
extern CxArrayReallocator *cx_array_default_reallocator;
/**
* Creates a new array reallocator.
*
- * When @p allocator is @c NULL, the stdlib default allocator will be used.
+ * When @p allocator is @c NULL, the cxDefaultAllocator will be used.
*
* When @p stackmem is not @c NULL, the reallocator is supposed to be used
* @em only for the specific array that is initially located at @p stackmem.
* to cx_cmp_ptr(), if none is given.
*
* @param allocator the allocator for allocating the list memory
- * (if @c NULL, a default stdlib allocator will be used)
+ * (if @c NULL, the cxDefaultAllocator will be used)
* @param comparator the comparator for the elements
* (if @c NULL, and the list is not storing pointers, sort and find
* functions will not work)
* @param capacity the capacity of the buffer
* @param allocator the allocator this buffer shall use for automatic
* memory management
- * (if @c NULL, a default stdlib allocator will be used)
+ * (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
*/
* @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)
+ * (if @c NULL, the cxDefaultAllocator 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
*/
* In other words, when the iterator is finished, @c index==size .
*
* @param allocator the allocator to use
- * (if @c NULL, a default stdlib allocator will be used)
+ * (if @c NULL, the cxDefaultAllocator will be used)
* @param itemsize the size of one element
* @param buckets the initial number of buckets in this hash map
* @return a pointer to the new hash map
* to cx_cmp_ptr(), if none is given.
*
* @param allocator the allocator for allocating the list nodes
- * (if @c NULL, a default stdlib allocator will be used)
+ * (if @c NULL, the cxDefaultAllocator will be used)
* @param comparator the comparator for the elements
* (if @c NULL, and the list is not storing pointers, sort and find
* functions will not work)
/**
* Member function for inserting a single element.
+ * The data pointer may be @c NULL in which case the function shall only allocate memory.
+ * Returns a pointer to the data of the inserted element.
*/
- int (*insert_element)(
+ void *(*insert_element)(
struct cx_list_s *list,
size_t index,
const void *data
* @retval zero success
* @retval non-zero memory allocation failure
* @see cxListAddArray()
+ * @see cxListEmplace()
*/
cx_attr_nonnull
static inline int cxListAdd(
const void *elem
) {
list->collection.sorted = false;
- return list->cl->insert_element(list, list->collection.size, elem);
+ return list->cl->insert_element(list, list->collection.size, elem) == NULL;
}
/**
* @retval non-zero memory allocation failure or the index is out of bounds
* @see cxListInsertAfter()
* @see cxListInsertBefore()
+ * @see cxListEmplaceAt()
*/
cx_attr_nonnull
static inline int cxListInsert(
const void *elem
) {
list->collection.sorted = false;
- return list->cl->insert_element(list, index, elem);
+ return list->cl->insert_element(list, index, elem) == NULL;
+}
+
+/**
+ * Allocates memory for an element at the specified index and returns a pointer to that memory.
+ *
+ * @remark When the list is storing pointers, this will return a @c void**.
+ *
+ * @param list the list
+ * @param index the index where to emplace the element
+ * @return a pointer to the allocated memory; @c NULL when the operation fails, or the index is out-of-bounds
+ * @see cxListEmplace()
+ * @see cxListInsert()
+ */
+cx_attr_nonnull
+static inline void *cxListEmplaceAt(CxList *list, size_t index) {
+ list->collection.sorted = false;
+ return list->cl->insert_element(list, index, NULL);
+}
+
+
+/**
+ * Allocates memory for an element at the end of the list and returns a pointer to that memory.
+ *
+ * @remark When the list is storing pointers, this will return a @c void**.
+ *
+ * @param list the list
+ * @return a pointer to the allocated memory; @c NULL when the operation fails, or the index is out-of-bounds
+ * @see cxListEmplaceAt()
+ * @see cxListAdd()
+ */
+cx_attr_nonnull
+static inline void *cxListEmplace(CxList *list) {
+ list->collection.sorted = false;
+ return list->cl->insert_element(list, list->collection.size, NULL);
}
/**
/**
* Removes and returns the element at the specified index.
*
- * No destructor is called and instead the element is copied to the
+ * No destructor is called, and instead the element is copied to the
* @p targetbuf which MUST be large enough to hold the removed element.
+ * If the list is storing pointers, only the pointer is copied to @p targetbuf.
*
* @param list the list
* @param index the index of the element
return list->cl->remove(list, index, 1, targetbuf) == 0;
}
+/**
+ * Removes and returns the first element of the list.
+ *
+ * No destructor is called, and instead the element is copied to the
+ * @p targetbuf which MUST be large enough to hold the removed element.
+ * If the list is storing pointers, only the pointer is copied to @p targetbuf.
+ *
+ * @param list the list
+ * @param targetbuf a buffer where to copy the element
+ * @retval zero success
+ * @retval non-zero list is empty
+ * @see cxListPopFront()
+ * @see cxListRemoveAndGetLast()
+ */
+cx_attr_nonnull
+cx_attr_access_w(2)
+static inline int cxListRemoveAndGetFirst(
+ CxList *list,
+ void *targetbuf
+) {
+ return list->cl->remove(list, 0, 1, targetbuf) == 0;
+}
+
+/**
+ * Removes and returns the first element of the list.
+ *
+ * Alias for cxListRemoveAndGetFirst().
+ *
+ * No destructor is called, and instead the element is copied to the
+ * @p targetbuf which MUST be large enough to hold the removed element.
+ * If the list is storing pointers, only the pointer is copied to @p targetbuf.
+ *
+ * @param list (@c CxList*) the list
+ * @param targetbuf (@c void*) a buffer where to copy the element
+ * @retval zero success
+ * @retval non-zero list is empty
+ * @see cxListRemoveAndGetFirst()
+ * @see cxListPop()
+ */
+#define cxListPopFront(list, targetbuf) cxListRemoveAndGetFirst((list), (targetbuf))
+
+
+/**
+ * Removes and returns the last element of the list.
+ *
+ * No destructor is called, and instead the element is copied to the
+ * @p targetbuf which MUST be large enough to hold the removed element.
+ * If the list is storing pointers, only the pointer is copied to @p targetbuf.
+ *
+ * @param list the list
+ * @param targetbuf a buffer where to copy the element
+ * @retval zero success
+ * @retval non-zero list is empty
+ */
+cx_attr_nonnull
+cx_attr_access_w(2)
+static inline int cxListRemoveAndGetLast(
+ CxList *list,
+ void *targetbuf
+) {
+ // note: index may wrap - member function will catch that
+ return list->cl->remove(list, list->collection.size - 1, 1, targetbuf) == 0;
+}
+
+/**
+ * Removes and returns the last element of the list.
+ *
+ * Alias for cxListRemoveAndGetLast().
+ *
+ * No destructor is called, and instead the element is copied to the
+ * @p targetbuf which MUST be large enough to hold the removed element.
+ * If the list is storing pointers, only the pointer is copied to @p targetbuf.
+ *
+ * @param list (@c CxList*) the list
+ * @param targetbuf (@c void*) a buffer where to copy the element
+ * @retval zero success
+ * @retval non-zero list is empty
+ * @see cxListRemoveAndGetLast()
+ * @see cxListPopFront()
+ */
+#define cxListPop(list, targetbuf) cxListRemoveAndGetLast((list), (targetbuf))
+
/**
* Removes multiple element starting at the specified index.
*
* If an element destructor function is specified, it is called for each
* element. It is guaranteed that the destructor is called before removing
- * the element, however, due to possible optimizations it is neither guaranteed
+ * the element. However, due to possible optimizations, it is neither guaranteed
* that the destructors are invoked for all elements before starting to remove
* them, nor that the element is removed immediately after the destructor call
* before proceeding to the next element.
}
/**
- * Removes and returns multiple element starting at the specified index.
+ * Removes and returns multiple elements starting at the specified index.
*
- * No destructor is called and instead the elements are copied to the
+ * No destructor is called, and instead the elements are copied to the
* @p targetbuf which MUST be large enough to hold all removed elements.
+ * If the list is storing pointers, @p targetbuf is expected to be an array of pointers.
*
* @param list the list
* @param index the index of the element
/**
* Swaps two items in the list.
*
- * Implementations should only allocate temporary memory for the swap, if
+ * Implementations should only allocate temporary memory for the swap if
* it is necessary.
*
* @param list the list
* @param i the index of the first element
* @param j the index of the second element
* @retval zero success
- * @retval non-zero one of the indices is out of bounds
- * or the swap needed extra memory but allocation failed
+ * @retval non-zero one of the indices is out of bounds,
+ * or the swap needed extra memory, but allocation failed
*/
cx_attr_nonnull
static inline int cxListSwap(
/**
* Returns a pointer to the element at the specified index.
*
+ * If the list is storing pointers, returns the pointer stored at the specified index.
+ *
* @param list the list
* @param index the index of the element
* @return a pointer to the element or @c NULL if the index is out of bounds
return list->cl->at(list, index);
}
+/**
+ * Returns a pointer to the first element.
+ *
+ * If the list is storing pointers, returns the first pointer stored in the list.
+ *
+ * @param list the list
+ * @return a pointer to the first element or @c NULL if the list is empty
+ */
+cx_attr_nonnull
+static inline void *cxListFirst(const CxList *list) {
+ return list->cl->at(list, 0);
+}
+
+/**
+ * Returns a pointer to the last element.
+ *
+ * If the list is storing pointers, returns the last pointer stored in the list.
+ *
+ * @param list the list
+ * @return a pointer to the last element or @c NULL if the list is empty
+ */
+cx_attr_nonnull
+static inline void *cxListLast(const CxList *list) {
+ return list->cl->at(list, list->collection.size - 1);
+}
/**
* Sets the element at the specified index in the list
extern "C" {
#endif
-/** Internal structure for pooled memory. */
-struct cx_mempool_memory_s;
+/** A memory block in a simple memory pool. */
+struct cx_mempool_memory_s {
+ /** The destructor. */
+ cx_destructor_func destructor;
+ /** The actual memory. */
+ char c[];
+};
+
+/** A memory block in an advanced memory pool. */
+struct cx_mempool_memory2_s {
+ /** The destructor. */
+ cx_destructor_func2 destructor;
+ /** Data for the destructor. */
+ void *data;
+ /** The actual memory. */
+ char c[];
+};
+
+/** Represents memory that is not allocated by, but registered with a pool. */
+struct cx_mempool_foreign_memory_s {
+ /** The foreign memory. */
+ void* mem;
+ union {
+ /** Simple destructor. */
+ cx_destructor_func destr;
+ /** Advanced destructor. */
+ cx_destructor_func2 destr2;
+ };
+ /** Data for the advanced destructor. */
+ void *destr2_data;
+};
+
+/** Specifies how individual blocks are allocated. */
+enum cx_mempool_type {
+ /**
+ * Allows registration of cx_destructor_func for each memory block.
+ */
+ CX_MEMPOOL_TYPE_SIMPLE,
+ /**
+ * Allows registration of cx_destructor_func2 for each memory block.
+ */
+ CX_MEMPOOL_TYPE_ADVANCED,
+ /**
+ * No individual destructor registration allowed.
+ *
+ * In this mode, no additional memory per block is allocated.
+ */
+ CX_MEMPOOL_TYPE_PURE,
+};
/**
* The basic structure of a memory pool.
* Should be the first member of an actual memory pool implementation.
*/
struct cx_mempool_s {
+ /** The used allocator, initialized with the cxDefaultAllocator. */
+ const CxAllocator * const base_allocator;
+
/** The provided allocator. */
const CxAllocator *allocator;
- /**
- * A destructor that shall be automatically registered for newly allocated memory.
- * This destructor MUST NOT free the memory.
- */
- cx_destructor_func auto_destr;
-
/** Array of pooled memory. */
- struct cx_mempool_memory_s **data;
+ void **data;
/** Number of pooled memory items. */
size_t size;
/** Memory pool capacity. */
size_t capacity;
+
+ /** Array of registered memory. */
+ struct cx_mempool_foreign_memory_s *registered;
+
+ /** Number of registered memory items. */
+ size_t registered_size;
+
+ /** Capacity for registered memory. */
+ size_t registered_capacity;
+
+ /**
+ * A destructor that shall be called before deallocating a memory block.
+ * This destructor MUST NOT free the memory itself.
+ *
+ * It is guaranteed that this destructor is called after the individual
+ * destructor of the memory block and before @c destr2.
+ */
+ cx_destructor_func destr;
+
+ /**
+ * A destructor that shall be called before deallocating a memory block.
+ * This destructor MUST NOT free the memory itself.
+ *
+ * It is guaranteed that this destructor is called after the individual
+ * destructor of the memory block and @c destr.
+ */
+ cx_destructor_func2 destr2;
+
+ /**
+ * Additional data for the @c destr2.
+ */
+ void *destr2_data;
};
/**
void cxMempoolFree(CxMempool *pool);
/**
- * Creates an array-based memory pool with a shared destructor function.
+ * Creates an array-based memory pool.
*
- * This destructor MUST NOT free the memory.
+ * The type determines how much additional memory is allocated per block
+ * to register a destructor function.
*
- * @param capacity the initial capacity of the pool
- * @param destr optional destructor function to use for allocated memory
+ * @param capacity the initial capacity of the pool (an implementation default if zero)
+ * @param type the type of memory pool
* @return the created memory pool or @c NULL if allocation failed
*/
cx_attr_nodiscard
cx_attr_malloc
cx_attr_dealloc(cxMempoolFree, 1)
cx_attr_export
-CxMempool *cxMempoolCreate(size_t capacity, cx_destructor_func destr);
+CxMempool *cxMempoolCreate(size_t capacity, enum cx_mempool_type type);
+
+/**
+ * Creates a basic array-based memory pool.
+ *
+ * Convenience macro to create a memory pool of type #CX_MEMPOOL_TYPE_SIMPLE.
+ *
+ * @param capacity (@c size_t) the initial capacity of the pool
+ * @return (@c CxMempool*) the created memory pool or @c NULL if allocation failed
+ */
+#define cxMempoolCreateSimple(capacity) cxMempoolCreate(capacity, CX_MEMPOOL_TYPE_SIMPLE)
+
+/**
+ * Creates a basic array-based memory pool.
+ *
+ * Convenience macro to create a memory pool of type #CX_MEMPOOL_TYPE_ADVANCED.
+ *
+ * @param capacity (@c size_t) the initial capacity of the pool
+ * @return (@c CxMempool*) the created memory pool or @c NULL if allocation failed
+ */
+#define cxMempoolCreateAdvanced(capacity) cxMempoolCreate(capacity, CX_MEMPOOL_TYPE_ADVANCED)
/**
* Creates a basic array-based memory pool.
*
+ * Convenience macro to create a memory pool of type #CX_MEMPOOL_TYPE_PURE.
+ *
* @param capacity (@c size_t) the initial capacity of the pool
* @return (@c CxMempool*) the created memory pool or @c NULL if allocation failed
*/
-#define cxMempoolCreateSimple(capacity) cxMempoolCreate(capacity, NULL)
+#define cxMempoolCreatePure(capacity) cxMempoolCreate(capacity, CX_MEMPOOL_TYPE_PURE)
+
+/**
+ * Sets the global destructor for all memory blocks within the specified pool.
+ *
+ * @param pool the memory pool
+ * @param fnc the destructor that shall be applied to all memory blocks
+ */
+cx_attr_nonnull_arg(1)
+cx_attr_export
+void cxMempoolGlobalDestructor(CxMempool *pool, cx_destructor_func fnc);
+
+/**
+ * Sets the global destructor for all memory blocks within the specified pool.
+ *
+ * @param pool the memory pool
+ * @param fnc the destructor that shall be applied to all memory blocks
+ * @param data additional data for the destructor function
+ */
+cx_attr_nonnull_arg(1)
+cx_attr_export
+void cxMempoolGlobalDestructor2(CxMempool *pool, cx_destructor_func2 fnc, void *data);
/**
* Sets the destructor function for a specific allocated memory object.
*
+ * If the type of memory pool is not #CX_MEMPOOL_TYPE_SIMPLE, the behavior is undefined.
* If the memory is not managed by a UCX memory pool, the behavior is undefined.
* The destructor MUST NOT free the memory.
*
);
/**
- * Removes the destructor function for a specific allocated memory object.
+ * Sets the destructor function for a specific allocated memory object.
*
+ * If the type of memory pool is not #CX_MEMPOOL_TYPE_ADVANCED, the behavior is undefined.
* If the memory is not managed by a UCX memory pool, the behavior is undefined.
* The destructor MUST NOT free the memory.
*
* @param memory the object allocated in the pool
+ * @param fnc the destructor function
+ * @param data additional data for the destructor function
+ */
+cx_attr_nonnull
+cx_attr_export
+void cxMempoolSetDestructor2(
+ void *memory,
+ cx_destructor_func2 fnc,
+ void *data
+);
+
+/**
+ * Removes the destructor function for a specific allocated memory object.
+ *
+ * If the type of memory pool is not #CX_MEMPOOL_TYPE_SIMPLE, the behavior is undefined.
+ * If the memory is not managed by a UCX memory pool, the behavior is undefined.
+ *
+ * @param memory the object allocated in the pool
*/
cx_attr_nonnull
cx_attr_export
void cxMempoolRemoveDestructor(void *memory);
+/**
+ * Removes the destructor function for a specific allocated memory object.
+ *
+ * If the type of memory pool is not #CX_MEMPOOL_TYPE_ADVANCED, the behavior is undefined.
+ * If the memory is not managed by a UCX memory pool, the behavior is undefined.
+ *
+ * @param memory the object allocated in the pool
+ */
+cx_attr_nonnull
+cx_attr_export
+void cxMempoolRemoveDestructor2(void *memory);
+
/**
* Registers foreign memory with this pool.
*
* The destructor, in contrast to memory allocated by the pool, MUST free the memory.
+ * This function can be used with any pool of any type, since destructors for registered memory
+ * are entirely independent of the pool's memory management.
*
- * A small portion of memory will be allocated to register the information in the pool.
- * If that allocation fails, this function will return non-zero.
+ * The destructor for the registered memory will be called after all pooled items have been freed.
*
* @param pool the pool
* @param memory the object to register (MUST NOT be already allocated in the pool)
cx_destructor_func destr
);
+
+/**
+ * Registers foreign memory with this pool.
+ *
+ * The destructor, in contrast to memory allocated by the pool, MUST free the memory.
+ * This function can be used with any pool of any type, since destructors for registered memory
+ * are entirely independent of the pool's memory management.
+ *
+ * The destructor for the registered memory will be called after all pooled items have been freed.
+ *
+ * @attention The data pointer MUST NOT be @c NULL.
+ * If you wish to register a destructor without additional data, use cxMempoolRegister().
+ *
+ * @param pool the pool
+ * @param memory the object to register (MUST NOT be already allocated in the pool)
+ * @param destr the destructor function
+ * @param data additional data for the destructor function
+ * @retval zero success
+ * @retval non-zero failure
+ */
+cx_attr_nonnull
+cx_attr_export
+int cxMempoolRegister2(
+ CxMempool *pool,
+ void *memory,
+ cx_destructor_func2 destr,
+ void *data
+);
+
/**
* Transfers all the memory managed by one pool to another.
*
*
* The source pool will get a completely new allocator and can be reused or destroyed afterward.
*
+ * This function fails when the destination pool has a different type than the source pool.
+ *
* @param source the pool to move the memory from
* @param dest the pool where to transfer the memory to
* @retval zero success
- * @retval non-zero failure
+ * @retval non-zero allocation failure or incompatible pools
*/
cx_attr_nonnull
cx_attr_export
/**
* Transfers an object from one pool to another.
*
- * You can only transfer objects that have been allocated by this pool.
- * Objects that have been registered with cxMempoolRegister() cannot be transferred this way.
+ * This function fails when the destination pool has a different type than the source pool.
*
* @attention If the object maintains a reference to the pool's allocator,
* you must make sure to update that reference to the allocator of the destination pool.
* @param dest the pool where to transfer the memory to
* @param obj pointer to the object that shall be transferred
* @retval zero success
- * @retval non-zero failure or object was not found in the source pool
+ * @retval non-zero failure, or the object was not found in the source pool, or the pools are incompatible
*/
cx_attr_nonnull
cx_attr_export
* zero-terminated C strings (@c char*), which means the @p map should have been
* created with #CX_STORE_POINTERS.
*
- * The default stdlib allocator will be used, unless you specify a custom
+ * The cxDefaultAllocator will be used unless you specify a custom
* allocator in the optional @c data field of the returned sink.
*
* @param map the map that shall consume the k/v-pairs.
#endif
/**
- * Passes the pointer in this string to @c free().
+ * Passes the pointer in this string to the cxDefaultAllocator's @c free() function.
*
* The pointer in the struct is set to @c NULL and the length is set to zero
* which means that this function protects you against double-free.
*
* The string in @p dest is guaranteed to be zero-terminated, regardless of whether @p src is.
*
- * @param alloc (@c CxAllocator*) the allocator
* @param dest (@c cxmutstr*) a pointer to the structure where to copy the contents to
* @param src (@c cxstring) the source string
*
/**
* Concatenates strings and returns a new string.
*
- * The resulting string will be allocated by standard @c malloc().
+ * The resulting string will be allocated by the cxDefaultAllocator.
* So developers @em must pass the return value to cx_strfree() eventually.
*
* If memory allocation fails, the pointer in the returned string will
/**
* Concatenates strings.
*
- * The resulting string will be allocated by standard @c malloc().
+ * The resulting string will be allocated by the cxDefaultAllocator.
* So developers @em must pass the return value to cx_strfree() eventually.
*
* If @p str already contains a string, the memory will be reallocated and
/**
* Creates a duplicate of the specified string.
*
- * The new string will contain a copy allocated by standard
- * @c malloc(). So developers @em must pass the return value to cx_strfree().
+ * The new string will contain a copy allocated by the cxDefaultAllocator.
+ * So developers @em must pass the return value to cx_strfree().
*
* @note The returned string is guaranteed to be zero-terminated.
*
*
* Replaces at most @p replmax occurrences.
*
- * The returned string will be allocated by @c malloc() and is guaranteed
+ * The returned string will be allocated by the cxDefaultAllocator and is guaranteed
* to be zero-terminated.
*
* If allocation fails, or the input string is empty,
/**
* Replaces a string with another string.
*
- * The returned string will be allocated by @c malloc() and is guaranteed
+ * The returned string will be allocated by the cxDefaultAllocator and is guaranteed
* to be zero-terminated.
*
* If allocation fails, or the input string is empty,
*/
cx_attr_nonnull
static inline void cxTreeIteratorDispose(CxTreeIterator *iter) {
- free(iter->stack);
+ cxFreeDefault(iter->stack);
iter->stack = NULL;
}
struct cx_tree_visitor_queue_s *q = visitor->queue_next;
while (q != NULL) {
struct cx_tree_visitor_queue_s *next = q->next;
- free(q);
+ cxFreeDefault(q);
q = next;
}
}
* Creates a depth-first iterator for a tree with the specified root node.
*
* @note A tree iterator needs to maintain a stack of visited nodes, which is
- * allocated using stdlib malloc().
+ * allocated using the cxDefaultAllocator.
* When the iterator becomes invalid, this memory is automatically released.
* However, if you wish to cancel the iteration before the iterator becomes
* invalid by itself, you MUST call cxTreeIteratorDispose() manually to release
/**
* Creates a breadth-first iterator for a tree with the specified root node.
*
- * @note A tree visitor needs to maintain a queue of to be visited nodes, which
- * is allocated using stdlib malloc().
+ * @note A tree visitor needs to maintain a queue of to-be visited nodes, which
+ * is allocated using the cxDefaultAllocator.
* When the visitor becomes invalid, this memory is automatically released.
* However, if you wish to cancel the iteration before the visitor becomes
* invalid by itself, you MUST call cxTreeVisitorDispose() manually to release
* will free the nodes with the allocator's free() method.
*
* @param allocator the allocator that shall be used
- * (if @c NULL, a default stdlib allocator will be used)
+ * (if @c NULL, the cxDefaultAllocator will be used)
* @param create_func a function that creates new nodes
* @param search_func a function that compares two nodes
* @param search_data_func a function that compares a node with data
* tree, you need to specify those functions afterwards.
*
* @param allocator the allocator that was used for nodes of the wrapped tree
- * (if @c NULL, a default stdlib allocator is assumed)
+ * (if @c NULL, the cxDefaultAllocator is assumed)
* @param root the root node of the tree that shall be wrapped
* @param loc_parent offset in the node struct for the parent pointer
* @param loc_children offset in the node struct for the children linked list
if (all_printable && escape) {
size_t capa = str.length + 32;
- char *space = malloc(capa);
+ char *space = cxMallocDefault(capa);
if (space == NULL) return cx_mutstrn(NULL, 0);
cxBufferInit(&buf, space, capa, NULL, CX_BUFFER_AUTO_EXTEND);
cxBufferWrite(str.ptr, 1, i, &buf);
void cxJsonDestroy(CxJson *json) {
cxBufferDestroy(&json->buffer);
if (json->states != json->states_internal) {
- free(json->states);
+ cxFreeDefault(json->states);
}
if (json->vbuf != json->vbuf_internal) {
- free(json->vbuf);
+ cxFreeDefault(json->vbuf);
}
cxJsonValueFree(json->parsed);
json->parsed = NULL;
if (values[i] == NULL) break;
cxJsonValueFree(values[i]);
}
- free(values);
+ cxFreeDefault(values);
}
// LCOV_EXCL_STOP
int cxJsonArrAddNumbers(CxJsonValue* arr, const double* num, size_t count) {
- CxJsonValue** values = calloc(count, sizeof(CxJsonValue*));
+ CxJsonValue** values = cxCallocDefault(count, sizeof(CxJsonValue*));
if (values == NULL) return -1;
for (size_t i = 0; i < count; i++) {
values[i] = cxJsonCreateNumber(arr->allocator, num[i]);
if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; }
}
int ret = cxJsonArrAddValues(arr, values, count);
- free(values);
+ cxFreeDefault(values);
return ret;
}
int cxJsonArrAddIntegers(CxJsonValue* arr, const int64_t* num, size_t count) {
- CxJsonValue** values = calloc(count, sizeof(CxJsonValue*));
+ CxJsonValue** values = cxCallocDefault(count, sizeof(CxJsonValue*));
if (values == NULL) return -1;
for (size_t i = 0; i < count; i++) {
values[i] = cxJsonCreateInteger(arr->allocator, num[i]);
if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; }
}
int ret = cxJsonArrAddValues(arr, values, count);
- free(values);
+ cxFreeDefault(values);
return ret;
}
int cxJsonArrAddStrings(CxJsonValue* arr, const char* const* str, size_t count) {
- CxJsonValue** values = calloc(count, sizeof(CxJsonValue*));
+ CxJsonValue** values = cxCallocDefault(count, sizeof(CxJsonValue*));
if (values == NULL) return -1;
for (size_t i = 0; i < count; i++) {
values[i] = cxJsonCreateString(arr->allocator, str[i]);
if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; }
}
int ret = cxJsonArrAddValues(arr, values, count);
- free(values);
+ cxFreeDefault(values);
return ret;
}
int cxJsonArrAddCxStrings(CxJsonValue* arr, const cxstring* str, size_t count) {
- CxJsonValue** values = calloc(count, sizeof(CxJsonValue*));
+ CxJsonValue** values = cxCallocDefault(count, sizeof(CxJsonValue*));
if (values == NULL) return -1;
for (size_t i = 0; i < count; i++) {
values[i] = cxJsonCreateCxString(arr->allocator, str[i]);
if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; }
}
int ret = cxJsonArrAddValues(arr, values, count);
- free(values);
+ cxFreeDefault(values);
return ret;
}
int cxJsonArrAddLiterals(CxJsonValue* arr, const CxJsonLiteral* lit, size_t count) {
- CxJsonValue** values = calloc(count, sizeof(CxJsonValue*));
+ CxJsonValue** values = cxCallocDefault(count, sizeof(CxJsonValue*));
if (values == NULL) return -1;
for (size_t i = 0; i < count; i++) {
values[i] = cxJsonCreateLiteral(arr->allocator, lit[i]);
if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; }
}
int ret = cxJsonArrAddValues(arr, values, count);
- free(values);
+ cxFreeDefault(values);
return ret;
}
) {
void *sbo[CX_LINKED_LIST_SORT_SBO_SIZE];
void **sorted = length >= CX_LINKED_LIST_SORT_SBO_SIZE ?
- malloc(sizeof(void *) * length) : sbo;
+ cxMallocDefault(sizeof(void *) * length) : sbo;
if (sorted == NULL) abort();
void *rc, *lc;
*begin = sorted[0];
*end = sorted[length - 1];
if (sorted != sbo) {
- free(sorted);
+ cxFreeDefault(sorted);
}
}
// initialize new new_node
new_node->prev = new_node->next = NULL;
- memcpy(new_node->payload, elem, list->collection.elem_size);
+ if (elem != NULL) {
+ memcpy(new_node->payload, elem, list->collection.elem_size);
+ }
// insert
cx_linked_list *ll = (cx_linked_list *) list;
return n;
}
-static int cx_ll_insert_element(
+static void *cx_ll_insert_element(
struct cx_list_s *list,
size_t index,
const void *element
) {
- return 1 != cx_ll_insert_array(list, index, element, 1);
+ // out-of-bounds check
+ if (index > list->collection.size) return NULL;
+
+ // find position efficiently
+ cx_linked_list_node *node = index == 0 ? NULL : cx_ll_node_at((cx_linked_list *) list, index - 1);
+
+ // perform first insert
+ if (cx_ll_insert_at(list, node, element)) return NULL;
+
+ // return a pointer to the data of the inserted node
+ if (node == NULL) {
+ return ((cx_linked_list *) list)->begin->payload;
+ } else {
+ return node->next->payload;
+ }
}
static _Thread_local cx_compare_func cx_ll_insert_sorted_cmp_func;
}
return result;
} else {
- int result = cx_ll_insert_element(list, list->collection.size, elem);
- if (result == 0) {
- iter->elem_count++;
- iter->index = list->collection.size;
+ if (cx_ll_insert_element(list, list->collection.size, elem) == NULL) {
+ return 1;
}
- return result;
+ iter->elem_count++;
+ iter->index = list->collection.size;
+ return 0;
}
}
list->climpl->deallocate(list);
}
-static int cx_pl_insert_element(
+static void *cx_pl_insert_element(
struct cx_list_s *list,
size_t index,
const void *element
const char *src = data;
size_t i = 0;
for (; i < n; i++) {
- if (0 != invoke_list_func(
+ if (NULL == invoke_list_func(
insert_element, list, index + i,
src + (i * elem_size))) return i;
}
// insert the elements at location si
if (ins == 1) {
- if (0 != invoke_list_func(
+ if (NULL == invoke_list_func(
insert_element, list, di, src)) return inserted;
} else {
size_t r = invoke_list_func(insert_array, list, di, src, ins);
void cx_list_default_sort(struct cx_list_s *list) {
size_t elem_size = list->collection.elem_size;
size_t list_size = list->collection.size;
- void *tmp = malloc(elem_size * list_size);
+ void *tmp = cxMallocDefault(elem_size * list_size);
if (tmp == NULL) abort();
// copy elements from source array
loc += elem_size;
}
- free(tmp);
+ cxFreeDefault(tmp);
}
int cx_list_default_swap(struct cx_list_s *list, size_t i, size_t j) {
size_t elem_size = list->collection.elem_size;
- void *tmp = malloc(elem_size);
+ void *tmp = cxMallocDefault(elem_size);
if (tmp == NULL) return 1;
void *ip = invoke_list_func(at, list, i);
memcpy(ip, jp, elem_size);
memcpy(jp, tmp, elem_size);
- free(tmp);
+ cxFreeDefault(tmp);
return 0;
}
#include <string.h>
#include <errno.h>
-struct cx_mempool_memory_s {
- /** The destructor. */
- cx_destructor_func destructor;
- /** The actual memory. */
- char c[];
-};
-
static int cx_mempool_ensure_capacity(
struct cx_mempool_s *pool,
size_t needed_capacity
size_t newcap = pool->capacity >= 1000 ?
pool->capacity + 1000 : pool->capacity * 2;
size_t newmsize;
- if (pool->capacity > newcap || cx_szmul(newcap,
- sizeof(struct cx_mempool_memory_s*), &newmsize)) {
+ // LCOV_EXCL_START
+ if (pool->capacity > newcap
+ || cx_szmul(newcap, sizeof(void*), &newmsize)) {
errno = EOVERFLOW;
return 1;
- }
- struct cx_mempool_memory_s **newdata = realloc(pool->data, newmsize);
+ } // LCOV_EXCL_STOP
+ void **newdata = cxRealloc(pool->base_allocator, pool->data, newmsize);
if (newdata == NULL) return 1;
pool->data = newdata;
pool->capacity = newcap;
return 0;
}
-static void *cx_mempool_malloc(
+static int cx_mempool_ensure_registered_capacity(
+ struct cx_mempool_s *pool,
+ size_t needed_capacity
+) {
+ if (needed_capacity <= pool->registered_capacity) return 0;
+ // we do not expect so many registrations
+ size_t newcap = pool->registered_capacity + 8;
+ size_t newmsize;
+ // LCOV_EXCL_START
+ if (pool->registered_capacity > newcap || cx_szmul(newcap,
+ sizeof(struct cx_mempool_foreign_memory_s), &newmsize)) {
+ errno = EOVERFLOW;
+ return 1;
+ } // LCOV_EXCL_STOP
+ void *newdata = cxRealloc(pool->base_allocator, pool->registered, newmsize);
+ if (newdata == NULL) return 1;
+ pool->registered = newdata;
+ pool->registered_capacity = newcap;
+ return 0;
+}
+
+static void *cx_mempool_malloc_simple(
void *p,
size_t n
) {
struct cx_mempool_s *pool = p;
if (cx_mempool_ensure_capacity(pool, pool->size + 1)) {
- return NULL;
+ return NULL; // LCOV_EXCL_LINE
}
- struct cx_mempool_memory_s *mem = malloc(sizeof(cx_destructor_func) + n);
+ struct cx_mempool_memory_s *mem =
+ cxMalloc(pool->base_allocator, sizeof(struct cx_mempool_memory_s) + n);
if (mem == NULL) return NULL;
-
- mem->destructor = pool->auto_destr;
+ mem->destructor = NULL;
pool->data[pool->size] = mem;
pool->size++;
return mem->c;
}
-static void *cx_mempool_calloc(
+static void *cx_mempool_calloc_simple(
void *p,
size_t nelem,
size_t elsize
errno = EOVERFLOW;
return NULL;
}
- void *ptr = cx_mempool_malloc(p, msz);
+ void *ptr = cx_mempool_malloc_simple(p, msz);
if (ptr == NULL) return NULL;
memset(ptr, 0, nelem * elsize);
return ptr;
}
-static void *cx_mempool_realloc(
+static void cx_mempool_free_simple(
+ void *p,
+ void *ptr
+) {
+ if (!ptr) return;
+ struct cx_mempool_s *pool = p;
+
+ struct cx_mempool_memory_s *mem =
+ (void*) ((char *) ptr - sizeof(struct cx_mempool_memory_s));
+
+ for (size_t i = 0; i < pool->size; i++) {
+ if (mem == pool->data[i]) {
+ if (mem->destructor) {
+ mem->destructor(mem->c);
+ }
+ if (pool->destr) {
+ pool->destr(mem->c);
+ }
+ if (pool->destr2) {
+ pool->destr2(pool->destr2_data, mem->c);
+ }
+ cxFree(pool->base_allocator, mem);
+ size_t last_index = pool->size - 1;
+ if (i != last_index) {
+ pool->data[i] = pool->data[last_index];
+ pool->data[last_index] = NULL;
+ }
+ pool->size--;
+ return;
+ }
+ }
+ abort(); // LCOV_EXCL_LINE
+}
+
+static void *cx_mempool_realloc_simple(
void *p,
void *ptr,
size_t n
) {
+ if (ptr == NULL) {
+ return cx_mempool_malloc_simple(p, n);
+ }
+ if (n == 0) {
+ cx_mempool_free_simple(p, ptr);
+ return NULL;
+ }
struct cx_mempool_s *pool = p;
- struct cx_mempool_memory_s *mem, *newm;
- mem = (struct cx_mempool_memory_s*)(((char *) ptr) - sizeof(cx_destructor_func));
- newm = realloc(mem, n + sizeof(cx_destructor_func));
+ const unsigned overhead = sizeof(struct cx_mempool_memory_s);
+ struct cx_mempool_memory_s *mem =
+ (void *) (((char *) ptr) - overhead);
+ struct cx_mempool_memory_s *newm =
+ cxRealloc(pool->base_allocator, mem, n + overhead);
if (newm == NULL) return NULL;
if (mem != newm) {
for (size_t i = 0; i < pool->size; i++) {
if (pool->data[i] == mem) {
pool->data[i] = newm;
- return ((char*)newm) + sizeof(cx_destructor_func);
+ return ((char*)newm) + overhead;
}
}
abort(); // LCOV_EXCL_LINE
} else {
- return ptr;
+ // unfortunately glibc() realloc seems to always move
+ return ptr; // LCOV_EXCL_LINE
+ }
+}
+
+static void cx_mempool_free_all_simple(const struct cx_mempool_s *pool) {
+ const bool has_destr = pool->destr;
+ const bool has_destr2 = pool->destr2;
+ for (size_t i = 0; i < pool->size; i++) {
+ struct cx_mempool_memory_s *mem = pool->data[i];
+ if (mem->destructor) {
+ mem->destructor(mem->c);
+ }
+ if (has_destr) {
+ pool->destr(mem->c);
+ }
+ if (has_destr2) {
+ pool->destr2(pool->destr2_data, mem->c);
+ }
+ cxFree(pool->base_allocator, mem);
+ }
+}
+
+static cx_allocator_class cx_mempool_simple_allocator_class = {
+ cx_mempool_malloc_simple,
+ cx_mempool_realloc_simple,
+ cx_mempool_calloc_simple,
+ cx_mempool_free_simple
+};
+
+static void *cx_mempool_malloc_advanced(
+ void *p,
+ size_t n
+) {
+ struct cx_mempool_s *pool = p;
+
+ if (cx_mempool_ensure_capacity(pool, pool->size + 1)) {
+ return NULL; // LCOV_EXCL_LINE
+ }
+
+ struct cx_mempool_memory2_s *mem =
+ cxMalloc(pool->base_allocator, sizeof(struct cx_mempool_memory2_s) + n);
+ if (mem == NULL) return NULL;
+ mem->destructor = NULL;
+ mem->data = NULL;
+ pool->data[pool->size] = mem;
+ pool->size++;
+
+ return mem->c;
+}
+
+static void *cx_mempool_calloc_advanced(
+ void *p,
+ size_t nelem,
+ size_t elsize
+) {
+ size_t msz;
+ if (cx_szmul(nelem, elsize, &msz)) {
+ errno = EOVERFLOW;
+ return NULL;
}
+ void *ptr = cx_mempool_malloc_advanced(p, msz);
+ if (ptr == NULL) return NULL;
+ memset(ptr, 0, nelem * elsize);
+ return ptr;
}
-static void cx_mempool_free(
+static void cx_mempool_free_advanced(
void *p,
void *ptr
) {
if (!ptr) return;
struct cx_mempool_s *pool = p;
- struct cx_mempool_memory_s *mem = (struct cx_mempool_memory_s *)
- ((char *) ptr - sizeof(cx_destructor_func));
+ struct cx_mempool_memory2_s *mem =
+ (void*) ((char *) ptr - sizeof(struct cx_mempool_memory2_s));
for (size_t i = 0; i < pool->size; i++) {
if (mem == pool->data[i]) {
if (mem->destructor) {
- mem->destructor(mem->c);
+ mem->destructor(mem->data, mem->c);
}
- free(mem);
+ if (pool->destr) {
+ pool->destr(mem->c);
+ }
+ if (pool->destr2) {
+ pool->destr2(pool->destr2_data, mem->c);
+ }
+ cxFree(pool->base_allocator, mem);
size_t last_index = pool->size - 1;
if (i != last_index) {
pool->data[i] = pool->data[last_index];
abort(); // LCOV_EXCL_LINE
}
-void cxMempoolFree(CxMempool *pool) {
- if (pool == NULL) return;
- struct cx_mempool_memory_s *mem;
+static void *cx_mempool_realloc_advanced(
+ void *p,
+ void *ptr,
+ size_t n
+) {
+ if (ptr == NULL) {
+ return cx_mempool_malloc_advanced(p, n);
+ }
+ if (n == 0) {
+ cx_mempool_free_advanced(p, ptr);
+ return NULL;
+ }
+ struct cx_mempool_s *pool = p;
+
+ const unsigned overhead = sizeof(struct cx_mempool_memory2_s);
+ struct cx_mempool_memory2_s *mem =
+ (void *) (((char *) ptr) - overhead);
+ struct cx_mempool_memory2_s *newm =
+ cxRealloc(pool->base_allocator, mem, n + overhead);
+
+ if (newm == NULL) return NULL;
+ if (mem != newm) {
+ for (size_t i = 0; i < pool->size; i++) {
+ if (pool->data[i] == mem) {
+ pool->data[i] = newm;
+ return ((char*)newm) + overhead;
+ }
+ }
+ abort(); // LCOV_EXCL_LINE
+ } else {
+ // unfortunately glibc() realloc seems to always move
+ return ptr; // LCOV_EXCL_LINE
+ }
+}
+
+static void cx_mempool_free_all_advanced(const struct cx_mempool_s *pool) {
+ const bool has_destr = pool->destr;
+ const bool has_destr2 = pool->destr2;
for (size_t i = 0; i < pool->size; i++) {
- mem = pool->data[i];
+ struct cx_mempool_memory2_s *mem = pool->data[i];
if (mem->destructor) {
- mem->destructor(mem->c);
+ mem->destructor(mem->data, mem->c);
+ }
+ if (has_destr) {
+ pool->destr(mem->c);
}
- free(mem);
+ if (has_destr2) {
+ pool->destr2(pool->destr2_data, mem->c);
+ }
+ cxFree(pool->base_allocator, mem);
}
- free(pool->data);
- free((void*) pool->allocator);
- free(pool);
+}
+
+static cx_allocator_class cx_mempool_advanced_allocator_class = {
+ cx_mempool_malloc_advanced,
+ cx_mempool_realloc_advanced,
+ cx_mempool_calloc_advanced,
+ cx_mempool_free_advanced
+};
+
+
+static void *cx_mempool_malloc_pure(
+ void *p,
+ size_t n
+) {
+ struct cx_mempool_s *pool = p;
+
+ if (cx_mempool_ensure_capacity(pool, pool->size + 1)) {
+ return NULL; // LCOV_EXCL_LINE
+ }
+
+ void *mem = cxMalloc(pool->base_allocator, n);
+ if (mem == NULL) return NULL;
+ pool->data[pool->size] = mem;
+ pool->size++;
+
+ return mem;
+}
+
+static void *cx_mempool_calloc_pure(
+ void *p,
+ size_t nelem,
+ size_t elsize
+) {
+ size_t msz;
+ if (cx_szmul(nelem, elsize, &msz)) {
+ errno = EOVERFLOW;
+ return NULL;
+ }
+ void *ptr = cx_mempool_malloc_pure(p, msz);
+ if (ptr == NULL) return NULL;
+ memset(ptr, 0, nelem * elsize);
+ return ptr;
+}
+
+static void cx_mempool_free_pure(
+ void *p,
+ void *ptr
+) {
+ if (!ptr) return;
+ struct cx_mempool_s *pool = p;
+
+ for (size_t i = 0; i < pool->size; i++) {
+ if (ptr == pool->data[i]) {
+ if (pool->destr) {
+ pool->destr(ptr);
+ }
+ if (pool->destr2) {
+ pool->destr2(pool->destr2_data, ptr);
+ }
+ cxFree(pool->base_allocator, ptr);
+ size_t last_index = pool->size - 1;
+ if (i != last_index) {
+ pool->data[i] = pool->data[last_index];
+ pool->data[last_index] = NULL;
+ }
+ pool->size--;
+ return;
+ }
+ }
+ abort(); // LCOV_EXCL_LINE
+}
+
+static void *cx_mempool_realloc_pure(
+ void *p,
+ void *ptr,
+ size_t n
+) {
+ if (ptr == NULL) {
+ return cx_mempool_malloc_pure(p, n);
+ }
+ if (n == 0) {
+ cx_mempool_free_pure(p, ptr);
+ return NULL;
+ }
+ struct cx_mempool_s *pool = p;
+ void *newm = cxRealloc(pool->base_allocator, ptr, n);
+ if (newm == NULL) return NULL;
+ if (ptr != newm) {
+ for (size_t i = 0; i < pool->size; i++) {
+ if (pool->data[i] == ptr) {
+ pool->data[i] = newm;
+ return newm;
+ }
+ }
+ abort(); // LCOV_EXCL_LINE
+ } else {
+ // unfortunately glibc() realloc seems to always move
+ return ptr; // LCOV_EXCL_LINE
+ }
+}
+
+static void cx_mempool_free_all_pure(const struct cx_mempool_s *pool) {
+ const bool has_destr = pool->destr;
+ const bool has_destr2 = pool->destr2;
+ for (size_t i = 0; i < pool->size; i++) {
+ void *mem = pool->data[i];
+ if (has_destr) {
+ pool->destr(mem);
+ }
+ if (has_destr2) {
+ pool->destr2(pool->destr2_data, mem);
+ }
+ cxFree(pool->base_allocator, mem);
+ }
+}
+
+static cx_allocator_class cx_mempool_pure_allocator_class = {
+ cx_mempool_malloc_pure,
+ cx_mempool_realloc_pure,
+ cx_mempool_calloc_pure,
+ cx_mempool_free_pure
+};
+
+static void cx_mempool_free_foreign(const struct cx_mempool_s *pool) {
+ for (size_t i = 0; i < pool->registered_size; i++) {
+ struct cx_mempool_foreign_memory_s info = pool->registered[i];
+ if (info.destr2_data == NULL) {
+ if (info.destr) {
+ info.destr(info.mem);
+ }
+ } else {
+ info.destr2(info.destr2_data, info.mem);
+ }
+ }
+}
+
+void cxMempoolFree(CxMempool *pool) {
+ if (pool == NULL) return;
+ if (pool->allocator->cl == &cx_mempool_simple_allocator_class) {
+ cx_mempool_free_all_simple(pool);
+ } else if (pool->allocator->cl == &cx_mempool_advanced_allocator_class) {
+ cx_mempool_free_all_advanced(pool);
+ } else {
+ cx_mempool_free_all_pure(pool);
+ }
+ cx_mempool_free_foreign(pool);
+ cxFree(pool->base_allocator, pool->data);
+ cxFree(pool->base_allocator, pool->registered);
+ cxFree(pool->base_allocator, (void*) pool->allocator);
+ cxFree(pool->base_allocator, pool);
}
void cxMempoolSetDestructor(
*(cx_destructor_func *) ((char *) ptr - sizeof(cx_destructor_func)) = func;
}
+void cxMempoolSetDestructor2(
+ void *ptr,
+ cx_destructor_func2 func,
+ void *data
+) {
+ struct cx_mempool_memory2_s *info =
+ (void*)((char *) ptr - sizeof(struct cx_mempool_memory2_s));
+ info->destructor = func;
+ info->data = data;
+}
+
void cxMempoolRemoveDestructor(void *ptr) {
*(cx_destructor_func *) ((char *) ptr - sizeof(cx_destructor_func)) = NULL;
}
-struct cx_mempool_foreign_mem_s {
- cx_destructor_func destr;
- void* mem;
-};
-
-static void cx_mempool_destr_foreign_mem(void* ptr) {
- struct cx_mempool_foreign_mem_s *fm = ptr;
- fm->destr(fm->mem);
+void cxMempoolRemoveDestructor2(void *ptr) {
+ struct cx_mempool_memory2_s *info =
+ (void*)((char *) ptr - sizeof(struct cx_mempool_memory2_s));
+ info->destructor = NULL;
+ info->data = NULL;
}
int cxMempoolRegister(
void *memory,
cx_destructor_func destr
) {
- struct cx_mempool_foreign_mem_s *fm = cx_mempool_malloc(
- pool,
- sizeof(struct cx_mempool_foreign_mem_s)
- );
- if (fm == NULL) return 1;
+ if (cx_mempool_ensure_registered_capacity(pool, pool->registered_size + 1)) {
+ return 1; // LCOV_EXCL_LINE
+ }
- fm->mem = memory;
- fm->destr = destr;
- *(cx_destructor_func *) ((char *) fm - sizeof(cx_destructor_func)) = cx_mempool_destr_foreign_mem;
+ pool->registered[pool->registered_size++] =
+ (struct cx_mempool_foreign_memory_s) {
+ .mem = memory,
+ .destr = destr,
+ .destr2_data = NULL
+ };
return 0;
}
-static cx_allocator_class cx_mempool_allocator_class = {
- cx_mempool_malloc,
- cx_mempool_realloc,
- cx_mempool_calloc,
- cx_mempool_free
-};
+int cxMempoolRegister2(
+ CxMempool *pool,
+ void *memory,
+ cx_destructor_func2 destr,
+ void *data
+) {
+ if (cx_mempool_ensure_registered_capacity(pool, pool->registered_size + 1)) {
+ return 1; // LCOV_EXCL_LINE
+ }
+
+ pool->registered[pool->registered_size++] =
+ (struct cx_mempool_foreign_memory_s) {
+ .mem = memory,
+ .destr2 = destr,
+ .destr2_data = data
+ };
+
+ return 0;
+}
CxMempool *cxMempoolCreate(
size_t capacity,
- cx_destructor_func destr
+ enum cx_mempool_type type
) {
+ if (capacity == 0) capacity = 16;
size_t poolsize;
- if (cx_szmul(capacity, sizeof(struct cx_mempool_memory_s*), &poolsize)) {
+ if (cx_szmul(capacity, sizeof(void*), &poolsize)) {
+ // LCOV_EXCL_START
errno = EOVERFLOW;
return NULL;
- }
-
- struct cx_mempool_s *pool =
- malloc(sizeof(struct cx_mempool_s));
- if (pool == NULL) return NULL;
+ } // LCOV_EXCL_STOP
- CxAllocator *provided_allocator = malloc(sizeof(CxAllocator));
+ CxAllocator *provided_allocator = cxMallocDefault(sizeof(CxAllocator));
if (provided_allocator == NULL) { // LCOV_EXCL_START
- free(pool);
return NULL;
} // LCOV_EXCL_STOP
- provided_allocator->cl = &cx_mempool_allocator_class;
- provided_allocator->data = pool;
+ CxMempool *pool = cxCallocDefault(1, sizeof(CxMempool));
+ if (pool == NULL) { // LCOV_EXCL_START
+ cxFreeDefault(provided_allocator);
+ return NULL;
+ } // LCOV_EXCL_STOP
+
+ provided_allocator->data = pool;
+ *((const CxAllocator**)&pool->base_allocator) = cxDefaultAllocator;
pool->allocator = provided_allocator;
+ if (type == CX_MEMPOOL_TYPE_SIMPLE) {
+ provided_allocator->cl = &cx_mempool_simple_allocator_class;
+ } else if (type == CX_MEMPOOL_TYPE_ADVANCED) {
+ provided_allocator->cl = &cx_mempool_advanced_allocator_class;
+ } else {
+ provided_allocator->cl = &cx_mempool_pure_allocator_class;
+ }
- pool->data = malloc(poolsize);
+ pool->data = cxMallocDefault(poolsize);
if (pool->data == NULL) { // LCOV_EXCL_START
- free(provided_allocator);
- free(pool);
+ cxFreeDefault(provided_allocator);
+ cxFreeDefault(pool);
return NULL;
} // LCOV_EXCL_STOP
pool->size = 0;
pool->capacity = capacity;
- pool->auto_destr = destr;
return pool;
}
+void cxMempoolGlobalDestructor(CxMempool *pool, cx_destructor_func fnc) {
+ pool->destr = fnc;
+}
+
+void cxMempoolGlobalDestructor2(CxMempool *pool, cx_destructor_func2 fnc, void *data) {
+ pool->destr2 = fnc;
+ pool->destr2_data = data;
+}
+
+static void cx_mempool_free_transferred_allocator(void *base_al, void *al) {
+ cxFree(base_al, al);
+}
+
int cxMempoolTransfer(
CxMempool *source,
CxMempool *dest
) {
- // safety check
+ // safety checks
if (source == dest) return 1;
+ if (source->allocator->cl != dest->allocator->cl) return 1;
+ if (source->base_allocator->cl != dest->base_allocator->cl) return 1;
// ensure enough capacity in the destination pool
- if (cx_mempool_ensure_capacity(dest, dest->size + source->size + 1)) {
+ if (cx_mempool_ensure_capacity(dest, dest->size + source->size)) {
+ return 1; // LCOV_EXCL_LINE
+ }
+ if (cx_mempool_ensure_registered_capacity(dest,
+ dest->registered_size + source->registered_size)) {
return 1; // LCOV_EXCL_LINE
}
// allocate a replacement allocator for the source pool
- CxAllocator *new_source_allocator = malloc(sizeof(CxAllocator));
+ CxAllocator *new_source_allocator =
+ cxMalloc(source->base_allocator, sizeof(CxAllocator));
if (new_source_allocator == NULL) { // LCOV_EXCL_START
return 1;
} // LCOV_EXCL_STOP
- new_source_allocator->cl = &cx_mempool_allocator_class;
+ new_source_allocator->cl = source->allocator->cl;
new_source_allocator->data = source;
// transfer all the data
- memcpy(&dest->data[dest->size], source->data, sizeof(source->data[0])*source->size);
+ memcpy(&dest->data[dest->size], source->data, sizeof(void*)*source->size);
dest->size += source->size;
+ // transfer all registered memory
+ memcpy(&dest->registered[dest->registered_size], source->registered,
+ sizeof(struct cx_mempool_foreign_memory_s) * source->size);
+ dest->registered_size += source->registered_size;
+
// register the old allocator with the new pool
// we have to remove const-ness for this, but that's okay here
+ // also register the base allocator, s.t. the pool knows how to free it
CxAllocator *transferred_allocator = (CxAllocator*) source->allocator;
transferred_allocator->data = dest;
- cxMempoolRegister(dest, transferred_allocator, free);
+ cxMempoolRegister2(dest, transferred_allocator,
+ cx_mempool_free_transferred_allocator, (void*)source->base_allocator);
// prepare the source pool for re-use
source->allocator = new_source_allocator;
- memset(source->data, 0, source->size * sizeof(source->data[0]));
+ memset(source->data, 0, source->size * sizeof(void*));
+ memset(source->registered, 0,
+ source->registered_size * sizeof(struct cx_mempool_foreign_memory_s));
source->size = 0;
+ source->registered_size = 0;
return 0;
}
CxMempool *dest,
const void *obj
) {
- // safety check
+ // safety checks
if (source == dest) return 1;
+ if (source->allocator->cl != dest->allocator->cl) return 1;
+ if (source->base_allocator->cl != dest->base_allocator->cl) return 1;
- // first, make sure that the dest pool can take the object
- if (cx_mempool_ensure_capacity(dest, dest->size + 1)) {
- return 1; // LCOV_EXCL_LINE
- }
// search for the object
for (size_t i = 0; i < source->size; i++) {
struct cx_mempool_memory_s *mem = source->data[i];
if (mem->c == obj) {
+ // first, make sure that the dest pool can take the object
+ if (cx_mempool_ensure_capacity(dest, dest->size + 1)) {
+ return 1; // LCOV_EXCL_LINE
+ }
// remove from the source pool
size_t last_index = source->size - 1;
if (i != last_index) {
return 0;
}
}
+ // search in the registered objects
+ for (size_t i = 0; i < source->registered_size; i++) {
+ struct cx_mempool_foreign_memory_s *mem = &source->registered[i];
+ if (mem->mem == obj) {
+ // first, make sure that the dest pool can take the object
+ if (cx_mempool_ensure_registered_capacity(dest,
+ dest->registered_size + 1)) {
+ return 1; // LCOV_EXCL_LINE
+ }
+ dest->registered[dest->registered_size++] = *mem;
+ // remove from the source pool
+ size_t last_index = source->registered_size - 1;
+ if (i != last_index) {
+ source->registered[i] = source->registered[last_index];
+ memset(&source->registered[last_index], 0,
+ sizeof(struct cx_mempool_foreign_memory_s));
+ }
+ source->registered_size--;
+ return 0;
+ }
+ }
// not found
return 1;
}
return (int) wfc(buf, 1, ret, stream);
} else {
int len = ret + 1;
- char *newbuf = malloc(len);
+ char *newbuf = cxMallocDefault(len);
if (!newbuf) { // LCOV_EXCL_START
va_end(ap2);
return -1;
if (ret > 0) {
ret = (int) wfc(newbuf, 1, ret, stream);
}
- free(newbuf);
+ cxFreeDefault(newbuf);
}
return ret;
}
if (s.ptr) {
ret = vsnprintf(s.ptr, len, fmt, ap2);
if (ret < 0) {
- free(s.ptr);
+ cxFree(a, s.ptr);
s.ptr = NULL;
} else {
s.length = (size_t) ret;
cx_attr_unused CxProperties *prop,
CxPropertiesSource *src
) {
- src->data_ptr = malloc(src->data_size);
+ src->data_ptr = cxMallocDefault(src->data_size);
if (src->data_ptr == NULL) return 1;
return 0;
}
cx_attr_unused CxProperties *prop,
CxPropertiesSource *src
) {
- free(src->data_ptr);
+ cxFreeDefault(src->data_ptr);
}
CxPropertiesSource cxPropertiesStringSource(cxstring str) {
*/
#include "cx/streams.h"
+#include "cx/allocator.h"
#ifndef CX_STREAM_BCOPY_BUF_SIZE
#define CX_STREAM_BCOPY_BUF_SIZE 8192
lbuf = buf;
} else {
if (bufsize == 0) bufsize = CX_STREAM_BCOPY_BUF_SIZE;
- lbuf = malloc(bufsize);
+ lbuf = cxMallocDefault(bufsize);
if (lbuf == NULL) return 0;
}
}
if (lbuf != buf) {
- free(lbuf);
+ cxFreeDefault(lbuf);
}
return ncp;
void cx_strfree(cxmutstr *str) {
if (str == NULL) return;
- free(str->ptr);
+ cxFreeDefault(str->ptr);
str->ptr = NULL;
str->length = 0;
}
// check needle length and use appropriate prefix table
// if the pattern exceeds static prefix table, allocate on the heap
const bool useheap = needle.length >= CX_STRSTR_SBO_SIZE;
- register size_t *ptable = useheap ? calloc(needle.length + 1,
- sizeof(size_t)) : s_prefix_table;
+ register size_t *ptable = useheap
+ ? cxCallocDefault(needle.length + 1, sizeof(size_t))
+ : s_prefix_table;
// keep counter in registers
register size_t i, j;
// if prefix table was allocated on the heap, free it
if (useheap) {
- free(ptable);
+ cxFreeDefault(ptable);
}
return result;
if (children == NULL) {
// search for the next node
- void *next;
+ void *next = NULL;
cx_tree_iter_search_next:
// check if there is a sibling, but only if we are not a (subtree-)root
if (iter->exiting) {
// invalidate the iterator and free the node stack
iter->node = iter->node_next = NULL;
iter->stack_capacity = iter->depth = 0;
- free(iter->stack);
+ cxFreeDefault(iter->stack);
iter->stack = NULL;
} else {
// the parent node can be obtained from the top of stack
iter.node = root;
if (root != NULL) {
iter.stack_capacity = 16;
- iter.stack = malloc(sizeof(void *) * 16);
+ iter.stack = cxMallocDefault(sizeof(void *) * 16);
iter.stack[0] = root;
iter.counter = 1;
iter.depth = 1;
node = tree_next(node);
while (node != NULL) {
struct cx_tree_visitor_queue_s *q;
- q = malloc(sizeof(struct cx_tree_visitor_queue_s));
+ q = cxMallocDefault(sizeof(struct cx_tree_visitor_queue_s));
q->depth = iter->queue_last->depth;
q->node = node;
iter->queue_last->next = q;
}
if (children != NULL) {
struct cx_tree_visitor_queue_s *q;
- q = malloc(sizeof(struct cx_tree_visitor_queue_s));
+ q = cxMallocDefault(sizeof(struct cx_tree_visitor_queue_s));
q->depth = iter->depth + 1;
q->node = children;
if (iter->queue_last == NULL) {
assert(iter->queue_last == q);
iter->queue_last = NULL;
}
- free(q);
+ cxFreeDefault(q);
}
// increment the node counter
}
void* ui_document_new(size_t size) {
- CxMempool *mp = cxMempoolCreate(256, NULL);
+ CxMempool *mp = cxMempoolCreateSimple(256);
const CxAllocator *a = mp->allocator;
UiContext *ctx = uic_context(NULL, mp);