static int qthr_load_attachments(LoadAttachmentsJob *job) {
- //CxMempool *mp = cxMempoolCreateSimple(32);
- //job->temp_mp = mp;
- // TODO: use separate mempool and transfer it to the resource mempool
+ CxMempool *mp = cxMempoolCreateSimple(32);
+ job->temp_mp = mp;
DBUQuery *q = connection->createQuery(connection, NULL);
dbuQuerySetSQL(q, SQL_ATTACHMENTS_GET);
dbuQuerySetParamInt64(q, 1, job->note->resource_id);
- DBUObjectBuilder *builder = dbuObjectBuilder(attachments_class, q, job->note->model->note_allocator); // use mp mempool allocator
+ DBUObjectBuilder *builder = dbuObjectBuilder(attachments_class, q, mp->allocator);
job->result = dbuObjectBuilderGetList(builder);
if(!job->result) {
job->error = 1;
static void uithr_load_attachments_finished(UiEvent *event, LoadAttachmentsJob *job) {
if(job->result) {
- job->note->attachments = job->result; // TODO: transfer temp_mp first
+ CxMempool *note_mp = job->note->model->note_allocator->data;
+ cxMempoolTransfer(job->temp_mp, note_mp);
+ cxMempoolFree(job->temp_mp);
+ job->note->attachments = job->result;
}
if(job->resultcb) {
npos = 0;
break;
default:
+ errno = EINVAL;
return -1;
}
}
int cxBufferTerminate(CxBuffer *buffer) {
- bool success = 0 == cxBufferPut(buffer, 0);
- if (success) {
- buffer->pos--;
- buffer->size--;
+ if (0 == cxBufferPut(buffer, 0)) {
+ buffer->size = buffer->pos - 1;
return 0;
} else {
return -1;
*
* If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of
* copies of the added elements and the compare function will be automatically set
- * to cx_cmp_ptr(), if none is given.
+ * to cx_cmp_ptr().
*
* @param elem_size (@c size_t) the size of each element in bytes
* @param initial_capacity (@c size_t) the initial number of elements the array can store
/**
* Writes a terminating zero to a buffer at the current position.
*
- * On successful write, @em neither the position @em nor the size of the buffer is
- * increased.
+ * If successful, sets the size to the current position and advances the position by one.
*
* The purpose of this function is to have the written data ready to be used as
- * a C string.
+ * a C string with the buffer's size being the length of that string.
*
* @param buffer the buffer to write to
* @return zero, if the terminator could be written, non-zero otherwise
* Repositories:<br>
* <a href="https://sourceforge.net/p/ucx/code">https://sourceforge.net/p/ucx/code</a>
* - or -
- * <a href="https://develop.uap-core.de/hg/ucx">https://develop.uap-core.de/hg/ucx</a>
+ * <a href="https://uap-core.de/hg/ucx">https://uap-core.de/hg/ucx</a>
* </p>
*
* <h2>LICENCE</h2>
*/
#define cx_attr_malloc __attribute__((__malloc__))
-#ifndef __clang__
+#if !defined(__clang__) && __GNUC__ >= 11
/**
* The pointer returned by the attributed function is supposed to be freed
* by @p freefunc.
*
* If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of
* copies of the added elements and the compare function will be automatically set
- * to cx_cmp_ptr(), if none is given.
+ * to cx_cmp_ptr().
*
* @param elem_size (@c size_t) the size of each element in bytes
* @return (@c CxList*) the created list
* @li @p loc_next and @p loc_prev (ancestor node is determined by using the prev pointer, overall O(1) performance)
* @li @p loc_next and @p begin (ancestor node is determined by list traversal, overall O(n) performance)
*
- * @remark The @c next and @c prev pointers of the removed node are not cleared by this function and may still be used
+ * @remark The @c next and @c prev pointers of the removed chain are not cleared by this function and may still be used
* to traverse to a former adjacent node in the list, or within the chain.
*
* @param begin a pointer to the beginning node pointer (optional)
*/
cx_attr_nonnull
static inline void cxListSort(CxList *list) {
+ if (list->collection.sorted) return;
list->cl->sort(list);
list->collection.sorted = true;
}
cx_destructor_func destr
);
+/**
+ * Transfers all the memory managed by one pool to another.
+ *
+ * The allocator of the source pool will also be transferred and registered with the destination pool
+ * and stays valid, as long as the destination pool is not destroyed.
+ *
+ * The source pool will get a completely new allocator and can be reused or destroyed afterward.
+ *
+ * @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
+ */
+cx_attr_nonnull
+cx_attr_export
+int cxMempoolTransfer(
+ CxMempool *source,
+ CxMempool *dest
+);
+
+/**
+ * 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.
+ *
+ * @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 source the pool to move the memory from
+ * @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
+ */
+cx_attr_nonnull
+cx_attr_export
+int cxMempoolTransferObject(
+ CxMempool *source,
+ CxMempool *dest,
+ const void *obj
+);
+
#ifdef __cplusplus
} // extern "C"
#endif
cx_attr_cstr_arg(4)
cx_attr_export
int cx_sprintf_a(
- CxAllocator *alloc,
+ const CxAllocator *alloc,
char **str,
size_t *len,
const char *fmt,
cx_attr_access_rw(3)
cx_attr_export
int cx_vsprintf_a(
- CxAllocator *alloc,
+ const CxAllocator *alloc,
char **str,
size_t *len,
const char *fmt,
cx_attr_access_rw(4)
cx_attr_export
int cx_sprintf_sa(
- CxAllocator *alloc,
+ const CxAllocator *alloc,
char *buf,
size_t *len,
char **str,
cx_attr_cstr_arg(5)
cx_attr_export
int cx_vsprintf_sa(
- CxAllocator *alloc,
+ const CxAllocator *alloc,
char *buf,
size_t *len,
char **str,
/**
* Creates a properties sink for an UCX map.
*
- * The values stored in the map will be pointers to strings allocated
- * by #cx_strdup_a().
+ * The values stored in the map will be pointers to freshly allocated,
+ * 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
- * allocator in the optional @c data of the sink.
+ * allocator in the optional @c data field of the returned sink.
*
* @param map the map that shall consume the k/v-pairs.
* @return the sink
*
* @param literal the string literal
*/
-#define CX_STR(literal) (cxstring){literal, sizeof(literal) - 1}
+#define CX_STR(literal) ((cxstring){literal, sizeof(literal) - 1})
#endif
* @see cx_strfree_a()
*/
#define cx_strdup_a(allocator, string) \
- cx_strdup_a_((allocator), cx_strcast((string)))
+ cx_strdup_a_((allocator), cx_strcast(string))
/**
* Creates a duplicate of the specified string.
* @see cx_strdup_a()
* @see cx_strfree()
*/
-#define cx_strdup(string) cx_strdup_a_(cxDefaultAllocator, string)
+#define cx_strdup(string) cx_strdup_a(cxDefaultAllocator, string)
/**
* Omits leading and trailing spaces.
* @return (@c CxStrtokCtx) a new string tokenization context
*/
#define cx_strtok(str, delim, limit) \
- cx_strtok_(cx_strcast((str)), cx_strcast((delim)), (limit))
+ cx_strtok_(cx_strcast(str), cx_strcast(delim), (limit))
/**
* Returns the next token.
const void *elem,
bool remove
) {
+ if (list->collection.size == 0) return 0;
+
size_t index;
cx_linked_list *ll = ((cx_linked_list *) list);
cx_linked_list_node *node = cx_linked_list_find(
char c[];
};
+static int cx_mempool_ensure_capacity(
+ struct cx_mempool_s *pool,
+ size_t needed_capacity
+) {
+ if (needed_capacity <= pool->capacity) return 0;
+ 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)) {
+ errno = EOVERFLOW;
+ return 1;
+ }
+ struct cx_mempool_memory_s **newdata = realloc(pool->data, newmsize);
+ if (newdata == NULL) return 1;
+ pool->data = newdata;
+ pool->capacity = newcap;
+ return 0;
+}
+
static void *cx_mempool_malloc(
void *p,
size_t n
) {
struct cx_mempool_s *pool = p;
- if (pool->size >= pool->capacity) {
- size_t newcap = pool->capacity - (pool->capacity % 16) + 16;
- size_t newmsize;
- if (pool->capacity > newcap || cx_szmul(newcap,
- sizeof(struct cx_mempool_memory_s*), &newmsize)) {
- errno = EOVERFLOW;
- return NULL;
- }
- struct cx_mempool_memory_s **newdata = realloc(pool->data, newmsize);
- if (newdata == NULL) return NULL;
- pool->data = newdata;
- pool->capacity = newcap;
+ if (cx_mempool_ensure_capacity(pool, pool->size + 1)) {
+ return NULL;
}
struct cx_mempool_memory_s *mem = malloc(sizeof(cx_destructor_func) + n);
return pool;
}
+
+int cxMempoolTransfer(
+ CxMempool *source,
+ CxMempool *dest
+) {
+ // safety check
+ if (source == dest) return 1;
+
+ // ensure enough capacity in the destination pool
+ if (cx_mempool_ensure_capacity(dest, dest->size + source->size + 1)) {
+ return 1; // LCOV_EXCL_LINE
+ }
+
+ // allocate a replacement allocator for the source pool
+ CxAllocator *new_source_allocator = malloc(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->data = source;
+
+ // transfer all the data
+ memcpy(&dest->data[dest->size], source->data, sizeof(source->data[0])*source->size);
+ dest->size += source->size;
+
+ // register the old allocator with the new pool
+ // we have to remove const-ness for this, but that's okay here
+ CxAllocator *transferred_allocator = (CxAllocator*) source->allocator;
+ transferred_allocator->data = dest;
+ cxMempoolRegister(dest, transferred_allocator, free);
+
+ // prepare the source pool for re-use
+ source->allocator = new_source_allocator;
+ memset(source->data, 0, source->size * sizeof(source->data[0]));
+ source->size = 0;
+
+ return 0;
+}
+
+int cxMempoolTransferObject(
+ CxMempool *source,
+ CxMempool *dest,
+ const void *obj
+) {
+ // safety check
+ if (source == dest) 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) {
+ // remove from the source pool
+ size_t last_index = source->size - 1;
+ if (i != last_index) {
+ source->data[i] = source->data[last_index];
+ source->data[last_index] = NULL;
+ }
+ source->size--;
+ // add to the target pool
+ dest->data[dest->size++] = mem;
+ return 0;
+ }
+ }
+ // not found
+ return 1;
+}
return s;
}
-int cx_sprintf_a(CxAllocator *alloc, char **str, size_t *len, const char *fmt, ... ) {
+int cx_sprintf_a(
+ const CxAllocator *alloc,
+ char **str,
+ size_t *len,
+ const char *fmt,
+ ...
+) {
va_list ap;
va_start(ap, fmt);
int ret = cx_vsprintf_a(alloc, str, len, fmt, ap);
return ret;
}
-int cx_vsprintf_a(CxAllocator *alloc, char **str, size_t *len, const char *fmt, va_list ap) {
+int cx_vsprintf_a(
+ const CxAllocator *alloc,
+ char **str,
+ size_t *len,
+ const char *fmt,
+ va_list ap
+) {
va_list ap2;
va_copy(ap2, ap);
int ret = vsnprintf(*str, *len, fmt, ap);
return ret;
}
-int cx_sprintf_sa(CxAllocator *alloc, char *buf, size_t *len, char **str, const char *fmt, ... ) {
+int cx_sprintf_sa(
+ const CxAllocator *alloc,
+ char *buf,
+ size_t *len,
+ char **str,
+ const char *fmt,
+ ...
+) {
va_list ap;
va_start(ap, fmt);
int ret = cx_vsprintf_sa(alloc, buf, len, str, fmt, ap);
return ret;
}
-int cx_vsprintf_sa(CxAllocator *alloc, char *buf, size_t *len, char **str, const char *fmt, va_list ap) {
+int cx_vsprintf_sa(
+ const CxAllocator *alloc,
+ char *buf,
+ size_t *len,
+ char **str,
+ const char *fmt,
+ va_list ap
+) {
va_list ap2;
va_copy(ap2, ap);
int ret = vsnprintf(buf, *len, fmt, ap);