bool file_args = 0;
if(argc > 1) {
- open_file_arg = cxArrayListCreateSimple(CX_STORE_POINTERS, argc-1);
+ open_file_arg = cxArrayListCreate(NULL, CX_STORE_POINTERS, argc-1);
for(int i=1;i<argc;i++) {
char *arg = argv[i];
if(file_args || arg[0] != '-') {
static int json_strcmp(CxJsonValue *value, const char *str) {
if(value->type != CX_JSON_STRING) return 1;
- return cx_strcmp(value->value.string, str);
+ return cx_strcmp(value->string, str);
}
void PlayerOpenFile(MainWindow *win) {
CxJsonValue *event = NULL;
if(request_id_v && request_id_v->type == CX_JSON_STRING) {
int request_id = 0;
- if(request_id_v->value.string.length == 2) {
- request_id = 10 * (request_id_v->value.string.ptr[0] - '0') + (request_id_v->value.string.ptr[1] - '0');
+ if(request_id_v->string.length == 2) {
+ request_id = 10 * (request_id_v->string.ptr[0] - '0') + (request_id_v->string.ptr[1] - '0');
handle_json_rpc_reqid(player, v, request_id);
return;
}
switch(reqid) {
case REQ_ID_PLAYBACK_TIME_INT: {
if(data->type == CX_JSON_NUMBER) {
- player->playback_time = data->value.number;
+ player->playback_time = data->number;
}
break;
}
case REQ_ID_WIDTH_INT: {
if(data->type == CX_JSON_INTEGER) {
- player_set_size(player, data->value.integer, -1);
+ player_set_size(player, data->integer, -1);
}
break;
}
case REQ_ID_HEIGHT_INT: {
if(data->type == CX_JSON_INTEGER) {
- player_set_size(player, -1, data->value.integer);
+ player_set_size(player, -1, data->integer);
}
break;
}
CxJsonValue *data = cxJsonObjGet(v, "data");
if(!json_strcmp(name, "playback-time")) {
if(data && data->type == CX_JSON_NUMBER) {
- p->playback_time = data->value.number;
+ p->playback_time = data->number;
//printf("playback-time: %f\n", p->playback_time);
check_hide_cursor(p);
}
} else if(!json_strcmp(name, "eof-reached")) {
- if(data && data->type == CX_JSON_LITERAL && data->value.literal == CX_JSON_TRUE) {
+ if(data && data->type == CX_JSON_LITERAL && data->literal == CX_JSON_TRUE) {
PlayerEOF(p);
}
} else if(!json_strcmp(name, "osd-height")) {
if(data->type == CX_JSON_NUMBER) {
- p->osd_height = data->value.number;
+ p->osd_height = data->number;
}
}
} else if(!p->isstarted && !json_strcmp(event, "playback-restart")) {
free(p);
}
-
-static void json_print(CxJsonValue *value, char *name, int indent) {
- if(name) {
- printf("%*s%s: ", indent*4, "", name);
- } else {
- printf("%*s", indent*4, "");
- }
-
-
- switch(value->type) {
- case CX_JSON_OBJECT: {
- printf("{\n");
-
- for(int i=0;i<value->value.object.values_size;i++) {
- CxJsonObjValue val = value->value.object.values[i];
- json_print(val.value, val.name.ptr, indent+1);
- if(i+1 < value->value.object.values_size) {
- printf(",\n");
- } else {
- printf("\n");
- }
- }
-
- printf("%*s}", indent*4, "");
- break;
- }
- case CX_JSON_ARRAY: {
- printf("[\n");
-
- for(int i=0;i<value->value.array.array_size;i++) {
- CxJsonValue *v = value->value.array.array[i];
- json_print(v, NULL, indent+1);
- if(i+1 < value->value.array.array_size) {
- printf(",\n");
- } else {
- printf("\n");
- }
- }
-
- printf("%*s]", indent*4, "");
- break;
- }
- case CX_JSON_STRING: {
- printf("\"%.*s\"", (int)value->value.string.length, value->value.string.ptr);
- break;
- }
- case CX_JSON_INTEGER: {
- printf("%i", (int)value->value.integer);
- break;
- }
- case CX_JSON_NUMBER: {
- printf("%f", (float)value->value.number);
- break;
- }
- case CX_JSON_LITERAL: {
- char *lit = "NULL";
- switch(value->value.literal) {
- case CX_JSON_NULL: break;
- case CX_JSON_TRUE: lit = "true"; break;
- case CX_JSON_FALSE: lit = "false"; break;
- }
- printf("%s\n", lit);
- break;
- }
- }
-
- if(indent == 0) {
- putchar('\n');
- }
-}
-
static int play_next(void *data) {
MainWindow *win = GetMainWindow();
PlayListPlayNext(win, false);
#include <cx/linked_list.h>
void PlayListInit(MainWindow *win) {
- win->playlist.tracks = cxArrayListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS, 64);
+ win->playlist.tracks = cxArrayListCreate(cxDefaultAllocator, CX_STORE_POINTERS, 64);
win->playlist.tracks->collection.simple_destructor = free;
- win->playlist.tracks->collection.cmpfunc = (cx_compare_func)strcmp;
- win->playlist.history = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS);
+ cxSetCompareFunc(win->playlist.tracks, (cx_compare_func)strcmp);
+ win->playlist.history = cxLinkedListCreate(cxDefaultAllocator, CX_STORE_POINTERS);
win->playlist.history->collection.simple_destructor = free;
- win->playlist.history->collection.cmpfunc = (cx_compare_func)strcmp;
+ cxSetCompareFunc(win->playlist.history, (cx_compare_func)strcmp);
win->playlist.current_track = -1;
win->playlist.current_history_pos = -1;
}
return;
}
- CxJsonObject *s = &settings->value.object;
-
- for(size_t i=0;i<s->values_size;i++) {
- CxJsonObjValue *gs = &s->values[i];
- if(gs->value->type == CX_JSON_STRING) {
- cxMapPut(uwp_settings, gs->name, cx_strdup(gs->value->value.string).ptr);
+ CxMapIterator i = cxMapIterator(settings->object);
+ cx_foreach(CxMapEntry *, entry, i) {
+ CxJsonValue *v = entry->value;
+ if(cxJsonIsString(v)) {
+ cxMapPut(uwp_settings, *entry->key, cx_strdup(v->string).ptr);
}
}
}
static void* player_bin_search_thread(void *data) {
CxBuffer buf;
- cxBufferInit(&buf, NULL, 256, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS);
+ cxBufferInit(&buf, cxDefaultAllocator, NULL, 256, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS);
FILE *f = popen("which mpv", "r");
if(f) {
}
if(msg.length > 0) {
- if(cx_strprefix(msg, CX_STR("open "))) {
+ if(cx_strprefix(msg, "open ")) {
cxstring file = cx_strsubs(msg, 5);
cxmutstr mfile = cx_strdup(file);
ui_call_mainthread(cmd_open, mfile.ptr);
listen(instance_socket, 8);
CxBuffer msgbuf;
- cxBufferInit(&msgbuf, NULL, 1024, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS);
+ cxBufferInit(&msgbuf, cxDefaultAllocator, NULL, 1024, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS);
char buf[1024];
memset(window, 0, sizeof(MainWindow));
main_window = window;
- UiObject *obj = ui_window("uwplayer", NULL);
+ UiObject *obj = ui_window("uwplayer");
UiContext *ctx = obj->ctx;
obj->window = window;
window->obj = obj;
#include <errno.h>
#include <string.h>
+#ifdef _WIN32
+#include <Windows.h>
+#include <sysinfoapi.h>
+unsigned long cx_system_page_size(void) {
+ static unsigned long ps = 0;
+ if (ps == 0) {
+ SYSTEM_INFO sysinfo;
+ GetSystemInfo(&sysinfo);
+ ps = (unsigned long) sysinfo.dwPageSize;
+ }
+ return ps;
+}
+#else
+#include <unistd.h>
+unsigned long cx_system_page_size(void) {
+ static unsigned long ps = 0;
+ if (ps == 0) {
+ long sc = sysconf(_SC_PAGESIZE);
+ if (sc < 0) {
+ // fallback for systems which do not report a value here
+ ps = 4096; // LCOV_EXCL_LINE
+ } else {
+ ps = (unsigned long) sc;
+ }
+ }
+ return ps;
+}
+#endif
+
static void *cx_malloc_stdlib(
cx_attr_unused void *d,
size_t n
void **mem,
size_t n
) {
+ if (n == 0) {
+ free(*mem);
+ *mem = NULL;
+ return 0;
+ }
void *nmem = realloc(*mem, n);
if (nmem == NULL) {
return 1; // LCOV_EXCL_LINE
size_t nmemb,
size_t size
) {
+ if (nmemb == 0 || size == 0) {
+ free(*mem);
+ *mem = NULL;
+ return 0;
+ }
size_t n;
if (cx_szmul(nmemb, size, &n)) {
errno = EOVERFLOW;
void **mem,
size_t n
) {
+ if (n == 0) {
+ cxFree(allocator, *mem);
+ *mem = NULL;
+ return 0;
+ }
void *nmem = allocator->cl->realloc(allocator->data, *mem, n);
if (nmem == NULL) {
return 1; // LCOV_EXCL_LINE
size_t nmemb,
size_t size
) {
+ if (nmemb == 0 || size == 0) {
+ cxFree(allocator, *mem);
+ *mem = NULL;
+ return 0;
+ }
void *nmem = cxReallocArray(allocator, *mem, nmemb, size);
if (nmem == NULL) {
return 1; // LCOV_EXCL_LINE
) {
allocator->cl->free(allocator->data, mem);
}
+
+void cxFreeDefault(void *mem) {
+ cxDefaultAllocator->cl->free(cxDefaultAllocator->data, mem);
+}
* POSSIBILITY OF SUCH DAMAGE.
*/
+#ifdef WITH_MEMRCHR
+#define _GNU_SOURCE
+#endif
+
#include "cx/array_list.h"
#include "cx/compare.h"
#include <assert.h>
#include <string.h>
#include <errno.h>
-// Default array reallocator
-
-static void *cx_array_default_realloc(
- void *array,
- cx_attr_unused size_t old_capacity,
- size_t new_capacity,
- size_t elem_size,
- cx_attr_unused CxArrayReallocator *alloc
-) {
- size_t n;
- // LCOV_EXCL_START
- if (cx_szmul(new_capacity, elem_size, &n)) {
- errno = EOVERFLOW;
- return NULL;
- } // LCOV_EXCL_STOP
- return cxReallocDefault(array, n);
-}
-
-CxArrayReallocator cx_array_default_reallocator_impl = {
- cx_array_default_realloc, NULL, NULL
-};
-
-CxArrayReallocator *cx_array_default_reallocator = &cx_array_default_reallocator_impl;
-
-// Stack-aware array reallocator
-
-static void *cx_array_advanced_realloc(
- void *array,
- size_t old_capacity,
- size_t new_capacity,
- size_t elem_size,
- cx_attr_unused CxArrayReallocator *alloc
-) {
- // check for overflow
- size_t n;
- // LCOV_EXCL_START
- if (cx_szmul(new_capacity, elem_size, &n)) {
- errno = EOVERFLOW;
- return NULL;
- } // LCOV_EXCL_STOP
-
- // retrieve the pointer to the actual allocator
- const CxAllocator *al = alloc->allocator;
-
- // check if the array is still located on the stack
- void *newmem;
- if (array == alloc->stack_ptr) {
- newmem = cxMalloc(al, n);
- if (newmem != NULL && array != NULL) {
- memcpy(newmem, array, old_capacity*elem_size);
- }
- } else {
- newmem = cxRealloc(al, array, n);
- }
- return newmem;
-}
-
-struct cx_array_reallocator_s cx_array_reallocator(
- const struct cx_allocator_s *allocator,
- const void *stack_ptr
-) {
- if (allocator == NULL) {
- allocator = cxDefaultAllocator;
- }
- return (struct cx_array_reallocator_s) {
- cx_array_advanced_realloc,
- allocator, stack_ptr,
- };
-}
-
// LOW LEVEL ARRAY LIST FUNCTIONS
/**
return cap - (cap % alignment) + alignment;
}
-int cx_array_reserve(
- void **array,
- void *size,
- void *capacity,
- unsigned width,
- size_t elem_size,
- size_t elem_count,
- CxArrayReallocator *reallocator
-) {
- // assert pointers
- assert(array != NULL);
- assert(size != NULL);
- assert(capacity != NULL);
+int cx_array_init_(const CxAllocator *allocator, CxArray *array, size_t elem_size, size_t capacity) {
+ memset(array, 0, sizeof(CxArray));
+ return cx_array_reserve_(allocator, array, elem_size, capacity);
+}
- // default reallocator
- if (reallocator == NULL) {
- reallocator = cx_array_default_reallocator;
- }
+void cx_array_init_fixed_(CxArray *array, const void *data, size_t capacity, size_t size) {
+ array->data = (void*) data;
+ array->capacity = capacity;
+ array->size = size;
+}
- // determine size and capacity
- size_t oldcap;
- size_t oldsize;
- size_t max_size;
- if (width == 0 || width == sizeof(size_t)) {
- oldcap = *(size_t*) capacity;
- oldsize = *(size_t*) size;
- max_size = SIZE_MAX;
- } else if (width == sizeof(uint16_t)) {
- oldcap = *(uint16_t*) capacity;
- oldsize = *(uint16_t*) size;
- max_size = UINT16_MAX;
- } else if (width == sizeof(uint8_t)) {
- oldcap = *(uint8_t*) capacity;
- oldsize = *(uint8_t*) size;
- max_size = UINT8_MAX;
+int cx_array_reserve_(const CxAllocator *allocator, CxArray *array, size_t elem_size, size_t capacity) {
+ if (cxReallocateArray(allocator, &array->data, capacity, elem_size)) {
+ return -1; // LCOV_EXCL_LINE
}
-#if CX_WORDSIZE == 64
- else if (width == sizeof(uint32_t)) {
- oldcap = *(uint32_t*) capacity;
- oldsize = *(uint32_t*) size;
- max_size = UINT32_MAX;
- }
-#endif
- else {
- errno = EINVAL;
- return 1;
+ array->capacity = capacity;
+ if (array->size > capacity) {
+ array->size = capacity;
}
-
- // assert that the array is allocated when it has capacity
- assert(*array != NULL || oldcap == 0);
-
- // check for overflow
- if (elem_count > max_size - oldsize) {
- errno = EOVERFLOW;
- return 1;
- }
-
- // determine new capacity
- size_t newcap = oldsize + elem_count;
-
- // reallocate if possible
- if (newcap > oldcap) {
- void *newmem = reallocator->realloc(
- *array, oldcap, newcap, elem_size, reallocator
- );
- if (newmem == NULL) {
- return 1; // LCOV_EXCL_LINE
- }
-
- // store new pointer
- *array = newmem;
-
- // store new capacity
- if (width == 0 || width == sizeof(size_t)) {
- *(size_t*) capacity = newcap;
- } else if (width == sizeof(uint16_t)) {
- *(uint16_t*) capacity = (uint16_t) newcap;
- } else if (width == sizeof(uint8_t)) {
- *(uint8_t*) capacity = (uint8_t) newcap;
- }
-#if CX_WORDSIZE == 64
- else if (width == sizeof(uint32_t)) {
- *(uint32_t*) capacity = (uint32_t) newcap;
- }
-#endif
- }
-
return 0;
}
-int cx_array_copy(
- void **target,
- void *size,
- void *capacity,
- unsigned width,
- size_t index,
- const void *src,
- size_t elem_size,
- size_t elem_count,
- CxArrayReallocator *reallocator
-) {
- // assert pointers
- assert(target != NULL);
- assert(size != NULL);
- assert(capacity != NULL);
- assert(src != NULL);
-
- // default reallocator
- if (reallocator == NULL) {
- reallocator = cx_array_default_reallocator;
- }
-
- // determine size and capacity
- size_t oldcap;
- size_t oldsize;
- size_t max_size;
- if (width == 0 || width == sizeof(size_t)) {
- oldcap = *(size_t*) capacity;
- oldsize = *(size_t*) size;
- max_size = SIZE_MAX;
- } else if (width == sizeof(uint16_t)) {
- oldcap = *(uint16_t*) capacity;
- oldsize = *(uint16_t*) size;
- max_size = UINT16_MAX;
- } else if (width == sizeof(uint8_t)) {
- oldcap = *(uint8_t*) capacity;
- oldsize = *(uint8_t*) size;
- max_size = UINT8_MAX;
- }
-#if CX_WORDSIZE == 64
- else if (width == sizeof(uint32_t)) {
- oldcap = *(uint32_t*) capacity;
- oldsize = *(uint32_t*) size;
- max_size = UINT32_MAX;
- }
-#endif
- else {
- errno = EINVAL;
- return 1;
+int cx_array_copy_to_new_(const CxAllocator *allocator, CxArray *array, size_t elem_size, size_t capacity) {
+ CxArray heap_array;
+ if (cx_array_init_(allocator, &heap_array, elem_size, capacity)) {
+ return -1; // LCOV_EXCL_LINE
}
+ heap_array.size = array->size;
+ memcpy(heap_array.data, array->data, elem_size * array->size);
+ *array = heap_array;
+ return 0;
+}
- // assert that the array is allocated when it has capacity
- assert(*target != NULL || oldcap == 0);
+int cx_array_insert_(const CxAllocator *allocator, CxArray *array,
+ size_t elem_size, size_t index, const void *other, size_t n) {
+ // out of bounds and special case check
+ if (index > array->size) return -1;
+ if (n == 0) return 0;
- // check for overflow
- if (index > max_size || elem_count > max_size - index) {
+ // calculate required capacity
+ size_t req_capacity = array->size + n;
+ if (req_capacity <= array->size) {
errno = EOVERFLOW;
- return 1;
+ return -1;
}
- // check if resize is required
- const size_t minsize = index + elem_count;
- const size_t newsize = oldsize < minsize ? minsize : oldsize;
-
- // reallocate if necessary
- const size_t newcap = cx_array_grow_capacity(oldcap, newsize);
- if (newcap > oldcap) {
- // check if we need to repair the src pointer
- uintptr_t targetaddr = (uintptr_t) *target;
- uintptr_t srcaddr = (uintptr_t) src;
- bool repairsrc = targetaddr <= srcaddr
- && srcaddr < targetaddr + oldcap * elem_size;
-
- // perform reallocation
- void *newmem = reallocator->realloc(
- *target, oldcap, newcap, elem_size, reallocator
- );
- if (newmem == NULL) {
- return 1; // LCOV_EXCL_LINE
+ // guarantee enough capacity
+ if (array->capacity < req_capacity) {
+ const size_t new_capacity = cx_array_grow_capacity(array->capacity,req_capacity);
+ if (cxReallocateArray(allocator, &array->data, new_capacity, elem_size)) {
+ return -1; // LCOV_EXCL_LINE
}
+ array->capacity = new_capacity;
+ }
- // repair src pointer, if necessary
- if (repairsrc) {
- src = ((char *) newmem) + (srcaddr - targetaddr);
- }
+ // determine insert position
+ char *dst = array->data;
+ dst += index * elem_size;
- // store new pointer
- *target = newmem;
+ // do we need to move some elements?
+ size_t elems_to_move = array->size - index;
+ if (elems_to_move > 0) {
+ char *target = dst + n * elem_size;
+ memmove(target, dst, elems_to_move * elem_size);
}
- // determine target pointer
- char *start = *target;
- start += index * elem_size;
-
- // copy elements and set new size
- // note: no overflow check here, b/c we cannot get here w/o allocation
- memmove(start, src, elem_count * elem_size);
-
- // if any of size or capacity changed, store them back
- if (newsize != oldsize || newcap != oldcap) {
- if (width == 0 || width == sizeof(size_t)) {
- *(size_t*) capacity = newcap;
- *(size_t*) size = newsize;
- } else if (width == sizeof(uint16_t)) {
- *(uint16_t*) capacity = (uint16_t) newcap;
- *(uint16_t*) size = (uint16_t) newsize;
- } else if (width == sizeof(uint8_t)) {
- *(uint8_t*) capacity = (uint8_t) newcap;
- *(uint8_t*) size = (uint8_t) newsize;
- }
-#if CX_WORDSIZE == 64
- else if (width == sizeof(uint32_t)) {
- *(uint32_t*) capacity = (uint32_t) newcap;
- *(uint32_t*) size = (uint32_t) newsize;
- }
-#endif
+ // place the new elements, if any
+ // otherwise, this function just reserved the memory (a.k.a emplace)
+ if (other != NULL) {
+ memcpy(dst, other, n * elem_size);
}
+ array->size += n;
- // return successfully
return 0;
}
-static int cx_array_insert_sorted_impl(
- void **target,
- size_t *size,
- size_t *capacity,
- cx_compare_func cmp_func,
- const void *sorted_data,
+int cx_array_insert_sorted_c_(
+ const CxAllocator *allocator,
+ CxArray *array,
size_t elem_size,
- size_t elem_count,
- CxArrayReallocator *reallocator,
+ const void *sorted_data,
+ size_t n,
+ cx_compare_func2 cmp_func,
+ void *context,
bool allow_duplicates
) {
// assert pointers
- assert(target != NULL);
- assert(size != NULL);
- assert(capacity != NULL);
+ assert(allocator != NULL);
+ assert(array != NULL);
assert(cmp_func != NULL);
assert(sorted_data != NULL);
- // default reallocator
- if (reallocator == NULL) {
- reallocator = cx_array_default_reallocator;
- }
-
// corner case
- if (elem_count == 0) return 0;
+ if (n == 0) return 0;
// overflow check
// LCOV_EXCL_START
- if (elem_count > SIZE_MAX - *size) {
+ if (n > SIZE_MAX - array->size) {
errno = EOVERFLOW;
return 1;
}
// LCOV_EXCL_STOP
// store some counts
- const size_t old_size = *size;
- const size_t old_capacity = *capacity;
+ const size_t old_size = array->size;
+ const size_t old_capacity = array->capacity;
// the necessary capacity is the worst case assumption, including duplicates
- const size_t needed_capacity = cx_array_grow_capacity(old_capacity, old_size + elem_count);
+ const size_t needed_capacity = cx_array_grow_capacity(old_capacity, old_size + n);
// if we need more than we have, try a reallocation
if (needed_capacity > old_capacity) {
- void *new_mem = reallocator->realloc(
- *target, old_capacity, needed_capacity, elem_size, reallocator
- );
- if (new_mem == NULL) {
- // give it up right away, there is no contract
- // that requires us to insert as much as we can
- return 1; // LCOV_EXCL_LINE
+ if (cxReallocateArray(allocator, &array->data, needed_capacity, elem_size)) {
+ return -1; // LCOV_EXCL_LINE
}
- *target = new_mem;
- *capacity = needed_capacity;
+ array->capacity = needed_capacity;
}
// now we have guaranteed that we can insert everything
- size_t new_size = old_size + elem_count;
- *size = new_size;
+ size_t new_size = old_size + n;
+ array->size = new_size;
// declare the source and destination indices/pointers
size_t si = 0, di = 0;
const char *src = sorted_data;
- char *dest = *target;
+ char *dest = array->data;
// find the first insertion point
- di = cx_array_binary_search_sup(dest, old_size, elem_size, src, cmp_func);
+ di = cx_array_binary_search_sup_c(dest, old_size, elem_size, src, cmp_func, context);
dest += di * elem_size;
// move the remaining elements in the array completely to the right
// we will call it the "buffer" for parked elements
size_t buf_size = old_size - di;
size_t bi = new_size - buf_size;
- char *bptr = ((char *) *target) + bi * elem_size;
+ char *bptr = ((char *) array->data) + bi * elem_size;
memmove(bptr, dest, buf_size * elem_size);
// while there are both source and buffered elements left,
// copy them interleaving
- while (si < elem_count && bi < new_size) {
+ while (si < n && bi < new_size) {
// determine how many source elements can be inserted.
// the first element that shall not be inserted is the smallest element
// that is strictly larger than the first buffered element
// Therefore, the buffer can never contain an element that is smaller
// than any element in the source and the infimum exists.
size_t copy_len, bytes_copied;
- copy_len = cx_array_binary_search_inf(
- src, elem_count - si, elem_size, bptr, cmp_func
+ copy_len = cx_array_binary_search_inf_c(
+ src, n - si, elem_size, bptr, cmp_func, context
);
copy_len++;
// for being a duplicate of the bptr
const char *end_of_src = src + (copy_len - 1) * elem_size;
size_t skip_len = 0;
- while (copy_len > 0 && cmp_func(bptr, end_of_src) == 0) {
+ while (copy_len > 0 && cmp_func(bptr, end_of_src, context) == 0) {
end_of_src -= elem_size;
skip_len++;
copy_len--;
}
- char *last = dest == *target ? NULL : dest - elem_size;
+ char *last = dest == array->data ? NULL : dest - elem_size;
// then iterate through the source chunk
// and skip all duplicates with the last element in the array
size_t more_skipped = 0;
for (unsigned j = 0; j < copy_len; j++) {
- if (last != NULL && cmp_func(last, src) == 0) {
+ if (last != NULL && cmp_func(last, src, context) == 0) {
// duplicate - skip
src += elem_size;
si++;
si += skip_len;
skip_len += more_skipped;
// reduce the actual size by the number of skipped elements
- *size -= skip_len;
+ array->size -= skip_len;
}
}
// when all source elements are in place, we are done
- if (si >= elem_count) break;
+ if (si >= n) break;
// determine how many buffered elements need to be restored
- copy_len = cx_array_binary_search_sup(
+ copy_len = cx_array_binary_search_sup_c(
bptr,
new_size - bi,
elem_size,
src,
- cmp_func
+ cmp_func,
+ context
);
// restore the buffered elements
}
// still source elements left?
- if (si < elem_count) {
+ if (si < n) {
if (allow_duplicates) {
// duplicates allowed or nothing inserted yet: simply copy everything
- memcpy(dest, src, elem_size * (elem_count - si));
+ memcpy(dest, src, elem_size * (n - si));
} else {
// we must check the remaining source elements one by one
// to skip the duplicates.
// Note that no source element can equal the last element in the
// destination, because that would have created an insertion point
// and a buffer, s.t. the above loop already handled the duplicates
- while (si < elem_count) {
+ while (si < n) {
// find a chain of elements that can be copied
size_t copy_len = 1, skip_len = 0;
{
const char *left_src = src;
- while (si + copy_len + skip_len < elem_count) {
+ while (si + copy_len + skip_len < n) {
const char *right_src = left_src + elem_size;
- int d = cmp_func(left_src, right_src);
+ int d = cmp_func(left_src, right_src, context);
if (d < 0) {
if (skip_len > 0) {
// new larger element found;
left_src += elem_size;
skip_len++;
} else {
- break;
+ // should be unreachable because the requirement is
+ // that the source array is sorted
+ break; // LCOV_EXCL_LINE
}
}
}
src += bytes_copied + skip_len * elem_size;
si += copy_len + skip_len;
di += copy_len;
- *size -= skip_len;
+ array->size -= skip_len;
}
}
}
// buffered elements need to be moved when we skipped duplicates
- size_t total_skipped = new_size - *size;
+ size_t total_skipped = new_size - array->size;
if (bi < new_size && total_skipped > 0) {
// move the remaining buffer to the end of the array
memmove(dest, bptr, elem_size * (new_size - bi));
return 0;
}
-int cx_array_insert_sorted(
- void **target,
- size_t *size,
- size_t *capacity,
- cx_compare_func cmp_func,
- const void *sorted_data,
+int cx_array_insert_sorted_(
+ const CxAllocator *allocator,
+ CxArray *array,
size_t elem_size,
- size_t elem_count,
- CxArrayReallocator *reallocator
+ const void *sorted_data,
+ size_t n,
+ cx_compare_func cmp_func,
+ bool allow_duplicates
) {
- return cx_array_insert_sorted_impl(target, size, capacity,
- cmp_func, sorted_data, elem_size, elem_count, reallocator, true);
+ cx_compare_func_wrapper wrapper = {cmp_func};
+ return cx_array_insert_sorted_c_(allocator, array, elem_size, sorted_data,
+ n, cx_cmp_wrap, &wrapper, allow_duplicates);
}
-int cx_array_insert_unique(
- void **target,
- size_t *size,
- size_t *capacity,
- cx_compare_func cmp_func,
- const void *sorted_data,
- size_t elem_size,
- size_t elem_count,
- CxArrayReallocator *reallocator
-) {
- return cx_array_insert_sorted_impl(target, size, capacity,
- cmp_func, sorted_data, elem_size, elem_count, reallocator, false);
+#ifndef WITH_QSORT_R
+static cx_thread_local cx_compare_func2 cx_array_fn_for_qsort;
+static cx_thread_local void *cx_array_context_for_qsort;
+static int cx_array_qsort_wrapper(const void *l, const void *r) {
+ return cx_array_fn_for_qsort(l, r, cx_array_context_for_qsort);
+}
+#endif
+
+void cx_array_qsort_c(void *array, size_t nmemb, size_t size,
+ cx_compare_func2 fn, void *context) {
+#ifdef WITH_QSORT_R
+ qsort_r(array, nmemb, size, fn, context);
+#else
+ cx_array_fn_for_qsort = fn;
+ cx_array_context_for_qsort = context;
+ qsort(array, nmemb, size, cx_array_qsort_wrapper);
+#endif
+}
+
+void cx_array_sort_(CxArray *array, size_t elem_size,
+ cx_compare_func fn) {
+ qsort(array->data, array->size, elem_size, fn);
+}
+
+void cx_array_sort_c_(CxArray *array, size_t elem_size,
+ cx_compare_func2 fn, void *context) {
+ cx_array_qsort_c(array->data, array->size, elem_size, fn, context);
+}
+
+CxIterator cx_array_iterator_(CxArray *array, size_t elem_size) {
+ return cxIterator(array->data, elem_size, array->size);
+}
+
+CxIterator cx_array_iterator_ptr_(CxArray *array) {
+ return cxIteratorPtr(array->data, array->size);
+}
+
+void cx_array_remove_(CxArray *array, size_t elem_size, size_t index, size_t n, bool fast) {
+ if (n == 0) return;
+ if (index >= array->size) return;
+ if (index + n >= array->size) {
+ // only tail elements are removed
+ array->size = index;
+ return;
+ }
+ array->size -= n;
+ size_t remaining = array->size - index;
+ char *dest = ((char*)array->data) + index * elem_size;
+ if (fast) {
+ char *src = dest + remaining * elem_size;
+ if (n == 1 && elem_size <= CX_WORDSIZE/8) {
+ // try to optimize int-sized values
+ // (from likely to unlikely)
+ if (elem_size == sizeof(int32_t)) {
+ *(int32_t*)dest = *(int32_t*)src;
+ return;
+ }
+#if CX_WORDSIZE == 64
+ if (elem_size == sizeof(int64_t)) {
+ *(int64_t*)dest = *(int64_t*)src;
+ return;
+ }
+#endif
+ if (elem_size == sizeof(int8_t)) {
+ *(int8_t*)dest = *(int8_t*)src;
+ return;
+ }
+ if (elem_size == sizeof(int16_t)) {
+ *(int16_t*)dest = *(int16_t*)src;
+ return;
+ }
+ // note we cannot optimize the last branch, because
+ // the elem_size could be crazily misaligned
+ }
+ memcpy(dest, src, n * elem_size);
+ } else {
+ char *src = dest + n * elem_size;
+ memmove(dest, src, remaining * elem_size);
+ }
+}
+
+void cx_array_free_(const CxAllocator *allocator, CxArray *array) {
+ cxFree(allocator, array->data);
+ array->data = NULL;
+ array->size = array->capacity = 0;
}
+
// implementation that finds ANY index
static size_t cx_array_binary_search_inf_impl(
const void *arr,
size_t size,
size_t elem_size,
const void *elem,
- cx_compare_func cmp_func
+ cx_compare_func2 cmp_func,
+ void *context
) {
// special case: empty array
if (size == 0) return 0;
const char *array = arr;
// check the first array element
- result = cmp_func(elem, array);
+ result = cmp_func(elem, array, context);
if (result < 0) {
return size;
} else if (result == 0) {
if (size == 1) return 0;
// check the last array element
- result = cmp_func(elem, array + elem_size * (size - 1));
+ result = cmp_func(elem, array + elem_size * (size - 1), context);
if (result >= 0) {
return size - 1;
}
// so start the binary search
size_t left_index = 1;
size_t right_index = size - 1;
- size_t pivot_index;
+ size_t pivot_index = 0;
while (left_index <= right_index) {
pivot_index = left_index + (right_index - left_index) / 2;
const char *arr_elem = array + pivot_index * elem_size;
- result = cmp_func(elem, arr_elem);
+ result = cmp_func(elem, arr_elem, context);
if (result == 0) {
// found it!
return pivot_index;
return result < 0 ? (pivot_index - 1) : pivot_index;
}
-size_t cx_array_binary_search_inf(
+size_t cx_array_binary_search_inf_c(
const void *arr,
size_t size,
size_t elem_size,
const void *elem,
- cx_compare_func cmp_func
+ cx_compare_func2 cmp_func,
+ void *context
) {
size_t index = cx_array_binary_search_inf_impl(
- arr, size, elem_size, elem, cmp_func);
+ arr, size, elem_size, elem, cmp_func, context);
// in case of equality, report the largest index
const char *e = ((const char *) arr) + (index + 1) * elem_size;
- while (index + 1 < size && cmp_func(e, elem) == 0) {
+ while (index + 1 < size && cmp_func(e, elem, context) == 0) {
e += elem_size;
index++;
}
return index;
}
-size_t cx_array_binary_search(
+size_t cx_array_binary_search_c(
const void *arr,
size_t size,
size_t elem_size,
const void *elem,
- cx_compare_func cmp_func
+ cx_compare_func2 cmp_func,
+ void *context
) {
- size_t index = cx_array_binary_search_inf(
- arr, size, elem_size, elem, cmp_func
+ size_t index = cx_array_binary_search_inf_c(
+ arr, size, elem_size, elem, cmp_func, context
);
- if (index < size &&
- cmp_func(((const char *) arr) + index * elem_size, elem) == 0) {
+ if (index < size && cmp_func(((const char *) arr) + index * elem_size,
+ elem, context) == 0) {
return index;
} else {
return size;
}
}
-size_t cx_array_binary_search_sup(
+size_t cx_array_binary_search_sup_c(
const void *arr,
size_t size,
size_t elem_size,
const void *elem,
- cx_compare_func cmp_func
+ cx_compare_func2 cmp_func,
+ void *context
) {
size_t index = cx_array_binary_search_inf_impl(
- arr, size, elem_size, elem, cmp_func
+ arr, size, elem_size, elem, cmp_func, context
);
const char *e = ((const char *) arr) + index * elem_size;
if (index == size) {
// no infimum means the first element is supremum
return 0;
- } else if (cmp_func(e, elem) == 0) {
+ } else if (cmp_func(e, elem, context) == 0) {
// found an equal element, search the smallest index
e -= elem_size; // e now contains the element at index-1
- while (index > 0 && cmp_func(e, elem) == 0) {
+ while (index > 0 && cmp_func(e, elem, context) == 0) {
e -= elem_size;
index--;
}
}
}
+size_t cx_array_binary_search_inf(
+ const void *arr,
+ size_t size,
+ size_t elem_size,
+ const void *elem,
+ cx_compare_func cmp_func
+) {
+ cx_compare_func_wrapper wrapper = {cmp_func};
+ return cx_array_binary_search_inf_c(arr, size, elem_size, elem, cx_cmp_wrap, &wrapper);
+}
+
+size_t cx_array_binary_search(
+ const void *arr,
+ size_t size,
+ size_t elem_size,
+ const void *elem,
+ cx_compare_func cmp_func
+) {
+ cx_compare_func_wrapper wrapper = {cmp_func};
+ return cx_array_binary_search_c(arr, size, elem_size, elem, cx_cmp_wrap, &wrapper);
+}
+
+size_t cx_array_binary_search_sup(
+ const void *arr,
+ size_t size,
+ size_t elem_size,
+ const void *elem,
+ cx_compare_func cmp_func
+) {
+ cx_compare_func_wrapper wrapper = {cmp_func};
+ return cx_array_binary_search_sup_c(arr, size, elem_size, elem, cx_cmp_wrap, &wrapper);
+}
+
#ifndef CX_ARRAY_SWAP_SBO_SIZE
#define CX_ARRAY_SWAP_SBO_SIZE 128
#endif
struct cx_list_s base;
void *data;
size_t capacity;
- CxArrayReallocator reallocator;
} cx_array_list;
static void cx_arl_destructor(struct cx_list_s *list) {
const void *array,
size_t n
) {
- // out of bounds and special case check
- if (index > list->collection.size || n == 0) return 0;
-
- // get a correctly typed pointer to the list
cx_array_list *arl = (cx_array_list *) list;
-
- // guarantee enough capacity
- if (arl->capacity < list->collection.size + n) {
- const size_t new_capacity = cx_array_grow_capacity(arl->capacity,list->collection.size + n);
- if (cxReallocateArray(
- list->collection.allocator,
- &arl->data, new_capacity,
- list->collection.elem_size)
- ) {
- return 0; // LCOV_EXCL_LINE
- }
- arl->capacity = new_capacity;
- }
-
- // determine insert position
- char *arl_data = arl->data;
- char *insert_pos = arl_data + index * list->collection.elem_size;
-
- // 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);
+ CxArray wrap = {
+ arl->data, list->collection.size, arl->capacity
+ };
+ if (cx_array_insert_(list->collection.allocator, &wrap,
+ list->collection.elem_size, index, array, n)) {
+ return 0;
}
- list->collection.size += n;
-
+ arl->data = wrap.data;
+ arl->capacity = wrap.capacity;
+ list->collection.size = wrap.size;
return n;
}
-static size_t cx_arl_insert_sorted(
+static size_t cx_arl_insert_sorted_impl(
struct cx_list_s *list,
const void *sorted_data,
- size_t n
+ size_t n,
+ bool allow_duplicates
) {
- // get a correctly typed pointer to the list
cx_array_list *arl = (cx_array_list *) list;
+ CxArray wrap = {
+ arl->data, list->collection.size, arl->capacity
+ };
- if (cx_array_insert_sorted(
- &arl->data,
- &list->collection.size,
- &arl->capacity,
- list->collection.cmpfunc,
- sorted_data,
+ if (cx_array_insert_sorted_c_(
+ list->collection.allocator,
+ &wrap,
list->collection.elem_size,
+ sorted_data,
n,
- &arl->reallocator
+ cx_list_compare_wrapper,
+ list,
+ allow_duplicates
)) {
// array list implementation is "all or nothing"
return 0; // LCOV_EXCL_LINE
- } else {
- return n;
}
+ arl->data = wrap.data;
+ arl->capacity = wrap.capacity;
+ list->collection.size = wrap.size;
+ return n;
}
-static size_t cx_arl_insert_unique(
+static size_t cx_arl_insert_sorted(
struct cx_list_s *list,
const void *sorted_data,
size_t n
) {
- // get a correctly typed pointer to the list
- cx_array_list *arl = (cx_array_list *) list;
+ return cx_arl_insert_sorted_impl(list, sorted_data, n, true);
+}
- if (cx_array_insert_unique(
- &arl->data,
- &list->collection.size,
- &arl->capacity,
- list->collection.cmpfunc,
- sorted_data,
- list->collection.elem_size,
- n,
- &arl->reallocator
- )) {
- // array list implementation is "all or nothing"
- return 0; // LCOV_EXCL_LINE
- } else {
- return n;
- }
+static size_t cx_arl_insert_unique(
+ struct cx_list_s *list,
+ const void *sorted_data,
+ size_t n
+) {
+ return cx_arl_insert_sorted_impl(list, sorted_data, n, false);
}
static void *cx_arl_insert_element(
);
}
+ // calculate how many elements would need to be moved
+ size_t remaining = list->collection.size - index - remove;
+
// short-circuit removal of last elements
- if (index + remove == list->collection.size) {
+ if (remaining == 0) {
list->collection.size -= remove;
return remove;
}
// just move the elements to the left
- cx_array_copy(
- &arl->data,
- &list->collection.size,
- &arl->capacity,
- 0,
- index,
- ((char *) arl->data) + (index + remove) * list->collection.elem_size,
- list->collection.elem_size,
- list->collection.size - index - remove,
- &arl->reallocator
- );
+ char *dst_move = arl->data;
+ dst_move += index * list->collection.elem_size;
+ char *first_remaining = dst_move + remove * list->collection.elem_size;
+ memmove(dst_move, first_remaining, remaining * list->collection.elem_size);
// decrease the size
list->collection.size -= remove;
bool remove
) {
assert(list != NULL);
- assert(list->collection.cmpfunc != NULL);
if (list->collection.size == 0) return 0;
char *cur = ((const cx_array_list *) list)->data;
// optimize with binary search, when sorted
if (list->collection.sorted) {
- size_t i = cx_array_binary_search(
+ size_t i = cx_array_binary_search_c(
cur,
list->collection.size,
list->collection.elem_size,
elem,
- list->collection.cmpfunc
+ cx_list_compare_wrapper,
+ list
);
if (remove && i < list->collection.size) {
cx_arl_remove(list, i, 1, NULL);
// fallback: linear search
for (size_t i = 0; i < list->collection.size; i++) {
- if (0 == list->collection.cmpfunc(elem, cur)) {
+ if (0 == cx_list_compare_wrapper(elem, cur, list)) {
if (remove) {
cx_arl_remove(list, i, 1, NULL);
}
}
static void cx_arl_sort(struct cx_list_s *list) {
- assert(list->collection.cmpfunc != NULL);
- qsort(((cx_array_list *) list)->data,
+ cx_array_qsort_c(((cx_array_list *) list)->data,
list->collection.size,
list->collection.elem_size,
- list->collection.cmpfunc
+ cx_list_compare_wrapper,
+ list
);
}
const struct cx_list_s *list,
const struct cx_list_s *other
) {
- assert(list->collection.cmpfunc != NULL);
if (list->collection.size == other->collection.size) {
const char *left = ((const cx_array_list *) list)->data;
const char *right = ((const cx_array_list *) other)->data;
for (size_t i = 0; i < list->collection.size; i++) {
- int d = list->collection.cmpfunc(left, right);
+ int d = cx_list_compare_wrapper(left, right, (void*)list);
if (d != 0) {
return d;
}
CxList *cxArrayListCreate(
const CxAllocator *allocator,
- cx_compare_func comparator,
size_t elem_size,
size_t initial_capacity
) {
cx_array_list *list = cxCalloc(allocator, 1, sizeof(cx_array_list));
if (list == NULL) return NULL;
- cx_list_init((CxList*)list, &cx_array_list_class,
- allocator, comparator, elem_size);
+ cx_list_init((CxList*)list, &cx_array_list_class, allocator, elem_size);
list->capacity = initial_capacity;
// allocate the array after the real elem_size is known
return NULL;
} // LCOV_EXCL_STOP
- // configure the reallocator
- list->reallocator = cx_array_reallocator(allocator, NULL);
-
return (CxList *) list;
}
#include <string.h>
#include <errno.h>
-#ifdef _WIN32
-#include <Windows.h>
-#include <sysinfoapi.h>
-static unsigned long system_page_size(void) {
- static unsigned long ps = 0;
- if (ps == 0) {
- SYSTEM_INFO sysinfo;
- GetSystemInfo(&sysinfo);
- ps = sysinfo.dwPageSize;
- }
- return ps;
-}
-#else
-#include <unistd.h>
-static unsigned long system_page_size(void) {
- static unsigned long ps = 0;
- if (ps == 0) {
- long sc = sysconf(_SC_PAGESIZE);
- if (sc < 0) {
- // fallback for systems which do not report a value here
- ps = 4096; // LCOV_EXCL_LINE
- } else {
- ps = (unsigned long) sc;
- }
- }
- return ps;
-}
-#endif
-
static int buffer_copy_on_write(CxBuffer* buffer) {
if (0 == (buffer->flags & CX_BUFFER_COPY_ON_WRITE)) return 0;
void *newspace = cxMalloc(buffer->allocator, buffer->capacity);
int cxBufferInit(
CxBuffer *buffer,
+ const CxAllocator *allocator,
void *space,
size_t capacity,
- const CxAllocator *allocator,
int flags
-) {
+ ) {
if (allocator == NULL) {
allocator = cxDefaultAllocator;
}
buffer->bytes = space;
}
buffer->capacity = capacity;
+ buffer->max_capacity = SIZE_MAX;
buffer->size = 0;
buffer->pos = 0;
- buffer->flush = NULL;
-
- return 0;
-}
-
-int cxBufferEnableFlushing(
- CxBuffer *buffer,
- CxBufferFlushConfig config
-) {
- buffer->flush = cxMallocDefault(sizeof(CxBufferFlushConfig));
- if (buffer->flush == NULL) return -1; // LCOV_EXCL_LINE
- memcpy(buffer->flush, &config, sizeof(CxBufferFlushConfig));
return 0;
}
void cxBufferDestroy(CxBuffer *buffer) {
- if (buffer->flags & CX_BUFFER_FREE_CONTENTS) {
+ if ((buffer->flags & (CX_BUFFER_FREE_CONTENTS | CX_BUFFER_DO_NOT_FREE))
+ == CX_BUFFER_FREE_CONTENTS) {
cxFree(buffer->allocator, buffer->bytes);
}
- cxFreeDefault(buffer->flush);
memset(buffer, 0, sizeof(CxBuffer));
}
CxBuffer *cxBufferCreate(
+ const CxAllocator *allocator,
void *space,
size_t capacity,
- const CxAllocator *allocator,
int flags
-) {
+ ) {
if (allocator == NULL) {
allocator = cxDefaultAllocator;
}
CxBuffer *buf = cxMalloc(allocator, sizeof(CxBuffer));
if (buf == NULL) return NULL; // LCOV_EXCL_LINE
- if (0 == cxBufferInit(buf, space, capacity, allocator, flags)) {
+ if (0 == cxBufferInit(buf, allocator, space, capacity, flags)) {
return buf;
} else {
// LCOV_EXCL_START
}
int cxBufferReserve(CxBuffer *buffer, size_t newcap) {
- if (newcap <= buffer->capacity) {
+ if (newcap == buffer->capacity) {
return 0;
}
+ if (newcap > buffer->max_capacity) {
+ return -1;
+ }
const int force_copy_flags = CX_BUFFER_COPY_ON_WRITE | CX_BUFFER_COPY_ON_EXTEND;
if (buffer->flags & force_copy_flags) {
void *newspace = cxMalloc(buffer->allocator, newcap);
return 0;
} else if (cxReallocate(buffer->allocator,
(void **) &buffer->bytes, newcap) == 0) {
+ buffer->flags |= CX_BUFFER_FREE_CONTENTS;
buffer->capacity = newcap;
+ if (buffer->size > newcap) {
+ buffer->size = newcap;
+ }
return 0;
} else {
return -1; // LCOV_EXCL_LINE
}
}
-static size_t cx_buffer_calculate_minimum_capacity(size_t mincap) {
- unsigned long pagesize = system_page_size();
- // if page size is larger than 64 KB - for some reason - truncate to 64 KB
- if (pagesize > 65536) pagesize = 65536;
- if (mincap < pagesize) {
- // when smaller as one page, map to the next power of two
- mincap--;
- mincap |= mincap >> 1;
- mincap |= mincap >> 2;
- mincap |= mincap >> 4;
- // last operation only needed for pages larger 4096 bytes
- // but if/else would be more expensive than just doing this
- mincap |= mincap >> 8;
- mincap++;
- } else {
- // otherwise, map to a multiple of the page size
- mincap -= mincap % pagesize;
- mincap += pagesize;
- // note: if newcap is already page aligned,
- // this gives a full additional page (which is good)
+int cxBufferMaximumCapacity(CxBuffer *buffer, size_t capacity) {
+ if (capacity < buffer->capacity) {
+ return -1;
}
- return mincap;
+ buffer->max_capacity = capacity;
+ return 0;
}
int cxBufferMinimumCapacity(CxBuffer *buffer, size_t newcap) {
if (newcap <= buffer->capacity) {
return 0;
}
- newcap = cx_buffer_calculate_minimum_capacity(newcap);
+ if (newcap > buffer->max_capacity) {
+ return -1;
+ }
+ if (newcap < buffer->max_capacity) {
+ unsigned long pagesize = cx_system_page_size();
+ // if page size is larger than 64 KB - for some reason - truncate to 64 KB
+ if (pagesize > 65536) pagesize = 65536;
+ if (newcap < pagesize) {
+ // when smaller as one page, map to the next power of two
+ newcap--;
+ newcap |= newcap >> 1;
+ newcap |= newcap >> 2;
+ newcap |= newcap >> 4;
+ // last operation only needed for pages larger 4096 bytes
+ // but if/else would be more expensive than just doing this
+ newcap |= newcap >> 8;
+ newcap++;
+ } else {
+ // otherwise, map to a multiple of the page size
+ newcap -= newcap % pagesize;
+ newcap += pagesize;
+ // note: if newcap is already page aligned,
+ // this gives a full additional page (which is good)
+ }
+ if (newcap > buffer->max_capacity) {
+ newcap = buffer->max_capacity;
+ }
+ }
return cxBufferReserve(buffer, newcap);
}
}
}
-static size_t cx_buffer_flush_helper(
- const CxBuffer *buffer,
- const unsigned char *src,
- size_t size,
- size_t nitems
-) {
- // flush data from an arbitrary source
- // does not need to be the buffer's contents
- size_t max_items = buffer->flush->blksize / size;
- size_t fblocks = 0;
- size_t flushed_total = 0;
- while (nitems > 0 && fblocks < buffer->flush->blkmax) {
- fblocks++;
- size_t items = nitems > max_items ? max_items : nitems;
- size_t flushed = buffer->flush->wfunc(
- src, size, items, buffer->flush->target);
- if (flushed > 0) {
- flushed_total += flushed;
- src += flushed * size;
- nitems -= flushed;
- } else {
- // if no bytes can be flushed out anymore, we give up
- break;
- }
- }
- return flushed_total;
-}
-
-static size_t cx_buffer_flush_impl(CxBuffer *buffer, size_t size) {
- // flush the current contents of the buffer
- unsigned char *space = buffer->bytes;
- size_t remaining = buffer->pos / size;
- size_t flushed_total = cx_buffer_flush_helper(
- buffer, space, size, remaining);
-
- // shift the buffer left after flushing
- // IMPORTANT: up to this point, copy on write must have been
- // performed already, because we can't do error handling here
- cxBufferShiftLeft(buffer, flushed_total*size);
-
- return flushed_total;
-}
-
-size_t cxBufferFlush(CxBuffer *buffer) {
- if (buffer_copy_on_write(buffer)) return 0;
- return cx_buffer_flush_impl(buffer, 1);
-}
-
size_t cxBufferWrite(
const void *ptr,
size_t size,
size_t nitems,
CxBuffer *buffer
) {
+ // trivial case
+ if (size == 0 || nitems == 0) return 0;
+
// optimize for easy case
if (size == 1 && (buffer->capacity - buffer->pos) >= nitems) {
if (buffer_copy_on_write(buffer)) return 0;
return nitems;
}
- size_t len, total_flushed = 0;
-cx_buffer_write_retry:
+ size_t len;
if (cx_szmul(size, nitems, &len)) {
errno = EOVERFLOW;
- return total_flushed;
+ return 0;
}
if (buffer->pos > SIZE_MAX - len) {
errno = EOVERFLOW;
- return total_flushed;
+ return 0;
}
+ const size_t required = buffer->pos + len;
- size_t required = buffer->pos + len;
- bool perform_flush = false;
+ // check if we need to auto-extend
if (required > buffer->capacity) {
if (buffer->flags & CX_BUFFER_AUTO_EXTEND) {
- if (buffer->flush != NULL) {
- size_t newcap = cx_buffer_calculate_minimum_capacity(required);
- if (newcap > buffer->flush->threshold) {
- newcap = buffer->flush->threshold;
- }
- if (cxBufferReserve(buffer, newcap)) {
- return total_flushed; // LCOV_EXCL_LINE
- }
- if (required > newcap) {
- perform_flush = true;
- }
- } else {
- if (cxBufferMinimumCapacity(buffer, required)) {
- return total_flushed; // LCOV_EXCL_LINE
- }
- }
- } else {
- if (buffer->flush != NULL) {
- perform_flush = true;
- } else {
- // truncate data, if we can neither extend nor flush
- len = buffer->capacity - buffer->pos;
- if (size > 1) {
- len -= len % size;
- }
- nitems = len / size;
+ size_t newcap = required < buffer->max_capacity
+ ? required : buffer->max_capacity;
+ if (cxBufferMinimumCapacity(buffer, newcap)) {
+ return 0; // LCOV_EXCL_LINE
}
}
}
+ // check again and truncate data if capacity is still not enough
+ if (required > buffer->capacity) {
+ len = buffer->capacity - buffer->pos;
+ if (size > 1) {
+ len -= len % size;
+ }
+ nitems = len / size;
+ }
+
// check here and not above because of possible truncation
if (len == 0) {
- return total_flushed;
+ return 0;
}
// check if we need to copy
if (buffer_copy_on_write(buffer)) return 0;
// perform the operation
- if (perform_flush) {
- size_t items_flushed;
- if (buffer->pos == 0) {
- // if we don't have data in the buffer, but are instructed
- // to flush, it means that we are supposed to relay the data
- items_flushed = cx_buffer_flush_helper(buffer, ptr, size, nitems);
- if (items_flushed == 0) {
- // we needed to relay data, but could not flush anything
- // i.e. we have to give up to avoid endless trying
- return 0;
- }
- nitems -= items_flushed;
- total_flushed += items_flushed;
- if (nitems > 0) {
- ptr = ((unsigned char*)ptr) + items_flushed * size;
- goto cx_buffer_write_retry;
- }
- return total_flushed;
- } else {
- items_flushed = cx_buffer_flush_impl(buffer, size);
- if (items_flushed == 0) {
- // flush target is full, let's try to truncate
- size_t remaining_space;
- if (buffer->flags & CX_BUFFER_AUTO_EXTEND) {
- remaining_space = buffer->flush->threshold > buffer->pos
- ? buffer->flush->threshold - buffer->pos
- : 0;
- } else {
- remaining_space = buffer->capacity > buffer->pos
- ? buffer->capacity - buffer->pos
- : 0;
- }
- nitems = remaining_space / size;
- if (nitems == 0) {
- return total_flushed;
- }
- }
- goto cx_buffer_write_retry;
- }
- } else {
- memcpy(buffer->bytes + buffer->pos, ptr, len);
- buffer->pos += len;
- if (buffer->pos > buffer->size) {
- buffer->size = buffer->pos;
- }
- return total_flushed + nitems;
+ memcpy(buffer->bytes + buffer->pos, ptr, len);
+ buffer->pos += len;
+ if (buffer->pos > buffer->size) {
+ buffer->size = buffer->pos;
}
+ return nitems;
}
size_t cxBufferAppend(
size_t nitems,
CxBuffer *buffer
) {
- size_t pos = buffer->pos;
- size_t append_pos = buffer->size;
- buffer->pos = append_pos;
- size_t written = cxBufferWrite(ptr, size, nitems, buffer);
- // the buffer might have been flushed
- // we must compute a possible delta for the position
- // expected: pos = append_pos + written
- // -> if this is not the case, there is a delta
- size_t delta = append_pos + written*size - buffer->pos;
- if (delta > pos) {
- buffer->pos = 0;
- } else {
- buffer->pos = pos - delta;
- }
+ // trivial case
+ if (size == 0 || nitems == 0) return 0;
+
+ const size_t pos = buffer->pos;
+ buffer->pos = buffer->size;
+ const size_t written = cxBufferWrite(ptr, size, nitems, buffer);
+ buffer->pos = pos;
return written;
}
}
int cxBufferTerminate(CxBuffer *buffer) {
- if (0 == cxBufferPut(buffer, 0)) {
- buffer->size = buffer->pos - 1;
- return 0;
+ // try to extend / shrink the buffer
+ if (buffer->pos >= buffer->capacity) {
+ if ((buffer->flags & CX_BUFFER_AUTO_EXTEND) == 0) {
+ return -1;
+ }
+ if (cxBufferReserve(buffer, buffer->pos + 1)) {
+ return -1; // LCOV_EXCL_LINE
+ }
} else {
- return -1;
+ buffer->size = buffer->pos;
+ cxBufferShrink(buffer, 1);
+ // set the capacity explicitly, in case shrink was skipped due to CoW
+ buffer->capacity = buffer->size + 1;
}
+
+ // check if we are still on read-only memory
+ if (buffer_copy_on_write(buffer)) return -1;
+
+ // write the terminator and exit
+ buffer->space[buffer->pos] = '\0';
+ return 0;
}
-size_t cxBufferPutString(
- CxBuffer *buffer,
- const char *str
-) {
- return cxBufferWrite(str, 1, strlen(str), buffer);
+size_t cx_buffer_put_string(CxBuffer *buffer, cxstring str) {
+ return cxBufferWrite(str.ptr, 1, str.length, buffer);
+}
+
+size_t cx_buffer_append_string(CxBuffer *buffer, cxstring str) {
+ return cxBufferAppend(str.ptr, 1, str.length, buffer);
}
size_t cxBufferRead(
#include "cx/compare.h"
#include <math.h>
+#include <string.h>
int cx_vcmp_int(int a, int b) {
if (a == b) {
return p1 < p2 ? -1 : 1;
}
}
+
+int cx_cmp_wrap(
+ const void *ptr1,
+ const void *ptr2,
+ void *w
+) {
+ cx_compare_func_wrapper *wrapper = w;
+ return wrapper->cmp(ptr1, ptr2);
+}
* @return either the specified @p target, a pointer to the allocated memory,
* or @c NULL, if any error occurred
*/
-typedef void*(cx_clone_func)(void *target, const void *source,
+typedef void*(*cx_clone_func)(void *target, const void *source,
const CxAllocator *allocator, void *data);
/**
- * Reallocate a previously allocated block and changes the pointer in-place,
- * if necessary.
+ * Returns the system's memory page size.
*
- * @note This will use stdlib reallocate and @em not the cxDefaultAllocator.
+ * If the page size cannot be retrieved from the system,
+ * a default of 4096 bytes is assumed.
*
- * @par Error handling
- * @c errno will be set by realloc() on failure.
+ * @return the system's memory page size in bytes
+ */
+cx_attr_nodiscard
+CX_EXPORT unsigned long cx_system_page_size(void);
+
+/**
+ * Reallocate a previously allocated block.
+ *
+ * Internal function - do not use.
*
* @param mem pointer to the pointer to allocated block
* @param n the new size in bytes
CX_EXPORT int cx_reallocate_(void **mem, size_t n);
/**
- * Reallocate a previously allocated block and changes the pointer in-place,
- * if necessary.
+ * Reallocate a previously allocated block.
*
- * 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.
+ * Internal function - do not use.
*
* @param mem pointer to the pointer to allocated block
* @param nmemb the number of elements
*
* @note Re-allocating a block allocated by a different allocator is undefined.
*
+ * @attention This function is bug-prone. Consider using cxReallocate().
+ *
* @param allocator the allocator
* @param mem pointer to the previously allocated block
* @param n the new size in bytes
CX_EXPORT void *cxRealloc(const CxAllocator *allocator, void *mem, size_t n);
/**
- * Reallocate the previously allocated block in @p mem, making the new block
- * @p n bytes long.
+ * Reallocate the previously allocated block in @p mem.
+ *
* This function may return the same pointer passed to it if moving
* the memory was not necessary.
*
*
* @note Re-allocating a block allocated by a different allocator is undefined.
*
+ * @attention This function is bug-prone. Consider using cxReallocateArray().
+ *
* @param allocator the allocator
* @param mem pointer to the previously allocated block
* @param nmemb the number of elements
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.
+ * Reallocate a previously allocated block.
*
- * @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.
+ * Internal function - do not use.
*
* @param allocator the allocator
* @param mem pointer to the pointer to allocated block
cxReallocate_(allocator, (void**)(mem), n)
/**
- * 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.
+ * Reallocate a previously allocated block.
*
- * @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.
+ * Internal function - do not use.
*
* @param allocator the allocator
* @param mem pointer to the pointer to allocated block
cxReallocateArray_(allocator, (void**) (mem), nmemb, size)
/**
- * Allocate @p nmemb elements of @p n bytes each, all initialized to zero.
+ * Allocate @p nmemb elements of @p size bytes each, all initialized to zero.
*
* @param allocator the allocator
* @param nmemb the number of elements
CX_EXPORT void *cxZalloc(const CxAllocator *allocator, size_t n);
/**
+ * Allocate @p n bytes of memory.
+ *
* Convenience macro that invokes cxMalloc() with the cxDefaultAllocator.
+ *
+ * @param n (@c size_t) the number of bytes
+ * @return (@c void*) a pointer to the allocated memory
*/
-#define cxMallocDefault(...) cxMalloc(cxDefaultAllocator, __VA_ARGS__)
+#define cxMallocDefault(n) cxMalloc(cxDefaultAllocator, n)
+
/**
+ * Allocate @p n bytes of memory and sets every byte to zero.
+ *
* Convenience macro that invokes cxZalloc() with the cxDefaultAllocator.
+ *
+ * @param n (@c size_t) the number of bytes
+ * @return (@c void*) a pointer to the allocated memory
*/
-#define cxZallocDefault(...) cxZalloc(cxDefaultAllocator, __VA_ARGS__)
+#define cxZallocDefault(n) cxZalloc(cxDefaultAllocator, n)
+
/**
+ * Allocate @p nmemb elements of @p size bytes each, all initialized to zero.
+ *
* Convenience macro that invokes cxCalloc() with the cxDefaultAllocator.
+ *
+ * @param nmemb (@c size_t) the number of elements
+ * @param size (@c size_t) the size of each element in bytes
+ * @return (@c void*) a pointer to the allocated memory
*/
-#define cxCallocDefault(...) cxCalloc(cxDefaultAllocator, __VA_ARGS__)
+#define cxCallocDefault(nmemb, size) cxCalloc(cxDefaultAllocator, nmemb, size)
+
/**
+ * Reallocate the previously allocated block in @p mem.
+ *
+ * This function may return the same pointer passed to it if moving
+ * the memory was not necessary.
+ *
* Convenience macro that invokes cxRealloc() with the cxDefaultAllocator.
+ *
+ * @attention This function is bug-prone. Consider using cxReallocateDefault().
+ *
+ * @param mem (@c void*) pointer to the previously allocated block
+ * @param n (@c size_t) the new size in bytes
+ * @return (@c void*) a pointer to the reallocated memory
*/
-#define cxReallocDefault(...) cxRealloc(cxDefaultAllocator, __VA_ARGS__)
+#define cxReallocDefault(mem, n) cxRealloc(cxDefaultAllocator, mem, n)
+
/**
+ * 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.
+ *
* Convenience macro that invokes cxReallocate() with the cxDefaultAllocator.
+ *
+ * @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 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 cxReallocateDefault(...) cxReallocate(cxDefaultAllocator, __VA_ARGS__)
+#define cxReallocateDefault(mem, n) cxReallocate(cxDefaultAllocator, mem, n)
+
/**
+ * 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.
+ *
* Convenience macro that invokes cxReallocateArray() with the cxDefaultAllocator.
+ *
+ * @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 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 cxReallocateArrayDefault(...) cxReallocateArray(cxDefaultAllocator, __VA_ARGS__)
+#define cxReallocateArrayDefault(mem, nmemb, size) \
+ cxReallocateArray(cxDefaultAllocator, mem, nmemb, size)
+
/**
+ * Reallocate the previously allocated block in @p mem.
+ *
* Convenience macro that invokes cxReallocArray() with the cxDefaultAllocator.
+ *
+ * This function may return the same pointer 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.
+ *
+ * @attention This function is bug-prone. Consider using cxReallocateArrayDefault().
+ *
+ * @param mem (@c void*) pointer to the previously allocated block
+ * @param nmemb (@c size_t) the number of elements
+ * @param size (@c size_t) the size of each element
+ * @return (@c void*) a pointer to the reallocated memory
*/
-#define cxReallocArrayDefault(...) cxReallocArray(cxDefaultAllocator, __VA_ARGS__)
+#define cxReallocArrayDefault(mem, nmemb, size) cxReallocArray(cxDefaultAllocator, mem, nmemb, size)
+
/**
- * Convenience macro that invokes cxFree() with the cxDefaultAllocator.
+ * Free a block of memory.
+ *
+ * Convenience function that invokes cxFree() with the cxDefaultAllocator.
+ *
+ * @param mem the memory to deallocate
*/
-#define cxFreeDefault(...) cxFree(cxDefaultAllocator, __VA_ARGS__)
+CX_EXPORT void cxFreeDefault(void *mem);
#ifdef __cplusplus
} // extern "C"
CX_EXPORT extern const unsigned cx_array_swap_sbo_size;
/**
- * Declares variables for an array that can be used with the convenience macros.
+ * Declares a typed array with size and capacity.
*
- * @par Examples
- * @code
- * // integer array with at most 255 elements
- * CX_ARRAY_DECLARE_SIZED(int, myarray, uint8_t)
+ * @param type the type of the elements
+ * @param name the name of the array
+ */
+#define CX_ARRAY(type, name) \
+ struct { \
+ type *data; \
+ size_t size; \
+ size_t capacity; \
+ } name
+
+/**
+ * Internal structure for arrays.
*
- * // array of MyObject* pointers where size and capacity are stored as unsigned int
- * CX_ARRAY_DECLARE_SIZED(MyObject*, objects, unsigned int)
+ * A generalization of array structures declared with CX_ARRAY().
+ */
+typedef struct cx_array_s {
+ /** The array data. */
+ void *data;
+ /** The number of elements. */
+ size_t size;
+ /** The maximum number of elements. */
+ size_t capacity;
+} CxArray;
+
+/**
+ * Initializes an array by allocating memory.
*
- * // initializing code
- * cx_array_initialize(myarray, 16); // reserve space for 16
- * cx_array_initialize(objects, 100); // reserve space for 100
- * @endcode
+ * Internal function - do not use manually.
*
- * @param type the type of the data
- * @param name the name of the array
- * @param size_type the type of the size (should be uint8_t, uint16_t, uint32_t, or size_t)
+ * @param allocator the allocator for the array
+ * @param array a pointer to the array structure
+ * @param elem_size size of one element
+ * @param capacity the initial maximum number of elements
+ * @retval zero allocation was successful
+ * @retval non-zero allocation failed
+ */
+cx_attr_nonnull
+CX_EXPORT int cx_array_init_(const CxAllocator *allocator, CxArray *array, size_t elem_size, size_t capacity);
+
+/**
+ * Initializes an array by allocating memory.
+ *
+ * The size is set to zero.
*
- * @see cx_array_initialize()
- * @see cx_array_simple_add()
- * @see cx_array_simple_copy()
- * @see cx_array_simple_add_sorted()
- * @see cx_array_simple_insert_sorted()
+ * @attention If the array was already initialized, this will leak memory.
+ * Use cx_array_reserve() to change the capacity of an initialized array.
+ *
+ * @param allocator (@c CxAllocator*) the allocator for the array
+ * @param array the name of the array
+ * @param capacity (@c size_t) the initial maximum number of elements
+ * @retval zero allocation was successful
+ * @retval non-zero allocation failed
*/
-#define CX_ARRAY_DECLARE_SIZED(type, name, size_type) \
- type * name; \
- /** Array size. */ size_type name##_size; \
- /** Array capacity. */ size_type name##_capacity
+#define cx_array_init_a(allocator, array, capacity) cx_array_init_(allocator, (CxArray*)&(array), sizeof((array).data[0]), capacity)
/**
- * Declares variables for an array that can be used with the convenience macros.
+ * Initializes an array by allocating memory.
*
- * The size and capacity variables will have type @c size_t.
- * Use #CX_ARRAY_DECLARE_SIZED() to specify a different type.
+ * The size is set to zero.
*
- * @par Examples
- * @code
- * // int array
- * CX_ARRAY_DECLARE(int, myarray)
+ * @attention If the array was already initialized, this will leak memory.
*
- * // initializing code
- * cx_array_initialize(myarray, 32); // reserve space for 32
- * @endcode
+ * @param array the name of the array
+ * @param capacity (@c size_t) the initial maximum number of elements
+ * @retval zero allocation was successful
+ * @retval non-zero allocation failed
+ */
+#define cx_array_init(array, capacity) cx_array_init_a(cxDefaultAllocator, array, capacity)
+
+/**
+ * Initializes an array with fixed size memory.
*
- * @param type the type of the data
- * @param name the name of the array
+ * Internal function - do not use manually.
*
- * @see cx_array_initialize()
- * @see cx_array_simple_add()
- * @see cx_array_simple_copy()
- * @see cx_array_simple_add_sorted()
- * @see cx_array_simple_insert_sorted()
+ * @param array a pointer to the array structure
+ * @param data the fixed size array
+ * @param capacity the capacity of the fixed size array
+ * @param size the number of initialized elements in the fixed size array
*/
-#define CX_ARRAY_DECLARE(type, name) CX_ARRAY_DECLARE_SIZED(type, name, size_t)
+cx_attr_nonnull
+CX_EXPORT void cx_array_init_fixed_(CxArray *array, const void *data, size_t capacity, size_t size);
/**
- * Initializes an array with the given capacity.
+ * Initializes an array with fixed size memory.
*
- * The type of the capacity depends on the type used during declaration.
+ * This is useful, for example, when you want to work with memory on the stack
+ * and only want to move to the heap when the stack memory is not enough.
*
- * @par Examples
- * @code
- * CX_ARRAY_DECLARE_SIZED(int, arr1, uint8_t)
- * CX_ARRAY_DECLARE(int, arr2) // size and capacity are implicitly size_t
+ * With the @p num_initialized argument you can specify how many elements in the
+ * fixed size array are already correctly initialized, which determines the
+ * initial size of the array.
*
- * // initializing code
- * cx_array_initialize(arr1, 500); // error: maximum for uint8_t is 255
- * cx_array_initialize(arr2, 500); // OK
- * @endcode
+ * The capacity is determined automatically by the compiler.
*
+ * @attention When you add elements to an array that was initialized with fixed
+ * size memory, you MUST check the capacity before adding the element and invoke
+ * cx_array_copy_to_new() when you intend to exceed the capacity.
*
- * The memory for the array is allocated with the cxDefaultAllocator.
+ * @attention When you pass a pointer to an array that does not have a fixed
+ * size, the behavior is unspecified.
*
- * @param array the name of the array
- * @param capacity the initial capacity
- * @see cx_array_initialize_a()
- * @see CX_ARRAY_DECLARE_SIZED()
- * @see CX_ARRAY_DECLARE()
+ * @param array the name of the array to initialize
+ * @param fixed_size_array (@c void*) the fixed size array
+ * @param num_initialized (@c size_t) the number of already initialized elements in the fixed size array
+ * @see cx_array_copy_to_new()
*/
-#define cx_array_initialize(array, capacity) \
- array##_capacity = capacity; \
- array##_size = 0; \
- array = cxMallocDefault(sizeof(array[0]) * capacity)
+#define cx_array_init_fixed(array, fixed_size_array, num_initialized) \
+ cx_array_init_fixed_((CxArray*)&(array), fixed_size_array, cx_nmemb(fixed_size_array), num_initialized)
/**
- * Initializes an array with the given capacity using the specified allocator.
+ * Changes the capacity of an array.
*
- * @par Example
- * @code
- * CX_ARRAY_DECLARE(int, myarray)
+ * Internal function - do not use.
*
+ * @param allocator the allocator
+ * @param array a pointer to the array structure
+ * @param elem_size the size of one element
+ * @param capacity the new capacity
+ * @retval zero allocation was successful
+ * @retval non-zero allocation failed
+ */
+cx_attr_nonnull
+CX_EXPORT int cx_array_reserve_(const CxAllocator *allocator, CxArray *array, size_t elem_size, size_t capacity);
+
+/**
+ * Changes the capacity of an array.
*
- * const CxAllocator *al = // ...
- * cx_array_initialize_a(al, myarray, 128);
- * // ...
- * cxFree(al, myarray); // remember to free with the same allocator
- * @endcode
+ * If required, the size is reduced to fit into the new capacity.
*
- * @param allocator (@c CxAllocator*) the allocator
+ * @param allocator (@c CxAllocator*) the allocator for the array
* @param array the name of the array
- * @param capacity the initial capacity
- * @see cx_array_initialize()
- * @see CX_ARRAY_DECLARE_SIZED()
- * @see CX_ARRAY_DECLARE()
+ * @param capacity (@c size_t) the new maximum number of elements
+ * @retval zero allocation was successful
+ * @retval non-zero allocation failed
*/
-#define cx_array_initialize_a(allocator, array, capacity) \
- array##_capacity = capacity; \
- array##_size = 0; \
- array = cxMalloc(allocator, sizeof(array[0]) * capacity)
+#define cx_array_reserve_a(allocator, array, capacity) \
+ cx_array_reserve_(allocator, (CxArray*)&(array), sizeof((array).data[0]), capacity)
/**
- * Defines a reallocation mechanism for arrays.
- * You can create your own, use cx_array_reallocator(), or
- * use the #cx_array_default_reallocator.
+ * Changes the capacity of an array.
+ *
+ * If required, the size is reduced to fit into the new capacity.
+ *
+ * @param array the name of the array
+ * @param capacity (@c size_t) the new maximum number of elements
+ * @retval zero allocation was successful
+ * @retval non-zero allocation failed
*/
-struct cx_array_reallocator_s {
- /**
- * Reallocates space for the given array.
- *
- * Implementations are not required to free the original array.
- * This allows reallocation of static or stack memory by allocating heap memory
- * and copying the array contents; namely when @c stack_ptr in this struct
- * is not @c NULL and @p array equals @c stack_ptr.
- *
- * @param array the array to reallocate
- * @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
- */
- void *(*realloc)( void *array, size_t old_capacity, size_t new_capacity,
- size_t elem_size, struct cx_array_reallocator_s *alloc);
+#define cx_array_reserve(array, capacity) \
+ cx_array_reserve_a(cxDefaultAllocator, array, capacity)
- /**
- * The allocator that shall be used for the reallocations.
- */
- const CxAllocator *allocator;
- /**
- * Optional pointer to stack memory
- * if the array is originally located on the stack.
- */
- const void *stack_ptr;
-};
+/**
+ * Copies the array to a new memory region.
+ *
+ * Internal function - do not use.
+ *
+ * @param allocator the allocator for new new memory
+ * @param array a pointer to the array structure
+ * @param elem_size the size of one element
+ * @param capacity the new capacity
+ * @retval zero allocation was successful
+ * @retval non-zero allocation failed
+ */
+cx_attr_nonnull
+CX_EXPORT int cx_array_copy_to_new_(const CxAllocator *allocator, CxArray *array, size_t elem_size, size_t capacity);
+
+/**
+ * Copies the array to a new memory region.
+ *
+ * This is useful when you have initialized the array with a fixed size memory
+ * using cx_array_init_fixed(), and now you want to increase the capacity.
+ *
+ * @attention When the original memory does not belong to stack memory, and
+ * you do not have another reference to this memory, it will leak.
+ *
+ * @param allocator (@c CxAllocator*) the allocator for the new memory
+ * @param array the name of the array
+ * @param capacity (@c size_t) the new maximum number of elements
+ * @retval zero allocation was successful
+ * @retval non-zero allocation failed
+ * @see cx_array_init_fixed()
+ */
+#define cx_array_copy_to_new_a(allocator, array, capacity) \
+ cx_array_copy_to_new_(allocator, (CxArray*)&(array), sizeof((array).data[0]), capacity)
/**
- * Typedef for the array reallocator struct.
+ * Copies the array to a new memory region.
+ *
+ * This is useful when you have initialized the array with a fixed size memory
+ * using cx_array_init_fixed(), and now you want to increase the capacity.
+ *
+ * @attention When the original memory does not belong to stack memory, and
+ * you do not have another reference to this memory, it will leak.
+ *
+ * @param array the name of the array
+ * @param capacity (@c size_t) the new maximum number of elements
+ * @retval zero allocation was successful
+ * @retval non-zero allocation failed
+ * @see cx_array_init_fixed()
*/
-typedef struct cx_array_reallocator_s CxArrayReallocator;
+#define cx_array_copy_to_new(array, capacity) \
+ cx_array_copy_to_new_a(cxDefaultAllocator, array, capacity)
/**
- * A default array reallocator that is based on the cxDefaultAllocator.
+ * Inserts data into an array.
+ *
+ * Internal function - do not use.
+ *
+ * @param allocator the allocator to use for a possible reallocation
+ * @param array a pointer to the array structure
+ * @param elem_size the size of one element
+ * @param index the index where to insert the @p other data
+ * @param other a pointer to an array of data that shall be inserted
+ * @param n the number of elements that shall be inserted
+ * @retval zero success
+ * @retval non-zero a re-allocation was necessary but failed
*/
-CX_EXPORT extern CxArrayReallocator *cx_array_default_reallocator;
+cx_attr_nonnull_arg(1, 2)
+CX_EXPORT int cx_array_insert_(const CxAllocator *allocator, CxArray *array,
+ size_t elem_size, size_t index, const void *other, size_t n);
/**
- * Creates a new array reallocator.
+ * Appends an element to an array.
*
- * When @p allocator is @c NULL, the cxDefaultAllocator will be used.
+ * When the capacity is not enough to hold the new element, a re-allocation is attempted.
*
- * When @p stack_ptr is not @c NULL, the reallocator is supposed to be used
- * @em only for the specific array initially located at @p stack_ptr.
- * When reallocation is needed, the reallocator checks if the array is
- * still located at @p stack_ptr and copies the contents to the heap.
+ * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation
+ * @param array the name of the array where the element shall be added
+ * @param element the element that shall be added
+ * @retval zero success
+ * @retval non-zero a re-allocation was necessary but failed
+ */
+#define cx_array_add_a(allocator, array, element) \
+ cx_array_insert_(allocator, (CxArray*)&(array), sizeof((array).data[0]), (array).size, (void*)&(element), 1)
+
+/**
+ * Appends an element to an array.
*
- * @note Invoking this function with both arguments being @c NULL will return a
- * reallocator that behaves like #cx_array_default_reallocator.
+ * When the capacity is not enough to hold the new element, a re-allocation is attempted.
*
- * @param allocator the allocator this reallocator shall be based on
- * @param stack_ptr the address of the array when the array is initially located
- * on the stack or shall not reallocate in place
- * @return an array reallocator
+ * @param array the name of the array where the element shall be added
+ * @param element (@c void*) a pointer to the element that shall be added
+ * @retval zero success
+ * @retval non-zero a re-allocation was necessary but failed
*/
-CX_EXPORT CxArrayReallocator cx_array_reallocator(
- const struct cx_allocator_s *allocator, const void *stack_ptr);
+#define cx_array_add(array, element) \
+ cx_array_add_a(cxDefaultAllocator, array, element)
/**
- * Reserves memory for additional elements.
+ * Inserts an element into an array.
+ *
+ * When the capacity is not enough to hold the new element, a re-allocation is attempted.
*
- * This function checks if the @p capacity of the array is sufficient to hold
- * at least @p size plus @p elem_count elements. If not, a reallocation is
- * performed with the specified @p reallocator.
- * You can create your own reallocator by hand, use #cx_array_default_reallocator,
- * or use the convenience function cx_array_reallocator() to create a custom reallocator.
+ * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation
+ * @param array the name of the array where the element shall be inserted
+ * @param index (@c size_t) the index where to insert the @p element
+ * @param element the element that shall be inserted
+ * @retval zero success
+ * @retval non-zero a re-allocation was necessary but failed
+ */
+#define cx_array_insert_a(allocator, array, index, element) \
+ cx_array_insert_(allocator, (CxArray*)&(array), sizeof((array).data[0]), index, (void*)&(element), 1)
+
+/**
+ * Inserts an element into an array.
*
- * This function can be useful to replace subsequent calls to cx_array_copy()
- * with one single cx_array_reserve() and then - after guaranteeing a
- * sufficient capacity - use simple memmove() or memcpy().
+ * When the capacity is not enough to hold the new element, a re-allocation is attempted.
*
- * The @p width in bytes refers to the size and capacity.
- * Both must have the same width.
- * Supported are 0, 1, 2, and 4, as well as 8 if running on a 64-bit
- * architecture. If set to zero, the native word width is used.
+ * @param array the name of the array where the element shall be inserted
+ * @param index (@c size_t) the index where to insert the @p element
+ * @param element (@c void*) a pointer to the element that shall be inserted
+ * @retval zero success
+ * @retval non-zero a re-allocation was necessary but failed
+ */
+#define cx_array_insert(array, index, element) \
+ cx_array_insert_a(cxDefaultAllocator, array, index, element)
+
+/**
+ * Inserts data into an array.
*
- * @note This function will reserve the minimum required capacity to hold
- * the additional elements and does not perform an overallocation.
+ * When the capacity is not enough to hold the new elements, a re-allocation is attempted.
*
- * @param array a pointer to the target array
- * @param size a pointer to the size of the array
- * @param capacity a pointer to the capacity of the array
- * @param width the width in bytes for the @p size and @p capacity or zero for default
- * @param elem_size the size of one element
- * @param elem_count the number of expected additional elements
- * @param reallocator the array reallocator to use
- * (@c NULL defaults to #cx_array_default_reallocator)
+ * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation
+ * @param array the name of the array where the elements shall be inserted
+ * @param index (@c size_t) the index where to insert the @p other data
+ * @param other (@c void*) a pointer to an array of data that shall be inserted
+ * @param n (@c size_t) the number of elements that shall be inserted
* @retval zero success
- * @retval non-zero failure
- * @see cx_array_reallocator()
+ * @retval non-zero a re-allocation was necessary but failed
*/
-cx_attr_nonnull_arg(1, 2, 3)
-CX_EXPORT int cx_array_reserve(void **array, void *size, void *capacity,
- unsigned width, size_t elem_size, size_t elem_count,
- CxArrayReallocator *reallocator);
+#define cx_array_insert_array_a(allocator, array, index, other, n) \
+ cx_array_insert_(allocator, (CxArray*)&(array), sizeof((array).data[0]), index, other, n)
/**
- * Copies elements from one array to another.
+ * Inserts data into an array.
*
- * The elements are copied to the @p target array at the specified @p index,
- * overwriting possible elements. The @p index does not need to be in range of
- * the current array @p size. If the new index plus the number of elements added
- * extends the array's size, the remaining @p capacity is used.
+ * When the capacity is not enough to hold the new elements, a re-allocation is attempted.
*
- * If the @p capacity is also insufficient to hold the new data, a reallocation
- * attempt is made with the specified @p reallocator.
- * You can create your own reallocator by hand, use #cx_array_default_reallocator,
- * or use the convenience function cx_array_reallocator() to create a custom reallocator.
+ * @param array the name of the array where the elements shall be inserted
+ * @param index (@c size_t) the index where to insert the @p other data
+ * @param other (@c void*) a pointer to an array of data that shall be inserted
+ * @param n (@c size_t) the number of elements that shall be inserted
+ * @retval zero success
+ * @retval non-zero a re-allocation was necessary but failed
+ */
+#define cx_array_insert_array(array, index, other, n) \
+ cx_array_insert_array_a(cxDefaultAllocator, array, index, other, n)
+
+/**
+ * Appends data to an array.
*
- * The @p width in bytes refers to the size and capacity.
- * Both must have the same width.
- * Supported are 0, 1, 2, and 4, as well as 8 if running on a 64-bit
- * architecture. If set to zero, the native word width is used.
+ * When the capacity is not enough to hold the new elements, a re-allocation is attempted.
*
- * @note When this function does reallocate the array, it may allocate more
- * space than required to avoid further allocations in the near future.
+ * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation
+ * @param array the name of the array where the elements shall be added
+ * @param other (@c void*) a pointer to an array of data that shall be added
+ * @param n (@c size_t) the number of elements that shall be added
+ * @retval zero success
+ * @retval non-zero a re-allocation was necessary but failed
+ */
+#define cx_array_add_array_a(allocator, array, other, n) \
+ cx_array_insert_(allocator, (CxArray*)&(array), sizeof((array).data[0]), (array).size, other, n)
+
+/**
+ * Appends data to an array.
*
- * @param target a pointer to the target array
- * @param size a pointer to the size of the target array
- * @param capacity a pointer to the capacity of the target array
- * @param width the width in bytes for the @p size and @p capacity or zero for default
- * @param index the index where the copied elements shall be placed
- * @param src the source array
- * @param elem_size the size of one element
- * @param elem_count the number of elements to copy
- * @param reallocator the array reallocator to use
- * (@c NULL defaults to #cx_array_default_reallocator)
+ * When the capacity is not enough to hold the new elements, a re-allocation is attempted.
+ *
+ * @param array the name of the array where the elements shall be added
+ * @param other (@c void*) a pointer to an array of data that shall be added
+ * @param n (@c size_t) the number of elements that shall be added
* @retval zero success
- * @retval non-zero failure
- * @see cx_array_reallocator()
- * @see cx_array_reserve()
+ * @retval non-zero a re-allocation was necessary but failed
*/
-cx_attr_nonnull_arg(1, 2, 3, 6)
-CX_EXPORT int cx_array_copy(void **target, void *size, void *capacity, unsigned width,
- size_t index, const void *src, size_t elem_size, size_t elem_count,
- CxArrayReallocator *reallocator);
+#define cx_array_add_array(array, other, n) \
+ cx_array_add_array_a(cxDefaultAllocator, array, other, n)
/**
- * Convenience macro that uses cx_array_copy() with a default layout and
- * the specified reallocator.
+ * Inserts sorted data into a sorted array.
*
- * @param reallocator (@c CxArrayReallocator*) the array reallocator to use
- * @param array the name of the array (NOT a pointer or alias to the array)
- * @param index (@c size_t) the index where the copied elements shall be placed
- * @param src (@c void*) the source array
- * @param count (@c size_t) the number of elements to copy
+ * Internal function - do not use.
+ *
+ * @param allocator the allocator to use for a possible reallocation
+ * @param array a pointer to the array structure
+ * @param elem_size the size of one element
+ * @param sorted_data a pointer to an array of data that shall be inserted
+ * @param n the number of elements that shall be inserted
+ * @param cmp_func the compare function
+ * @param allow_duplicates @c false if duplicates shall be skipped during insertion
* @retval zero success
- * @retval non-zero failure
- * @see CX_ARRAY_DECLARE()
- * @see cx_array_simple_copy()
+ * @retval non-zero a re-allocation was necessary but failed
*/
-#define cx_array_simple_copy_a(reallocator, array, index, src, count) \
- cx_array_copy((void**)&(array), &(array##_size), &(array##_capacity), \
- sizeof(array##_size), index, src, sizeof((array)[0]), count, \
- reallocator)
+cx_attr_nonnull
+CX_EXPORT int cx_array_insert_sorted_(const CxAllocator *allocator, CxArray *array,
+ size_t elem_size, const void *sorted_data, size_t n,
+ cx_compare_func cmp_func, bool allow_duplicates);
/**
- * Convenience macro that uses cx_array_copy() with a default layout and
- * the default reallocator.
+ * Inserts an element into a sorted array.
*
- * @param array the name of the array (NOT a pointer or alias to the array)
- * @param index (@c size_t) the index where the copied elements shall be placed
- * @param src (@c void*) the source array
- * @param count (@c size_t) the number of elements to copy
+ * When the capacity is not enough to hold the new element, a re-allocation is attempted.
+ *
+ * @attention if the array is not sorted according to the specified @p cmp_func, the behavior is undefined.
+ *
+ * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation
+ * @param array the name of the array where the elements shall be inserted
+ * @param element the element that shall be inserted
+ * @param cmp_func (@c cx_compare_func) the compare function that establishes the order
* @retval zero success
- * @retval non-zero failure
- * @see CX_ARRAY_DECLARE()
- * @see cx_array_simple_copy_a()
+ * @retval non-zero a re-allocation was necessary but failed
*/
-#define cx_array_simple_copy(array, index, src, count) \
- cx_array_simple_copy_a(NULL, array, index, src, count)
+#define cx_array_insert_sorted_a(allocator, array, element, cmp_func) \
+ cx_array_insert_sorted_(allocator, (CxArray*)&(array), sizeof((array).data[0]), (void*)&(element), 1, cmp_func, true)
/**
- * Convenience macro that uses cx_array_reserve() with a default layout and
- * the specified reallocator.
+ * Inserts an element into a sorted array.
+ *
+ * When the capacity is not enough to hold the new element, a re-allocation is attempted.
+ *
+ * @attention if the array is not sorted according to the specified @p cmp_func, the behavior is undefined.
*
- * @param reallocator (@c CxArrayReallocator*) the array reallocator to use
- * @param array the name of the array (NOT a pointer or alias to the array)
- * @param count (@c size_t) the number of expected @em additional elements
+ * @param array the name of the array where the elements shall be inserted
+ * @param element the element that shall be inserted
+ * @param cmp_func (@c cx_compare_func) the compare function that establishes the order
* @retval zero success
- * @retval non-zero failure
- * @see CX_ARRAY_DECLARE()
- * @see cx_array_simple_reserve()
+ * @retval non-zero a re-allocation was necessary but failed
*/
-#define cx_array_simple_reserve_a(reallocator, array, count) \
- cx_array_reserve((void**)&(array), &(array##_size), &(array##_capacity), \
- sizeof(array##_size), sizeof((array)[0]), count, \
- reallocator)
+#define cx_array_insert_sorted(array, element, cmp_func) \
+ cx_array_insert_sorted_a(cxDefaultAllocator, array, element, cmp_func)
/**
- * Convenience macro that uses cx_array_reserve() with a default layout and
- * the default reallocator.
+ * Inserts sorted data into a sorted array.
*
- * @param array the name of the array (NOT a pointer or alias to the array)
- * @param count (@c size_t) the number of expected additional elements
+ * When the capacity is not enough to hold the new elements, a re-allocation is attempted.
+ *
+ * @attention if either array is not sorted according to the specified @p cmp_func, the behavior is undefined.
+ *
+ * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation
+ * @param array the name of the array where the elements shall be inserted
+ * @param sorted_data (@c void*) a pointer to an array of sorted data that shall be inserted
+ * @param n (@c size_t) the number of elements that shall be inserted
+ * @param cmp_func (@c cx_compare_func) the compare function that establishes the order
* @retval zero success
- * @retval non-zero failure
- * @see CX_ARRAY_DECLARE()
- * @see cx_array_simple_reserve_a()
+ * @retval non-zero a re-allocation was necessary but failed
*/
-#define cx_array_simple_reserve(array, count) \
- cx_array_simple_reserve_a(NULL, array, count)
+#define cx_array_insert_sorted_array_a(allocator, array, sorted_data, n, cmp_func) \
+ cx_array_insert_sorted_(allocator, (CxArray*)&(array), sizeof((array).data[0]), sorted_data, n, cmp_func, true)
/**
- * Adds an element to an array with the possibility of allocating more space.
+ * Inserts sorted data into a sorted array.
*
- * The element @p elem is added to the end of the @p target array which contains
- * @p size elements, already. The @p capacity must point to a variable denoting
- * the current maximum number of elements the array can hold.
+ * When the capacity is not enough to hold the new elements, a re-allocation is attempted.
*
- * If the capacity is insufficient to hold the new element, an attempt to
- * increase the @p capacity is made and the new capacity is written back.
+ * @attention if either array is not sorted according to the specified @p cmp_func, the behavior is undefined.
*
- * The \@ SIZE_TYPE is flexible and can be any unsigned integer type.
- * It is important, however, that @p size and @p capacity are pointers to
- * variables of the same type.
+ * @param array the name of the array where the elements shall be inserted
+ * @param sorted_data (@c void*) a pointer to an array of sorted data that shall be inserted
+ * @param n (@c size_t) the number of elements that shall be inserted
+ * @param cmp_func (@c cx_compare_func) the compare function that establishes the order
+ * @retval zero success
+ * @retval non-zero a re-allocation was necessary but failed
+ */
+#define cx_array_insert_sorted_array(array, sorted_data, n, cmp_func) \
+ cx_array_insert_sorted_array_a(cxDefaultAllocator, array, sorted_data, n, cmp_func)
+
+/**
+ * Inserts an element into a sorted array if it is not already contained.
+ *
+ * When the capacity is not enough to hold the new element, a re-allocation is attempted.
*
- * @param target (@c void**) a pointer to the target array
- * @param size (@c SIZE_TYPE*) a pointer to the size of the target array
- * @param capacity (@c SIZE_TYPE*) a pointer to the capacity of the target array
- * @param elem_size (@c size_t) the size of one element
- * @param elem (@c void*) a pointer to the element to add
- * @param reallocator (@c CxArrayReallocator*) the array reallocator to use
+ * @attention if the array is not sorted according to the specified @p cmp_func, the behavior is undefined.
+ *
+ * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation
+ * @param array the name of the array where the elements shall be inserted
+ * @param element the element that shall be inserted
+ * @param cmp_func (@c cx_compare_func) the compare function that establishes the order
* @retval zero success
- * @retval non-zero failure
+ * @retval non-zero a re-allocation was necessary but failed
*/
-#define cx_array_add(target, size, capacity, elem_size, elem, reallocator) \
- cx_array_copy((void**)(target), size, capacity, sizeof(*(size)), \
- *(size), elem, elem_size, 1, reallocator)
+#define cx_array_insert_unique_a(allocator, array, element, cmp_func) \
+ cx_array_insert_sorted_(allocator, (CxArray*)&(array), sizeof((array).data[0]), (void*)&(element), 1, cmp_func, false)
/**
- * Convenience macro that uses cx_array_add() with a default layout and
- * the specified reallocator.
+ * Inserts an element into a sorted array if it is not already contained.
+ *
+ * When the capacity is not enough to hold the new element, a re-allocation is attempted.
*
- * @param reallocator (@c CxArrayReallocator*) the array reallocator to use
- * @param array the name of the array (NOT a pointer or alias to the array)
- * @param elem the element to add (NOT a pointer, address is automatically taken)
+ * @attention if the array is not sorted according to the specified @p cmp_func, the behavior is undefined.
+ *
+ * @param array the name of the array where the elements shall be inserted
+ * @param element the element that shall be inserted
+ * @param cmp_func (@c cx_compare_func) the compare function that establishes the order
* @retval zero success
- * @retval non-zero failure
- * @see CX_ARRAY_DECLARE()
- * @see cx_array_simple_add()
+ * @retval non-zero a re-allocation was necessary but failed
*/
-#define cx_array_simple_add_a(reallocator, array, elem) \
- cx_array_simple_copy_a(reallocator, array, array##_size, &(elem), 1)
+#define cx_array_insert_unique(array, element, cmp_func) \
+ cx_array_insert_unique_a(cxDefaultAllocator, array, element, cmp_func)
/**
- * Convenience macro that uses cx_array_add() with a default layout and
- * the default reallocator.
+ * Inserts sorted data into a sorted array, skipping duplicates.
+ *
+ * When the capacity is not enough to hold the new elements, a re-allocation is attempted.
+ *
+ * @attention if either array is not sorted according to the specified @p cmp_func, the behavior is undefined.
*
- * @param array the name of the array (NOT a pointer or alias to the array)
- * @param elem the element to add (NOT a pointer, address is automatically taken)
+ * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation
+ * @param array the name of the array where the elements shall be inserted
+ * @param sorted_data (@c void*) a pointer to an array of sorted data that shall be inserted
+ * @param n (@c size_t) the number of elements that shall be inserted
+ * @param cmp_func (@c cx_compare_func) the compare function that establishes the order
* @retval zero success
- * @retval non-zero failure
- * @see CX_ARRAY_DECLARE()
- * @see cx_array_simple_add_a()
+ * @retval non-zero a re-allocation was necessary but failed
*/
-#define cx_array_simple_add(array, elem) \
- cx_array_simple_add_a(cx_array_default_reallocator, array, elem)
+#define cx_array_insert_unique_array_a(allocator, array, sorted_data, n, cmp_func) \
+ cx_array_insert_sorted_(allocator, (CxArray*)&(array), sizeof((array).data[0]), sorted_data, n, cmp_func, false)
/**
- * Inserts a sorted array into another sorted array.
+ * Inserts sorted data into a sorted array, skipping duplicates.
+ *
+ * When the capacity is not enough to hold the new elements, a re-allocation is attempted.
+ *
+ * @attention if either array is not sorted according to the specified @p cmp_func, the behavior is undefined.
*
- * If either the target or the source array is not already sorted with respect
- * to the specified @p cmp_func, the behavior is undefined.
+ * @param array the name of the array where the elements shall be inserted
+ * @param sorted_data (@c void*) a pointer to an array of sorted data that shall be inserted
+ * @param n (@c size_t) the number of elements that shall be inserted
+ * @param cmp_func (@c cx_compare_func) the compare function that establishes the order
+ * @retval zero success
+ * @retval non-zero a re-allocation was necessary but failed
+ */
+#define cx_array_insert_unique_array(array, sorted_data, n, cmp_func) \
+ cx_array_insert_unique_array_a(cxDefaultAllocator, array, sorted_data, n, cmp_func)
+
+/**
+ * Inserts sorted data into a sorted array.
*
- * If the capacity is insufficient to hold the new data, a reallocation
- * attempt is made.
- * You can create your own reallocator by hand, use #cx_array_default_reallocator,
- * or use the convenience function cx_array_reallocator() to create a custom reallocator.
+ * Internal function - do not use.
*
- * @param target a pointer to the target array
- * @param size a pointer to the size of the target array
- * @param capacity a pointer to the capacity of the target array
- * @param cmp_func the compare function for the elements
- * @param src the source array
+ * @param allocator the allocator to use for a possible reallocation
+ * @param array a pointer to the array structure
* @param elem_size the size of one element
- * @param elem_count the number of elements to insert
- * @param reallocator the array reallocator to use
- * (@c NULL defaults to #cx_array_default_reallocator)
+ * @param sorted_data a pointer to an array of data that shall be inserted
+ * @param n the number of elements that shall be inserted
+ * @param cmp_func the compare function
+ * @param context additional context for the compare function
+ * @param allow_duplicates @c false if duplicates shall be skipped during insertion
* @retval zero success
- * @retval non-zero failure
+ * @retval non-zero a re-allocation was necessary but failed
*/
-cx_attr_nonnull_arg(1, 2, 3, 5)
-CX_EXPORT int cx_array_insert_sorted(void **target, size_t *size, size_t *capacity,
- cx_compare_func cmp_func, const void *src, size_t elem_size, size_t elem_count,
- CxArrayReallocator *reallocator);
+cx_attr_nonnull_arg(1, 2, 4, 6)
+CX_EXPORT int cx_array_insert_sorted_c_(const CxAllocator *allocator, CxArray *array,
+ size_t elem_size, const void *sorted_data, size_t n,
+ cx_compare_func2 cmp_func, void *context, bool allow_duplicates);
/**
* Inserts an element into a sorted array.
*
- * If the target array is not already sorted with respect
- * to the specified @p cmp_func, the behavior is undefined.
+ * When the capacity is not enough to hold the new element, a re-allocation is attempted.
*
- * If the capacity is not enough to hold the new data, a reallocation
- * attempt is made.
+ * @attention if the array is not sorted according to the specified @p cmp_func, the behavior is undefined.
*
- * The \@ SIZE_TYPE is flexible and can be any unsigned integer type.
- * It is important, however, that @p size and @p capacity are pointers to
- * variables of the same type.
+ * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation
+ * @param array the name of the array where the elements shall be inserted
+ * @param element the element that shall be inserted
+ * @param cmp_func (@c cx_compare_func2) the compare function that establishes the order
+ * @param context (@c void*) additional context for the compare function
+ * @retval zero success
+ * @retval non-zero a re-allocation was necessary but failed
+ */
+#define cx_array_insert_sorted_ca(allocator, array, element, cmp_func) \
+ cx_array_insert_sorted_c_(allocator, (CxArray*)&(array), sizeof((array).data[0]), (void*)&(element), 1, cmp_func, context, true)
+
+/**
+ * Inserts an element into a sorted array.
*
- * @param target (@c void**) a pointer to the target array
- * @param size (@c SIZE_TYPE*) a pointer to the size of the target array
- * @param capacity (@c SIZE_TYPE*) a pointer to the capacity of the target array
- * @param elem_size (@c size_t) the size of one element
- * @param elem (@c void*) a pointer to the element to add
- * @param cmp_func (@c cx_cmp_func) the compare function for the elements
- * @param reallocator (@c CxArrayReallocator*) the array reallocator to use
+ * When the capacity is not enough to hold the new element, a re-allocation is attempted.
+ *
+ * @attention if the array is not sorted according to the specified @p cmp_func, the behavior is undefined.
+ *
+ * @param array the name of the array where the elements shall be inserted
+ * @param element the element that shall be inserted
+ * @param cmp_func (@c cx_compare_func2) the compare function that establishes the order
+ * @param context (@c void*) additional context for the compare function
* @retval zero success
- * @retval non-zero failure
+ * @retval non-zero a re-allocation was necessary but failed
*/
-#define cx_array_add_sorted(target, size, capacity, elem_size, elem, cmp_func, reallocator) \
- cx_array_insert_sorted((void**)(target), size, capacity, cmp_func, elem, elem_size, 1, reallocator)
+#define cx_array_insert_sorted_c(array, element, cmp_func, context) \
+ cx_array_insert_sorted_ca(cxDefaultAllocator, array, element, cmp_func, context)
/**
- * Convenience macro for cx_array_add_sorted() with a default
- * layout and the specified reallocator.
+ * Inserts sorted data into a sorted array.
+ *
+ * When the capacity is not enough to hold the new elements, a re-allocation is attempted.
+ *
+ * @attention if either array is not sorted according to the specified @p cmp_func, the behavior is undefined.
*
- * @param reallocator (@c CxArrayReallocator*) the array reallocator to use
- * @param array the name of the array (NOT a pointer or alias to the array)
- * @param elem the element to add (NOT a pointer, address is automatically taken)
- * @param cmp_func (@c cx_cmp_func) the compare function for the elements
+ * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation
+ * @param array the name of the array where the elements shall be inserted
+ * @param sorted_data (@c void*) a pointer to an array of sorted data that shall be inserted
+ * @param n (@c size_t) the number of elements that shall be inserted
+ * @param cmp_func (@c cx_compare_func2) the compare function that establishes the order
+ * @param context (@c void*) additional context for the compare function
* @retval zero success
- * @retval non-zero failure
- * @see CX_ARRAY_DECLARE()
- * @see cx_array_simple_add_sorted()
+ * @retval non-zero a re-allocation was necessary but failed
*/
-#define cx_array_simple_add_sorted_a(reallocator, array, elem, cmp_func) \
- cx_array_add_sorted(&array, &(array##_size), &(array##_capacity), \
- sizeof((array)[0]), &(elem), cmp_func, reallocator)
+#define cx_array_insert_sorted_array_ca(allocator, array, sorted_data, n, cmp_func, context) \
+ cx_array_insert_sorted_c_(allocator, (CxArray*)&(array), sizeof((array).data[0]), sorted_data, n, cmp_func, context, true)
/**
- * Convenience macro for cx_array_add_sorted() with a default
- * layout and the default reallocator.
+ * Inserts sorted data into a sorted array.
*
- * @param array the name of the array (NOT a pointer or alias to the array)
- * @param elem the element to add (NOT a pointer, address is automatically taken)
- * @param cmp_func (@c cx_cmp_func) the compare function for the elements
+ * When the capacity is not enough to hold the new elements, a re-allocation is attempted.
+ *
+ * @attention if either array is not sorted according to the specified @p cmp_func, the behavior is undefined.
+ *
+ * @param array the name of the array where the elements shall be inserted
+ * @param sorted_data (@c void*) a pointer to an array of sorted data that shall be inserted
+ * @param n (@c size_t) the number of elements that shall be inserted
+ * @param cmp_func (@c cx_compare_func2) the compare function that establishes the order
+ * @param context (@c void*) additional context for the compare function
* @retval zero success
- * @retval non-zero failure
- * @see CX_ARRAY_DECLARE()
- * @see cx_array_simple_add_sorted_a()
+ * @retval non-zero a re-allocation was necessary but failed
*/
-#define cx_array_simple_add_sorted(array, elem, cmp_func) \
- cx_array_simple_add_sorted_a(NULL, array, elem, cmp_func)
+#define cx_array_insert_sorted_array_c(array, sorted_data, n, cmp_func, context) \
+ cx_array_insert_sorted_array_ca(cxDefaultAllocator, array, sorted_data, n, cmp_func, context)
/**
- * Convenience macro for cx_array_insert_sorted() with a default
- * layout and the specified reallocator.
+ * Inserts an element into a sorted array if it is not already contained.
+ *
+ * When the capacity is not enough to hold the new element, a re-allocation is attempted.
*
- * @param reallocator (@c CxArrayReallocator*) the array reallocator to use
- * @param array the name of the array (NOT a pointer or alias to the array)
- * @param src (@c void*) pointer to the source array
- * @param n (@c size_t) number of elements in the source array
- * @param cmp_func (@c cx_cmp_func) the compare function for the elements
+ * @attention if the array is not sorted according to the specified @p cmp_func, the behavior is undefined.
+ *
+ * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation
+ * @param array the name of the array where the elements shall be inserted
+ * @param element the element that shall be inserted
+ * @param cmp_func (@c cx_compare_func2) the compare function that establishes the order
+ * @param context (@c void*) additional context for the compare function
* @retval zero success
- * @retval non-zero failure
- * @see CX_ARRAY_DECLARE()
- * @see cx_array_simple_insert_sorted()
+ * @retval non-zero a re-allocation was necessary but failed
*/
-#define cx_array_simple_insert_sorted_a(reallocator, array, src, n, cmp_func) \
- cx_array_insert_sorted((void**)(&array), &(array##_size), &(array##_capacity), \
- cmp_func, src, sizeof((array)[0]), n, reallocator)
+#define cx_array_insert_unique_ca(allocator, array, element, cmp_func, context) \
+ cx_array_insert_sorted_c_(allocator, (CxArray*)&(array), sizeof((array).data[0]), (void*)&(element), 1, cmp_func, context, false)
/**
- * Convenience macro for cx_array_insert_sorted() with a default
- * layout and the default reallocator.
+ * Inserts an element into a sorted array if it is not already contained.
+ *
+ * When the capacity is not enough to hold the new element, a re-allocation is attempted.
*
- * @param array the name of the array (NOT a pointer or alias to the array)
- * @param src (@c void*) pointer to the source array
- * @param n (@c size_t) number of elements in the source array
- * @param cmp_func (@c cx_cmp_func) the compare function for the elements
+ * @attention if the array is not sorted according to the specified @p cmp_func, the behavior is undefined.
+ *
+ * @param array the name of the array where the elements shall be inserted
+ * @param element the element that shall be inserted
+ * @param cmp_func (@c cx_compare_func2) the compare function that establishes the order
+ * @param context (@c void*) additional context for the compare function
* @retval zero success
- * @retval non-zero failure
- * @see CX_ARRAY_DECLARE()
- * @see cx_array_simple_insert_sorted_a()
+ * @retval non-zero a re-allocation was necessary but failed
*/
-#define cx_array_simple_insert_sorted(array, src, n, cmp_func) \
- cx_array_simple_insert_sorted_a(NULL, array, src, n, cmp_func)
+#define cx_array_insert_unique_c(array, element, cmp_func, context) \
+ cx_array_insert_unique_ca(cxDefaultAllocator, array, element, cmp_func, context)
+/**
+ * Inserts sorted data into a sorted array, skipping duplicates.
+ *
+ * When the capacity is not enough to hold the new elements, a re-allocation is attempted.
+ *
+ * @attention if either array is not sorted according to the specified @p cmp_func, the behavior is undefined.
+ *
+ * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation
+ * @param array the name of the array where the elements shall be inserted
+ * @param sorted_data (@c void*) a pointer to an array of sorted data that shall be inserted
+ * @param n (@c size_t) the number of elements that shall be inserted
+ * @param cmp_func (@c cx_compare_func2) the compare function that establishes the order
+ * @param context (@c void*) additional context for the compare function
+ * @retval zero success
+ * @retval non-zero a re-allocation was necessary but failed
+ */
+#define cx_array_insert_unique_array_ca(allocator, array, sorted_data, n, cmp_func, context) \
+ cx_array_insert_sorted_c_(allocator, (CxArray*)&(array), sizeof((array).data[0]), sorted_data, n, cmp_func, context, false)
/**
- * Inserts a sorted array into another sorted array, avoiding duplicates.
+ * Inserts sorted data into a sorted array, skipping duplicates.
*
- * If either the target or the source array is not already sorted with respect
- * to the specified @p cmp_func, the behavior is undefined.
+ * When the capacity is not enough to hold the new elements, a re-allocation is attempted.
*
- * If the capacity is insufficient to hold the new data, a reallocation
- * attempt is made.
- * You can create your own reallocator by hand, use #cx_array_default_reallocator,
- * or use the convenience function cx_array_reallocator() to create a custom reallocator.
+ * @attention if either array is not sorted according to the specified @p cmp_func, the behavior is undefined.
*
- * @param target a pointer to the target array
- * @param size a pointer to the size of the target array
- * @param capacity a pointer to the capacity of the target array
- * @param cmp_func the compare function for the elements
- * @param src the source array
- * @param elem_size the size of one element
- * @param elem_count the number of elements to insert
- * @param reallocator the array reallocator to use
- * (@c NULL defaults to #cx_array_default_reallocator)
+ * @param array the name of the array where the elements shall be inserted
+ * @param sorted_data (@c void*) a pointer to an array of sorted data that shall be inserted
+ * @param n (@c size_t) the number of elements that shall be inserted
+ * @param cmp_func (@c cx_compare_func2) the compare function that establishes the order
+ * @param context (@c void*) additional context for the compare function
* @retval zero success
- * @retval non-zero failure
+ * @retval non-zero a re-allocation was necessary but failed
*/
-cx_attr_nonnull_arg(1, 2, 3, 5)
-CX_EXPORT int cx_array_insert_unique(void **target, size_t *size, size_t *capacity,
- cx_compare_func cmp_func, const void *src, size_t elem_size, size_t elem_count,
- CxArrayReallocator *reallocator);
+#define cx_array_insert_unique_array_c(array, sorted_data, n, cmp_func, context) \
+ cx_array_insert_unique_array_ca(cxDefaultAllocator, array, sorted_data, n, cmp_func, context)
/**
- * Inserts an element into a sorted array if it does not exist.
+ * An alternative to qsort_r() when that is not available on your platform.
*
- * If the target array is not already sorted with respect
- * to the specified @p cmp_func, the behavior is undefined.
+ * If it is available, qsort_r() is used directly.
*
- * If the capacity is insufficient to hold the new data, a reallocation
- * attempt is made.
+ * @param array the array that shall be sorted
+ * @param nmemb the number of elements in the array
+ * @param size the size of one element
+ * @param fn the compare function
+ * @param context the context for the compare function
+ */
+CX_EXPORT void cx_array_qsort_c(void *array, size_t nmemb, size_t size,
+ cx_compare_func2 fn, void *context);
+
+/**
+ * Sorts an array.
*
- * The \@ SIZE_TYPE is flexible and can be any unsigned integer type.
- * It is important, however, that @p size and @p capacity are pointers to
- * variables of the same type.
+ * Internal function - do not use.
*
- * @param target (@c void**) a pointer to the target array
- * @param size (@c SIZE_TYPE*) a pointer to the size of the target array
- * @param capacity (@c SIZE_TYPE*) a pointer to the capacity of the target array
- * @param elem_size (@c size_t) the size of one element
- * @param elem (@c void*) a pointer to the element to add
- * @param cmp_func (@c cx_cmp_func) the compare function for the elements
- * @param reallocator (@c CxArrayReallocator*) the array reallocator to use
- * @retval zero success (also when the element was already present)
- * @retval non-zero failure
+ * @param array a pointer to the array structure
+ * @param elem_size the size of one element
+ * @param fn the compare function
*/
-#define cx_array_add_unique(target, size, capacity, elem_size, elem, cmp_func, reallocator) \
- cx_array_insert_unique((void**)(target), size, capacity, cmp_func, elem, elem_size, 1, reallocator)
+CX_EXPORT void cx_array_sort_(CxArray *array, size_t elem_size,
+ cx_compare_func fn);
/**
- * Convenience macro for cx_array_add_unique() with a default
- * layout and the specified reallocator.
+ * Sorts an array.
*
- * @param reallocator (@c CxArrayReallocator*) the array reallocator to use
- * @param array the name of the array (NOT a pointer or alias to the array)
- * @param elem the element to add (NOT a pointer, address is automatically taken)
- * @param cmp_func (@c cx_cmp_func) the compare function for the elements
- * @retval zero success
- * @retval non-zero failure
- * @see CX_ARRAY_DECLARE()
- * @see cx_array_simple_add_unique()
+ * Internal function - do not use.
+ *
+ * @param array a pointer to the array structure
+ * @param elem_size the size of one element
+ * @param fn the compare function
+ * @param context the context for the compare function
*/
-#define cx_array_simple_add_unique_a(reallocator, array, elem, cmp_func) \
- cx_array_add_unique(&array, &(array##_size), &(array##_capacity), \
- sizeof((array)[0]), &(elem), cmp_func, reallocator)
+CX_EXPORT void cx_array_sort_c_(CxArray *array, size_t elem_size,
+ cx_compare_func2 fn, void *context);
/**
- * Convenience macro for cx_array_add_unique() with a default
- * layout and the default reallocator.
+ * Sorts an array.
*
- * @param array the name of the array (NOT a pointer or alias to the array)
- * @param elem the element to add (NOT a pointer, address is automatically taken)
- * @param cmp_func (@c cx_cmp_func) the compare function for the elements
- * @retval zero success
- * @retval non-zero failure
- * @see CX_ARRAY_DECLARE()
- * @see cx_array_simple_add_unique_a()
+ * @param array the name of the array
+ * @param fn (@c cx_compare_func) the compare function
+ * @param context (@c void*) the context for the compare function
*/
-#define cx_array_simple_add_unique(array, elem, cmp_func) \
- cx_array_simple_add_unique_a(NULL, array, elem, cmp_func)
+#define cx_array_sort(array, fn) \
+ cx_array_sort_((CxArray*)&(array), sizeof((array).data[0]), fn)
/**
- * Convenience macro for cx_array_insert_unique() with a default
- * layout and the specified reallocator.
+ * Sorts an array.
*
- * @param reallocator (@c CxArrayReallocator*) the array reallocator to use
- * @param array the name of the array (NOT a pointer or alias to the array)
- * @param src (@c void*) pointer to the source array
- * @param n (@c size_t) number of elements in the source array
- * @param cmp_func (@c cx_cmp_func) the compare function for the elements
- * @retval zero success
- * @retval non-zero failure
- * @see CX_ARRAY_DECLARE()
- * @see cx_array_simple_insert_unique()
+ * @param array the name of the array
+ * @param fn (@c cx_compare_func2) the compare function
+ * @param context (@c void*) the context for the compare function
*/
-#define cx_array_simple_insert_unique_a(reallocator, array, src, n, cmp_func) \
- cx_array_insert_unique((void**)(&array), &(array##_size), &(array##_capacity), \
- cmp_func, src, sizeof((array)[0]), n, reallocator)
+#define cx_array_sort_c(array, fn, context) \
+ cx_array_sort_c_((CxArray*)&(array), sizeof((array).data[0]), fn, context)
/**
- * Convenience macro for cx_array_insert_unique() with a default
- * layout and the default reallocator.
+ * Creates an iterator over the elements of an array.
*
- * @param array the name of the array (NOT a pointer or alias to the array)
- * @param src (@c void*) pointer to the source array
- * @param n (@c size_t) number of elements in the source array
- * @param cmp_func (@c cx_cmp_func) the compare function for the elements
- * @retval zero success
- * @retval non-zero failure
- * @see CX_ARRAY_DECLARE()
- * @see cx_array_simple_insert_unique_a()
+ * Internal function - do not use.
+ *
+ * @param array a pointer to the array structure
+ * @param elem_size the size of one element
+ * @return an iterator over the elements
*/
-#define cx_array_simple_insert_unique(array, src, n, cmp_func) \
- cx_array_simple_insert_unique_a(NULL, array, src, n, cmp_func)
+cx_attr_nodiscard cx_attr_nonnull
+CX_EXPORT CxIterator cx_array_iterator_(CxArray *array, size_t elem_size);
+
+/**
+ * Creates an iterator over the elements of an array.
+ *
+ * The iterator will yield pointers to the elements.
+ *
+ * This iterator cannot be used to remove elements
+ * because it does not get a modifiable reference to the array's size.
+ *
+ * @param array the name of the array
+ * @return an iterator over the elements
+ * @see cx_array_iterator_ptr()
+ */
+#define cx_array_iterator(array) \
+ cx_array_iterator_((CxArray*)&(array), sizeof((array).data[0]))
+
+/**
+ * Creates an iterator over the elements of an array containing pointers.
+ *
+ * Internal function - do not use.
+ *
+ * @param array the name of the array
+ * @return an iterator over the elements
+ */
+cx_attr_nodiscard cx_attr_nonnull
+CX_EXPORT CxIterator cx_array_iterator_ptr_(CxArray *array);
+
+/**
+ * Creates an iterator over the elements of an array containing pointers.
+ *
+ * The iterator will yield the elements themselves, which are supposed to
+ * be pointers.
+ *
+ * This iterator cannot be used to remove elements
+ * because it does not get a modifiable reference to the array's size.
+ *
+ * @param array the name of the array
+ * @return an iterator over the elements
+ * @see cx_array_iterator()
+ */
+#define cx_array_iterator_ptr(array) \
+ cx_array_iterator_ptr_((CxArray*)&(array))
+
+
+/**
+ * Removes elements from the array.
+ *
+ * Internal function - do not use.
+ *
+ * @param array a pointer to the array structure
+ * @param elem_size the size of one element
+ * @param index the index of the first element to remove
+ * @param n the number of elements to remove
+ * @param fast indicates whether tail elements should be copied into the gap
+ */
+cx_attr_nonnull
+CX_EXPORT void cx_array_remove_(CxArray *array, size_t elem_size, size_t index, size_t n, bool fast);
+
+/**
+ * Removes one element from the array.
+ *
+ * Tail elements are all moved by one. If you don't need a stable order
+ * in the array, consider using cx_array_remove_fast().
+ *
+ * If the index is out of bounds, this function does nothing.
+ *
+ * @param array the name of the array
+ * @param index (@c size_t) the index of the element to remove
+ * @see cx_array_remove_fast()
+ */
+#define cx_array_remove(array, index) \
+ cx_array_remove_((CxArray*)&(array), sizeof((array).data[0]), index, 1, false)
+
+/**
+ * Removes one element from the array.
+ *
+ * The gap will be filled with a copy of the last element in the array.
+ * This changes the order of elements. If you want a stable order,
+ * use cx_array_remove() instead.
+ *
+ * If the index is out of bounds, this function does nothing.
+ *
+ * @param array the name of the array
+ * @param index (@c size_t) the index of the element to remove
+ * @see cx_array_remove()
+ */
+#define cx_array_remove_fast(array, index) \
+ cx_array_remove_((CxArray*)&(array), sizeof((array).data[0]), index, 1, true)
+
+/**
+ * Removes multiple elements from the array.
+ *
+ * Tail elements are all moved to close the gap. If you don't need a stable
+ * order in the array, consider using cx_array_remove_array_fast().
+ *
+ * If the index is out of bounds, this function does nothing.
+ * If @n overflows the array, this function removes as many elements as it can.
+ *
+ * @param array the name of the array
+ * @param index (@c size_t) the index of the first element to remove
+ * @param n (@c size_t) the number of elements to remove
+ * @see cx_array_remove_array_fast()
+ */
+#define cx_array_remove_array(array, index, n) \
+ cx_array_remove_((CxArray*)&(array), sizeof((array).data[0]), index, n, false)
+
+/**
+ * Removes multiple elements from the array.
+ *
+ * Tail elements are copied into the gap. If you have more tail elements
+ * than the number of elements that are removed, this will change the order
+ * of elements. If you want a stable order, use cx_array_remove_array() instead.
+ *
+ * If the index is out of bounds, this function does nothing.
+ * If @n overflows the array, this function removes as many elements as it can.
+ *
+ * @param array the name of the array
+ * @param index (@c size_t) the index of the first element to remove
+ * @param n (@c size_t) the number of elements to remove
+ * @see cx_array_remove_array()
+ */
+#define cx_array_remove_array_fast(array, index, n) \
+ cx_array_remove_((CxArray*)&(array), sizeof((array).data[0]), index, n, true)
+
+/**
+ * Deallocates an array.
+ *
+ * Internal function - do not use.
+ *
+ * @param allocator (@c CxAllocator*) the allocator which was used to allocate the array
+ * @param array a pointer to the array structure
+ */
+cx_attr_nonnull
+CX_EXPORT void cx_array_free_(const CxAllocator *allocator, CxArray *array);
+
+/**
+ * Deallocates an array.
+ *
+ * The structure is reset to zero and can be re-initialized with
+ * cx_array_inita().
+ *
+ * @param array the name of the array
+ */
+#define cx_array_free(array) cx_array_free_(cxDefaultAllocator, (CxArray*)&(array))
+
+/**
+ * Deallocates an array.
+ *
+ * The structure is reset to zero and can be re-initialized with
+ * cx_array_init_a().
+ *
+ * @param allocator (@c CxAllocator*) the allocator which was used to allocate the array
+ * @param array the name of the array
+ */
+#define cx_array_free_a(allocator, array) cx_array_free_(allocator, (CxArray*)&(array))
+
/**
* Searches the largest lower bound in a sorted array.
CX_EXPORT size_t cx_array_binary_search_sup(const void *arr, size_t size,
size_t elem_size, const void *elem, cx_compare_func cmp_func);
+
+/**
+ * Searches the largest lower bound in a sorted array.
+ *
+ * In other words, this function returns the index of the largest element
+ * in @p arr that is less or equal to @p elem with respect to @p cmp_func.
+ * When no such element exists, @p size is returned.
+ *
+ * When such an element exists more than once, the largest index of all those
+ * elements is returned.
+ *
+ * If @p elem is contained in the array, this is identical to
+ * #cx_array_binary_search().
+ *
+ * If the array is not sorted with respect to the @p cmp_func, the behavior
+ * is undefined.
+ *
+ * @param arr the array to search
+ * @param size the size of the array
+ * @param elem_size the size of one element
+ * @param elem the element to find
+ * @param cmp_func the compare function
+ * @param context the context for the compare function
+ * @return the index of the largest lower bound, or @p size
+ * @see cx_array_binary_search_sup()
+ * @see cx_array_binary_search()
+ */
+cx_attr_nonnull
+CX_EXPORT size_t cx_array_binary_search_inf_c(const void *arr, size_t size,
+ size_t elem_size, const void *elem, cx_compare_func2 cmp_func, void *context);
+
+/**
+ * Searches an item in a sorted array.
+ *
+ * When such an element exists more than once, the largest index of all those
+ * elements is returned.
+ *
+ * If the array is not sorted with respect to the @p cmp_func, the behavior
+ * is undefined.
+ *
+ * @param arr the array to search
+ * @param size the size of the array
+ * @param elem_size the size of one element
+ * @param elem the element to find
+ * @param cmp_func the compare function
+ * @param context the context for the compare function
+ * @return the index of the element in the array, or @p size if the element
+ * cannot be found
+ * @see cx_array_binary_search_inf()
+ * @see cx_array_binary_search_sup()
+ */
+cx_attr_nonnull
+CX_EXPORT size_t cx_array_binary_search_c(const void *arr, size_t size,
+ size_t elem_size, const void *elem, cx_compare_func2 cmp_func, void *context);
+
+/**
+ * Searches the smallest upper bound in a sorted array.
+ *
+ * In other words, this function returns the index of the smallest element
+ * in @p arr that is greater or equal to @p elem with respect to @p cmp_func.
+ * When no such element exists, @p size is returned.
+ *
+ * When such an element exists more than once, the smallest index of all those
+ * elements is returned.
+ *
+ * If @p elem is contained in the array, this is identical to
+ * #cx_array_binary_search().
+ *
+ * If the array is not sorted with respect to the @p cmp_func, the behavior
+ * is undefined.
+ *
+ * @param arr the array to search
+ * @param size the size of the array
+ * @param elem_size the size of one element
+ * @param elem the element to find
+ * @param cmp_func the compare function
+ * @param context the context for the compare function
+ * @return the index of the smallest upper bound, or @p size
+ * @see cx_array_binary_search_inf()
+ * @see cx_array_binary_search()
+ */
+cx_attr_nonnull
+CX_EXPORT size_t cx_array_binary_search_sup_c(const void *arr, size_t size,
+ size_t elem_size, const void *elem, cx_compare_func2 cmp_func, void *context);
+
/**
* Swaps two array elements.
*
*
* 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 allocator the allocator for allocating the list memory
* (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 elem_size the size of each element in bytes
* @param initial_capacity the initial number of elements the array can store
* @return the created list
cx_attr_malloc
cx_attr_dealloc(cxListFree, 1)
CX_EXPORT CxList *cxArrayListCreate(const CxAllocator *allocator,
- cx_compare_func comparator, size_t elem_size, size_t initial_capacity);
-
-/**
- * Allocates an array list for storing elements with @p elem_size bytes each.
- *
- * The list will use the cxDefaultAllocator and @em NO compare function.
- * If you want to call functions that need a compare function, you have to
- * set it immediately after creation or use cxArrayListCreate().
- *
- * 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().
- *
- * @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
- * @return the created list
- */
-#define cxArrayListCreateSimple(elem_size, initial_capacity) \
- cxArrayListCreate(NULL, NULL, elem_size, initial_capacity)
+ size_t elem_size, size_t initial_capacity);
#ifdef __cplusplus
} // extern "C"
#include "common.h"
#include "allocator.h"
+#include "string.h"
#ifdef __cplusplus
extern "C" {
*/
#define CX_BUFFER_COPY_ON_EXTEND 0x08
+/**
+ * If this flag is enabled, the buffer will never free its contents regardless of #CX_BUFFER_FREE_CONTENTS.
+ *
+ * This is useful, for example, when you want to keep a pointer to the data after destroying the buffer.
+ */
+#define CX_BUFFER_DO_NOT_FREE 0x10
+
/**
* Function pointer for cxBufferWrite that is compatible with cx_write_func.
* @see cx_write_func
*/
#define cxBufferReadFunc ((cx_read_func) cxBufferRead)
-/**
- * Configuration for automatic flushing.
- */
-struct cx_buffer_flush_config_s {
- /**
- * The buffer may not extend beyond this threshold before starting to flush.
- *
- * Only used when the buffer uses #CX_BUFFER_AUTO_EXTEND.
- * The threshold will be the maximum capacity the buffer is extended to
- * before flushing.
- */
- size_t threshold;
- /**
- * The block size for the elements to flush.
- */
- size_t blksize;
- /**
- * The maximum number of blocks to flush in one cycle.
- *
- * @attention While it is guaranteed that cxBufferFlush() will not flush
- * more blocks, this is not necessarily the case for cxBufferWrite().
- * After performing a flush cycle, cxBufferWrite() will retry the write
- * operation and potentially trigger another flush cycle, until the
- * flush target accepts no more data.
- */
- size_t blkmax;
-
- /**
- * The target for the write function.
- */
- void *target;
-
- /**
- * The write-function used for flushing.
- * If NULL, the flushed content gets discarded.
- */
- cx_write_func wfunc;
-};
-
-/**
- * Type alias for the flush configuration struct.
- *
- * @code
- * struct cx_buffer_flush_config_s {
- * size_t threshold;
- * size_t blksize;
- * size_t blkmax;
- * void *target;
- * cx_write_func wfunc;
- * };
- * @endcode
- */
-typedef struct cx_buffer_flush_config_s CxBufferFlushConfig;
/** Structure for the UCX buffer data. */
struct cx_buffer_s {
};
/** The allocator to use for automatic memory management. */
const CxAllocator *allocator;
- /**
- * Optional flush configuration
- *
- * @see cxBufferEnableFlushing()
- */
- CxBufferFlushConfig *flush;
/** Current position of the buffer. */
size_t pos;
/** Current capacity (i.e. maximum size) of the buffer. */
size_t capacity;
+ /** Maximum capacity that this buffer may grow to. */
+ size_t max_capacity;
/** Current size of the buffer content. */
size_t size;
/**
* space will be leaking after the copy-on-write operation.
*
* @param buffer the buffer to initialize
- * @param space pointer to the memory area, or @c NULL to allocate
- * new memory
- * @param capacity the capacity of the buffer
* @param allocator the allocator this buffer shall use for automatic
* memory management
* (if @c NULL, the cxDefaultAllocator will be used)
+ * @param space pointer to the memory area, or @c NULL to allocate
+ * new memory
+ * @param capacity the capacity of the buffer
* @param flags buffer features (see cx_buffer_s.flags)
* @return zero on success, non-zero if a required allocation failed
*/
cx_attr_nonnull_arg(1)
-CX_EXPORT int cxBufferInit(CxBuffer *buffer, void *space, size_t capacity,
- const CxAllocator *allocator, int flags);
-
-/**
- * Configures the buffer for flushing.
- *
- * Flushing can happen automatically when data is written
- * to the buffer (see cxBufferWrite()) or manually when
- * cxBufferFlush() is called.
- *
- * @param buffer the buffer
- * @param config the flush configuration
- * @retval zero success
- * @retval non-zero failure
- * @see cxBufferFlush()
- * @see cxBufferWrite()
- */
-cx_attr_nonnull
-CX_EXPORT int cxBufferEnableFlushing(CxBuffer *buffer, CxBufferFlushConfig config);
+CX_EXPORT int cxBufferInit(CxBuffer *buffer, const CxAllocator *allocator,
+ void *space, size_t capacity, int flags);
/**
* Destroys the buffer contents.
* Then this function will allocate the space and enforce
* the #CX_BUFFER_FREE_CONTENTS flag.
*
- * @param space pointer to the memory area, or @c NULL to allocate
- * new memory
- * @param capacity the capacity of the buffer
* @param allocator the allocator to use for allocating the structure and the automatic
* memory management within the buffer
* (if @c NULL, the cxDefaultAllocator will be used)
+ * @param space pointer to the memory area, or @c NULL to allocate
+ * new memory
+ * @param capacity the capacity of the buffer
* @param flags buffer features (see cx_buffer_s.flags)
* @return a pointer to the buffer on success, @c NULL if a required allocation failed
*/
cx_attr_malloc cx_attr_dealloc(cxBufferFree, 1) cx_attr_nodiscard
-CX_EXPORT CxBuffer *cxBufferCreate(void *space, size_t capacity,
- const CxAllocator *allocator, int flags);
+CX_EXPORT CxBuffer *cxBufferCreate(const CxAllocator *allocator, void *space,
+ size_t capacity, int flags);
/**
* Shifts the contents of the buffer by the given offset.
* Ensures that the buffer has the required capacity.
*
* If the current capacity is not sufficient, the buffer will be extended.
+ * If the current capacity is larger, the buffer is shrunk and superfluous
+ * content is discarded.
*
* This function will reserve no more bytes than requested, in contrast to
* cxBufferMinimumCapacity(), which may reserve more bytes to improve the
*
* @param buffer the buffer
* @param capacity the required capacity for this buffer
- * @retval zero the capacity was already sufficient or successfully increased
+ * @retval zero on success
* @retval non-zero on allocation failure
* @see cxBufferShrink()
* @see cxBufferMinimumCapacity()
cx_attr_nonnull
CX_EXPORT int cxBufferReserve(CxBuffer *buffer, size_t capacity);
+/**
+ * Limits the buffer's capacity.
+ *
+ * If the current capacity is already larger, this function fails and returns
+ * non-zero.
+ *
+ * The capacity limit will affect auto-extension features, as well as future
+ * calls to cxBufferMinimumCapacity() and cxBufferReserve().
+ *
+ * @param buffer the buffer
+ * @param capacity the maximum allowed capacity for this buffer
+ * @retval zero the limit is applied
+ * @retval non-zero the new limit is smaller than the current capacity
+ * @see cxBufferReserve()
+ * @see cxBufferMinimumCapacity()
+ */
+cx_attr_nonnull
+CX_EXPORT int cxBufferMaximumCapacity(CxBuffer *buffer, size_t capacity);
+
/**
* Ensures that the buffer has a minimum capacity.
*
* @param capacity the minimum required capacity for this buffer
* @retval zero the capacity was already sufficient or successfully increased
* @retval non-zero on allocation failure
+ * @see cxBufferMaximumCapacity()
* @see cxBufferReserve()
* @see cxBufferShrink()
*/
/**
* Writes data to a CxBuffer.
*
- * If automatic flushing is not enabled, the data is simply written into the
- * buffer at the current position, and the position of the buffer is increased
- * by the number of bytes written.
- *
- * If flushing is enabled and the buffer needs to flush, the data is flushed to
- * the target until the target signals that it cannot take more data by
- * returning zero via the respective write function. In that case, the remaining
- * data in this buffer is shifted to the beginning of this buffer so that the
- * newly available space can be used to append as much data as possible.
- *
- * This function only stops writing more elements when the flush target and this
- * buffer are both incapable of taking more data or all data has been written.
+ * If auto-extension is enabled, the buffer's capacity is automatically
+ * increased when it is not large enough to hold all data.
+ * By default, the capacity grows indefinitely, unless limited with
+ * cxBufferMaximumCapacity().
+ * When auto-extension fails, this function writes no data and returns zero.
*
- * If, after flushing, the number of items that shall be written still exceeds
- * the capacity or flush threshold, this function tries to write all items directly
- * to the flush target, if possible.
- *
- * The number returned by this function is the number of elements from
- * @c ptr that could be written to either the flush target or the buffer.
- * That means it does @em not include the number of items that were already in
- * the buffer and were also flushed during the process.
- *
- * @attention
- * When @p size is larger than one and the contents of the buffer are not aligned
- * with @p size, flushing stops after all complete items have been flushed, leaving
- * the misaligned part in the buffer.
- * Afterward, this function only writes as many items as possible to the buffer.
+ * The position of the buffer is moved alongside the written data.
*
* @note The signature is compatible with the fwrite() family of functions.
*
CX_EXPORT size_t cxBufferAppend(const void *ptr, size_t size,
size_t nitems, CxBuffer *buffer);
-/**
- * Performs a single flush-run on the specified buffer.
- *
- * Does nothing when the position in the buffer is zero.
- * Otherwise, the data until the current position minus
- * one is considered for flushing.
- * Note carefully that flushing will never exceed the
- * current @em position, even when the size of the
- * buffer is larger than the current position.
- *
- * One flush run will try to flush @c blkmax many
- * blocks of size @c blksize until either the @p buffer
- * has no more data to flush or the write function
- * used for flushing returns zero.
- *
- * The buffer is shifted left for that many bytes
- * the flush operation has successfully flushed.
- *
- * @par Example 1
- * Assume you have a buffer with size 340 and you are
- * at position 200. The flush configuration is
- * @c blkmax=4 and @c blksize=64 .
- * Assume that the entire flush operation is successful.
- * All 200 bytes on the left-hand-side from the current
- * position are written.
- * That means the size of the buffer is now 140 and the
- * position is zero.
- *
- * @par Example 2
- * Same as Example 1, but now the @c blkmax is 1.
- * The size of the buffer is now 276, and the position is 136.
- *
- * @par Example 3
- * Same as Example 1, but now assume the flush target
- * only accepts 100 bytes before returning zero.
- * That means the flush operation manages to flush
- * one complete block and one partial block, ending
- * up with a buffer with size 240 and position 100.
- *
- * @remark Just returns zero when flushing was not enabled with
- * cxBufferEnableFlushing().
- *
- * @remark When the buffer uses copy-on-write, the memory
- * is copied first, before attempting any flush.
- * This is, however, considered an erroneous use of the
- * buffer because it makes little sense to put
- * readonly data into an UCX buffer for flushing instead
- * of writing it directly to the target.
- *
- * @param buffer the buffer
- * @return the number of successfully flushed bytes
- * @see cxBufferEnableFlushing()
- */
-cx_attr_nonnull
-CX_EXPORT size_t cxBufferFlush(CxBuffer *buffer);
-
/**
* Reads data from a CxBuffer.
*
*
* The least significant byte of the argument is written to the buffer. If the
* end of the buffer is reached and #CX_BUFFER_AUTO_EXTEND feature is enabled,
- * the buffer capacity is extended by cxBufferMinimumCapacity(). If the feature
- * is disabled or the buffer extension fails, @c EOF is returned.
+ * the buffer capacity is extended, unless a limit set by
+ * cxBufferMaximumCapacity() is reached.
+ * If the feature is disabled or the buffer extension fails, @c EOF is returned.
*
* On successful writing, the position of the buffer is increased.
*
*
* @param buffer the buffer to write to
* @param c the character to write
- * @return the byte that has been written or @c EOF when the end of the stream is
- * reached, and automatic extension is not enabled or not possible
+ * @return the byte that has been written or @c EOF when the end of the
+ * stream is reached, and automatic extension is not enabled or not possible
* @see cxBufferTerminate()
*/
cx_attr_nonnull
/**
* Writes a terminating zero to a buffer at the current position.
*
- * If successful, sets the size to the current position and advances the position by one.
+ * If successful, also sets the size to the current position and shrinks the buffer.
*
* The purpose of this function is to have the written data ready to be used as
* a C string with the buffer's size being the length of that string.
*
* @param buffer the buffer to write to
* @return zero, if the terminator could be written, non-zero otherwise
+ * @see cxBufferShrink()
*/
cx_attr_nonnull
CX_EXPORT int cxBufferTerminate(CxBuffer *buffer);
/**
- * Writes a string to a buffer.
+ * Internal function - do not use.
*
- * This is a convenience function for <code>cxBufferWrite(str, 1, strlen(str), buffer)</code>.
+ * @param buffer the buffer
+ * @param str the string
+ * @return the number of bytes written
+ * @see cxBufferPutString()
+ */
+cx_attr_nonnull
+CX_EXPORT size_t cx_buffer_put_string(CxBuffer *buffer, cxstring str);
+
+/**
+ * Writes a string to a buffer with cxBufferWrite().
+ *
+ * @param buffer (@c CxBuffer*) the buffer
+ * @param str (any string) the zero-terminated string
+ * @return (@c size_t) the number of bytes written
+ * @see cxBufferWrite()
+ * @see cx_strcast()
+ */
+#define cxBufferPutString(buffer, str) cx_buffer_put_string(buffer, cx_strcast(str))
+
+/**
+ * Internal function - do not use.
*
* @param buffer the buffer
- * @param str the zero-terminated string
+ * @param str the string
* @return the number of bytes written
+ * @see cxBufferPutString()
+ */
+cx_attr_nonnull
+CX_EXPORT size_t cx_buffer_append_string(CxBuffer *buffer, cxstring str);
+
+/**
+ * Appends a string to a buffer with cxBufferAppend().
+ *
+ * @param buffer (@c CxBuffer*) the buffer
+ * @param str (any string) the zero-terminated string
+ * @return (@c size_t) the number of bytes written
+ * @see cxBufferAppend()
+ * @see cx_strcast()
*/
-cx_attr_nonnull cx_attr_cstr_arg(2)
-CX_EXPORT size_t cxBufferPutString(CxBuffer *buffer, const char *str);
+#define cxBufferAppendString(buffer, str) cx_buffer_append_string(buffer, cx_strcast(str))
/**
* Gets a character from a buffer.
cx_attr_nonnull
CX_EXPORT int cxBufferGet(CxBuffer *buffer);
+/**
+ * Gets the data in a buffer as a @c cxstring.
+ *
+ * @param buffer the buffer
+ * @return the data in the buffer interpreted as a @c cxstring
+ */
+CX_INLINE cxstring cx_bstr(CxBuffer *buffer) {
+ return cx_strn(buffer->space, buffer->size);
+}
+
+/**
+ * Gets the data in a buffer as a @c cxmutstr.
+ *
+ * @param buffer the buffer
+ * @return the data in the buffer interpreted as a @c cxmutstr
+ */
+CX_INLINE cxmutstr cx_bstr_m(CxBuffer *buffer) {
+ return cx_mutstrn(buffer->space, buffer->size);
+}
+
#ifdef __cplusplus
}
#endif
* The allocator to use.
*/
const CxAllocator *allocator;
- /**
- * The comparator function for the elements.
- */
- cx_compare_func cmpfunc;
/**
* The size of each element.
*/
* The number of currently stored elements.
*/
size_t size;
+ /**
+ * A two-argument comparator function for the elements.
+ */
+ cx_compare_func simple_cmp;
+ /**
+ * A three-argument comparator function for the elements.
+ * If specified, this function has precedence over the @c simple_cmp function.
+ */
+ cx_compare_func2 advanced_cmp;
+ /**
+ * A pointer to custom data for the @c advanced_cmp function
+ */
+ void *cmp_data;
/**
* An optional simple destructor for the collection's elements.
*
*/
#define cxCollectionStoresPointers(c) ((c)->collection.store_pointer)
+
+/**
+ * Convenience macro for adding indirection to an element if the collection is storing pointers.
+ *
+ * @param c a pointer to a struct that contains #CX_COLLECTION_BASE
+ * @param elem the pointer that shall be taken the address from, if the collection is storing pointers
+ * @return if the collection is storing pointers, takes the address of @p elem, otherwise returns @p elem
+ */
+#define cx_ref(c, elem) (cxCollectionStoresPointers(c) ? ((void*)&(elem)) : (elem))
+
+/**
+ * Convenience macro for dereferencing an element if the collection is storing pointers.
+ *
+ * @param c a pointer to a struct that contains #CX_COLLECTION_BASE
+ * @param elem a pointer to the collection element
+ * @return if the collection is storing pointers, dereferences @p elem, otherwise returns @p elem
+ */
+#define cx_deref(c, elem) (cxCollectionStoresPointers(c) ? *((void**)(elem)) : (elem))
+
/**
* Indicates whether the collection can guarantee that the stored elements are currently sorted.
*
#define cxCollectionSorted(c) ((c)->collection.sorted || (c)->collection.size == 0)
/**
- * Sets the compare function for a collection.
+ * Sets a simple compare function for a collection.
+ *
+ * Erases a possible advanced compare function.
+ * If you want to set both, because you want to access the simple function
+ * in your advanced function, you must set the simple function first.
*
* @param c a pointer to a struct that contains #CX_COLLECTION_BASE
- * @param func (@c cx_compare_func) the compare function that shall be used by @c c
+ * @param func (@c cx_compare_func) the compare function
*/
-#define cxCollectionCompareFunc(c, func) (c)->collection.cmpfunc = (func)
+#define cxSetCompareFunc(c, func) \
+ (c)->collection.simple_cmp = (cx_compare_func)(func); \
+ (c)->collection.advanced_cmp = NULL
+
+/**
+ * Sets an advanced compare function that supports custom data for a collection.
+ *
+ * @param c a pointer to a struct that contains #CX_COLLECTION_BASE
+ * @param func (@c cx_compare_func2) the compare function
+ * @param data (@c void*) the pointer to custom data that is passed to the compare function
+ */
+#define cxSetAdvancedCompareFunc(c, func, data) \
+ (c)->collection.advanced_cmp = (cx_compare_func2) func; \
+ (c)->collection.cmp_data = data
+
+/**
+ * Invokes the simple comparator function for two elements.
+ *
+ * Usually only used by collection implementations. There should be no need
+ * to invoke this macro manually.
+ *
+ * @param c a pointer to a struct that contains #CX_COLLECTION_BASE
+ * @param left (@c void*) pointer to data
+ * @param right (@c void*) pointer to data
+ */
+#define cx_invoke_simple_compare_func(c, left, right) \
+ (c)->collection.simple_cmp(left, right)
+
+/**
+ * Invokes the advanced comparator function for two elements.
+ *
+ * Usually only used by collection implementations. There should be no need
+ * to invoke this macro manually.
+ *
+ * @param c a pointer to a struct that contains #CX_COLLECTION_BASE
+ * @param left (@c void*) pointer to data
+ * @param right (@c void*) pointer to data
+ */
+#define cx_invoke_advanced_compare_func(c, left, right) \
+ (c)->collection.advanced_cmp(left, right, (c)->collection.cmp_data)
+
+
+/**
+ * Invokes the configured comparator function for two elements.
+ *
+ * Usually only used by collection implementations. There should be no need
+ * to invoke this macro manually.
+ *
+ * @param c a pointer to a struct that contains #CX_COLLECTION_BASE
+ * @param left (@c void*) pointer to data
+ * @param right (@c void*) pointer to data
+ */
+#define cx_invoke_compare_func(c, left, right) \
+ (((c)->collection.advanced_cmp) ? \
+ cx_invoke_advanced_compare_func(c,left,right) : \
+ cx_invoke_simple_compare_func(c,left,right))
/**
* Sets a simple destructor function for this collection.
*
* @param c a pointer to a struct that contains #CX_COLLECTION_BASE
- * @param destr the destructor function
+ * @param destr (@c cx_destructor_func) the destructor function
*/
-#define cxDefineDestructor(c, destr) \
+#define cxSetDestructor(c, destr) \
(c)->collection.simple_destructor = (cx_destructor_func) destr
/**
- * Sets a simple destructor function for this collection.
+ * Sets an advanced destructor function for this collection.
*
* @param c a pointer to a struct that contains #CX_COLLECTION_BASE
- * @param destr the destructor function
+ * @param destr (@c cx_destructor_func2) the destructor function
+ * @param data (@c void*) the additional data the advanced destructor is invoked with
*/
-#define cxDefineAdvancedDestructor(c, destr, data) \
+#define cxSetAdvancedDestructor(c, destr, data) \
(c)->collection.advanced_destructor = (cx_destructor_func2) destr; \
(c)->collection.destructor_data = data
#define UCX_COMMON_H
/** Major UCX version as integer constant. */
-#define UCX_VERSION_MAJOR 3
+#define UCX_VERSION_MAJOR 4
/** Minor UCX version as integer constant. */
-#define UCX_VERSION_MINOR 1
+#define UCX_VERSION_MINOR 0
/** Version constant which ensures to increase monotonically. */
#define UCX_VERSION (((UCX_VERSION_MAJOR)<<16)|UCX_VERSION_MINOR)
// ---------------------------------------------------------------------------
-// MSVC specifics
+// Support for thread_local
// ---------------------------------------------------------------------------
+#ifdef __cplusplus
+#define cx_thread_local thread_local
+#else // ! __cplusplus
#ifdef _MSC_VER
-// fix missing _Thread_local support
-#define _Thread_local __declspec(thread)
+#define cx_thread_local __declspec(thread)
+#else // ! _MSC_VER
+#if __STDC_VERSION__ < 202300L
+#define cx_thread_local _Thread_local
+#else // C23 or newer
+#define cx_thread_local thread_local
+#endif // C23
#endif // _MSC_VER
+#endif // __cplusplus
// ---------------------------------------------------------------------------
// Exported and inlined functions
*/
typedef int (*cx_compare_func)(const void *left, const void *right);
+/**
+ * A comparator function comparing two arbitrary values.
+ *
+ * Functions with this signature allow specifying a pointer to custom data.
+ */
+typedef int (*cx_compare_func2)(const void *left, const void *right, void *data);
+
/**
* Compares two integers of type int.
*
cx_attr_nonnull cx_attr_nodiscard
CX_EXPORT int cx_cmp_ptr(const void *ptr1, const void *ptr2);
+/** Wraps a compare function for cx_cmp_wrap. */
+typedef struct {
+ /** The wrapped compare function */
+ cx_compare_func cmp;
+} cx_compare_func_wrapper;
+
+/**
+ * A @c cx_compare_func2 wrapper for a @c cx_compare_func().
+ *
+ * @param ptr1 pointer one
+ * @param ptr2 pointer two
+ * @param cmp_wrapper a pointer to a @c cx_compare_func_wrapper
+ * @return the result of the invoked compare function
+ * @see cx_compare_func_wrapper
+ */
+cx_attr_nonnull cx_attr_nodiscard
+CX_EXPORT int cx_cmp_wrap(const void *ptr1, const void *ptr2, void* cmp_wrapper);
+
#ifdef __cplusplus
} // extern "C"
#endif
cx_attr_nodiscard cx_attr_nonnull
CX_EXPORT int cx_hash_key_cmp(const void *left, const void *right);
+/**
+ * Interprets the key data as a string and returns it.
+ *
+ * @param key the key
+ * @return the key data as a string
+ */
+CX_EXPORT cxstring cx_hash_key_as_string(const CxHashKey *key);
+
#ifdef __cplusplus
} // extern "C"
CX_EXPORT CxMap *cxHashMapCreate(const CxAllocator *allocator,
size_t itemsize, size_t buckets);
-/**
- * Creates a new hash map with a default number of buckets.
- *
- * If @p elem_size is #CX_STORE_POINTERS, the created map stores pointers instead of
- * copies of the added elements.
- *
- * @note Iterators provided by this hash map implementation provide the remove operation.
- * The index value of an iterator is incremented when the iterator advanced without
- * removing an entry.
- * In other words, when the iterator is finished, @c index==size .
- *
- * @param itemsize (@c size_t) the size of one element
- * @return (@c CxMap*) a pointer to the new hash map
- */
-#define cxHashMapCreateSimple(itemsize) cxHashMapCreate(NULL, itemsize, 0)
-
/**
* Increases the number of buckets, if necessary.
*
* use cxIteratorPtr() to create an iterator which directly
* yields the stored pointers.
*
- * While the iterator is in use, the array may only be altered by removing
- * elements through #cxIteratorFlagRemoval(). Every other change to the array
- * will bring this iterator to an undefined state.
- *
- * When @p remove_keeps_order is set to @c false, removing an element will only
- * move the last element to the position of the removed element, instead of
- * moving all subsequent elements by one. Usually, when the order of elements is
- * not important, this parameter should be set to @c false.
- *
* @param array a pointer to the array (can be @c NULL)
* @param elem_size the size of one array element
* @param elem_count the number of elements in the array
- * @param remove_keeps_order @c true if the order of elements must be preserved
- * when removing an element
* @return an iterator for the specified array
* @see cxIteratorPtr()
*/
cx_attr_nodiscard
CX_EXPORT CxIterator cxIterator(const void *array,
- size_t elem_size, size_t elem_count, bool remove_keeps_order);
+ size_t elem_size, size_t elem_count);
/**
* Creates an iterator for the specified plain pointer array.
* hand, an iterator created with cxIterator() would return the
* addresses of those pointers within the array).
*
- * While the iterator is in use, the array may only be altered by removing
- * elements through #cxIteratorFlagRemoval(). Every other change to the array
- * will bring this iterator to an undefined state.
- *
- * When @p remove_keeps_order is set to @c false, removing an element will only
- * move the last element to the position of the removed element, instead of
- * moving all subsequent elements by one. Usually, when the order of elements is
- * not important, this parameter should be set to @c false.
- *
* @param array a pointer to the array (can be @c NULL)
* @param elem_count the number of elements in the array
- * @param remove_keeps_order @c true if the order of elements must be preserved
- * when removing an element
* @return an iterator for the specified array
* @see cxIterator()
*/
cx_attr_nodiscard
-CX_EXPORT CxIterator cxIteratorPtr(const void *array, size_t elem_count,
- bool remove_keeps_order);
+CX_EXPORT CxIterator cxIteratorPtr(const void *array, size_t elem_count);
#ifdef __cplusplus
} // extern "C"
#include "string.h"
#include "buffer.h"
#include "array_list.h"
-
-#include <string.h>
+#include "map.h"
#ifdef __cplusplus
extern "C" {
typedef struct cx_json_value_s CxJsonValue;
/**
- * Type alias for the JSON array struct.
- */
-typedef struct cx_json_array_s CxJsonArray;
-/**
- * Type alias for the JSON object struct.
+ * Type alias for the map representing a JSON object.
+ * The map contains pointers of type @c CxJsonValue.
*/
-typedef struct cx_json_object_s CxJsonObject;
+typedef CxMap* CxJsonObject;
/**
* Type alias for a JSON string.
*/
*/
typedef enum cx_json_literal CxJsonLiteral;
-/**
- * Type alias for a key/value pair in a JSON object.
- */
-typedef struct cx_json_obj_value_s CxJsonObjValue;
-
-/**
- * JSON array structure.
- */
-struct cx_json_array_s {
- /**
- * The array data.
- */
- CX_ARRAY_DECLARE(CxJsonValue*, array);
-};
-
-/**
- * JSON object structure.
- */
-struct cx_json_object_s {
- /**
- * The key/value entries.
- */
- CX_ARRAY_DECLARE(CxJsonObjValue, values);
- /**
- * The original indices to reconstruct the order in which the members were added.
- */
- size_t *indices;
-};
-
-/**
- * Structure for a key/value entry in a JSON object.
- */
-struct cx_json_obj_value_s {
- /**
- * The key (or name in JSON terminology) of the value.
- */
- cxmutstr name;
- /**
- * The value.
- */
- CxJsonValue *value;
-};
-
/**
* Structure for a JSON value.
*/
/**
* The array data if the type is #CX_JSON_ARRAY.
*/
- CxJsonArray array;
+ CX_ARRAY(CxJsonValue*, array);
/**
* The object data if the type is #CX_JSON_OBJECT.
*/
* The literal type if the type is #CX_JSON_LITERAL.
*/
CxJsonLiteral literal;
- } value;
+ };
};
/**
* The allocator used for produced JSON values.
*/
const CxAllocator *allocator;
+
/**
* The input buffer.
*/
CxJsonValue *parsed;
/**
- * A pointer to an intermediate state of a currently parsed object member.
+ * The name of a not yet completely parsed object member.
*
* Never access this value manually.
*/
- CxJsonObjValue uncompleted_member;
+ cxmutstr uncompleted_member_name;
/**
* State stack.
*/
- CX_ARRAY_DECLARE_SIZED(int, states, unsigned);
+ CX_ARRAY(int, states);
/**
* Value buffer stack.
*/
- CX_ARRAY_DECLARE_SIZED(CxJsonValue*, vbuf, unsigned);
+ CX_ARRAY(CxJsonValue*, vbuf);
/**
* Internally reserved memory for the state stack.
* Set true to enable pretty output.
*/
bool pretty;
- /**
- * Set false to output the members in the order in which they were added.
- */
- bool sort_members;
/**
* The maximum number of fractional digits in a number value.
* The default value is 6 and values larger than 15 are reduced to 15.
CX_EXPORT int cxJsonWrite(void* target, const CxJsonValue* value,
cx_write_func wfunc, const CxJsonWriter* settings);
+
+/**
+ * Produces a compact string representation of the specified JSON value.
+ *
+ * @param allocator the allocator for the string
+ * @param value the JSON value
+ * @return the produced string
+ * @see cxJsonWrite()
+ * @see cxJsonWriterCompact()
+ * @see cxJsonToPrettyString()
+ */
+cx_attr_nonnull_arg(2)
+CX_EXPORT cxmutstr cxJsonToString(const CxAllocator *allocator, CxJsonValue *value);
+
+/**
+ * Produces a pretty string representation of the specified JSON value.
+ *
+ * @param allocator the allocator for the string
+ * @param value the JSON value
+ * @return the produced string
+ * @see cxJsonWrite()
+ * @see cxJsonWriterPretty()
+ * @see cxJsonToString()
+ */
+cx_attr_nonnull_arg(2)
+CX_EXPORT cxmutstr cxJsonToPrettyString(const CxAllocator *allocator, CxJsonValue *value);
+
/**
* Initializes the JSON interface.
*
/**
* Destroys and re-initializes the JSON interface.
*
- * You might want to use this to reset the parser after
- * encountering a syntax error.
+ * You must use this to reset the parser after encountering a syntax error
+ * if you want to continue using it.
*
* @param json the JSON interface
*/
*/
#define cxJsonFill(json, str) cx_json_fill(json, cx_strcast(str))
+
+/**
+ * Internal function - use cxJsonFromString() instead.
+ *
+ * @param allocator the allocator for the JSON value
+ * @param str the string to parse
+ * @param value a pointer where the JSON value shall be stored to
+ * @return status code
+ */
+cx_attr_nonnull_arg(3)
+CX_EXPORT CxJsonStatus cx_json_from_string(const CxAllocator *allocator,
+ cxstring str, CxJsonValue **value);
+
+/**
+ * Parses a string into a JSON value.
+ *
+ * @param allocator (@c CxAllocator*) the allocator for the JSON value
+ * @param str (any string) the string to parse
+ * @param value (@c CxJsonValue**) a pointer where the JSON value shall be stored to
+ * @retval CX_JSON_NO_ERROR success
+ * @retval CX_JSON_NO_DATA the string was empty or blank
+ * @retval CX_JSON_INCOMPLETE_DATA the string unexpectedly ended
+ * @retval CX_JSON_BUFFER_ALLOC_FAILED allocating internal buffer space failed
+ * @retval CX_JSON_VALUE_ALLOC_FAILED allocating memory for the CxJsonValue failed
+ * @retval CX_JSON_FORMAT_ERROR_NUMBER the JSON text contains an illegally formatted number
+ * @retval CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN JSON syntax error
+ */
+#define cxJsonFromString(allocator, str, value) \
+ cx_json_from_string(allocator, cx_strcast(str), value)
+
/**
* Creates a new (empty) JSON object.
*
/**
* Creates a new (empty) JSON array.
*
+ * Optionally, this function already allocates memory with the given capacity.
+ *
* @param allocator the allocator to use
+ * @param capacity optional capacity or zero if it's unknown how many elements the array will have
* @return the new JSON array or @c NULL if allocation fails
* @see cxJsonObjPutArr()
* @see cxJsonArrAddValues()
*/
cx_attr_nodiscard
-CX_EXPORT CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator);
+CX_EXPORT CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator, size_t capacity);
/**
* Creates a new JSON number value.
*
* @param obj the target JSON object
* @param name the name of the new value
+ * @param capacity optional initial capacity
* @return the new value or @c NULL if allocation fails
* @see cxJsonObjPut()
* @see cxJsonCreateArr()
*/
cx_attr_nonnull
-CX_EXPORT CxJsonValue* cx_json_obj_put_arr(CxJsonValue* obj, cxstring name);
+CX_EXPORT CxJsonValue* cx_json_obj_put_arr(CxJsonValue* obj, cxstring name, size_t capacity);
/**
* Creates a new JSON array and adds it to an object.
*
* @param obj (@c CxJsonValue*) the target JSON object
* @param name (any string) the name of the new value
+ * @param capacity (@c size_t) optional initial capacity
* @return (@c CxJsonValue*) the new value or @c NULL if allocation fails
* @see cxJsonObjPut()
* @see cxJsonCreateArr()
*/
-#define cxJsonObjPutArr(obj, name) cx_json_obj_put_arr(obj, cx_strcast(name))
+#define cxJsonObjPutArr(obj, name, capacity) cx_json_obj_put_arr(obj, cx_strcast(name), capacity)
/**
* Creates a new JSON number and adds it to an object.
*/
cx_attr_nonnull
CX_INLINE bool cxJsonIsBool(const CxJsonValue *value) {
- return cxJsonIsLiteral(value) && value->value.literal != CX_JSON_NULL;
+ return cxJsonIsLiteral(value) && value->literal != CX_JSON_NULL;
}
/**
*/
cx_attr_nonnull
CX_INLINE bool cxJsonIsTrue(const CxJsonValue *value) {
- return cxJsonIsLiteral(value) && value->value.literal == CX_JSON_TRUE;
+ return cxJsonIsLiteral(value) && value->literal == CX_JSON_TRUE;
}
/**
*/
cx_attr_nonnull
CX_INLINE bool cxJsonIsFalse(const CxJsonValue *value) {
- return cxJsonIsLiteral(value) && value->value.literal == CX_JSON_FALSE;
+ return cxJsonIsLiteral(value) && value->literal == CX_JSON_FALSE;
}
/**
*/
cx_attr_nonnull
CX_INLINE bool cxJsonIsNull(const CxJsonValue *value) {
- return cxJsonIsLiteral(value) && value->value.literal == CX_JSON_NULL;
+ return cxJsonIsLiteral(value) && value->literal == CX_JSON_NULL;
}
/**
*/
cx_attr_nonnull
CX_INLINE bool cxJsonAsBool(const CxJsonValue *value) {
- return value->value.literal == CX_JSON_TRUE;
+ return value->literal == CX_JSON_TRUE;
}
/**
*/
cx_attr_nonnull
CX_INLINE size_t cxJsonArrSize(const CxJsonValue *value) {
- return value->value.array.array_size;
+ return value->array.size;
}
/**
*/
cx_attr_nonnull
CX_INLINE size_t cxJsonObjSize(const CxJsonValue *value) {
- return value->value.object.values_size;
+ return cxCollectionSize(value->object);
}
/**
- * Returns an iterator over the JSON object members.
+ * Returns a map iterator over the JSON object members.
*
- * The iterator yields values of type @c CxJsonObjValue* which
- * contain the name and value of the member.
+ * The iterator yields values of type @c CxMapEntry* which
+ * contain the name and the @c CxJsonObjValue* of the member.
*
* If the @p value is not a JSON object, the behavior is undefined.
*
* @see cxJsonIsObject()
*/
cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT CxIterator cxJsonObjIter(const CxJsonValue *value);
+CX_EXPORT CxMapIterator cxJsonObjIter(const CxJsonValue *value);
/**
* Internal function, do not use.
*/
#define cxJsonObjRemove(value, name) cx_json_obj_remove(value, cx_strcast(name))
+/**
+ * Performs a deep comparison of two JSON values.
+ *
+ * The order of object members is ignored during comparison.
+ *
+ * @param json the JSON value
+ * @param other the other JSON value that the JSON value is compared to
+ * @retval zero the values are equal (except for ordering of object members)
+ * @retval non-zero the values differ
+ */
+CX_EXPORT int cxJsonCompare(const CxJsonValue *json, const CxJsonValue *other);
+
+
+/**
+ * Creates a deep copy of the specified JSON value.
+ *
+ * If you need a @c cx_clone_func compatible version, see cxJsonCloneFunc().
+ *
+ * @note when you are cloning @c NULL, you will get a pointer to a statically
+ * allocated value which represents nothing.
+ *
+ * @param value the value to be cloned
+ * @param allocator the allocator for the new value
+ * @return the new value or @c NULL if any allocation was unsuccessful
+ * @see cxJsonCloneFunc()
+ */
+cx_attr_nodiscard
+CX_EXPORT CxJsonValue* cxJsonClone(const CxJsonValue* value,
+ const CxAllocator* allocator);
+
+
+/**
+ * A @c cx_clone_func compatible version of cxJsonClone().
+ *
+ * Internal function - use cxJsonCloneFunc() to get a properly casted function pointer.
+ *
+ * @param target the target memory or @c NULL
+ * @param source the value to be cloned
+ * @param allocator the allocator for the new value
+ * @param data unused
+ * @return the new value or @c NULL if any allocation was unsuccessful
+ * @see cxJsonClone()
+ */
+cx_attr_nodiscard
+CX_EXPORT CxJsonValue* cx_json_clone_func(
+ CxJsonValue* target, const CxJsonValue* source,
+ const CxAllocator* allocator, void *data);
+
+/**
+ * A @c cx_clone_func compatible version of cxJsonClone().
+ *
+ * @param target (@c CxJsonValue*) the target memory or @c NULL
+ * @param source (@c CxJsonValue*) the value to be cloned
+ * @param allocator (@c CxAllocator*) the allocator for the new value
+ * @param data unused
+ * @return the new value or @c NULL if any allocation was unsuccessful
+ * @see cxJsonClone()
+ */
+#define cxJsonCloneFunc ((cx_clone_func) cx_json_clone_func)
+
#ifdef __cplusplus
}
#endif
*
* 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().
*
* After creating the list, it can also be used as a map after converting the pointer
* to a CxMap pointer with cxKvListAsMap().
*
* @param allocator the allocator for allocating the list nodes
* (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 elem_size the size of each element in bytes
* @return the created list
* @see cxKvListAsMap()
*/
cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxListFree, 1)
CX_EXPORT CxList *cxKvListCreate(const CxAllocator *allocator,
- cx_compare_func comparator, size_t elem_size);
+ size_t elem_size);
/**
* Allocates a linked list with a lookup-map for storing elements with @p elem_size bytes each.
*
* 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().
*
* This function creates the list with cxKvListCreate() and immediately applies
* cxKvListAsMap(). If you want to use the returned object as a list, you can call
*
* @param allocator the allocator for allocating the list nodes
* (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 elem_size the size of each element in bytes
* @return the created list wrapped into the CxMap interface
* @see cxKvListAsMap()
*/
cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxMapFree, 1)
CX_EXPORT CxMap *cxKvListCreateAsMap(const CxAllocator *allocator,
- cx_compare_func comparator, size_t elem_size);
-
-/**
- * Allocates a linked list with a lookup-map for storing elements with @p elem_size bytes each.
- *
- * The list will use cxDefaultAllocator and no comparator function. If you want
- * to call functions that need a comparator, you must either set one immediately
- * after list creation or use cxKvListCreate().
- *
- * 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().
- *
- * After creating the list, it can also be used as a map after converting the pointer
- * to a CxMap pointer with cxKvListAsMap().
- * When you want to use the list interface again, you can also convert the map pointer back
- * with cxKvListAsList().
- *
- * @param elem_size (@c size_t) the size of each element in bytes
- * @return (@c CxList*) the created list
- * @see cxKvListAsMap()
- * @see cxKvListAsList()
- */
-#define cxKvListCreateSimple(elem_size) cxKvListCreate(NULL, NULL, elem_size)
-
-/**
- * Allocates a linked list with a lookup-map for storing elements with @p elem_size bytes each.
- *
- * The list will use cxDefaultAllocator and no comparator function. If you want
- * to call functions that need a comparator, you must either set one immediately
- * after list creation or use cxKvListCreate().
- *
- * 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().
- *
- * This macro behaves as if the list was created with cxKvListCreateSimple() and
- * immediately followed up by cxKvListAsMap().
- * If you want to use the returned object as a list, you can call cxKvListAsList() later.
- *
- * @param elem_size (@c size_t) the size of each element in bytes
- * @return (@c CxMap*) the created list wrapped into the CxMap interface
- * @see cxKvListAsMap()
- * @see cxKvListAsList()
- */
-#define cxKvListCreateAsMapSimple(elem_size) cxKvListCreateAsMap(NULL, NULL, elem_size)
+ size_t elem_size);
/**
* Converts a map pointer belonging to a key-value-List back to the original list pointer.
/**
* Converts a map pointer belonging to a key-value-List back to the original list pointer.
*
- * @param list a list created by cxKvListCreate() or cxKvListCreateSimple()
+ * @param list a list created by cxKvListCreate()
* @return a map pointer that lets you use the list as if it was a map
*/
cx_attr_nodiscard cx_attr_nonnull cx_attr_returns_nonnull
*
* 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 allocator the allocator for allocating the list nodes
* (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 elem_size the size of each element in bytes
* @return the created list
*/
cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxListFree, 1)
CX_EXPORT CxList *cxLinkedListCreate(const CxAllocator *allocator,
- cx_compare_func comparator, size_t elem_size);
-
-/**
- * Allocates a linked list for storing elements with @p elem_size bytes each.
- *
- * The list will use cxDefaultAllocator and no comparator function. If you want
- * to call functions that need a comparator, you must either set one immediately
- * after list creation or use cxLinkedListCreate().
- *
- * 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().
- *
- * @param elem_size (@c size_t) the size of each element in bytes
- * @return (@c CxList*) the created list
- */
-#define cxLinkedListCreateSimple(elem_size) \
- cxLinkedListCreate(NULL, NULL, elem_size)
+ size_t elem_size);
/**
* Instructs the linked list to reserve extra data in each node.
* @param start a pointer to the start node
* @param loc_advance the location of the pointer to advance
* @param loc_data the location of the @c data pointer within your node struct
- * @param cmp_func a compare function to compare @p elem against the node data
* @param elem a pointer to the element to find
* @param found_index an optional pointer where the index of the found node
* (given that @p start has index 0) is stored
+ * @param cmp_func a compare function to compare @p elem against the node data
* @return a pointer to the found node or @c NULL if no matching node was found
*/
-cx_attr_nonnull_arg(1, 4, 5)
+cx_attr_nonnull_arg(1, 4, 6)
CX_EXPORT void *cx_linked_list_find(const void *start, ptrdiff_t loc_advance,
- ptrdiff_t loc_data, cx_compare_func cmp_func, const void *elem,
- size_t *found_index);
+ ptrdiff_t loc_data, const void *elem, size_t *found_index,
+ cx_compare_func cmp_func);
+
+/**
+ * Finds the node containing an element within a linked list.
+ *
+ * @param start a pointer to the start node
+ * @param loc_advance the location of the pointer to advance
+ * @param loc_data the location of the @c data pointer within your node struct
+ * @param elem a pointer to the element to find
+ * @param found_index an optional pointer where the index of the found node
+ * (given that @p start has index 0) is stored
+ * @param cmp_func a compare function to compare @p elem against the node data
+ * @param context additional context for the compare function
+ * @return a pointer to the found node or @c NULL if no matching node was found
+ */
+cx_attr_nonnull_arg(1, 4, 6)
+CX_EXPORT void *cx_linked_list_find_c(const void *start, ptrdiff_t loc_advance,
+ ptrdiff_t loc_data, const void *elem, size_t *found_index,
+ cx_compare_func2 cmp_func, void *context);
/**
* Finds the first node in a linked list.
CX_EXPORT void *cx_linked_list_insert_unique_chain(void **begin, void **end,
ptrdiff_t loc_prev, ptrdiff_t loc_next, void *insert_begin, cx_compare_func cmp_func);
+/**
+ * Inserts a node into a sorted linked list.
+ * The new node must not be part of any list yet.
+ *
+ * If the list starting with the node pointed to by @p begin is not sorted
+ * already, the behavior is undefined.
+ *
+ * @param begin a pointer to the beginning node pointer (required)
+ * @param end a pointer to the end node pointer (if your list has one)
+ * @param loc_prev the location of a @c prev pointer within your node struct (negative if your struct does not have one)
+ * @param loc_next the location of a @c next pointer within your node struct (required)
+ * @param new_node a pointer to the node that shall be inserted
+ * @param cmp_func a compare function that will receive the node pointers
+ * @param context context for the compare function
+ */
+cx_attr_nonnull_arg(1, 5, 6)
+CX_EXPORT void cx_linked_list_insert_sorted_c(void **begin, void **end,
+ ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node, cx_compare_func2 cmp_func, void *context);
+
+/**
+ * Inserts a chain of nodes into a sorted linked list.
+ * The chain must not be part of any list yet.
+ *
+ * If either the list starting with the node pointed to by @p begin or the list
+ * starting with @p insert_begin is not sorted, the behavior is undefined.
+ *
+ * @attention In contrast to cx_linked_list_insert_chain(), the source chain
+ * will be broken and inserted into the target list so that the resulting list
+ * will be sorted according to @p cmp_func. That means each node in the source
+ * chain may be re-linked with nodes from the target list.
+ *
+ * @param begin a pointer to the beginning node pointer (required)
+ * @param end a pointer to the end node pointer (if your list has one)
+ * @param loc_prev the location of a @c prev pointer within your node struct (negative if your struct does not have one)
+ * @param loc_next the location of a @c next pointer within your node struct (required)
+ * @param insert_begin a pointer to the first node of the chain that shall be inserted
+ * @param cmp_func a compare function that will receive the node pointers
+ * @param context context for the compare function
+ */
+cx_attr_nonnull_arg(1, 5, 6)
+CX_EXPORT void cx_linked_list_insert_sorted_chain_c(void **begin, void **end,
+ ptrdiff_t loc_prev, ptrdiff_t loc_next, void *insert_begin, cx_compare_func2 cmp_func, void *context);
+
+/**
+ * Inserts a node into a sorted linked list if no other node with the same value already exists.
+ * The new node must not be part of any list yet.
+ *
+ * If the list starting with the node pointed to by @p begin is not sorted
+ * already, the behavior is undefined.
+ *
+ * @param begin a pointer to the beginning node pointer (required)
+ * @param end a pointer to the end node pointer (if your list has one)
+ * @param loc_prev the location of a @c prev pointer within your node struct (negative if your struct does not have one)
+ * @param loc_next the location of a @c next pointer within your node struct (required)
+ * @param new_node a pointer to the node that shall be inserted
+ * @param cmp_func a compare function that will receive the node pointers
+ * @retval zero when the node was inserted
+ * @retval non-zero when a node with the same value already exists
+ */
+cx_attr_nonnull_arg(1, 5, 6)
+CX_EXPORT int cx_linked_list_insert_unique_c(void **begin, void **end,
+ ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node, cx_compare_func2 cmp_func, void *context);
+
+/**
+ * Inserts a chain of nodes into a sorted linked list, avoiding duplicates.
+ * The chain must not be part of any list yet.
+ *
+ * If either the list starting with the node pointed to by @p begin or the list
+ * starting with @p insert_begin is not sorted, the behavior is undefined.
+ *
+ * @attention In contrast to cx_linked_list_insert_sorted(), not all nodes of the
+ * chain might be added. This function returns a new chain consisting of all the duplicates.
+ *
+ * @param begin a pointer to the beginning node pointer (required)
+ * @param end a pointer to the end node pointer (if your list has one)
+ * @param loc_prev the location of a @c prev pointer within your node struct (negative if your struct does not have one)
+ * @param loc_next the location of a @c next pointer within your node struct (required)
+ * @param insert_begin a pointer to the first node of the chain that shall be inserted
+ * @param cmp_func a compare function that will receive the node pointers
+ * @param context context for the compare function
+ * @return a pointer to a new chain with all duplicates that were not inserted (or @c NULL when there were no duplicates)
+ */
+cx_attr_nonnull_arg(1, 5, 6) cx_attr_nodiscard
+CX_EXPORT void *cx_linked_list_insert_unique_chain_c(void **begin, void **end,
+ ptrdiff_t loc_prev, ptrdiff_t loc_next, void *insert_begin, cx_compare_func2 cmp_func, void *context);
+
/**
* Removes a chain of nodes from the linked list.
*
/**
* Sorts a linked list based on a comparison function.
*
- * This function can work with linked lists of the following structure:
- * @code
- * typedef struct node node;
- * struct node {
- * node* prev;
- * node* next;
- * my_payload data;
- * }
- * @endcode
- *
* @note This is a recursive function with at most logarithmic recursion depth.
*
* @param begin a pointer to the beginning node pointer (required)
CX_EXPORT void cx_linked_list_sort(void **begin, void **end,
ptrdiff_t loc_prev, ptrdiff_t loc_next, ptrdiff_t loc_data, cx_compare_func cmp_func);
+/**
+ * Sorts a linked list based on a comparison function.
+ *
+ * @note This is a recursive function with at most logarithmic recursion depth.
+ *
+ * @param begin a pointer to the beginning node pointer (required)
+ * @param end a pointer to the end node pointer (optional)
+ * @param loc_prev the location of a @c prev pointer within your node struct (negative if not present)
+ * @param loc_next the location of a @c next pointer within your node struct (required)
+ * @param loc_data the location of the @c data pointer within your node struct
+ * @param cmp_func the compare function defining the sort order
+ * @param context additional context for the compare function
+ */
+cx_attr_nonnull_arg(1, 6)
+CX_EXPORT void cx_linked_list_sort_c(void **begin, void **end,
+ ptrdiff_t loc_prev, ptrdiff_t loc_next, ptrdiff_t loc_data, cx_compare_func2 cmp_func, void *context);
+
/**
* Compares two lists element wise.
CX_EXPORT int cx_linked_list_compare(const void *begin_left, const void *begin_right,
ptrdiff_t loc_advance, ptrdiff_t loc_data, cx_compare_func cmp_func);
+/**
+ * Compares two lists element wise.
+ *
+ * @attention Both lists must have the same structure.
+ *
+ * @param begin_left the beginning of the left list (@c NULL denotes an empty list)
+ * @param begin_right the beginning of the right list (@c NULL denotes an empty list)
+ * @param loc_advance the location of the pointer to advance
+ * @param loc_data the location of the @c data pointer within your node struct
+ * @param cmp_func the function to compare the elements
+ * @return the first non-zero result of invoking @p cmp_func or: negative if the left list is smaller than the
+ * right list, positive if the left list is larger than the right list, zero if both lists are equal.
+ */
+cx_attr_nonnull_arg(5)
+CX_EXPORT int cx_linked_list_compare_c(const void *begin_left, const void *begin_right,
+ ptrdiff_t loc_advance, ptrdiff_t loc_data, cx_compare_func2 cmp_func, void *context);
+
/**
* Reverses the order of the nodes in a linked list.
*
* The list class definition.
*/
const cx_list_class *cl;
- /**
- * The actual implementation in case the list class is delegating.
- */
- const cx_list_class *climpl;
};
/**
* @param n the number of elements to insert
* @return the number of elements actually inserted
*/
-cx_attr_nonnull
+cx_attr_nonnull_arg(1)
CX_EXPORT size_t cx_list_default_insert_array(struct cx_list_s *list,
size_t index, const void *data, size_t n);
* The purpose of this function is to be called in the initialization code
* of your list to set certain members correctly.
*
- * This is particularly important when you want your list to support
- * #CX_STORE_POINTERS as @p elem_size. This function will wrap the list
- * class accordingly and make sure that you can implement your list as if
- * it was only storing objects, and the wrapper will automatically enable
- * the feature of storing pointers.
+ * This is particularly useful when you want your list to support
+ * #CX_STORE_POINTERS as @p elem_size.
*
* @par Example
*
* @code
* CxList *myCustomListCreate(
* const CxAllocator *allocator,
- * cx_compare_func comparator,
* size_t elem_size
* ) {
* if (allocator == NULL) {
* allocator = cxDefaultAllocator;
* }
*
- * MyCustomList *list = cxCalloc(allocator, 1, sizeof(MyCustomList));
+ * MyCustomList *list = cxZalloc(allocator, sizeof(MyCustomList));
* if (list == NULL) return NULL;
*
* // initialize
* cx_list_init((CxList*)list, &my_custom_list_class,
- * allocator, comparator, elem_size);
+ * allocator, elem_size);
*
* // ... some more custom stuff ...
*
* @param list the list to initialize
* @param cl the list class
* @param allocator the allocator for the elements
- * @param comparator a compare function for the elements
* @param elem_size the size of one element
*/
cx_attr_nonnull_arg(1, 2, 3)
CX_EXPORT void cx_list_init(struct cx_list_s *list,
struct cx_list_class_s *cl, const struct cx_allocator_s *allocator,
- cx_compare_func comparator, size_t elem_size);
+ size_t elem_size);
+
+/**
+ * A @c cx_compare_func2 compatible wrapper for the compare functions of a list.
+ *
+ * @param left first element
+ * @param right second element
+ * @param list the list which is comparing the elements
+ * @return the comparison result
+ */
+cx_attr_nonnull
+CX_EXPORT int cx_list_compare_wrapper(
+ const void *left, const void *right, void *list);
/**
* Returns the number of elements currently stored in the list.
* @param data optional additional data that is passed to the clone function
* @retval zero when all elements were successfully cloned
* @retval non-zero when an allocation error occurred
- * @see cxListCloneSimple()
+ * @see cxListCloneShallow()
*/
cx_attr_nonnull_arg(1, 2, 3)
CX_EXPORT int cxListClone(CxList *dst, const CxList *src,
* @param data optional additional data that is passed to the clone function
* @retval zero when the elements were successfully cloned
* @retval non-zero when an allocation error occurred
- * @see cxListDifferenceSimple()
+ * @see cxListDifferenceShallow()
*/
cx_attr_nonnull_arg(1, 2, 3, 4)
CX_EXPORT int cxListDifference(CxList *dst,
* @param data optional additional data that is passed to the clone function
* @retval zero when the elements were successfully cloned
* @retval non-zero when an allocation error occurred
- * @see cxListIntersectionSimple()
+ * @see cxListIntersectionShallow()
*/
cx_attr_nonnull_arg(1, 2, 3, 4)
CX_EXPORT int cxListIntersection(CxList *dst, const CxList *src, const CxList *other,
* @param data optional additional data that is passed to the clone function
* @retval zero when the elements were successfully cloned
* @retval non-zero when an allocation error occurred
- * @see cxListUnionSimple()
+ * @see cxListUnionShallow()
*/
cx_attr_nonnull_arg(1, 2, 3, 4)
CX_EXPORT int cxListUnion(CxList *dst, const CxList *src, const CxList *other,
* @see cxListClone()
*/
cx_attr_nonnull
-CX_EXPORT int cxListCloneSimple(CxList *dst, const CxList *src);
+CX_EXPORT int cxListCloneShallow(CxList *dst, const CxList *src);
/**
* Clones elements from a list only if they are not present in another list.
* @see cxListDifference()
*/
cx_attr_nonnull
-CX_EXPORT int cxListDifferenceSimple(CxList *dst,
+CX_EXPORT int cxListDifferenceShallow(CxList *dst,
const CxList *minuend, const CxList *subtrahend);
/**
* @see cxListIntersection()
*/
cx_attr_nonnull
-CX_EXPORT int cxListIntersectionSimple(CxList *dst, const CxList *src, const CxList *other);
+CX_EXPORT int cxListIntersectionShallow(CxList *dst, const CxList *src, const CxList *other);
/**
* Performs a deep clone of one list into another, skipping duplicates.
* @see cxListUnion()
*/
cx_attr_nonnull
-CX_EXPORT int cxListUnionSimple(CxList *dst, const CxList *src, const CxList *other);
+CX_EXPORT int cxListUnionShallow(CxList *dst, const CxList *src, const CxList *other);
/**
* Asks the list to reserve enough memory for a given total number of elements.
* Add or overwrite an element.
* If the @p value is @c NULL, the implementation
* shall only allocate memory instead of adding an existing value to the map.
- * Returns a pointer to the allocated memory or @c NULL if allocation fails.
+ * Returns a map entry where the pointer to the key is @c NULL if allocation fails.
*/
- void *(*put)(CxMap *map, CxHashKey key, void *value);
+ CxMapEntry (*put)(CxMap *map, CxHashKey key, void *value);
/**
* Returns an element.
* @param map the map
* @param key the key
* @return the pointer to the allocated memory or @c NULL if allocation fails
- * @retval zero success
- * @retval non-zero value on memory allocation failure
* @see cxMapEmplace()
*/
cx_attr_nonnull
* @param map (@c CxMap*) the map
* @param key (any supported key type) the key
* @return the pointer to the allocated memory or @c NULL if allocation fails
- * @retval zero success
- * @retval non-zero value on memory allocation failure
* @see CX_HASH_KEY()
*/
#define cxMapEmplace(map, key) cx_map_emplace(map, CX_HASH_KEY(key))
* @see cxMapClone()
*/
cx_attr_nonnull
-CX_EXPORT int cxMapCloneSimple(CxMap *dst, const CxMap *src);
+CX_EXPORT int cxMapCloneShallow(CxMap *dst, const CxMap *src);
/**
* Clones entries of a map if their key is not present in another map.
* @retval non-zero when an allocation error occurred
*/
cx_attr_nonnull
-CX_EXPORT int cxMapDifferenceSimple(CxMap *dst, const CxMap *minuend, const CxMap *subtrahend);
+CX_EXPORT int cxMapDifferenceShallow(CxMap *dst, const CxMap *minuend, const CxMap *subtrahend);
/**
* Clones entries of a map if their key is not present in a list.
* @see cxMapListDifference()
*/
cx_attr_nonnull
-CX_EXPORT int cxMapListDifferenceSimple(CxMap *dst, const CxMap *src, const CxList *keys);
+CX_EXPORT int cxMapListDifferenceShallow(CxMap *dst, const CxMap *src, const CxList *keys);
/**
* @retval non-zero when an allocation error occurred
*/
cx_attr_nonnull
-CX_EXPORT int cxMapIntersectionSimple(CxMap *dst, const CxMap *src, const CxMap *other);
+CX_EXPORT int cxMapIntersectionShallow(CxMap *dst, const CxMap *src, const CxMap *other);
/**
* Clones entries of a map only if their key is present in a list.
* @retval non-zero when an allocation error occurred
*/
cx_attr_nonnull
-CX_EXPORT int cxMapListIntersectionSimple(CxMap *dst, const CxMap *src, const CxList *keys);
+CX_EXPORT int cxMapListIntersectionShallow(CxMap *dst, const CxMap *src, const CxList *keys);
/**
* Clones entries into a map if their key does not exist yet.
* @retval non-zero when an allocation error occurred
*/
cx_attr_nonnull
-CX_EXPORT int cxMapUnionSimple(CxMap *dst, const CxMap *src);
+CX_EXPORT int cxMapUnionShallow(CxMap *dst, const CxMap *src);
+
+
+/**
+ * Compares the entries of two maps.
+ *
+ * @param map the map
+ * @param other the other map that the first map is compared to
+ * @retval zero when both maps have the same key sets
+ * and the values are pairwise equivalent
+ * @retval negative when the first @p map has fewer keys than the @p other map
+ * @retval positive when the first @p map has more keys than the @p other map
+ * @retval non-zero (unspecified whether positive or negative) when the size
+ * of both maps is equal but a key or a value is different
+ */
+cx_attr_nonnull
+CX_EXPORT int cxMapCompare(const CxMap *map, const CxMap *other);
#ifdef __cplusplus
} // extern "C"
#include "map.h"
#include "buffer.h"
-#include <stdio.h>
-#include <string.h>
-
#ifdef __cplusplus
extern "C" {
#endif
*/
char comment3;
- /*
+ /**
* The character, when appearing at the end of a line, continues that line.
* This is '\' by default.
*/
- /**
- * Reserved for future use.
- */
char continuation;
};
*/
CX_PROPERTIES_BUFFER_ALLOC_FAILED,
/**
- * Initializing the properties source failed.
- *
- * @see cx_properties_read_init_func
+ * A file operation failed.
+ * Only for cxPropertiesLoad().
+ * It is system-specific if errno is set.
*/
- CX_PROPERTIES_READ_INIT_FAILED,
+ CX_PROPERTIES_FILE_ERROR,
/**
- * Reading from a properties source failed.
- *
- * @see cx_properties_read_func
+ * A map operation failed.
+ * Only for cxPropertiesLoad().
*/
- CX_PROPERTIES_READ_FAILED,
- /**
- * Sinking a k/v-pair failed.
- *
- * @see cx_properties_sink_func
- */
- CX_PROPERTIES_SINK_FAILED,
+ CX_PROPERTIES_MAP_ERROR,
};
/**
*/
typedef struct cx_properties_s CxProperties;
-
-/**
- * Typedef for a properties sink.
- */
-typedef struct cx_properties_sink_s CxPropertiesSink;
-
-/**
- * A function that consumes a k/v-pair in a sink.
- *
- * The sink could be a map, and the sink function would be calling
- * a map function to store the k/v-pair.
- *
- * @param prop the properties interface that wants to sink a k/v-pair
- * @param sink the sink
- * @param key the key
- * @param value the value
- * @retval zero success
- * @retval non-zero sinking the k/v-pair failed
- */
-typedef int(*cx_properties_sink_func)(
- CxProperties *prop,
- CxPropertiesSink *sink,
- cxstring key,
- cxstring value
-);
-
-/**
- * Defines a sink for k/v-pairs.
- */
-struct cx_properties_sink_s {
- /**
- * The sink object.
- */
- void *sink;
- /**
- * Optional custom data.
- */
- void *data;
- /**
- * A function for consuming k/v-pairs into the sink.
- */
- cx_properties_sink_func sink_func;
-};
-
-
-/**
- * Typedef for a properties source.
- */
-typedef struct cx_properties_source_s CxPropertiesSource;
-
-/**
- * A function that reads data from a source.
- *
- * When the source is depleted, implementations SHALL provide an empty
- * string in the @p target and return zero.
- * A non-zero return value is only permitted in case of an error.
- *
- * The meaning of the optional parameters is implementation-dependent.
- *
- * @param prop the properties interface that wants to read from the source
- * @param src the source
- * @param target a string buffer where the read data shall be stored
- * @retval zero success
- * @retval non-zero reading the data failed
- */
-typedef int(*cx_properties_read_func)(
- CxProperties *prop,
- CxPropertiesSource *src,
- cxstring *target
-);
-
-/**
- * A function that may initialize additional memory for the source.
- *
- * @param prop the properties interface that wants to read from the source
- * @param src the source
- * @retval zero initialization was successful
- * @retval non-zero otherwise
- */
-typedef int(*cx_properties_read_init_func)(
- CxProperties *prop,
- CxPropertiesSource *src
-);
-
-/**
- * A function that cleans memory initialized by the read_init_func.
- *
- * @param prop the properties interface that wants to read from the source
- * @param src the source
- */
-typedef void(*cx_properties_read_clean_func)(
- CxProperties *prop,
- CxPropertiesSource *src
-);
-
-/**
- * Defines a properties source.
- */
-struct cx_properties_source_s {
- /**
- * The source object.
- *
- * For example, a file stream or a string.
- */
- void *src;
- /**
- * Optional additional data pointer.
- */
- void *data_ptr;
- /**
- * Optional size information.
- */
- size_t data_size;
- /**
- * A function that reads data from the source.
- */
- cx_properties_read_func read_func;
- /**
- * Optional function that may prepare the source for reading data.
- */
- cx_properties_read_init_func read_init_func;
- /**
- * Optional function that cleans additional memory allocated by the
- * read_init_func.
- */
- cx_properties_read_clean_func read_clean_func;
-};
-
/**
* Initialize a properties interface.
*
CX_EXPORT CxPropertiesStatus cxPropertiesNext(CxProperties *prop, cxstring *key, cxstring *value);
/**
- * Creates a properties sink for an UCX map.
- *
- * 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 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.
- * @return the sink
- * @see cxPropertiesLoad()
+ * The size of the stack memory that `cxPropertiesLoad()` will reserve with `cxPropertiesUseStack()`.
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT CxPropertiesSink cxPropertiesMapSink(CxMap *map);
+CX_EXPORT extern const unsigned cx_properties_load_buf_size;
/**
- * Creates a properties source based on an UCX string.
- *
- * @param str the string
- * @return the properties source
- * @see cxPropertiesLoad()
+ * The size of the stack memory that `cxPropertiesLoad()` will use to read contents from the file.
*/
-cx_attr_nodiscard
-CX_EXPORT CxPropertiesSource cxPropertiesStringSource(cxstring str);
+CX_EXPORT extern const unsigned cx_properties_load_fill_size;
/**
- * Creates a properties source based on C string with the specified length.
+ * Internal function - use cxPropertiesLoad() instead.
*
- * @param str the string
- * @param len the length
- * @return the properties source
- * @see cxPropertiesLoad()
+ * @param allocator the allocator for the values
+ * @param filename the file name
+ * @param target the target map
+ * @param config the parser config
+ * @return status code
*/
-cx_attr_nonnull cx_attr_nodiscard cx_attr_access_r(1, 2)
-CX_EXPORT CxPropertiesSource cxPropertiesCstrnSource(const char *str, size_t len);
+cx_attr_nonnull_arg(3)
+CX_EXPORT CxPropertiesStatus cx_properties_load(const CxAllocator *allocator,
+ cxstring filename, CxMap *target, CxPropertiesConfig config);
/**
- * Creates a properties source based on a C string.
+ * Loads properties from a file and inserts them into a map.
*
- * The length will be determined with strlen(), so the string MUST be
- * zero-terminated.
+ * Entries are added to the map, possibly overwriting existing entries.
*
- * @param str the string
- * @return the properties source
- * @see cxPropertiesLoad()
- */
-cx_attr_nonnull cx_attr_nodiscard cx_attr_cstr_arg(1)
-CX_EXPORT CxPropertiesSource cxPropertiesCstrSource(const char *str);
-
-/**
- * Creates a properties source based on an FILE.
+ * The map must either store pointers of type @c char*, or elements of type cxmutstr.
+ * Any other configuration is not supported.
*
- * @param file the file
- * @param chunk_size how many bytes may be read in one operation
+ * @note When the parser finds an error, all successfully parsed keys before the error
+ * are added to the map nonetheless.
*
- * @return the properties source
- * @see cxPropertiesLoad()
+ * @param allocator the allocator for the values that will be stored in the map
+ * @param filename (any string) the absolute or relative path to the file
+ * @param target (@c CxMap*) the map where the properties shall be added
+ * @param config the parser config
+ * @retval CX_PROPERTIES_NO_ERROR (zero) at least one key/value pair was found
+ * @retval CX_PROPERTIES_NO_DATA the file is syntactically OK, but does not contain properties
+ * @retval CX_PROPERTIES_INCOMPLETE_DATA unexpected end of file
+ * @retval CX_PROPERTIES_INVALID_EMPTY_KEY the properties data contains an illegal empty key
+ * @retval CX_PROPERTIES_INVALID_MISSING_DELIMITER the properties data contains a line without delimiter
+ * @retval CX_PROPERTIES_BUFFER_ALLOC_FAILED an internal allocation was necessary but failed
+ * @retval CX_PROPERTIES_FILE_ERROR a file operation failed; depending on the system @c errno might be set
+ * @retval CX_PROPERTIES_MAP_ERROR storing a key/value pair in the map failed
+ * @see cxPropertiesLoadDefault()
*/
-cx_attr_nonnull cx_attr_nodiscard cx_attr_access_r(1)
-CX_EXPORT CxPropertiesSource cxPropertiesFileSource(FILE *file, size_t chunk_size);
-
+#define cxPropertiesLoad(allocator, filename, target, config) \
+ cx_properties_load(allocator, cx_strcast(filename), target, config)
/**
- * Loads properties data from a source and transfers it to a sink.
+ * Loads properties from a file and inserts them into a map with a default config.
*
- * This function tries to read as much data from the source as possible.
- * When the source was completely consumed and at least on k/v-pair was found,
- * the return value will be #CX_PROPERTIES_NO_ERROR.
- * When the source was consumed but no k/v-pairs were found, the return value
- * will be #CX_PROPERTIES_NO_DATA.
- * In case the source data ends unexpectedly, the #CX_PROPERTIES_INCOMPLETE_DATA
- * is returned. In that case you should call this function again with the same
- * sink and either an updated source or the same source if the source is able to
- * yield the missing data.
+ * Entries are added to the map, possibly overwriting existing entries.
*
- * The other result codes apply, according to their description.
+ * The map must either store pointers of type @c char*, or elements of type cxmutstr.
+ * Any other configuration is not supported.
*
- * @param prop the properties interface
- * @param sink the sink
- * @param source the source
- * @retval CX_PROPERTIES_NO_ERROR (zero) a key/value pair was found
- * @retval CX_PROPERTIES_READ_INIT_FAILED initializing the source failed
- * @retval CX_PROPERTIES_READ_FAILED reading from the source failed
- * @retval CX_PROPERTIES_SINK_FAILED sinking the properties into the sink failed
- * @retval CX_PROPERTIES_NO_DATA the source did not provide any key/value pairs
- * @retval CX_PROPERTIES_INCOMPLETE_DATA the source did not provide enough data
+ * @note When the parser finds an error, all successfully parsed keys before the error
+ * are added to the map nonetheless.
+ *
+ * @param allocator the allocator for the values that will be stored in the map
+ * @param filename (any string) the absolute or relative path to the file
+ * @param target (@c CxMap*) the map where the properties shall be added
+ * @retval CX_PROPERTIES_NO_ERROR (zero) at least one key/value pair was found
+ * @retval CX_PROPERTIES_NO_DATA the file is syntactically OK, but does not contain properties
+ * @retval CX_PROPERTIES_INCOMPLETE_DATA unexpected end of file
* @retval CX_PROPERTIES_INVALID_EMPTY_KEY the properties data contains an illegal empty key
* @retval CX_PROPERTIES_INVALID_MISSING_DELIMITER the properties data contains a line without delimiter
* @retval CX_PROPERTIES_BUFFER_ALLOC_FAILED an internal allocation was necessary but failed
+ * @retval CX_PROPERTIES_FILE_ERROR a file operation failed; depending on the system @c errno might be set
+ * @retval CX_PROPERTIES_MAP_ERROR storing a key/value pair in the map failed
+ * @see cxPropertiesLoad()
*/
-cx_attr_nonnull
-CX_EXPORT CxPropertiesStatus cxPropertiesLoad(CxProperties *prop,
- CxPropertiesSink sink, CxPropertiesSource source);
+#define cxPropertiesLoadDefault(allocator, filename, target) \
+ cx_properties_load(allocator, cx_strcast(filename), target, cx_properties_config_default)
+
#ifdef __cplusplus
} // extern "C"
#include "common.h"
#include "allocator.h"
+#include <string.h>
+
/** Expands a UCX string as printf arguments. */
#define CX_SFMT(s) (int) (s).length, (s).ptr
*/
typedef struct cx_strtok_ctx_s CxStrtokCtx;
-#ifdef __cplusplus
-extern "C" {
-
-/**
- * A literal initializer for an UCX string structure.
- *
- * @param literal the string literal
- */
-#define CX_STR(literal) cxstring{literal, sizeof(literal) - 1}
-
-#else // __cplusplus
-
-/**
- * A literal initializer for an UCX string structure.
- *
- * The argument MUST be a string (const char*) @em literal.
- *
- * @param literal the string literal
- */
-#define CX_STR(literal) ((cxstring){literal, sizeof(literal) - 1})
-
-#endif
-
-
/**
* Wraps a mutable string that must be zero-terminated.
*
* @see cx_mutstrn()
*/
cx_attr_nodiscard cx_attr_cstr_arg(1)
-CX_EXPORT cxmutstr cx_mutstr(char *cstring);
+CX_INLINE cxmutstr cx_mutstr(char *cstring) {
+ cxmutstr str;
+ str.ptr = cstring;
+ str.length = cstring == NULL ? 0 : strlen(cstring);
+ return str;
+}
/**
* Wraps a string that does not need to be zero-terminated.
* @see cx_mutstr()
*/
cx_attr_nodiscard cx_attr_access_rw(1, 2)
-CX_EXPORT cxmutstr cx_mutstrn(char *cstring, size_t length);
+CX_INLINE cxmutstr cx_mutstrn(char *cstring, size_t length) {
+ cxmutstr str;
+ str.ptr = cstring;
+ str.length = length;
+ return str;
+}
/**
* Wraps a string that must be zero-terminated.
* @see cx_strn()
*/
cx_attr_nodiscard cx_attr_cstr_arg(1)
-CX_EXPORT cxstring cx_str(const char *cstring);
+CX_INLINE cxstring cx_str(const char *cstring) {
+ cxstring str;
+ str.ptr = cstring;
+ str.length = cstring == NULL ? 0 : strlen(cstring);
+ return str;
+}
/**
* @see cx_str()
*/
cx_attr_nodiscard cx_attr_access_r(1, 2)
-CX_EXPORT cxstring cx_strn(const char *cstring, size_t length);
+CX_INLINE cxstring cx_strn(const char *cstring, size_t length) {
+ cxstring str;
+ str.ptr = cstring;
+ str.length = length;
+ return str;
+}
#ifdef __cplusplus
-} // extern "C"
cx_attr_nodiscard
CX_CPPDECL cxstring cx_strcast(cxmutstr str) {
return cx_strn(str.ptr, str.length);
/**
* Copies a string.
*
- * The memory in the @p dest structure is either allocated or re-allocated to fit the entire
- * source string, including a zero-terminator.
- *
- * The string in @p dest is guaranteed to be zero-terminated, regardless of whether @p src is.
+ * Internal function - do not use.
*
* @param alloc the allocator
* @param dest a pointer to the structure where to copy the contents to
*
* @retval zero success
* @retval non-zero if re-allocation failed
+ * @see cx_strcpy_a()
*/
cx_attr_nonnull_arg(1)
-CX_EXPORT int cx_strcpy_a(const CxAllocator *alloc, cxmutstr *dest, cxstring src);
-
+CX_EXPORT int cx_strcpy_a_(const CxAllocator *alloc, cxmutstr *dest, cxstring src);
/**
* Copies a string.
*
* 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
+ * @param src the source string
+ * @retval zero success
+ * @retval non-zero if re-allocation failed
+ */
+#define cx_strcpy_a(alloc, dest, src) cx_strcpy_a_(alloc, dest, cx_strcast(src))
+
+/**
+ * Copies a string.
+ *
+ * The memory in the @p dest structure is either allocated or re-allocated to fit the entire
+ * source string, including a zero-terminator.
+ *
+ * The string in @p dest is guaranteed to be zero-terminated, regardless of whether @p src is.
*
+ * @param dest (@c cxmutstr*) a pointer to the structure where to copy the contents to
+ * @param src the source string
* @retval zero success
* @retval non-zero if re-allocation failed
*/
cx_attr_nodiscard
CX_EXPORT cxmutstr cx_strrchr_m(cxmutstr string, int chr);
+/**
+ * Searches for a specific substring.
+ *
+ * Internal function - do not use.
+ *
+ * @param haystack the string to be scanned
+ * @param needle string containing the sequence of characters to match
+ * @return a substring starting at the first occurrence of @p needle,
+ * or an empty string, if the sequence is not contained
+ * @see cx_strstr()
+ */
+cx_attr_nodiscard
+CX_EXPORT cxstring cx_strstr_(cxstring haystack, cxstring needle);
+
/**
* Returns a substring starting at the location of the first occurrence of the
* specified string.
* If @p needle is an empty string, the complete @p haystack is
* returned.
*
+ * @param haystack (@c cxstring) the string to be scanned
+ * @param needle string containing the sequence of characters to match
+ * @return (@c cxstring) a substring starting at the first occurrence of
+ * @p needle, or an empty string, if the sequence is not contained
+ * @see cx_strstr_m()
+ */
+#define cx_strstr(haystack, needle) cx_strstr_(haystack, cx_strcast(needle))
+
+/**
+ * Searches for a specific substring.
+ *
+ * Internal function - do not use.
+ *
* @param haystack the string to be scanned
- * @param needle string containing the sequence of characters to match
- * @return a substring starting at the first occurrence of
- * @p needle, or an empty string, if the sequence is not
- * contained
+ * @param needle string containing the sequence of characters to match
+ * @return a substring starting at the first occurrence of @p needle,
+ * or an empty string, if the sequence is not contained
* @see cx_strstr_m()
*/
cx_attr_nodiscard
-CX_EXPORT cxstring cx_strstr(cxstring haystack, cxstring needle);
+CX_EXPORT cxmutstr cx_strstr_m_(cxmutstr haystack, cxstring needle);
/**
* Returns a substring starting at the location of the first occurrence of the
* If @p needle is an empty string, the complete @p haystack is
* returned.
*
- * @param haystack the string to be scanned
- * @param needle string containing the sequence of characters to match
- * @return a substring starting at the first occurrence of
- * @p needle, or an empty string, if the sequence is not
- * contained
+ * @param haystack (@c cxmutstr) the string to be scanned
+ * @param needle string containing the sequence of characters to match
+ * @return (@c cxmutstr) a substring starting at the first occurrence of
+ * @p needle, or an empty string, if the sequence is not contained
* @see cx_strstr()
*/
-cx_attr_nodiscard
-CX_EXPORT cxmutstr cx_strstr_m(cxmutstr haystack, cxstring needle);
+#define cx_strstr_m(haystack, needle) cx_strstr_m_(haystack, cx_strcast(needle))
/**
* Splits a given string using a delimiter string.
*
- * @note The resulting array contains strings that point to the source
- * @p string. Use cx_strdup() to get copies.
+ * Internal function - do not use.
*
* @param string the string to split
* @param delim the delimiter
* @param limit the maximum number of split items
- * @param output a preallocated array of at least @p limit length
+ * @param output the output array
* @return the actual number of split items
+ * @see cx_strsplit()
*/
cx_attr_nodiscard cx_attr_nonnull cx_attr_access_w(4, 3)
-CX_EXPORT size_t cx_strsplit(cxstring string, cxstring delim,
+CX_EXPORT size_t cx_strsplit_(cxstring string, cxstring delim,
size_t limit, cxstring *output);
/**
* Splits a given string using a delimiter string.
*
- * The array pointed to by @p output will be allocated by @p allocator.
- *
- * @note The resulting array contains strings that point to the source
- * @p string. Use cx_strdup() to get copies.
- *
- * @attention If allocation fails, the @c NULL pointer will be written to
- * @p output and the number returned will be zero.
+ * Internal function - do not use.
*
* @param allocator the allocator to use for allocating the resulting array
* @param string the string to split
* @param delim the delimiter
* @param limit the maximum number of split items
- * @param output a pointer where the address of the allocated array shall be
- * written to
+ * @param output the output array
* @return the actual number of split items
+ * @see cx_strsplit_a()
*/
cx_attr_nodiscard cx_attr_nonnull cx_attr_access_w(5)
-CX_EXPORT size_t cx_strsplit_a(const CxAllocator *allocator,
+CX_EXPORT size_t cx_strsplit_a_(const CxAllocator *allocator,
cxstring string, cxstring delim,
size_t limit, cxstring **output);
/**
* Splits a given string using a delimiter string.
*
- * @note The resulting array contains strings that point to the source
- * @p string. Use cx_strdup() to get copies.
+ * Internal function - do not use.
*
* @param string the string to split
* @param delim the delimiter
* @param limit the maximum number of split items
- * @param output a preallocated array of at least @p limit length
+ * @param output the output array
* @return the actual number of split items
+ * @see cx_strsplit_m()
*/
cx_attr_nodiscard cx_attr_nonnull cx_attr_access_w(4, 3)
-CX_EXPORT size_t cx_strsplit_m(cxmutstr string, cxstring delim,
+CX_EXPORT size_t cx_strsplit_m_(cxmutstr string, cxstring delim,
size_t limit, cxmutstr *output);
+/**
+ * Splits a given string using a delimiter string.
+ *
+ * Internal function - do not use.
+ *
+ * @param allocator the allocator to use for allocating the resulting array
+ * @param string the string to split
+ * @param delim the delimiter
+ * @param limit the maximum number of split items
+ * @param output the output array
+ * @return the actual number of split items
+ * @see cx_strsplit_ma()
+ */
+cx_attr_nodiscard cx_attr_nonnull cx_attr_access_w(5)
+CX_EXPORT size_t cx_strsplit_ma_(const CxAllocator *allocator,
+ cxmutstr string, cxstring delim, size_t limit,
+ cxmutstr **output);
+
+/**
+ * Splits a given string using a delimiter string.
+ *
+ * @note The resulting array contains strings that point to the source
+ * @p string. Use cx_strdup() to get copies.
+ *
+ * @param string (@c cxstring) the string to split
+ * @param delim the delimiter
+ * @param limit (@c size_t) the maximum number of split items
+ * @param output (@c cxstring*) a preallocated array of at least @p limit length
+ * @return the actual number of split items
+ */
+#define cx_strsplit(string, delim, limit, output) \
+ cx_strsplit_(string, cx_strcast(delim), limit, output)
+
/**
* Splits a given string using a delimiter string.
*
* @attention If allocation fails, the @c NULL pointer will be written to
* @p output and the number returned will be zero.
*
- * @param allocator the allocator to use for allocating the resulting array
- * @param string the string to split
+ * @param allocator (@c CxAllocator*) the allocator to use for allocating the resulting array
+ * @param string (@c cxstring) the string to split
* @param delim the delimiter
- * @param limit the maximum number of split items
- * @param output a pointer where the address of the allocated array shall be
- * written to
+ * @param limit (@c size_t) the maximum number of split items
+ * @param output (@c cxstring**) a pointer where the address of the allocated
+ * array shall be written to
* @return the actual number of split items
*/
-cx_attr_nodiscard cx_attr_nonnull cx_attr_access_w(5)
-CX_EXPORT size_t cx_strsplit_ma(const CxAllocator *allocator,
- cxmutstr string, cxstring delim, size_t limit,
- cxmutstr **output);
+#define cx_strsplit_a(allocator, string, delim, limit, output) \
+ cx_strsplit_a_(allocator, string, cx_strcast(delim), limit, output)
+
+
+/**
+ * Splits a given string using a delimiter string.
+ *
+ * @note The resulting array contains strings that point to the source
+ * @p string. Use cx_strdup() to get copies.
+ *
+ * @param string (@c cxmutstr) the string to split
+ * @param delim the delimiter
+ * @param limit (@c size_t) the maximum number of split items
+ * @param output (@c cxmutstr*) a preallocated array of at least @p limit length
+ * @return the actual number of split items
+ */
+#define cx_strsplit_m(string, delim, limit, output) \
+ cx_strsplit_m_(string, cx_strcast(delim), limit, output)
+
+/**
+ * Splits a given string using a delimiter string.
+ *
+ * The array pointed to by @p output will be allocated by @p allocator.
+ *
+ * @note The resulting array contains strings that point to the source
+ * @p string. Use cx_strdup() to get copies.
+ *
+ * @attention If allocation fails, the @c NULL pointer will be written to
+ * @p output and the number returned will be zero.
+ *
+ * @param allocator (@c CxAllocator*) the allocator to use for allocating the resulting array
+ * @param string (@c cxmutstr) the string to split
+ * @param delim the delimiter
+ * @param limit (@c size_t) the maximum number of split items
+ * @param output (@c cxmutstr**) a pointer where the address of the allocated
+ * array shall be written to
+ * @return the actual number of split items
+ */
+#define cx_strsplit_ma(allocator, string, delim, limit, output) \
+ cx_strsplit_ma_(allocator, string, cx_strcast(delim), limit, output)
/**
* Compares two strings.
*/
#define cx_strcasesuffix(string, suffix) cx_strcasesuffix_(cx_strcast(string), cx_strcast(suffix))
+
+/**
+ * Replaces a string with another string.
+ *
+ * Internal function - do not use.
+ *
+ * @param allocator
+ * @param str
+ * @param search
+ * @param replacement
+ * @param replmax
+ * @return
+ * @see cx_strreplace_a()
+ * @see cx_strreplace()
+ * @see cx_strreplacen_a()
+ * @see cx_strreplacen()
+ */
+cx_attr_nodiscard cx_attr_nonnull
+CX_EXPORT cxmutstr cx_strreplace_(const CxAllocator *allocator,
+ cxstring str, cxstring search, cxstring replacement, size_t replmax);
+
/**
* Replaces a string with another string.
*
* If allocation fails, or the input string is empty,
* the returned string will be empty.
*
- * @param allocator the allocator to use
+ * @param allocator (@c CxAllocator*) the allocator to use
* @param str the string where replacements should be applied
* @param search the string to search for
* @param replacement the replacement string
- * @param replmax maximum number of replacements
- * @return the resulting string after applying the replacements
+ * @param replmax (@c size_t) maximum number of replacements
+ * @return (@c cxmutstr) the resulting string after applying the replacements
*/
-cx_attr_nodiscard cx_attr_nonnull
-CX_EXPORT cxmutstr cx_strreplacen_a(const CxAllocator *allocator,
- cxstring str, cxstring search, cxstring replacement, size_t replmax);
+#define cx_strreplacen_a(allocator, str, search, replacement, replmax) \
+ cx_strreplace_(allocator, cx_strcast(str), cx_strcast(search), cx_strcast(replacement), replmax)
/**
* Replaces a string with another string.
* If allocation fails, or the input string is empty,
* the returned string will be empty.
*
- * @param str (@c cxstring) the string where replacements should be applied
- * @param search (@c cxstring) the string to search for
- * @param replacement (@c cxstring) the replacement string
+ * @param str the string where replacements should be applied
+ * @param search the string to search for
+ * @param replacement the replacement string
* @param replmax (@c size_t) maximum number of replacements
* @return (@c cxmutstr) the resulting string after applying the replacements
*/
* the returned string will be empty.
*
* @param allocator (@c CxAllocator*) the allocator to use
- * @param str (@c cxstring) the string where replacements should be applied
- * @param search (@c cxstring) the string to search for
- * @param replacement (@c cxstring) the replacement string
+ * @param str the string where replacements should be applied
+ * @param search the string to search for
+ * @param replacement the replacement string
* @return (@c cxmutstr) the resulting string after applying the replacements
*/
#define cx_strreplace_a(allocator, str, search, replacement) \
* If allocation fails, or the input string is empty,
* the returned string will be empty.
*
- * @param str (@c cxstring) the string where replacements should be applied
- * @param search (@c cxstring) the string to search for
- * @param replacement (@c cxstring) the replacement string
+ * @param str the string where replacements should be applied
+ * @param search the string to search for
+ * @param replacement the replacement string
* @return (@c cxmutstr) the resulting string after applying the replacements
*/
#define cx_strreplace(str, search, replacement) \
char total[80];
int len = snprintf(
total, 80,
- " Total: %u\n Success: %u\n Failure: %u\n\n",
+ " Tests: %5u\n Success: %5u\n Failure: %5u\n\n",
suite->success + suite->failure, suite->success, suite->failure
);
out_writer(total, 1, len, out_target);
* Structure for holding the base data of a tree.
*/
struct cx_tree_s {
+ CX_COLLECTION_BASE;
/**
* The tree class definition.
*/
const cx_tree_class *cl;
- /**
- * Allocator to allocate new nodes.
- */
- const CxAllocator *allocator;
-
/**
* A pointer to the root node.
*
*/
cx_tree_node_create_func node_create;
- /**
- * An optional simple destructor for the tree nodes.
- */
- cx_destructor_func simple_destructor;
-
- /**
- * An optional advanced destructor for the tree nodes.
- */
- cx_destructor_func2 advanced_destructor;
-
- /**
- * The pointer to additional data that is passed to the advanced destructor.
- */
- void *destructor_data;
-
/**
* A function to compare two nodes.
*/
*/
cx_tree_search_data_func search_data;
- /**
- * The number of currently stored elements.
- */
- size_t size;
-
/**
* Offset in the node struct for the parent pointer.
*/
if (left->len == 0) return 0;
return memcmp(left->data, right->data, left->len);
}
+
+cxstring cx_hash_key_as_string(const CxHashKey *key) {
+ return cx_strn(key->data, key->len);
+}
cxFree(map->collection.allocator, map);
}
-static void *cx_hash_map_put(
+static CxMapEntry cx_hash_map_put(
CxMap *map,
CxHashKey key,
void *value
allocator,
sizeof(struct cx_hash_map_element_s) + map->collection.elem_size
);
- if (e == NULL) return NULL; // LCOV_EXCL_LINE
+ if (e == NULL) return (CxMapEntry){NULL, NULL}; // LCOV_EXCL_LINE
// write the value
if (value == NULL) {
void *kd = cxMalloc(allocator, key.len);
if (kd == NULL) { // LCOV_EXCL_START
cxFree(allocator, e);
- return NULL;
+ return (CxMapEntry){NULL, NULL};
} // LCOV_EXCL_STOP
memcpy(kd, key.data, key.len);
e->key.data = kd;
map->collection.size++;
}
- // return pointer to the element
- return elm->data;
+ // return the entry
+ return (CxMapEntry){&elm->key, elm->data};
}
static void cx_hash_map_unlink(
cx_hash_map_iterator,
};
+static int cx_map_cmpfunc2_safe_memcmp(const void *a, const void *b, void *c) {
+ // it is not safe to store a pointer to the size in the list
+ // because the entire list structure might get reallocated
+ size_t elem_size = (size_t)(uintptr_t)c;
+ return memcmp(a, b, elem_size);
+}
+
CxMap *cxHashMapCreate(
const CxAllocator *allocator,
size_t itemsize,
buckets = 16;
}
- struct cx_hash_map_s *map = cxCalloc(allocator, 1,
- sizeof(struct cx_hash_map_s));
+ struct cx_hash_map_s *map = cxZalloc(allocator, sizeof(struct cx_hash_map_s));
if (map == NULL) return NULL;
// initialize hash map members
if (itemsize > 0) {
map->base.collection.elem_size = itemsize;
+ map->base.collection.advanced_cmp = cx_map_cmpfunc2_safe_memcmp;
+ map->base.collection.cmp_data = (void*)(uintptr_t)itemsize;
} else {
map->base.collection.elem_size = sizeof(void *);
map->base.collection.store_pointer = true;
+ map->base.collection.simple_cmp = cx_cmp_ptr;
}
return (CxMap *) map;
#include "cx/iterator.h"
#include <string.h>
+#include <assert.h>
static bool cx_iter_valid(const void *it) {
const struct cx_iterator_s *iter = it;
return *(void**)iter->elem_handle;
}
-static void cx_iter_next_fast(void *it) {
+static void cx_iter_next(void *it) {
struct cx_iterator_s *iter = it;
- if (iter->base.remove) {
- iter->base.remove = false;
- iter->elem_count--;
- // only move the last element when we are not currently aiming
- // at the last element already
- if (iter->index < iter->elem_count) {
- void *last = ((char *) iter->src_handle)
- + iter->elem_count * iter->elem_size;
- memcpy(iter->elem_handle, last, iter->elem_size);
- }
- } else {
- iter->index++;
- iter->elem_handle = ((char *) iter->elem_handle) + iter->elem_size;
- }
+ assert(!iter->base.remove);
+ iter->index++;
+ iter->elem_handle = ((char *) iter->elem_handle) + iter->elem_size;
}
-static void cx_iter_next_slow(void *it) {
- struct cx_iterator_s *iter = it;
- if (iter->base.remove) {
- iter->base.remove = false;
- iter->elem_count--;
-
- // number of elements to move
- size_t remaining = iter->elem_count - iter->index;
- if (remaining > 0) {
- memmove(
- iter->elem_handle,
- ((char *) iter->elem_handle) + iter->elem_size,
- remaining * iter->elem_size
- );
- }
- } else {
- iter->index++;
- iter->elem_handle = ((char *) iter->elem_handle) + iter->elem_size;
- }
-}
-
-CxIterator cxIterator(
- const void *array,
- size_t elem_size,
- size_t elem_count,
- bool remove_keeps_order
-) {
+CxIterator cxIterator(const void *array, size_t elem_size, size_t elem_count) {
CxIterator iter;
iter.index = 0;
iter.elem_count = array == NULL ? 0 : elem_count;
iter.base.valid = cx_iter_valid;
iter.base.current = cx_iter_current;
- iter.base.next = remove_keeps_order ? cx_iter_next_slow : cx_iter_next_fast;
+ iter.base.next = cx_iter_next;
+ iter.base.valid_impl = NULL;
+ iter.base.current_impl = NULL;
+ iter.base.next_impl = NULL;
iter.base.remove = false;
iter.base.allow_remove = true;
return iter;
}
-CxIterator cxIteratorPtr(
- const void *array,
- size_t elem_count,
- bool remove_keeps_order
-) {
- CxIterator iter = cxIterator(array, sizeof(void*), elem_count, remove_keeps_order);
+CxIterator cxIteratorPtr(const void *array, size_t elem_count) {
+ CxIterator iter = cxIterator(array, sizeof(void*), elem_count);
iter.base.current = cx_iter_current_ptr;
return iter;
}
*/
#include "cx/json.h"
+#include "cx/kv_list.h"
#include <string.h>
#include <assert.h>
static CxJsonValue cx_json_value_nothing = {.type = CX_JSON_NOTHING};
-static int json_cmp_objvalue(const void *l, const void *r) {
- const CxJsonObjValue *left = l;
- const CxJsonObjValue *right = r;
- return cx_strcmp(cx_strcast(left->name), cx_strcast(right->name));
-}
-
-static size_t json_find_objvalue(const CxJsonValue *obj, cxstring name) {
- assert(obj->type == CX_JSON_OBJECT);
- CxJsonObjValue kv_dummy;
- kv_dummy.name = cx_mutstrn((char*) name.ptr, name.length);
- return cx_array_binary_search(
- obj->value.object.values,
- obj->value.object.values_size,
- sizeof(CxJsonObjValue),
- &kv_dummy,
- json_cmp_objvalue
- );
-}
-
-static int json_add_objvalue(CxJsonValue *objv, CxJsonObjValue member) {
- assert(objv->type == CX_JSON_OBJECT);
- const CxAllocator * const al = objv->allocator;
- CxJsonObject *obj = &(objv->value.object);
-
- // determine the index where we need to insert the new member
- size_t index = cx_array_binary_search_sup(
- obj->values,
- obj->values_size,
- sizeof(CxJsonObjValue),
- &member, json_cmp_objvalue
- );
-
- // is the name already present?
- if (index < obj->values_size && 0 == json_cmp_objvalue(&member, &obj->values[index])) {
- // free the original value
- cx_strfree_a(al, &obj->values[index].name);
- cxJsonValueFree(obj->values[index].value);
- // replace the item
- obj->values[index] = member;
-
- // nothing more to do
- return 0;
- }
-
- // determine the old capacity and reserve for one more element
- CxArrayReallocator arealloc = cx_array_reallocator(al, NULL);
- size_t oldcap = obj->values_capacity;
- if (cx_array_simple_reserve_a(&arealloc, obj->values, 1)) return 1;
-
- // check the new capacity, if we need to realloc the index array
- size_t newcap = obj->values_capacity;
- if (newcap > oldcap) {
- if (cxReallocateArray(al, &obj->indices, newcap, sizeof(size_t))) {
- return 1; // LCOV_EXCL_LINE
- }
- }
-
- // check if append or insert
- if (index < obj->values_size) {
- // move the other elements
- memmove(
- &obj->values[index+1],
- &obj->values[index],
- (obj->values_size - index) * sizeof(CxJsonObjValue)
- );
- // increase indices for the moved elements
- for (size_t i = 0; i < obj->values_size ; i++) {
- if (obj->indices[i] >= index) {
- obj->indices[i]++;
- }
- }
- }
-
- // insert the element and set the index
- obj->values[index] = member;
- obj->indices[obj->values_size] = index;
- obj->values_size++;
-
- return 0;
-}
-
static void token_destroy(CxJsonToken *token) {
if (token->allocated) {
cx_strfree(&token->content);
+ token->allocated = false;
}
}
ttype = CX_JSON_TOKEN_STRING;
} else {
cxstring s = cx_strcast(str);
- if (!cx_strcmp(s, CX_STR("true")) || !cx_strcmp(s, CX_STR("false"))
- || !cx_strcmp(s, CX_STR("null"))) {
+ if (!cx_strcmp(s, "true") || !cx_strcmp(s, "false")
+ || !cx_strcmp(s, "null")) {
ttype = CX_JSON_TOKEN_LITERAL;
} else {
ttype = token_numbertype(str.ptr, str.length);
}
}
- if (ttype != CX_JSON_NO_TOKEN) {
+ if (ttype == CX_JSON_NO_TOKEN) {
+ return CX_JSON_NO_DATA;
+ } else {
// uncompleted token
size_t uncompleted_len = json->buffer.size - token_part_start;
if (json->uncompleted.tokentype == CX_JSON_NO_TOKEN) {
}
// advance the buffer position - we saved the stuff in the uncompleted token
json->buffer.pos += uncompleted_len;
+ return CX_JSON_INCOMPLETE_DATA;
}
-
- return CX_JSON_INCOMPLETE_DATA;
}
// converts a Unicode codepoint to utf8
return result;
}
-static cxmutstr escape_string(cxmutstr str, bool escape_slash) {
+static cxmutstr escape_string(cxstring str, bool escape_slash) {
// note: this function produces the string without enclosing quotes
// the reason is that we don't want to allocate memory just for that
CxBuffer buf = {0};
size_t capa = str.length + 32;
char *space = cxMallocDefault(capa);
if (space == NULL) return cx_mutstrn(NULL, 0);
- cxBufferInit(&buf, space, capa, NULL, CX_BUFFER_AUTO_EXTEND);
+ cxBufferInit(&buf, NULL, space, capa, CX_BUFFER_AUTO_EXTEND);
cxBufferWrite(str.ptr, 1, i, &buf);
all_printable = false;
}
cxBufferPut(&buf, c);
}
}
- if (!all_printable) {
- str = cx_mutstrn(buf.space, buf.size);
+ cxmutstr ret;
+ if (all_printable) {
+ // don't copy the string when we don't need to escape anything
+ ret = cx_mutstrn((char*)str.ptr, str.length);
+ } else {
+ ret = cx_mutstrn(buf.space, buf.size);
}
cxBufferDestroy(&buf);
- return str;
+ return ret;
+}
+
+static CxJsonObject json_create_object_map(const CxAllocator *allocator) {
+ CxMap *map = cxKvListCreateAsMap(allocator, CX_STORE_POINTERS);
+ if (map == NULL) return NULL; // LCOV_EXCL_LINE
+ cxSetCompareFunc(map, cxJsonCompare);
+ cxSetDestructor(map, cxJsonValueFree);
+ return map;
+}
+
+static void json_free_object_map(CxJsonObject obj) {
+ cxMapFree(obj);
}
static CxJsonValue* json_create_value(CxJson *json, CxJsonValueType type) {
v->type = type;
v->allocator = json->allocator;
if (type == CX_JSON_ARRAY) {
- cx_array_initialize_a(json->allocator, v->value.array.array, 16);
- if (v->value.array.array == NULL) goto create_json_value_exit_error; // LCOV_EXCL_LINE
- } else if (type == CX_JSON_OBJECT) {
- cx_array_initialize_a(json->allocator, v->value.object.values, 16);
- v->value.object.indices = cxCalloc(json->allocator, 16, sizeof(size_t));
- if (v->value.object.values == NULL ||
- v->value.object.indices == NULL)
+ if (cx_array_init_a(json->allocator, v->array, 16)) {
goto create_json_value_exit_error; // LCOV_EXCL_LINE
+ }
+ } else if (type == CX_JSON_OBJECT) {
+ v->object = json_create_object_map(json->allocator);
+ if (v->object == NULL) goto create_json_value_exit_error; // LCOV_EXCL_LINE
}
// add the new value to a possible parent
- if (json->vbuf_size > 0) {
- CxJsonValue *parent = json->vbuf[json->vbuf_size - 1];
+ if (json->vbuf.size > 0) {
+ CxJsonValue *parent = json->vbuf.data[json->vbuf.size - 1];
assert(parent != NULL);
if (parent->type == CX_JSON_ARRAY) {
- CxArrayReallocator value_realloc = cx_array_reallocator(json->allocator, NULL);
- if (cx_array_simple_add_a(&value_realloc, parent->value.array.array, v)) {
+ if (cx_array_add_a(json->allocator, parent->array, v)) {
goto create_json_value_exit_error; // LCOV_EXCL_LINE
}
} else if (parent->type == CX_JSON_OBJECT) {
// the member was already created after parsing the name
- assert(json->uncompleted_member.name.ptr != NULL);
- json->uncompleted_member.value = v;
- if (json_add_objvalue(parent, json->uncompleted_member)) {
+ // store the pointer of the uncompleted value in the map
+ assert(json->uncompleted_member_name.ptr != NULL);
+ if (cxMapPut(parent->object, json->uncompleted_member_name, v)) {
goto create_json_value_exit_error; // LCOV_EXCL_LINE
}
- json->uncompleted_member.name = (cxmutstr) {NULL, 0};
+ cx_strfree_a(json->allocator, &json->uncompleted_member_name);
} else {
assert(false); // LCOV_EXCL_LINE
}
// add the new value to the stack, if it is an array or object
if (type == CX_JSON_ARRAY || type == CX_JSON_OBJECT) {
- CxArrayReallocator vbuf_realloc = cx_array_reallocator(NULL, json->vbuf_internal);
- if (cx_array_simple_add_a(&vbuf_realloc, json->vbuf, v)) {
- goto create_json_value_exit_error; // LCOV_EXCL_LINE
+ if (json->vbuf.size >= json->vbuf.capacity) {
+ int alloc_error;
+ if (json->vbuf.data == json->vbuf_internal) {
+ alloc_error = cx_array_copy_to_new(json->vbuf, json->vbuf.size+1);
+ } else {
+ alloc_error = cx_array_reserve(json->vbuf, json->vbuf.size+1);
+ }
+ if (alloc_error) {
+ goto create_json_value_exit_error; // LCOV_EXCL_LINE
+ }
}
+ json->vbuf.data[json->vbuf.size] = v;
+ json->vbuf.size++;
}
// if currently no value is parsed, this is now the value of interest
memset(json, 0, sizeof(CxJson));
json->allocator = allocator;
- json->states = json->states_internal;
- json->states_capacity = cx_nmemb(json->states_internal);
- json->states[0] = JP_STATE_VALUE_BEGIN;
- json->states_size = 1;
-
- json->vbuf = json->vbuf_internal;
- json->vbuf_capacity = cx_nmemb(json->vbuf_internal);
+ cx_array_init_fixed(json->states, json->states_internal, 1);
+ json->states.data[0] = JP_STATE_VALUE_BEGIN;
+ cx_array_init_fixed(json->vbuf, json->vbuf_internal, 0);
}
void cxJsonDestroy(CxJson *json) {
cxBufferDestroy(&json->buffer);
- if (json->states != json->states_internal) {
- cxFreeDefault(json->states);
+ if (json->states.data != json->states_internal) {
+ cx_array_free(json->states);
}
- if (json->vbuf != json->vbuf_internal) {
- cxFreeDefault(json->vbuf);
+ if (json->vbuf.data != json->vbuf_internal) {
+ cx_array_free(json->vbuf);
}
cxJsonValueFree(json->parsed);
json->parsed = NULL;
- if (json->uncompleted_member.name.ptr != NULL) {
- cx_strfree_a(json->allocator, &json->uncompleted_member.name);
- json->uncompleted_member = (CxJsonObjValue){{NULL, 0}, NULL};
- }
+ token_destroy(&json->uncompleted);
+ cx_strfree_a(json->allocator, &json->uncompleted_member_name);
}
void cxJsonReset(CxJson *json) {
// reinitialize the buffer
cxBufferDestroy(&json->buffer);
if (buf == NULL) buf = ""; // buffer must not be initialized with NULL
- cxBufferInit(&json->buffer, (char*) buf, size,
- NULL, CX_BUFFER_AUTO_EXTEND | CX_BUFFER_COPY_ON_WRITE);
+ cxBufferInit(&json->buffer, NULL, (char*) buf,
+ size, CX_BUFFER_AUTO_EXTEND | CX_BUFFER_COPY_ON_WRITE);
json->buffer.size = size;
return 0;
} else {
}
static void json_add_state(CxJson *json, int state) {
- // we have guaranteed the necessary space with cx_array_simple_reserve()
+ // we have guaranteed the necessary space
// therefore, we can safely add the state in the simplest way possible
- json->states[json->states_size++] = state;
+ json->states.data[json->states.size++] = state;
}
#define return_rec(code) \
}
// pop the current state
- assert(json->states_size > 0);
- int state = json->states[--json->states_size];
-
- // guarantee that at least two more states fit on the stack
- CxArrayReallocator state_realloc = cx_array_reallocator(NULL, json->states_internal);
- if (cx_array_simple_reserve_a(&state_realloc, json->states, 2)) {
- return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE
+ assert(json->states.size > 0);
+ int state = json->states.data[--json->states.size];
+
+ // guarantee that at least two more states fit into the array
+ const size_t required_states_depth = json->states.size + 2;
+ if (required_states_depth >= json->states.capacity) {
+ int alloc_error;
+ if (json->states.data == json->states_internal) {
+ alloc_error = cx_array_copy_to_new(json->states, required_states_depth);
+ } else {
+ alloc_error = cx_array_reserve(json->states, required_states_depth);
+ }
+ if (alloc_error) {
+ return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE
+ }
}
json_add_state(json, JP_STATE_OBJ_NAME_OR_CLOSE);
return_rec(CX_JSON_NO_ERROR);
}
+ case CX_JSON_TOKEN_END_ARRAY: {
+ if (state == JP_STATE_VALUE_BEGIN_AR) {
+ // discard the array from the value buffer
+ json->vbuf.size--;
+ json->states.size--;
+ return_rec(CX_JSON_NO_ERROR);
+ } else {
+ return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN);
+ }
+ }
case CX_JSON_TOKEN_STRING: {
if ((vbuf = json_create_value(json, CX_JSON_STRING)) == NULL) {
return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE
if (str.ptr == NULL) {
return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE
}
- vbuf->value.string = str;
+ vbuf->string = str;
return_rec(CX_JSON_NO_ERROR);
}
case CX_JSON_TOKEN_INTEGER:
return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE
}
if (type == CX_JSON_INTEGER) {
- if (cx_strtoi64(token.content, &vbuf->value.integer, 10)) {
+ if (cx_strtoi64(token.content, &vbuf->integer, 10)) {
return_rec(CX_JSON_FORMAT_ERROR_NUMBER);
}
} else {
- if (cx_strtod(token.content, &vbuf->value.number)) {
+ if (cx_strtod(token.content, &vbuf->number)) {
// TODO: at the moment this is unreachable, because the tokenizer is already stricter than cx_strtod()
return_rec(CX_JSON_FORMAT_ERROR_NUMBER); // LCOV_EXCL_LINE
}
if ((vbuf = json_create_value(json, CX_JSON_LITERAL)) == NULL) {
return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE
}
- if (0 == cx_strcmp(cx_strcast(token.content), cx_str("true"))) {
- vbuf->value.literal = CX_JSON_TRUE;
- } else if (0 == cx_strcmp(cx_strcast(token.content), cx_str("false"))) {
- vbuf->value.literal = CX_JSON_FALSE;
+ if (0 == cx_strcmp(token.content, "true")) {
+ vbuf->literal = CX_JSON_TRUE;
+ } else if (0 == cx_strcmp(token.content, "false")) {
+ vbuf->literal = CX_JSON_FALSE;
} else {
- vbuf->value.literal = CX_JSON_NULL;
+ vbuf->literal = CX_JSON_NULL;
}
return_rec(CX_JSON_NO_ERROR);
}
return_rec(CX_JSON_NO_ERROR);
} else if (token.tokentype == CX_JSON_TOKEN_END_ARRAY) {
// discard the array from the value buffer
- json->vbuf_size--;
+ json->vbuf.size--;
return_rec(CX_JSON_NO_ERROR);
} else {
return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN);
} else if (state == JP_STATE_OBJ_NAME_OR_CLOSE || state == JP_STATE_OBJ_NAME) {
if (state == JP_STATE_OBJ_NAME_OR_CLOSE && token.tokentype == CX_JSON_TOKEN_END_OBJECT) {
// discard the obj from the value buffer
- json->vbuf_size--;
+ json->vbuf.size--;
return_rec(CX_JSON_NO_ERROR);
} else {
// expect string
if (name.ptr == NULL) {
return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE
}
- assert(json->uncompleted_member.name.ptr == NULL);
- json->uncompleted_member.name = name;
- assert(json->vbuf_size > 0);
+ assert(json->uncompleted_member_name.ptr == NULL);
+ json->uncompleted_member_name = name;
+ assert(json->vbuf.size > 0);
// next state
json_add_state(json, JP_STATE_OBJ_COLON);
return_rec(CX_JSON_NO_ERROR);
} else if (token.tokentype == CX_JSON_TOKEN_END_OBJECT) {
// discard the obj from the value buffer
- json->vbuf_size--;
+ json->vbuf.size--;
return_rec(CX_JSON_NO_ERROR);
} else {
return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN);
CxJsonStatus result;
do {
result = json_parse(json);
- if (result == CX_JSON_NO_ERROR && json->states_size == 1) {
+ if (result == CX_JSON_NO_ERROR && json->states.size == 1) {
// final state reached
- assert(json->states[0] == JP_STATE_VALUE_END);
- assert(json->vbuf_size == 0);
+ assert(json->states.data[0] == JP_STATE_VALUE_END);
+ assert(json->vbuf.size == 0);
// write output value
*value = json->parsed;
json->parsed = NULL;
// re-initialize state machine
- json->states[0] = JP_STATE_VALUE_BEGIN;
+ json->states.data[0] = JP_STATE_VALUE_BEGIN;
return CX_JSON_NO_ERROR;
}
// the parser might think there is no data
// but when we did not reach the final state,
// we know that there must be more to come
- if (result == CX_JSON_NO_DATA && json->states_size > 1) {
+ if (result == CX_JSON_NO_DATA && json->states.size > 1) {
return CX_JSON_INCOMPLETE_DATA;
}
return result;
}
+CxJsonStatus cx_json_from_string(const CxAllocator *allocator,
+ cxstring str, CxJsonValue **value) {
+ *value = &cx_json_value_nothing;
+ CxJson parser;
+ cxJsonInit(&parser, allocator);
+ if (cxJsonFill(&parser, str)) {
+ // LCOV_EXCL_START
+ cxJsonDestroy(&parser);
+ return CX_JSON_BUFFER_ALLOC_FAILED;
+ // LCOV_EXCL_STOP
+ }
+ CxJsonStatus status = cxJsonNext(&parser, value);
+ // check if we consume the total string
+ CxJsonValue *chk_value = NULL;
+ CxJsonStatus chk_status = CX_JSON_NO_DATA;
+ if (status == CX_JSON_NO_ERROR) {
+ chk_status = cxJsonNext(&parser, &chk_value);
+ }
+ cxJsonDestroy(&parser);
+ if (chk_status == CX_JSON_NO_DATA) {
+ return status;
+ } else {
+ cxJsonValueFree(*value);
+ // if chk_value is nothing, the free is harmless
+ cxJsonValueFree(chk_value);
+ *value = &cx_json_value_nothing;
+ return CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN;
+ }
+
+}
+
void cxJsonValueFree(CxJsonValue *value) {
if (value == NULL || value->type == CX_JSON_NOTHING) return;
switch (value->type) {
case CX_JSON_OBJECT: {
- CxJsonObject obj = value->value.object;
- for (size_t i = 0; i < obj.values_size; i++) {
- cxJsonValueFree(obj.values[i].value);
- cx_strfree_a(value->allocator, &obj.values[i].name);
- }
- cxFree(value->allocator, obj.values);
- cxFree(value->allocator, obj.indices);
+ json_free_object_map(value->object);
break;
}
case CX_JSON_ARRAY: {
- CxJsonArray array = value->value.array;
- for (size_t i = 0; i < array.array_size; i++) {
- cxJsonValueFree(array.array[i]);
+ for (size_t i = 0; i < value->array.size; i++) {
+ cxJsonValueFree(value->array.data[i]);
}
- cxFree(value->allocator, array.array);
+ cx_array_free_a(value->allocator, value->array);
break;
}
case CX_JSON_STRING: {
- cxFree(value->allocator, value->value.string.ptr);
+ cxFree(value->allocator, value->string.ptr);
break;
}
default: {
if (v == NULL) return NULL;
v->allocator = allocator;
v->type = CX_JSON_OBJECT;
- cx_array_initialize_a(allocator, v->value.object.values, 16);
- if (v->value.object.values == NULL) { // LCOV_EXCL_START
- cxFree(allocator, v);
- return NULL;
- // LCOV_EXCL_STOP
- }
- v->value.object.indices = cxCalloc(allocator, 16, sizeof(size_t));
- if (v->value.object.indices == NULL) { // LCOV_EXCL_START
- cxFree(allocator, v->value.object.values);
+ v->object = json_create_object_map(allocator);
+ if (v->object == NULL) { // LCOV_EXCL_START
cxFree(allocator, v);
return NULL;
// LCOV_EXCL_STOP
return v;
}
-CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator) {
+CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator, size_t capacity) {
if (allocator == NULL) allocator = cxDefaultAllocator;
CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue));
if (v == NULL) return NULL;
v->allocator = allocator;
v->type = CX_JSON_ARRAY;
- cx_array_initialize_a(allocator, v->value.array.array, 16);
- if (v->value.array.array == NULL) { cxFree(allocator, v); return NULL; }
+ if (capacity > 0) {
+ if (cx_array_init_a(allocator, v->array, capacity)) {
+ // LCOV_EXCL_START
+ cxFree(allocator, v);
+ return NULL;
+ // LCOV_EXCL_STOP
+ }
+ } else {
+ v->array.data = NULL;
+ v->array.size = v->array.capacity = 0;
+ }
return v;
}
if (v == NULL) return NULL;
v->allocator = allocator;
v->type = CX_JSON_NUMBER;
- v->value.number = num;
+ v->number = num;
return v;
}
if (v == NULL) return NULL;
v->allocator = allocator;
v->type = CX_JSON_INTEGER;
- v->value.integer = num;
+ v->integer = num;
return v;
}
v->type = CX_JSON_STRING;
cxmutstr s = cx_strdup_a(allocator, str);
if (s.ptr == NULL) { cxFree(allocator, v); return NULL; }
- v->value.string = s;
+ v->string = s;
return v;
}
if (v == NULL) return NULL;
v->allocator = allocator;
v->type = CX_JSON_LITERAL;
- v->value.literal = lit;
+ v->literal = lit;
return v;
}
}
int cxJsonArrAddValues(CxJsonValue* arr, CxJsonValue* const* val, size_t count) {
- CxArrayReallocator value_realloc = cx_array_reallocator(arr->allocator, NULL);
assert(arr->type == CX_JSON_ARRAY);
- return cx_array_simple_copy_a(&value_realloc,
- arr->value.array.array,
- arr->value.array.array_size,
- val, count
- );
+ return cx_array_add_array_a(arr->allocator, arr->array, val, count);
}
int cx_json_obj_put(CxJsonValue* obj, cxstring name, CxJsonValue* child) {
- cxmutstr k = cx_strdup_a(obj->allocator, name);
- if (k.ptr == NULL) return -1;
- CxJsonObjValue kv = {k, child};
- if (json_add_objvalue(obj, kv)) {
- // LCOV_EXCL_START
- cx_strfree_a(obj->allocator, &k);
- return 1;
- // LCOV_EXCL_STOP
- } else {
- return 0;
- }
+ return cxMapPut(obj->object, name, child);
}
CxJsonValue* cx_json_obj_put_obj(CxJsonValue* obj, cxstring name) {
return v;
}
-CxJsonValue* cx_json_obj_put_arr(CxJsonValue* obj, cxstring name) {
- CxJsonValue* v = cxJsonCreateArr(obj->allocator);
+CxJsonValue* cx_json_obj_put_arr(CxJsonValue* obj, cxstring name, size_t capacity) {
+ CxJsonValue* v = cxJsonCreateArr(obj->allocator, capacity);
if (v == NULL) return NULL;
if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; }
return v;
}
CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index) {
- if (index >= value->value.array.array_size) {
+ if (index >= value->array.size) {
return &cx_json_value_nothing;
}
- return value->value.array.array[index];
+ return value->array.data[index];
}
CxJsonValue *cxJsonArrRemove(CxJsonValue *value, size_t index) {
- if (index >= value->value.array.array_size) {
+ if (index >= value->array.size) {
return NULL;
}
- CxJsonValue *ret = value->value.array.array[index];
- // TODO: replace with a low level cx_array_remove()
- size_t count = value->value.array.array_size - index - 1;
- if (count > 0) {
- memmove(value->value.array.array + index, value->value.array.array + index + 1, count * sizeof(CxJsonValue*));
- }
- value->value.array.array_size--;
+ CxJsonValue *ret = value->array.data[index];
+ cx_array_remove(value->array, index);
return ret;
}
char *cxJsonAsString(const CxJsonValue *value) {
- return value->value.string.ptr;
+ return value->string.ptr;
}
cxstring cxJsonAsCxString(const CxJsonValue *value) {
- return cx_strcast(value->value.string);
+ return cx_strcast(value->string);
}
cxmutstr cxJsonAsCxMutStr(const CxJsonValue *value) {
- return value->value.string;
+ return value->string;
}
double cxJsonAsDouble(const CxJsonValue *value) {
if (value->type == CX_JSON_INTEGER) {
- return (double) value->value.integer;
+ return (double) value->integer;
} else {
- return value->value.number;
+ return value->number;
}
}
int64_t cxJsonAsInteger(const CxJsonValue *value) {
if (value->type == CX_JSON_INTEGER) {
- return value->value.integer;
+ return value->integer;
} else {
- return (int64_t) value->value.number;
+ return (int64_t) value->number;
}
}
CxIterator cxJsonArrIter(const CxJsonValue *value) {
- return cxIteratorPtr(
- value->value.array.array,
- value->value.array.array_size,
- true // arrays need to keep order
- );
+ return cx_array_iterator_ptr(value->array);
}
-CxIterator cxJsonObjIter(const CxJsonValue *value) {
- return cxIterator(
- value->value.object.values,
- sizeof(CxJsonObjValue),
- value->value.object.values_size,
- true // TODO: objects do not always need to keep order
- );
+CxMapIterator cxJsonObjIter(const CxJsonValue *value) {
+ return cxMapIterator(value->object);
}
CxJsonValue *cx_json_obj_get(const CxJsonValue *value, cxstring name) {
- size_t index = json_find_objvalue(value, name);
- if (index >= value->value.object.values_size) {
+ CxJsonValue *v = cxMapGet(value->object, name);
+ if (v == NULL) {
return &cx_json_value_nothing;
} else {
- return value->value.object.values[index].value;
+ return v;
}
}
CxJsonValue *cx_json_obj_remove(CxJsonValue *value, cxstring name) {
- size_t index = json_find_objvalue(value, name);
- if (index >= value->value.object.values_size) {
- return NULL;
- } else {
- CxJsonObjValue kv = value->value.object.values[index];
- cx_strfree_a(value->allocator, &kv.name);
- // TODO: replace with cx_array_remove() / cx_array_remove_fast()
- value->value.object.values_size--;
- memmove(value->value.object.values + index, value->value.object.values + index + 1, (value->value.object.values_size - index) * sizeof(CxJsonObjValue));
- return kv.value;
- }
+ CxJsonValue *v = NULL;
+ cxMapRemoveAndGet(value->object, name, &v);
+ return v;
}
CxJsonWriter cxJsonWriterCompact(void) {
return (CxJsonWriter) {
false,
- true,
6,
false,
4,
CxJsonWriter cxJsonWriterPretty(bool use_spaces) {
return (CxJsonWriter) {
- true,
true,
6,
use_spaces,
expected++;
}
depth++;
- size_t elem_count = value->value.object.values_size;
- for (size_t look_idx = 0; look_idx < elem_count; look_idx++) {
- // get the member either via index array or directly
- size_t elem_idx = settings->sort_members
- ? look_idx
- : value->value.object.indices[look_idx];
- CxJsonObjValue *member = &value->value.object.values[elem_idx];
-
+ CxMapIterator member_iter = cxJsonObjIter(value);
+ cx_foreach(const CxMapEntry *, member, member_iter) {
// possible indentation
if (settings->pretty) {
if (cx_json_writer_indent(target, wfunc, settings, depth)) {
// the name
actual += wfunc("\"", 1, 1, target);
- cxmutstr name = escape_string(member->name, settings->escape_slash);
+ cxstring key = cx_hash_key_as_string(member->key);
+ cxmutstr name = escape_string(key, settings->escape_slash);
actual += wfunc(name.ptr, 1, name.length, target);
- if (name.ptr != member->name.ptr) {
- cx_strfree(&name);
- }
actual += wfunc("\"", 1, 1, target);
const char *obj_name_sep = ": ";
if (settings->pretty) {
actual += wfunc(obj_name_sep, 1, 2, target);
- expected += 4 + member->name.length;
+ expected += 4 + name.length;
} else {
actual += wfunc(obj_name_sep, 1, 1, target);
- expected += 3 + member->name.length;
+ expected += 3 + name.length;
+ }
+ if (name.ptr != key.ptr) {
+ cx_strfree(&name);
}
// the value
if (cx_json_write_rec(target, member->value, wfunc, settings, depth)) return 1;
// end of object-value
- if (look_idx < elem_count - 1) {
+ if (member_iter.index < member_iter.elem_count - 1) {
const char *obj_value_sep = ",\n";
if (settings->pretty) {
actual += wfunc(obj_value_sep, 1, 2, target);
}
case CX_JSON_STRING: {
actual += wfunc("\"", 1, 1, target);
- cxmutstr str = escape_string(value->value.string, settings->escape_slash);
+ cxmutstr str = escape_string(cx_strcast(value->string),
+ settings->escape_slash);
actual += wfunc(str.ptr, 1, str.length, target);
- if (str.ptr != value->value.string.ptr) {
+ actual += wfunc("\"", 1, 1, target);
+ expected += 2 + str.length;
+ if (str.ptr != value->string.ptr) {
cx_strfree(&str);
}
- actual += wfunc("\"", 1, 1, target);
- expected += 2 + value->value.string.length;
break;
}
case CX_JSON_NUMBER: {
// because of the way how %g is defined, we need to
// double the precision and truncate ourselves
precision = 1 + (precision > 15 ? 30 : 2 * precision);
- snprintf(numbuf, 40, "%.*g", precision, value->value.number);
+ snprintf(numbuf, 40, "%.*g", precision, value->number);
char *dot, *exp;
unsigned char max_digits;
// find the decimal separator and hope that it's one of . or ,
break;
}
case CX_JSON_INTEGER: {
- snprintf(numbuf, 32, "%" PRIi64, value->value.integer);
+ snprintf(numbuf, 32, "%" PRIi64, value->integer);
size_t len = strlen(numbuf);
actual += wfunc(numbuf, 1, len, target);
expected += len;
break;
}
case CX_JSON_LITERAL: {
- if (value->value.literal == CX_JSON_TRUE) {
+ if (value->literal == CX_JSON_TRUE) {
actual += wfunc("true", 1, 4, target);
expected += 4;
- } else if (value->value.literal == CX_JSON_FALSE) {
+ } else if (value->literal == CX_JSON_FALSE) {
actual += wfunc("false", 1, 5, target);
expected += 5;
} else {
}
return cx_json_write_rec(target, value, wfunc, settings, 0);
}
+
+static cxmutstr cx_json_to_string(CxJsonValue *value, const CxAllocator *allocator, CxJsonWriter *writer) {
+ if (allocator == NULL) allocator = cxDefaultAllocator;
+ CxBuffer buffer;
+ if (cxBufferInit(&buffer, allocator, NULL, 128,
+ CX_BUFFER_AUTO_EXTEND | CX_BUFFER_DO_NOT_FREE)) {
+ return (cxmutstr){NULL, 0};
+ }
+ if (cx_json_write_rec(&buffer, value, cxBufferWriteFunc, writer, 0)
+ || cxBufferTerminate(&buffer)) {
+ // LCOV_EXCL_START
+ buffer.flags &= ~CX_BUFFER_DO_NOT_FREE;
+ cxBufferDestroy(&buffer);
+ return (cxmutstr){NULL, 0};
+ // LCOV_EXCL_STOP
+ } else {
+ cxmutstr str = cx_bstr_m(&buffer);
+ cxBufferDestroy(&buffer);
+ return str;
+ }
+
+}
+
+cxmutstr cxJsonToString(const CxAllocator *allocator, CxJsonValue *value) {
+ CxJsonWriter writer = cxJsonWriterCompact();
+ return cx_json_to_string(value, allocator, &writer);
+}
+
+cxmutstr cxJsonToPrettyString(const CxAllocator *allocator, CxJsonValue *value) {
+ CxJsonWriter writer = cxJsonWriterPretty(true);
+ return cx_json_to_string(value, allocator, &writer);
+}
+
+int cxJsonCompare(const CxJsonValue *json, const CxJsonValue *other) {
+ if (json == other) return 0;
+ if (json == NULL || other == NULL) return -1;
+ if (json->type != other->type) {
+ if (!cxJsonIsNumber(json)) return -1;
+ if (!cxJsonIsNumber(other)) return -1;
+ }
+ switch (json->type) {
+ case CX_JSON_NOTHING:
+ return 0;
+ case CX_JSON_OBJECT:
+ return cxMapCompare(json->object, other->object);
+ case CX_JSON_ARRAY:
+ if (json->array.size != other->array.size) return -1;
+ for (size_t i = 0; i < json->array.size; i++) {
+ const int d = cxJsonCompare(json->array.data[i], other->array.data[i]);
+ if (d != 0) return d;
+ }
+ return 0;
+ case CX_JSON_STRING:
+ return cx_strcmp(json->string, other->string);
+ case CX_JSON_INTEGER:
+ if (other->type == CX_JSON_INTEGER) {
+ return cx_vcmp_int64(json->integer, other->integer);
+ } else {
+ return cx_vcmp_double(cxJsonAsDouble(json), other->number);
+ }
+ case CX_JSON_NUMBER:
+ return cx_vcmp_double(json->number, cxJsonAsDouble(other));
+ case CX_JSON_LITERAL:
+ return json->literal == other->literal ? 0 : -1;
+ default:
+ // LCOV_EXCL_START
+ // unreachable
+ assert(false);
+ return -1;
+ // LCOV_EXCL_STOP
+ }
+}
+
+CxJsonValue* cxJsonClone(const CxJsonValue* value, const CxAllocator* allocator) {
+ return cx_json_clone_func(NULL, value, allocator, NULL);
+}
+
+CxJsonValue* cx_json_clone_func(CxJsonValue* target, const CxJsonValue* source,
+ const CxAllocator* allocator, cx_attr_unused void *data) {
+ if (source == NULL || source->type == CX_JSON_NOTHING) {
+ return &cx_json_value_nothing;
+ }
+ if (allocator == NULL) allocator = cxDefaultAllocator;
+
+#define return_value(v) { \
+ CxJsonValue *ret = v; \
+ if (target == NULL) { \
+ return ret; \
+ } else { \
+ *target = *ret; \
+ cxFree(allocator, ret); \
+ return target; \
+ } \
+ }
+
+ switch (source->type) {
+ case CX_JSON_OBJECT: {
+ CxJsonValue *obj = cxJsonCreateObj(allocator);
+ if (obj == NULL) return NULL; // LCOV_EXCL_LINE
+ if (cxMapClone(obj->object, source->object, cxJsonCloneFunc, allocator, NULL)) {
+ // LCOV_EXCL_START
+ cxJsonValueFree(obj);
+ return NULL;
+ // LCOV_EXCL_STOP
+ }
+ return_value(obj);
+ }
+ case CX_JSON_ARRAY: {
+ const size_t elem_count = source->array.size;
+ CxJsonValue *arr = cxJsonCreateArr(allocator, elem_count);
+ if (arr == NULL) return NULL; // LCOV_EXCL_LINE
+ arr->array.size = elem_count;
+ for (size_t i = 0 ; i < elem_count ; i++) {
+ CxJsonValue *e = cx_json_clone_func(NULL, source->array.data[i], allocator, NULL);
+ if (e == NULL) {
+ // LCOV_EXCL_START
+ cxJsonValueFree(arr);
+ return NULL;
+ // LCOV_EXCL_STOP
+ }
+ arr->array.data[i] = e;
+ }
+ return_value(arr);
+ }
+ case CX_JSON_STRING:
+ return_value(cxJsonCreateString(allocator, source->string));
+ case CX_JSON_INTEGER:
+ return_value(cxJsonCreateInteger(allocator, source->integer));
+ case CX_JSON_NUMBER:
+ return_value(cxJsonCreateNumber(allocator, source->number));
+ case CX_JSON_LITERAL:
+ return_value(cxJsonCreateLiteral(allocator, source->literal));
+ default:
+ // LCOV_EXCL_START
+ // unreachable
+ assert(false);
+ return NULL;
+ // LCOV_EXCL_STOP
+ }
+#undef return_value
+}
size_t index;
cx_linked_list *ll = &kv_list->list;
- char *node = cx_linked_list_find(
+ char *node = cx_linked_list_find_c(
ll->begin,
ll->loc_next, ll->loc_data,
- list->collection.cmpfunc, elem,
- &index
+ elem, &index,
+ cx_list_compare_wrapper,
+ list
);
if (node == NULL) {
return list->collection.size;
static void cx_kvl_map_deallocate(struct cx_map_s *map) {
cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list;
+ cx_kv_list_update_destructors(kv_list);
kv_list->map_methods->deallocate(map);
kv_list->list_methods->deallocate(&kv_list->list.base);
}
kv_list->map_methods->clear(map);
}
-static void *cx_kvl_map_put(CxMap *map, CxHashKey key, void *value) {
- cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list;
- // if the hash has not yet been computed, do it now
- if (key.hash == 0) {
- cx_hash_murmur(&key);
- }
-
- // reserve memory in the map first
- void **map_data = kv_list->map_methods->put(map, key, NULL);
- if (map_data == NULL) return NULL; // LCOV_EXCL_LINE
-
- // insert the data into the list (which most likely destroys the sorted property)
- kv_list->list.base.collection.sorted = false;
- void *node_data = kv_list->list_methods->insert_element(
- &kv_list->list.base, kv_list->list.base.collection.size,
- kv_list->list.base.collection.store_pointer ? &value : value);
- if (node_data == NULL) { // LCOV_EXCL_START
- // non-destructively remove the key again
- kv_list->map_methods->remove(&kv_list->map->map_base.base, key, &map_data);
- return NULL;
- } // LCOV_EXCL_STOP
-
- // write the node pointer to the map entry
- *map_data = node_data;
-
- // copy the key to the node data
- CxHashKey *key_ptr = cx_kv_list_loc_key(kv_list, node_data);
- *key_ptr = key;
-
- // we must return node_data here and not map_data,
- // because the node_data is the actual element of this collection
- return node_data;
-}
-
-void *cx_kvl_map_get(const CxMap *map, CxHashKey key) {
+static void *cx_kvl_map_get(const CxMap *map, CxHashKey key) {
cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list;
void *node_data = kv_list->map_methods->get(map, key);
if (node_data == NULL) return NULL; // LCOV_EXCL_LINE
return kv_list->list.base.collection.store_pointer ? *(void**)node_data : node_data;
}
-int cx_kvl_map_remove(CxMap *map, CxHashKey key, void *targetbuf) {
+static int cx_kvl_map_remove(CxMap *map, CxHashKey key, void *targetbuf) {
cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list;
void *node_data;
return 0;
}
+static CxMapEntry cx_kvl_map_put(CxMap *map, CxHashKey key, void *value) {
+ cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list;
+ // if the hash has not yet been computed, do it now
+ if (key.hash == 0) {
+ cx_hash_murmur(&key);
+ }
+
+ // remove any existing element first
+ cx_kvl_map_remove(map, key, NULL);
+
+ // now reserve new memory in the map
+ CxMapEntry map_entry = kv_list->map_methods->put(map, key, NULL);
+ if (map_entry.key == NULL) return (CxMapEntry){NULL, NULL}; // LCOV_EXCL_LINE
+
+ // insert the data into the list (which most likely destroys the sorted property)
+ kv_list->list.base.collection.sorted = false;
+ void *node_data = kv_list->list_methods->insert_element(
+ &kv_list->list.base, kv_list->list.base.collection.size,
+ kv_list->list.base.collection.store_pointer ? &value : value);
+ if (node_data == NULL) { // LCOV_EXCL_START
+ // non-destructively remove the key again
+ void *dummy;
+ kv_list->map_methods->remove(&kv_list->map->map_base.base, key, &dummy);
+ return (CxMapEntry){NULL, NULL};
+ } // LCOV_EXCL_STOP
+
+ // write the node pointer to the map entry
+ *(void**)map_entry.value = node_data;
+
+ // copy the key to the node data
+ CxHashKey *key_ptr = cx_kv_list_loc_key(kv_list, node_data);
+ *key_ptr = *map_entry.key;
+
+ // we must return an entry that points to the node data!
+ return (CxMapEntry ){key_ptr, node_data};
+}
+
static void *cx_kvl_iter_current_entry(const void *it) {
const CxMapIterator *iter = it;
return (void*)&iter->entry;
return iter->elem != NULL;
}
-CxMapIterator cx_kvl_map_iterator(const CxMap *map, enum cx_map_iterator_type type) {
+static CxMapIterator cx_kvl_map_iterator(const CxMap *map, enum cx_map_iterator_type type) {
CxMapIterator iter = {0};
iter.type = type;
CxList *cxKvListCreate(
const CxAllocator *allocator,
- cx_compare_func comparator,
size_t elem_size
) {
if (allocator == NULL) {
}
// create a normal linked list and a normal hash map, first
- CxList *list = cxLinkedListCreate(allocator, comparator, elem_size);
+ CxList *list = cxLinkedListCreate(allocator, elem_size);
if (list == NULL) return NULL; // LCOV_EXCL_LINE
cx_linked_list *ll = (cx_linked_list*)list;
cx_linked_list_extra_data(ll, sizeof(CxHashKey));
// remember the base methods and override them
kv_list->map_methods = map->cl;
map->cl = &cx_kv_map_class;
- if (list->climpl == NULL) {
- kv_list->list_methods = list->cl;
- list->cl = &cx_kv_list_class;
- } else {
- kv_list->list_methods = list->climpl;
- list->climpl = &cx_kv_list_class;
- }
+ kv_list->list_methods = list->cl;
+ list->cl = &cx_kv_list_class;
return list;
}
CxMap *cxKvListCreateAsMap(
const CxAllocator *allocator,
- cx_compare_func comparator,
size_t elem_size
) {
- CxList *list = cxKvListCreate(allocator, comparator, elem_size);
+ CxList *list = cxKvListCreate(allocator, elem_size);
return list == NULL ? NULL : cxKvListAsMap(list);
}
return 1;
}
- // add the key to the map;
- if (NULL == kv_list->map_methods->put(&kv_list->map->map_base.base, key, node_data)) {
- return 1; // LCOV_EXCL_LINE
- }
+ // add the key to the map
+ const CxMapEntry entry = kv_list->map_methods->put(
+ &kv_list->map->map_base.base, key, node_data);
+ if (entry.key == NULL) return 1; // LCOV_EXCL_LINE
// write the key to the list's node
CxHashKey *loc_key = cx_kv_list_loc_key(kv_list, node_data);
- *loc_key = key;
+ *loc_key = *entry.key;
return 0;
}
cx_kv_list *kv_list = (cx_kv_list*)list;
// reserve memory in the map
- void **map_data = kv_list->map_methods->put(&kv_list->map->map_base.base, key, NULL);
- if (map_data == NULL) return 1; // LCOV_EXCL_LINE
+ CxMapEntry map_entry = kv_list->map_methods->put(&kv_list->map->map_base.base, key, NULL);
+ if (map_entry.key == NULL) return 1; // LCOV_EXCL_LINE
// insert the node
void *node_data = kv_list->list_methods->insert_element(&kv_list->list.base, index,
kv_list->list.base.collection.store_pointer ? &value : value);
if (node_data == NULL) { // LCOV_EXCL_START
// non-destructively remove the key again
- kv_list->map_methods->remove(&kv_list->map->map_base.base, key, &map_data);
+ void *dummy;
+ kv_list->map_methods->remove(&kv_list->map->map_base.base, key, &dummy);
return 1;
} // LCOV_EXCL_STOP
- *map_data = node_data;
+ *(void**)map_entry.value = node_data;
// write the key to the node
CxHashKey *loc_key = cx_kv_list_loc_key(kv_list, node_data);
- *loc_key = key;
+ *loc_key = *map_entry.key;
return 0;
}
return (void *) cur;
}
-void *cx_linked_list_find(
+void *cx_linked_list_find_c(
const void *start,
ptrdiff_t loc_advance,
ptrdiff_t loc_data,
- cx_compare_func cmp_func,
const void *elem,
- size_t *found_index
+ size_t *found_index,
+ cx_compare_func2 cmp_func,
+ void *context
) {
assert(start != NULL);
assert(loc_advance >= 0);
size_t index = 0;
do {
void *current = ll_data(node);
- if (cmp_func(current, elem) == 0) {
+ if (cmp_func(current, elem, context) == 0) {
if (found_index != NULL) {
*found_index = index;
}
return NULL;
}
+void *cx_linked_list_find(
+ const void *start,
+ ptrdiff_t loc_advance,
+ ptrdiff_t loc_data,
+ const void *elem,
+ size_t *found_index,
+ cx_compare_func cmp_func
+) {
+ cx_compare_func_wrapper wrapper = {cmp_func};
+ return cx_linked_list_find_c(start, loc_advance, loc_data,
+ elem, found_index, cx_cmp_wrap, &wrapper);
+}
+
void *cx_linked_list_first(
const void *node,
ptrdiff_t loc_prev
}
}
-void cx_linked_list_insert_sorted(
- void **begin,
- void **end,
- ptrdiff_t loc_prev,
- ptrdiff_t loc_next,
- void *new_node,
- cx_compare_func cmp_func
-) {
- assert(ll_next(new_node) == NULL);
- cx_linked_list_insert_sorted_chain(
- begin, end, loc_prev, loc_next, new_node, cmp_func);
-}
-
static void *cx_linked_list_insert_sorted_chain_impl(
void **begin,
void **end,
ptrdiff_t loc_prev,
ptrdiff_t loc_next,
void *insert_begin,
- cx_compare_func cmp_func,
+ cx_compare_func2 cmp_func,
+ void *context,
bool allow_duplicates
) {
assert(begin != NULL);
// determine the new start
{
- int d = source_original == NULL ? 1 : cmp_func(source_original, source_argument);
+ int d = source_original == NULL ? 1 : cmp_func(source_original, source_argument, context);
if (d <= 0) {
// the new chain starts with the original chain
new_begin = new_end = source_original;
// now successively compare the elements and add them to the correct chains
while (source_original != NULL && source_argument != NULL) {
- int d = cmp_func(source_original, source_argument);
+ int d = cmp_func(source_original, source_argument, context);
if (d <= 0) {
// the original is not larger, add it to the chain
cx_linked_list_link(new_end, source_original, loc_prev, loc_next);
} else {
// the original is larger, append the source argument to the chain
// check if we must discard the source argument as duplicate
- if (!allow_duplicates && cmp_func(new_end, source_argument) == 0) {
+ if (!allow_duplicates && cmp_func(new_end, source_argument, context) == 0) {
if (dup_end == NULL) {
dup_begin = dup_end = source_argument;
} else {
} else {
// otherwise we must check one-by-one
while (source_argument != NULL) {
- if (cmp_func(new_end, source_argument) == 0) {
+ if (cmp_func(new_end, source_argument, context) == 0) {
if (dup_end == NULL) {
dup_begin = dup_end = source_argument;
} else {
return dup_begin;
}
+void cx_linked_list_insert_sorted(
+ void **begin,
+ void **end,
+ ptrdiff_t loc_prev,
+ ptrdiff_t loc_next,
+ void *new_node,
+ cx_compare_func cmp_func
+) {
+ assert(ll_next(new_node) == NULL);
+ cx_linked_list_insert_sorted_chain(
+ begin, end, loc_prev, loc_next, new_node, cmp_func);
+}
+
void cx_linked_list_insert_sorted_chain(
void **begin,
void **end,
void *insert_begin,
cx_compare_func cmp_func
) {
+ cx_compare_func_wrapper wrapper = {cmp_func};
cx_linked_list_insert_sorted_chain_impl(
begin, end, loc_prev, loc_next,
- insert_begin, cmp_func, true);
+ insert_begin, cx_cmp_wrap, &wrapper, true);
}
int cx_linked_list_insert_unique(
ptrdiff_t loc_next,
void *insert_begin,
cx_compare_func cmp_func
+) {
+ cx_compare_func_wrapper wrapper = {cmp_func};
+ return cx_linked_list_insert_sorted_chain_impl(
+ begin, end, loc_prev, loc_next,
+ insert_begin, cx_cmp_wrap, &wrapper, false);
+}
+
+void cx_linked_list_insert_sorted_c(
+ void **begin,
+ void **end,
+ ptrdiff_t loc_prev,
+ ptrdiff_t loc_next,
+ void *new_node,
+ cx_compare_func2 cmp_func,
+ void *context
+) {
+ assert(ll_next(new_node) == NULL);
+ cx_linked_list_insert_sorted_chain_c(
+ begin, end, loc_prev, loc_next, new_node, cmp_func, context);
+}
+
+void cx_linked_list_insert_sorted_chain_c(
+ void **begin,
+ void **end,
+ ptrdiff_t loc_prev,
+ ptrdiff_t loc_next,
+ void *insert_begin,
+ cx_compare_func2 cmp_func,
+ void *context
+) {
+ cx_linked_list_insert_sorted_chain_impl(
+ begin, end, loc_prev, loc_next,
+ insert_begin, cmp_func, context, true);
+}
+
+int cx_linked_list_insert_unique_c(
+ void **begin,
+ void **end,
+ ptrdiff_t loc_prev,
+ ptrdiff_t loc_next,
+ void *new_node,
+ cx_compare_func2 cmp_func,
+ void *context
+) {
+ assert(ll_next(new_node) == NULL);
+ return NULL != cx_linked_list_insert_unique_chain_c(
+ begin, end, loc_prev, loc_next, new_node, cmp_func, context);
+}
+
+void *cx_linked_list_insert_unique_chain_c(
+ void **begin,
+ void **end,
+ ptrdiff_t loc_prev,
+ ptrdiff_t loc_next,
+ void *insert_begin,
+ cx_compare_func2 cmp_func,
+ void *context
) {
return cx_linked_list_insert_sorted_chain_impl(
begin, end, loc_prev, loc_next,
- insert_begin, cmp_func, false);
+ insert_begin, cmp_func, context, false);
}
size_t cx_linked_list_remove_chain(
#endif
static void cx_linked_list_sort_merge(
+ void **begin,
+ void **end,
ptrdiff_t loc_prev,
ptrdiff_t loc_next,
ptrdiff_t loc_data,
void *ls,
void *le,
void *re,
- cx_compare_func cmp_func,
- void **begin,
- void **end
+ cx_compare_func2 cmp_func,
+ void *context
) {
void *sbo[CX_LINKED_LIST_SORT_SBO_SIZE];
void **sorted = length >= CX_LINKED_LIST_SORT_SBO_SIZE ?
rc = le;
size_t n = 0;
while (lc && lc != le && rc != re) {
- if (cmp_func(ll_data(lc), ll_data(rc)) <= 0) {
+ if (cmp_func(ll_data(lc), ll_data(rc), context) <= 0) {
sorted[n] = lc;
lc = ll_next(lc);
} else {
}
}
-void cx_linked_list_sort( // NOLINT(misc-no-recursion) - purposely recursive function
+void cx_linked_list_sort_c( // NOLINT(misc-no-recursion) - purposely recursive function
void **begin,
void **end,
ptrdiff_t loc_prev,
ptrdiff_t loc_next,
ptrdiff_t loc_data,
- cx_compare_func cmp_func
+ cx_compare_func2 cmp_func,
+ void *context
) {
assert(begin != NULL);
assert(loc_next >= 0);
// check how many elements are already sorted
lc = ls;
size_t ln = 1;
- while (ll_next(lc) != NULL && cmp_func(ll_data(ll_next(lc)), ll_data(lc)) > 0) {
+ while (ll_next(lc) != NULL && cmp_func(ll_data(ll_next(lc)), ll_data(lc), context) > 0) {
lc = ll_next(lc);
ln++;
}
size_t rn = 1;
rc = le;
// skip already sorted elements
- while (ll_next(rc) != NULL && cmp_func(ll_data(ll_next(rc)), ll_data(rc)) > 0) {
+ while (ll_next(rc) != NULL && cmp_func(ll_data(ll_next(rc)), ll_data(rc), context) > 0) {
rc = ll_next(rc);
rn++;
}
// {ls,...,le->prev} and {rs,...,re->prev} are sorted - merge them
void *sorted_begin, *sorted_end;
- cx_linked_list_sort_merge(loc_prev, loc_next, loc_data,
+ cx_linked_list_sort_merge(&sorted_begin, &sorted_end,
+ loc_prev, loc_next, loc_data,
ln + rn, ls, le, re, cmp_func,
- &sorted_begin, &sorted_end);
+ context);
// Something left? Sort it!
size_t remainder_length = cx_linked_list_size(re, loc_next);
if (remainder_length > 0) {
void *remainder = re;
- cx_linked_list_sort(&remainder, NULL, loc_prev, loc_next, loc_data, cmp_func);
+ cx_linked_list_sort_c(&remainder, NULL, loc_prev, loc_next, loc_data, cmp_func, context);
// merge sorted list with (also sorted) remainder
- cx_linked_list_sort_merge(loc_prev, loc_next, loc_data,
+ cx_linked_list_sort_merge(&sorted_begin, &sorted_end,
+ loc_prev, loc_next, loc_data,
ln + rn + remainder_length,
sorted_begin, remainder, NULL, cmp_func,
- &sorted_begin, &sorted_end);
+ context);
}
*begin = sorted_begin;
if (end) *end = sorted_end;
}
}
-int cx_linked_list_compare(
+void cx_linked_list_sort(
+ void **begin,
+ void **end,
+ ptrdiff_t loc_prev,
+ ptrdiff_t loc_next,
+ ptrdiff_t loc_data,
+ cx_compare_func cmp_func
+) {
+ cx_compare_func_wrapper wrapper = {cmp_func};
+ cx_linked_list_sort_c(begin, end, loc_prev, loc_next, loc_data, cx_cmp_wrap, &wrapper);
+}
+
+int cx_linked_list_compare_c(
const void *begin_left,
const void *begin_right,
ptrdiff_t loc_advance,
ptrdiff_t loc_data,
- cx_compare_func cmp_func
+ cx_compare_func2 cmp_func,
+ void *context
) {
const void *left = begin_left, *right = begin_right;
while (left != NULL && right != NULL) {
const void *left_data = ll_data(left);
const void *right_data = ll_data(right);
- int result = cmp_func(left_data, right_data);
+ int result = cmp_func(left_data, right_data, context);
if (result != 0) return result;
left = ll_advance(left);
right = ll_advance(right);
else { return 0; }
}
+int cx_linked_list_compare(
+ const void *begin_left,
+ const void *begin_right,
+ ptrdiff_t loc_advance,
+ ptrdiff_t loc_data,
+ cx_compare_func cmp_func
+) {
+ cx_compare_func_wrapper wrapper = {cmp_func};
+ return cx_linked_list_compare_c(begin_left, begin_right,
+ loc_advance, loc_data, cx_cmp_wrap, &wrapper);
+}
+
void cx_linked_list_reverse(
void **begin,
void **end,
}
}
-static _Thread_local cx_compare_func cx_ll_insert_sorted_cmp_func;
-static _Thread_local off_t cx_ll_insert_sorted_loc_data;
-
-static int cx_ll_insert_sorted_cmp_helper(const void *l, const void *r) {
- const char *left = (const char*)l + cx_ll_insert_sorted_loc_data;
- const char *right = (const char*)r + cx_ll_insert_sorted_loc_data;
- return cx_ll_insert_sorted_cmp_func(left, right);
+static int cx_ll_insert_sorted_cmp_helper(const void *l, const void *r, void *c) {
+ cx_linked_list *list = c;
+ const char *left = (const char*)l + list->loc_data;
+ const char *right = (const char*)r + list->loc_data;
+ return cx_list_compare_wrapper(left, right, list);
}
static size_t cx_ll_insert_sorted_impl(
}
CX_LL_PTR(prev, ll->loc_next) = NULL;
- // invoke the low level function
- cx_ll_insert_sorted_cmp_func = list->collection.cmpfunc;
- cx_ll_insert_sorted_loc_data = ll->loc_data;
- if (allow_duplicates) {
- cx_linked_list_insert_sorted_chain(
- &ll->begin,
- &ll->end,
- ll->loc_prev,
- ll->loc_next,
- chain,
- cx_ll_insert_sorted_cmp_helper
- );
- list->collection.size += inserted;
- } else {
- void *duplicates = cx_linked_list_insert_unique_chain(
- &ll->begin,
- &ll->end,
- ll->loc_prev,
- ll->loc_next,
- chain,
- cx_ll_insert_sorted_cmp_helper
- );
- list->collection.size += inserted;
+ // invoke the low-level function
+ void *duplicates = cx_linked_list_insert_sorted_chain_impl(
+ &ll->begin,
+ &ll->end,
+ ll->loc_prev,
+ ll->loc_next,
+ chain,
+ cx_ll_insert_sorted_cmp_helper,
+ list,
+ allow_duplicates
+ );
+ list->collection.size += inserted;
+ if (!allow_duplicates) {
// free the nodes that did not make it into the list
while (duplicates != NULL) {
void *next = CX_LL_PTR(duplicates, ll->loc_next);
size_t index;
cx_linked_list *ll = (cx_linked_list *) list;
- char *node = cx_linked_list_find(
+ char *node = cx_linked_list_find_c(
ll->begin,
ll->loc_next, ll->loc_data,
- list->collection.cmpfunc, elem,
- &index
- );
+ elem, &index,
+ cx_list_compare_wrapper,
+ list);
if (node == NULL) {
return list->collection.size;
}
static void cx_ll_sort(struct cx_list_s *list) {
cx_linked_list *ll = (cx_linked_list *) list;
- cx_linked_list_sort(&ll->begin, &ll->end,
+ cx_linked_list_sort_c(&ll->begin, &ll->end,
ll->loc_prev, ll->loc_next, ll->loc_data,
- list->collection.cmpfunc);
+ cx_list_compare_wrapper, list);
}
static void cx_ll_reverse(struct cx_list_s *list) {
cx_linked_list *right = (cx_linked_list *) other;
assert(left->loc_next == right->loc_next);
assert(left->loc_data == right->loc_data);
- return cx_linked_list_compare(left->begin, right->begin,
+ return cx_linked_list_compare_c(left->begin, right->begin,
left->loc_next, left->loc_data,
- list->collection.cmpfunc);
+ cx_list_compare_wrapper, (void*)list);
}
static bool cx_ll_iter_valid(const void *it) {
CxList *cxLinkedListCreate(
const CxAllocator *allocator,
- cx_compare_func comparator,
size_t elem_size
) {
if (allocator == NULL) {
list->loc_extra = -1;
list->extra_data_len = 0;
cx_list_init((CxList*)list, &cx_linked_list_class,
- allocator, comparator, elem_size);
+ allocator, elem_size);
return (CxList *) list;
}
#include <string.h>
#include <assert.h>
-// <editor-fold desc="Store Pointers Functionality">
-
-static _Thread_local cx_compare_func cx_pl_cmpfunc_impl;
-
-static int cx_pl_cmpfunc(
- const void *l,
- const void *r
-) {
- // l and r are guaranteed to be non-NULL pointing to the list's memory
- void *const *lptr = l;
- void *const *rptr = r;
- const void *left = *lptr;
- const void *right = *rptr;
- if (left == NULL) {
- // NULL is smaller than any value except NULL
- return right == NULL ? 0 : -1;
- } else if (right == NULL) {
- // any value is larger than NULL
- return 1;
- }
- return cx_pl_cmpfunc_impl(left, right);
-}
-
-static void cx_pl_hack_cmpfunc(const struct cx_list_s *list) {
- // cast away const - this is the hacky thing
- struct cx_collection_s *l = (struct cx_collection_s*) &list->collection;
- cx_pl_cmpfunc_impl = l->cmpfunc;
- l->cmpfunc = cx_pl_cmpfunc;
-}
-
-static void cx_pl_unhack_cmpfunc(const struct cx_list_s *list) {
- // cast away const - this is the hacky thing
- struct cx_collection_s *l = (struct cx_collection_s*) &list->collection;
- l->cmpfunc = cx_pl_cmpfunc_impl;
-}
-
-static void cx_pl_destructor(struct cx_list_s *list) {
- list->climpl->deallocate(list);
-}
-
-static void *cx_pl_insert_element(
- struct cx_list_s *list,
- size_t index,
- const void *element
-) {
- return list->climpl->insert_element(list, index, &element);
-}
-
-static size_t cx_pl_insert_array(
- struct cx_list_s *list,
- size_t index,
- const void *array,
- size_t n
-) {
- return list->climpl->insert_array(list, index, array, n);
-}
-
-static size_t cx_pl_insert_sorted(
- struct cx_list_s *list,
- const void *array,
- size_t n
-) {
- cx_pl_hack_cmpfunc(list);
- size_t result = list->climpl->insert_sorted(list, array, n);
- cx_pl_unhack_cmpfunc(list);
- return result;
-}
-
-static size_t cx_pl_insert_unique(
- struct cx_list_s *list,
- const void *array,
- size_t n
-) {
- cx_pl_hack_cmpfunc(list);
- size_t result = list->climpl->insert_unique(list, array, n);
- cx_pl_unhack_cmpfunc(list);
- return result;
-}
-
-static int cx_pl_insert_iter(
- struct cx_iterator_s *iter,
- const void *elem,
- int prepend
-) {
- struct cx_list_s *list = iter->src_handle;
- return list->climpl->insert_iter(iter, &elem, prepend);
-}
-
-static size_t cx_pl_remove(
- struct cx_list_s *list,
- size_t index,
- size_t num,
- void *targetbuf
-) {
- return list->climpl->remove(list, index, num, targetbuf);
-}
-
-static void cx_pl_clear(struct cx_list_s *list) {
- list->climpl->clear(list);
-}
-
-static int cx_pl_swap(
- struct cx_list_s *list,
- size_t i,
- size_t j
-) {
- return list->climpl->swap(list, i, j);
-}
-
-static void *cx_pl_at(
- const struct cx_list_s *list,
- size_t index
-) {
- void **ptr = list->climpl->at(list, index);
- return ptr == NULL ? NULL : *ptr;
-}
-
-static size_t cx_pl_find_remove(
- struct cx_list_s *list,
- const void *elem,
- bool remove
-) {
- cx_pl_hack_cmpfunc(list);
- size_t ret = list->climpl->find_remove(list, &elem, remove);
- cx_pl_unhack_cmpfunc(list);
- return ret;
-}
-
-static void cx_pl_sort(struct cx_list_s *list) {
- cx_pl_hack_cmpfunc(list);
- list->climpl->sort(list);
- cx_pl_unhack_cmpfunc(list);
-}
-
-static int cx_pl_compare(
- const struct cx_list_s *list,
- const struct cx_list_s *other
-) {
- cx_pl_hack_cmpfunc(list);
- int ret = list->climpl->compare(list, other);
- cx_pl_unhack_cmpfunc(list);
- return ret;
-}
-
-static void cx_pl_reverse(struct cx_list_s *list) {
- list->climpl->reverse(list);
-}
-
-static void *cx_pl_iter_current(const void *it) {
- const struct cx_iterator_s *iter = it;
- void **ptr = iter->base.current_impl(it);
- return ptr == NULL ? NULL : *ptr;
-}
-
-static int cx_pl_change_capacity(struct cx_list_s *list, size_t cap) {
- if (list->climpl->change_capacity == NULL) {
- return 0;
+// we don't want to include the full array_list.h.
+// therefore, we only forward declare the one function we want to use
+CX_EXPORT void cx_array_qsort_c(void *array, size_t nmemb, size_t size,
+ cx_compare_func2 fn, void *context);
+
+
+int cx_list_compare_wrapper(const void *l, const void *r, void *c) {
+ CxList *list = c;
+ const void *left;
+ const void *right;
+ if (cxCollectionStoresPointers(list)) {
+ left = *(void**)l;
+ right = *(void**)r;
+ // for historic reasons, we are handling the NULL case here
+ // because every UCX compare function does not support NULL arguments
+ if (left == NULL) {
+ if (right == NULL) return 0;
+ return -1;
+ } else if (right == NULL) {
+ return 1;
+ }
} else {
- return list->climpl->change_capacity(list, cap);
+ left = l;
+ right = r;
}
+ return cx_invoke_compare_func(list, left, right);
}
-static struct cx_iterator_s cx_pl_iterator(
- const struct cx_list_s *list,
- size_t index,
- bool backwards
-) {
- struct cx_iterator_s iter = list->climpl->iterator(list, index, backwards);
- iter.base.current_impl = iter.base.current;
- iter.base.current = cx_pl_iter_current;
- return iter;
-}
-
-static cx_list_class cx_pointer_list_class = {
- cx_pl_destructor,
- cx_pl_insert_element,
- cx_pl_insert_array,
- cx_pl_insert_sorted,
- cx_pl_insert_unique,
- cx_pl_insert_iter,
- cx_pl_remove,
- cx_pl_clear,
- cx_pl_swap,
- cx_pl_at,
- cx_pl_find_remove,
- cx_pl_sort,
- cx_pl_compare,
- cx_pl_reverse,
- cx_pl_change_capacity,
- cx_pl_iterator,
-};
-// </editor-fold>
+#define cx_list_compare_wrapper(l, r, c) cx_list_compare_wrapper(l, r, (void*)c)
// <editor-fold desc="empty list implementation">
CxList cx_empty_list = {
{
- NULL,
NULL,
0,
0,
NULL,
NULL,
NULL,
+ NULL,
+ NULL,
+ NULL,
false,
true,
},
&cx_empty_list_class,
- NULL
};
CxList *const cxEmptyList = &cx_empty_list;
// </editor-fold>
-#define invoke_list_func(name, list, ...) \
- ((list)->climpl == NULL ? (list)->cl->name : (list)->climpl->name) \
- (list, __VA_ARGS__)
-
size_t cx_list_default_insert_array(
struct cx_list_s *list,
size_t index,
const char *src = data;
size_t i = 0;
for (; i < n; i++) {
- if (NULL == invoke_list_func(
- insert_element, list, index + i, src)
- ) {
+ if (NULL == list->cl->insert_element(list, index + i, src)) {
return i; // LCOV_EXCL_LINE
}
if (src != NULL) {
if (n == 0) return 0;
size_t elem_size = list->collection.elem_size;
- cx_compare_func cmp = list->collection.cmpfunc;
const char *src = sorted_data;
// track indices and number of inserted items
// search the list for insertion points
while (di < list->collection.size) {
- const void *list_elm = invoke_list_func(at, list, di);
+ const void *list_elm = list->cl->at(list, di);
// compare the current list element with the first source element
// if less, skip the list elements
// if equal, skip the list elements and optionally the source elements
{
- int d = cmp(list_elm, src);
+ int d = cx_list_compare_wrapper(list_elm, src, list);
if (d <= 0) {
if (!allow_duplicates && d == 0) {
src += elem_size;
si++;
processed++; // we also count duplicates for the return value
- while (si < n && cmp(list_elm, src) == 0) {
+ while (si < n && cx_list_compare_wrapper(list_elm, src, list) == 0) {
src += elem_size;
si++;
processed++;
while (++si < n) {
if (!allow_duplicates) {
// skip duplicates within the source
- if (cmp(next, next + elem_size) == 0) {
+ if (cx_list_compare_wrapper(next, next + elem_size, list) == 0) {
next += elem_size;
skip++;
continue;
}
next += elem_size;
// once we become larger than the list elem, break
- if (cmp(list_elm, next) <= 0) {
+ if (cx_list_compare_wrapper(list_elm, next, list) <= 0) {
break;
}
// otherwise, we can insert one more
// insert the elements at location si
if (ins == 1) {
- if (NULL == invoke_list_func(insert_element, list, di, src)) {
+ if (NULL == list->cl->insert_element(list, di, src)) {
return processed; // LCOV_EXCL_LINE
}
} else {
- size_t r = invoke_list_func(insert_array, list, di, src, ins);
+ size_t r = list->cl->insert_array(list, di, src, ins);
if (r < ins) {
return processed + r; // LCOV_EXCL_LINE
}
// insert remaining items
if (si < n) {
if (allow_duplicates) {
- processed += invoke_list_func(insert_array, list, di, src, n - si);
+ processed += list->cl->insert_array(list, di, src, n - si);
} else {
- const void *last = di == 0 ? NULL : invoke_list_func(at, list, di - 1);
+ const void *last = di == 0 ? NULL : list->cl->at(list, di - 1);
for (; si < n; si++) {
// skip duplicates within the source
- if (last == NULL || cmp(last, src) != 0) {
- if (NULL == invoke_list_func(insert_element, list, di, src)) {
+ if (last == NULL || cx_list_compare_wrapper(last, src, list) != 0) {
+ if (NULL == list->cl->insert_element(list, di, src)) {
return processed; // LCOV_EXCL_LINE
}
last = src;
// copy elements from source array
char *loc = tmp;
for (size_t i = 0; i < list_size; i++) {
- void *src = invoke_list_func(at, list, i);
+ void *src = list->cl->at(list, i);
memcpy(loc, src, elem_size);
loc += elem_size;
}
// qsort
- qsort(tmp, list_size, elem_size,
- list->collection.cmpfunc);
+ cx_array_qsort_c(tmp, list_size, elem_size, cx_list_compare_wrapper, list);
// copy elements back
loc = tmp;
for (size_t i = 0; i < list_size; i++) {
- void *dest = invoke_list_func(at, list, i);
+ void *dest = list->cl->at(list, i);
memcpy(dest, loc, elem_size);
loc += elem_size;
}
void *tmp = cxMallocDefault(elem_size);
if (tmp == NULL) return 1; // LCOV_EXCL_LINE
- void *ip = invoke_list_func(at, list, i);
- void *jp = invoke_list_func(at, list, j);
+ void *ip = list->cl->at(list, i);
+ void *jp = list->cl->at(list, j);
memcpy(tmp, ip, elem_size);
memcpy(ip, jp, elem_size);
return 0;
}
+static int cx_list_cmpfunc2_safe_memcmp(const void *a, const void *b, void *c) {
+ // it is not safe to store a pointer to the size in the list
+ // because the entire list structure might get reallocated
+ size_t elem_size = (size_t)(uintptr_t)c;
+ return memcmp(a, b, elem_size);
+}
+
void cx_list_init(
struct cx_list_s *list,
struct cx_list_class_s *cl,
const struct cx_allocator_s *allocator,
- cx_compare_func comparator,
size_t elem_size
) {
list->cl = cl;
list->collection.allocator = allocator;
- list->collection.cmpfunc = comparator;
+ list->collection.size = 0;
+ list->collection.sorted = false; // should be set by the implementation
if (elem_size > 0) {
list->collection.elem_size = elem_size;
+ list->collection.simple_cmp = NULL;
+ list->collection.advanced_cmp = cx_list_cmpfunc2_safe_memcmp;
+ list->collection.cmp_data = (void*)(uintptr_t)list->collection.elem_size;
+ list->collection.store_pointer = false;
} else {
list->collection.elem_size = sizeof(void *);
- if (list->collection.cmpfunc == NULL) {
- list->collection.cmpfunc = cx_cmp_ptr;
- }
+ list->collection.simple_cmp = cx_cmp_ptr;
+ list->collection.advanced_cmp = NULL;
+ list->collection.cmp_data = NULL;
list->collection.store_pointer = true;
- list->climpl = list->cl;
- list->cl = &cx_pointer_list_class;
}
}
const CxList *list,
const CxList *other
) {
+ // check if we cannot use the list internal function
bool cannot_optimize = false;
// if one is storing pointers but the other is not
cannot_optimize |= list->collection.store_pointer ^ other->collection.store_pointer;
- // if one class is wrapped but the other is not
- cannot_optimize |= (list->climpl == NULL) ^ (other->climpl == NULL);
-
- // if the compare functions do not match or both are NULL
- if (!cannot_optimize) {
- cx_compare_func list_cmp = (cx_compare_func) (list->climpl != NULL ?
- list->climpl->compare : list->cl->compare);
- cx_compare_func other_cmp = (cx_compare_func) (other->climpl != NULL ?
- other->climpl->compare : other->cl->compare);
- cannot_optimize |= list_cmp != other_cmp;
- cannot_optimize |= list_cmp == NULL;
- }
+ // check if the lists are incompatible or this list does not implement compare
+ cx_compare_func list_cmp = (cx_compare_func) list->cl->compare;
+ cx_compare_func other_cmp = (cx_compare_func) other->cl->compare;
+ cannot_optimize |= list_cmp != other_cmp;
+ cannot_optimize |= list_cmp == NULL;
if (cannot_optimize) {
// lists are definitely different - cannot use internal compare function
if (list->collection.size == other->collection.size) {
- CxIterator left = list->cl->iterator(list, 0, false);
- CxIterator right = other->cl->iterator(other, 0, false);
+ CxIterator left = cxListIterator(list);
+ CxIterator right = cxListIterator(other);
for (size_t i = 0; i < list->collection.size; i++) {
void *leftValue = cxIteratorCurrent(left);
void *rightValue = cxIteratorCurrent(right);
- int d = list->collection.cmpfunc(leftValue, rightValue);
+ // values are already unwrapped, invoke immediately
+ int d = cx_invoke_compare_func(list, leftValue, rightValue);
if (d != 0) {
return d;
}
int cxListAdd(CxList *list, const void *elem) {
list->collection.sorted = false;
- return list->cl->insert_element(list, list->collection.size, elem) == NULL;
+ return list->cl->insert_element(list, list->collection.size, cx_ref(list, elem)) == NULL;
}
size_t cxListAddArray(CxList *list, const void *array, size_t n) {
int cxListInsert(CxList *list, size_t index, const void *elem) {
list->collection.sorted = false;
- return list->cl->insert_element(list, index, elem) == NULL;
+ return list->cl->insert_element(list, index, cx_ref(list, elem)) == NULL;
}
void *cxListEmplaceAt(CxList *list, size_t index) {
iter.index = 0;
// replace the valid function to abort iteration when c is reached
iter.base.valid = cx_list_emplace_iterator_valid;
- // if we are storing pointers, we want to return the pure pointers.
- // therefore, we must unwrap the "current" method
- if (list->collection.store_pointer) {
- iter.base.current = iter.base.current_impl;
- }
return iter;
}
int cxListInsertSorted(CxList *list, const void *elem) {
assert(cxCollectionSorted(list));
list->collection.sorted = true;
- const void *data = list->collection.store_pointer ? &elem : elem;
- return list->cl->insert_sorted(list, data, 1) == 0;
+ return list->cl->insert_sorted(list, cx_ref(list, elem), 1) == 0;
}
int cxListInsertUnique(CxList *list, const void *elem) {
if (cxCollectionSorted(list)) {
list->collection.sorted = true;
- const void *data = list->collection.store_pointer ? &elem : elem;
- return list->cl->insert_unique(list, data, 1) == 0;
+ return list->cl->insert_unique(list, cx_ref(list, elem), 1) == 0;
} else {
if (cxListContains(list, elem)) {
return 0;
const char *source = array;
for (size_t i = 0 ; i < n; i++) {
// note: this also checks elements added in a previous iteration
- const void *data = list->collection.store_pointer ?
- *((const void**)source) : source;
+ const void *data = cx_deref(list, source);
if (!cxListContains(list, data)) {
if (cxListAdd(list, data)) {
return i; // LCOV_EXCL_LINE
}
int cxListInsertAfter(CxIterator *iter, const void *elem) {
- CxList* list = (CxList*)iter->src_handle;
+ CxList* list = iter->src_handle;
list->collection.sorted = false;
- return list->cl->insert_iter(iter, elem, 0);
+ return list->cl->insert_iter(iter, cx_ref(list, elem), 0);
}
int cxListInsertBefore(CxIterator *iter, const void *elem) {
- CxList* list = (CxList*)iter->src_handle;
+ CxList* list = iter->src_handle;
list->collection.sorted = false;
- return list->cl->insert_iter(iter, elem, 1);
+ return list->cl->insert_iter(iter, cx_ref(list, elem), 1);
}
int cxListRemove(CxList *list, size_t index) {
}
void *cxListAt(const CxList *list, size_t index) {
- return list->cl->at(list, index);
+ void *result = list->cl->at(list, index);
+ if (result == NULL) return NULL;
+ return cx_deref(list, result);
}
void *cxListFirst(const CxList *list) {
- return list->cl->at(list, 0);
+ return cxListAt(list, 0);
}
void *cxListLast(const CxList *list) {
- return list->cl->at(list, list->collection.size - 1);
+ return cxListAt(list, list->collection.size - 1);
}
int cxListSet(CxList *list, size_t index, const void *elem) {
}
if (list->collection.store_pointer) {
- // For pointer collections, always use climpl
- void **target = list->climpl->at(list, index);
+ void **target = list->cl->at(list, index);
*target = (void *)elem;
} else {
void *target = list->cl->at(list, index);
return 0;
}
+static void *cx_pl_iter_current(const void *it) {
+ const struct cx_iterator_s *iter = it;
+ void **ptr = iter->base.current_impl(it);
+ return ptr == NULL ? NULL : *ptr;
+}
+
+CX_INLINE CxIterator cx_pl_iter_wrap(const CxList *list, CxIterator iter) {
+ if (cxCollectionStoresPointers(list)) {
+ iter.base.current_impl = iter.base.current;
+ iter.base.current = cx_pl_iter_current;
+ return iter;
+ } else {
+ return iter;
+ }
+}
+
CxIterator cxListIteratorAt(const CxList *list, size_t index) {
if (list == NULL) list = cxEmptyList;
- return list->cl->iterator(list, index, false);
+ return cx_pl_iter_wrap(list, list->cl->iterator(list, index, false));
}
CxIterator cxListBackwardsIteratorAt(const CxList *list, size_t index) {
if (list == NULL) list = cxEmptyList;
- return list->cl->iterator(list, index, true);
+ return cx_pl_iter_wrap(list, list->cl->iterator(list, index, true));
}
CxIterator cxListIterator(const CxList *list) {
if (list == NULL) list = cxEmptyList;
- return list->cl->iterator(list, 0, false);
+ return cx_pl_iter_wrap(list, list->cl->iterator(list, 0, false));
}
CxIterator cxListBackwardsIterator(const CxList *list) {
if (list == NULL) list = cxEmptyList;
- return list->cl->iterator(list, list->collection.size - 1, true);
+ return cx_pl_iter_wrap(list, list->cl->iterator(list, list->collection.size - 1, true));
}
size_t cxListFind(const CxList *list, const void *elem) {
- return list->cl->find_remove((CxList*)list, elem, false);
+ return list->cl->find_remove((CxList*)list, cx_ref(list, elem), false);
}
bool cxListContains(const CxList* list, const void* elem) {
- return list->cl->find_remove((CxList*)list, elem, false) < list->collection.size;
+ return list->cl->find_remove((CxList*)list, cx_ref(list, elem), false) < list->collection.size;
}
bool cxListIndexValid(const CxList *list, size_t index) {
}
size_t cxListFindRemove(CxList *list, const void *elem) {
- return list->cl->find_remove(list, elem, true);
+ return list->cl->find_remove(list, cx_ref(list, elem), true);
}
void cxListSort(CxList *list) {
list->collection.advanced_destructor = destr2_bak;
}
-static void* cx_list_simple_clone_func(void *dst, const void *src, const CxAllocator *al, void *data) {
+static void* cx_list_shallow_clone_func(void *dst, const void *src, const CxAllocator *al, void *data) {
size_t elem_size = *(size_t*)data;
if (dst == NULL) dst = cxMalloc(al, elem_size);
if (dst != NULL) memcpy(dst, src, elem_size);
return dst;
}
-#define use_simple_clone_func(list) cx_list_simple_clone_func, NULL, (void*)&((list)->collection.elem_size)
+#define use_shallow_clone_func(list) cx_list_shallow_clone_func, NULL, (void*)&((list)->collection.elem_size)
int cxListClone(CxList *dst, const CxList *src, cx_clone_func clone_func,
const CxAllocator *clone_allocator, void *data) {
int d;
if (cxIteratorValid(sub_iter)) {
sub_elem = cxIteratorCurrent(sub_iter);
- cx_compare_func cmp = subtrahend->collection.cmpfunc;
- d = cmp(sub_elem, min_elem);
+ d = cx_list_compare_wrapper(sub_elem, min_elem, subtrahend);
} else {
// no more elements in the subtrahend,
// i.e., the min_elem is larger than any elem of the subtrahend
while (cxIteratorValid(src_iter) && cxIteratorValid(other_iter)) {
void *src_elem = cxIteratorCurrent(src_iter);
void *other_elem = cxIteratorCurrent(other_iter);
- int d = src->collection.cmpfunc(src_elem, other_elem);
+ int d = cx_list_compare_wrapper(src_elem, other_elem, src);
if (d == 0) {
// is contained, clone it
void **dst_mem = cxListEmplace(dst);
} else {
src_elem = cxIteratorCurrent(src_iter);
other_elem = cxIteratorCurrent(other_iter);
- d = src->collection.cmpfunc(src_elem, other_elem);
+ d = cx_list_compare_wrapper(src_elem, other_elem, src);
}
void *clone_from;
if (d < 0) {
return 0;
}
-int cxListCloneSimple(CxList *dst, const CxList *src) {
- return cxListClone(dst, src, use_simple_clone_func(src));
+int cxListCloneShallow(CxList *dst, const CxList *src) {
+ return cxListClone(dst, src, use_shallow_clone_func(src));
}
-int cxListDifferenceSimple(CxList *dst, const CxList *minuend, const CxList *subtrahend) {
- return cxListDifference(dst, minuend, subtrahend, use_simple_clone_func(minuend));
+int cxListDifferenceShallow(CxList *dst, const CxList *minuend, const CxList *subtrahend) {
+ return cxListDifference(dst, minuend, subtrahend, use_shallow_clone_func(minuend));
}
-int cxListIntersectionSimple(CxList *dst, const CxList *src, const CxList *other) {
- return cxListIntersection(dst, src, other, use_simple_clone_func(src));
+int cxListIntersectionShallow(CxList *dst, const CxList *src, const CxList *other) {
+ return cxListIntersection(dst, src, other, use_shallow_clone_func(src));
}
-int cxListUnionSimple(CxList *dst, const CxList *src, const CxList *other) {
- return cxListUnion(dst, src, other, use_simple_clone_func(src));
+int cxListUnionShallow(CxList *dst, const CxList *src, const CxList *other) {
+ return cxListUnion(dst, src, other, use_shallow_clone_func(src));
}
int cxListReserve(CxList *list, size_t capacity) {
\r
CxMap cx_empty_map = {\r
{\r
- NULL,\r
NULL,\r
0,\r
0,\r
NULL,\r
NULL,\r
NULL,\r
+ NULL,\r
+ NULL,\r
+ NULL,\r
false,\r
true\r
},\r
}\r
\r
int cx_map_put(CxMap *map, CxHashKey key, void *value) {\r
- return map->cl->put(map, key, value) == NULL;\r
+ return map->cl->put(map, key, value).key == NULL;\r
}\r
\r
void *cx_map_emplace(CxMap *map, CxHashKey key) {\r
- return map->cl->put(map, key, NULL);\r
+ const CxMapEntry entry = map->cl->put(map, key, NULL);\r
+ if (entry.key == NULL) return NULL;\r
+ return entry.value;\r
}\r
\r
void *cx_map_get(const CxMap *map, CxHashKey key) {\r
map->collection.advanced_destructor = destr2_bak;\r
}\r
\r
-static void* cx_map_simple_clone_func(void *dst, const void *src, const CxAllocator *al, void *data) {\r
+static void* cx_map_shallow_clone_func(void *dst, const void *src, const CxAllocator *al, void *data) {\r
size_t elem_size = *(size_t*)data;\r
if (dst == NULL) dst = cxMalloc(al, elem_size);\r
if (dst != NULL) memcpy(dst, src, elem_size);\r
return dst;\r
}\r
\r
-#define use_simple_clone_func(map) cx_map_simple_clone_func, NULL, (void*)&((map)->collection.elem_size)\r
+#define use_shallow_clone_func(map) cx_map_shallow_clone_func, NULL, (void*)&((map)->collection.elem_size)\r
\r
int cxMapClone(CxMap *dst, const CxMap *src, cx_clone_func clone_func,\r
const CxAllocator *clone_allocator, void *data) {\r
return 0;\r
}\r
\r
-int cxMapCloneSimple(CxMap *dst, const CxMap *src) {\r
- return cxMapClone(dst, src, use_simple_clone_func(src));\r
+int cxMapCloneShallow(CxMap *dst, const CxMap *src) {\r
+ return cxMapClone(dst, src, use_shallow_clone_func(src));\r
}\r
\r
-int cxMapDifferenceSimple(CxMap *dst, const CxMap *minuend, const CxMap *subtrahend) {\r
- return cxMapDifference(dst, minuend, subtrahend, use_simple_clone_func(minuend));\r
+int cxMapDifferenceShallow(CxMap *dst, const CxMap *minuend, const CxMap *subtrahend) {\r
+ return cxMapDifference(dst, minuend, subtrahend, use_shallow_clone_func(minuend));\r
}\r
\r
-int cxMapListDifferenceSimple(CxMap *dst, const CxMap *src, const CxList *keys) {\r
- return cxMapListDifference(dst, src, keys, use_simple_clone_func(src));\r
+int cxMapListDifferenceShallow(CxMap *dst, const CxMap *src, const CxList *keys) {\r
+ return cxMapListDifference(dst, src, keys, use_shallow_clone_func(src));\r
}\r
\r
-int cxMapIntersectionSimple(CxMap *dst, const CxMap *src, const CxMap *other) {\r
- return cxMapIntersection(dst, src, other, use_simple_clone_func(src));\r
+int cxMapIntersectionShallow(CxMap *dst, const CxMap *src, const CxMap *other) {\r
+ return cxMapIntersection(dst, src, other, use_shallow_clone_func(src));\r
}\r
\r
-int cxMapListIntersectionSimple(CxMap *dst, const CxMap *src, const CxList *keys) {\r
- return cxMapListIntersection(dst, src, keys, use_simple_clone_func(src));\r
+int cxMapListIntersectionShallow(CxMap *dst, const CxMap *src, const CxList *keys) {\r
+ return cxMapListIntersection(dst, src, keys, use_shallow_clone_func(src));\r
}\r
\r
-int cxMapUnionSimple(CxMap *dst, const CxMap *src) {\r
- return cxMapUnion(dst, src, use_simple_clone_func(src));\r
+int cxMapUnionShallow(CxMap *dst, const CxMap *src) {\r
+ return cxMapUnion(dst, src, use_shallow_clone_func(src));\r
+}\r
+\r
+int cxMapCompare(const CxMap *map, const CxMap *other) {\r
+ // compare map sizes\r
+ const size_t size_left = cxMapSize(map);\r
+ const size_t size_right = cxMapSize(other);\r
+ if (size_left < size_right) {\r
+ return -1;\r
+ } else if (size_left > size_right) {\r
+ return 1;\r
+ }\r
+\r
+ // iterate through the first map\r
+ CxMapIterator iter = cxMapIterator(map);\r
+ cx_foreach(const CxMapEntry *, entry, iter) {\r
+ const void *value_left = entry->value;\r
+ const void *value_right = cxMapGet(other, *entry->key);\r
+ // if the other map does not have the key, we are done\r
+ if (value_right == NULL) {\r
+ return -1;\r
+ }\r
+ // compare the values\r
+ const int d = cx_invoke_compare_func(map, value_left, value_right);\r
+ if (d != 0) {\r
+ return d;\r
+ }\r
+ }\r
+\r
+ return 0;\r
}\r
#include "cx/properties.h"
#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
const CxPropertiesConfig cx_properties_config_default = {
- '=',
- '#',
- '\0',
- '\0',
+ '=',
+ '#',
+ '\0',
+ '\0',
'\\',
};
if (cxBufferEof(&prop->input)) {
// destroy a possible previously initialized buffer
cxBufferDestroy(&prop->input);
- cxBufferInit(&prop->input, (void*) buf, len,
- NULL, CX_BUFFER_COPY_ON_WRITE | CX_BUFFER_AUTO_EXTEND);
+ cxBufferInit(&prop->input, NULL, (void*) buf,
+ len, CX_BUFFER_COPY_ON_WRITE | CX_BUFFER_AUTO_EXTEND);
prop->input.size = len;
} else {
if (cxBufferAppend(buf, 1, len, &prop->input) < len) return -1;
char *buf,
size_t capacity
) {
- cxBufferInit(&prop->buffer, buf, capacity, NULL, CX_BUFFER_COPY_ON_EXTEND);
+ cxBufferInit(&prop->buffer, NULL, buf, capacity, CX_BUFFER_COPY_ON_EXTEND);
}
CxPropertiesStatus cxPropertiesNext(
// a pointer to the buffer we want to read from
CxBuffer *current_buffer = &prop->input;
-
+
+ char comment1 = prop->config.comment1;
+ char comment2 = prop->config.comment2;
+ char comment3 = prop->config.comment3;
+ char delimiter = prop->config.delimiter;
+ char continuation = prop->config.continuation;
+
// check if we have rescued data
if (!cxBufferEof(&prop->buffer)) {
// check if we can now get a complete line
cxstring input = cx_strn(prop->input.space + prop->input.pos,
prop->input.size - prop->input.pos);
cxstring nl = cx_strchr(input, '\n');
+ while (nl.length > 0) {
+ // check for line continuation
+ char previous = nl.ptr > input.ptr ? nl.ptr[-1] : prop->buffer.space[prop->buffer.size-1];
+ if (previous == continuation) {
+ // this nl is a line continuation, check the next newline
+ nl = cx_strchr(cx_strsubs(nl, 1), '\n');
+ } else {
+ break;
+ }
+ }
+
if (nl.length > 0) {
// we add as much data to the rescue buffer as we need
// to complete the line
return CX_PROPERTIES_INCOMPLETE_DATA;
}
}
-
- char comment1 = prop->config.comment1;
- char comment2 = prop->config.comment2;
- char comment3 = prop->config.comment3;
- char delimiter = prop->config.delimiter;
-
+
// get one line and parse it
while (!cxBufferEof(current_buffer)) {
const char *buf = current_buffer->space + current_buffer->pos;
size_t delimiter_index = 0;
size_t comment_index = 0;
bool has_comment = false;
+ bool has_continuation = false;
size_t i = 0;
char c = 0;
if (delimiter_index == 0 && !has_comment) {
delimiter_index = i;
}
+ } else if (delimiter_index > 0 && c == continuation && i+1 < len && buf[i+1] == '\n') {
+ has_continuation = true;
+ i++;
} else if (c == '\n') {
break;
}
assert(cxBufferEof(&prop->buffer));
if (prop->buffer.space == NULL) {
// initialize a rescue buffer, if the user did not provide one
- cxBufferInit(&prop->buffer, NULL, 256, NULL, CX_BUFFER_AUTO_EXTEND);
+ cxBufferInit(&prop->buffer, NULL, NULL, 256, CX_BUFFER_AUTO_EXTEND);
} else {
// from a previous rescue there might be already read data
// reset the buffer to avoid unnecessary buffer extension
k = cx_strtrim(k);
val = cx_strtrim(val);
if (k.length > 0) {
+ current_buffer->pos += i + 1;
+ assert(current_buffer->pos <= current_buffer->size);
+ assert(current_buffer != &prop->buffer || current_buffer->pos == current_buffer->size);
+
+ if (has_continuation) {
+ char *ptr = (char*)val.ptr;
+ if (current_buffer != &prop->buffer) {
+ // move value to the rescue buffer
+ if (prop->buffer.space == NULL) {
+ cxBufferInit(&prop->buffer, NULL, NULL, 256, CX_BUFFER_AUTO_EXTEND);
+ }
+ prop->buffer.size = 0;
+ prop->buffer.pos = 0;
+ if (cxBufferWrite(val.ptr, 1, val.length, &prop->buffer) != val.length) {
+ return CX_PROPERTIES_BUFFER_ALLOC_FAILED;
+ }
+ val.ptr = prop->buffer.space;
+ ptr = prop->buffer.space;
+ }
+ // value.ptr is now inside the rescue buffer and we can
+ // remove the continuation character from the value
+ bool trim = false;
+ size_t x = 0;
+ for(size_t j=0;j<val.length;j++) {
+ c = ptr[j];
+ if (j+1 < val.length && c == '\\' && ptr[j+1] == '\n') {
+ // skip continuation and newline character
+ j++;
+ trim = true; // enable trim in the next line
+ continue;
+ }
+ if (j > x) {
+ if (trim) {
+ if (isspace((unsigned char)c)) {
+ continue;
+ }
+ trim = false;
+ }
+ ptr[x] = c;
+ }
+ x++;
+ }
+ val.length = x;
+ }
*key = k;
*value = val;
- current_buffer->pos += i + 1;
- assert(current_buffer->pos <= current_buffer->size);
+
return CX_PROPERTIES_NO_ERROR;
} else {
return CX_PROPERTIES_INVALID_EMPTY_KEY;
return CX_PROPERTIES_NO_DATA;
}
-static int cx_properties_sink_map(
- cx_attr_unused CxProperties *prop,
- CxPropertiesSink *sink,
- cxstring key,
- cxstring value
-) {
- CxMap *map = sink->sink;
- CxAllocator *alloc = sink->data;
- cxmutstr v = cx_strdup_a(alloc, value);
- int r = cxMapPut(map, key, v.ptr);
- if (r != 0) cx_strfree_a(alloc, &v);
- return r;
-}
-
-CxPropertiesSink cxPropertiesMapSink(CxMap *map) {
- CxPropertiesSink sink;
- sink.sink = map;
- sink.data = (void*) cxDefaultAllocator;
- sink.sink_func = cx_properties_sink_map;
- return sink;
-}
-
-static int cx_properties_read_string(
- CxProperties *prop,
- CxPropertiesSource *src,
- cxstring *target
-) {
- if (prop->input.space == src->src) {
- // when the input buffer already contains the string
- // we have nothing more to provide
- target->length = 0;
- } else {
- target->ptr = src->src;
- target->length = src->data_size;
+#ifndef CX_PROPERTIES_LOAD_FILL_SIZE
+#define CX_PROPERTIES_LOAD_FILL_SIZE 1024
+#endif
+const unsigned cx_properties_load_fill_size = CX_PROPERTIES_LOAD_FILL_SIZE;
+#ifndef CX_PROPERTIES_LOAD_BUF_SIZE
+#define CX_PROPERTIES_LOAD_BUF_SIZE 256
+#endif
+const unsigned cx_properties_load_buf_size = CX_PROPERTIES_LOAD_BUF_SIZE;
+
+CxPropertiesStatus cx_properties_load(const CxAllocator *allocator,
+ cxstring filename, CxMap *target, CxPropertiesConfig config) {
+ if (allocator == NULL) {
+ allocator = cxDefaultAllocator;
}
- return 0;
-}
-
-static int cx_properties_read_file(
- cx_attr_unused CxProperties *prop,
- CxPropertiesSource *src,
- cxstring *target
-) {
- target->ptr = src->data_ptr;
- target->length = fread(src->data_ptr, 1, src->data_size, src->src);
- return ferror((FILE*)src->src);
-}
-
-static int cx_properties_read_init_file(
- cx_attr_unused CxProperties *prop,
- CxPropertiesSource *src
-) {
- src->data_ptr = cxMallocDefault(src->data_size);
- if (src->data_ptr == NULL) return 1;
- return 0;
-}
-static void cx_properties_read_clean_file(
- cx_attr_unused CxProperties *prop,
- CxPropertiesSource *src
-) {
- cxFreeDefault(src->data_ptr);
-}
-
-CxPropertiesSource cxPropertiesStringSource(cxstring str) {
- CxPropertiesSource src;
- src.src = (void*) str.ptr;
- src.data_size = str.length;
- src.data_ptr = NULL;
- src.read_func = cx_properties_read_string;
- src.read_init_func = NULL;
- src.read_clean_func = NULL;
- return src;
-}
-
-CxPropertiesSource cxPropertiesCstrnSource(const char *str, size_t len) {
- CxPropertiesSource src;
- src.src = (void*) str;
- src.data_size = len;
- src.data_ptr = NULL;
- src.read_func = cx_properties_read_string;
- src.read_init_func = NULL;
- src.read_clean_func = NULL;
- return src;
-}
-
-CxPropertiesSource cxPropertiesCstrSource(const char *str) {
- CxPropertiesSource src;
- src.src = (void*) str;
- src.data_size = strlen(str);
- src.data_ptr = NULL;
- src.read_func = cx_properties_read_string;
- src.read_init_func = NULL;
- src.read_clean_func = NULL;
- return src;
-}
-
-CxPropertiesSource cxPropertiesFileSource(FILE *file, size_t chunk_size) {
- CxPropertiesSource src;
- src.src = file;
- src.data_size = chunk_size;
- src.data_ptr = NULL;
- src.read_func = cx_properties_read_file;
- src.read_init_func = cx_properties_read_init_file;
- src.read_clean_func = cx_properties_read_clean_file;
- return src;
-}
+ // sanity check for the map
+ const bool use_cstring = cxCollectionStoresPointers(target);
+ if (!use_cstring && cxCollectionElementSize(target) != sizeof(cxmutstr)) {
+ return CX_PROPERTIES_MAP_ERROR;
+ }
-CxPropertiesStatus cxPropertiesLoad(
- CxProperties *prop,
- CxPropertiesSink sink,
- CxPropertiesSource source
-) {
- assert(source.read_func != NULL);
- assert(sink.sink_func != NULL);
+ // create a duplicate to guarantee zero-termination
+ cxmutstr fname = cx_strdup(filename);
+ if (fname.ptr == NULL) {
+ return CX_PROPERTIES_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE
+ }
- // initialize reader
- if (source.read_init_func != NULL) {
- if (source.read_init_func(prop, &source)) {
- return CX_PROPERTIES_READ_INIT_FAILED; // LCOV_EXCL_LINE
- }
+ // open the file
+ FILE *f = fopen(fname.ptr, "r");
+ if (f == NULL) {
+ cx_strfree(&fname);
+ return CX_PROPERTIES_FILE_ERROR;
}
- // transfer the data from the source to the sink
+ // initialize the parser
+ char linebuf[cx_properties_load_buf_size];
+ char fillbuf[cx_properties_load_fill_size];
CxPropertiesStatus status;
- CxPropertiesStatus kv_status = CX_PROPERTIES_NO_DATA;
- bool found = false;
+ CxProperties parser;
+ cxPropertiesInit(&parser, config);
+ cxPropertiesUseStack(&parser, linebuf, cx_properties_load_buf_size);
+
+ // read/fill/parse loop
+ status = CX_PROPERTIES_NO_DATA;
+ size_t keys_found = 0;
while (true) {
- // read input
- cxstring input;
- if (source.read_func(prop, &source, &input)) { // LCOV_EXCL_START
- status = CX_PROPERTIES_READ_FAILED;
+ size_t r = fread(fillbuf, 1, cx_properties_load_fill_size, f);
+ if (ferror(f)) {
+ status = CX_PROPERTIES_FILE_ERROR;
break;
- } // LCOV_EXCL_STOP
-
- // no more data - break
- if (input.length == 0) {
- if (found) {
- // something was found, check the last kv_status
- if (kv_status == CX_PROPERTIES_INCOMPLETE_DATA) {
- status = CX_PROPERTIES_INCOMPLETE_DATA;
- } else {
- status = CX_PROPERTIES_NO_ERROR;
- }
- } else {
- // nothing found
- status = CX_PROPERTIES_NO_DATA;
- }
+ }
+ if (r == 0) {
break;
}
-
- // set the input buffer and read the k/v-pairs
- cxPropertiesFill(prop, input);
-
- do {
- cxstring key, value;
- kv_status = cxPropertiesNext(prop, &key, &value);
- if (kv_status == CX_PROPERTIES_NO_ERROR) {
- found = true;
- if (sink.sink_func(prop, &sink, key, value)) {
- kv_status = CX_PROPERTIES_SINK_FAILED; // LCOV_EXCL_LINE
+ if (cxPropertiesFilln(&parser, fillbuf, r)) {
+ status = CX_PROPERTIES_BUFFER_ALLOC_FAILED;
+ break;
+ }
+ cxstring key, value;
+ while (true) {
+ status = cxPropertiesNext(&parser, &key, &value);
+ if (status != CX_PROPERTIES_NO_ERROR) {
+ break;
+ } else {
+ cxmutstr v = cx_strdup_a(allocator, value);
+ if (v.ptr == NULL) {
+ status = CX_PROPERTIES_MAP_ERROR;
+ break;
+ }
+ void *mv = use_cstring ? (void*)v.ptr : &v;
+ if (cxMapPut(target, key, mv)) {
+ cx_strfree(&v);
+ status = CX_PROPERTIES_MAP_ERROR;
+ break;
}
+ keys_found++;
}
- } while (kv_status == CX_PROPERTIES_NO_ERROR);
-
- if (kv_status > CX_PROPERTIES_OK) {
- status = kv_status;
+ }
+ if (status > CX_PROPERTIES_OK) {
break;
}
}
- if (source.read_clean_func != NULL) {
- source.read_clean_func(prop, &source);
+ // cleanup and exit
+ fclose(f);
+ cxPropertiesDestroy(&parser);
+ cx_strfree(&fname);
+ if (status == CX_PROPERTIES_NO_DATA && keys_found > 0) {
+ return CX_PROPERTIES_NO_ERROR;
+ } else {
+ return status;
}
-
- return status;
}
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
-#ifdef MEMRCHR_NEED_GNU
+
+#ifdef WITH_MEMRCHR
#define _GNU_SOURCE
#endif
#define cx_strcasecmp_impl strncasecmp
#endif
-cxmutstr cx_mutstr(char *cstring) {
- return (cxmutstr) {cstring, cstring == NULL ? 0 : strlen(cstring)};
-}
-
-cxmutstr cx_mutstrn(
- char *cstring,
- size_t length
-) {
- return (cxmutstr) {cstring, length};
-}
-
-cxstring cx_str(const char *cstring) {
- return (cxstring) {cstring, cstring == NULL ? 0 : strlen(cstring)};
-}
-
-cxstring cx_strn(
- const char *cstring,
- size_t length
-) {
- return (cxstring) {cstring, length};
-}
-
void cx_strfree(cxmutstr *str) {
if (str == NULL) return;
cxFreeDefault(str->ptr);
str->length = 0;
}
-int cx_strcpy_a(
+int cx_strcpy_a_(
const CxAllocator *alloc,
cxmutstr *dest,
cxstring src
#endif
const unsigned cx_strstr_sbo_size = CX_STRSTR_SBO_SIZE;
-cxstring cx_strstr(
- cxstring haystack,
- cxstring needle
-) {
+cxstring cx_strstr_(cxstring haystack, cxstring needle) {
if (needle.length == 0) {
return haystack;
}
return result;
}
-cxmutstr cx_strstr_m(
+cxmutstr cx_strstr_m_(
cxmutstr haystack,
cxstring needle
) {
return (cxmutstr) {(char *) result.ptr, result.length};
}
-size_t cx_strsplit(
+size_t cx_strsplit_(
cxstring string,
cxstring delim,
size_t limit,
return n;
}
-size_t cx_strsplit_a(
+size_t cx_strsplit_a_(
const CxAllocator *allocator,
cxstring string,
cxstring delim,
}
}
*output = cxCalloc(allocator, n, sizeof(cxstring));
- return cx_strsplit(string, delim, n, *output);
+ return cx_strsplit_(string, delim, n, *output);
}
-size_t cx_strsplit_m(
+size_t cx_strsplit_m_(
cxmutstr string,
cxstring delim,
size_t limit,
cxmutstr *output
) {
- return cx_strsplit(cx_strcast(string),
+ return cx_strsplit_(cx_strcast(string),
delim, limit, (cxstring *) output);
}
-size_t cx_strsplit_ma(
+size_t cx_strsplit_ma_(
const CxAllocator *allocator,
cxmutstr string,
cxstring delim,
size_t limit,
cxmutstr **output
) {
- return cx_strsplit_a(allocator, cx_strcast(string),
+ return cx_strsplit_a_(allocator, cx_strcast(string),
delim, limit, (cxstring **) output);
}
#endif
}
-cxmutstr cx_strreplacen_a(
+cxmutstr cx_strreplace_(
const CxAllocator *allocator,
cxstring str,
cxstring search,
#include "cx/tree.h"
-#include "cx/array_list.h"
-
#include <assert.h>
#define CX_TREE_PTR(cur, off) (*(void**)(((char*)(cur))+(off)))
}
} else {
// node has children, push the first child onto the stack and enter it
- cx_array_simple_add(iter->stack, children);
+ if (iter->stack_size >= iter->stack_capacity) {
+ const size_t newcap = iter->stack_capacity + 8;
+ if (cxReallocArrayDefault(&iter->stack, newcap, sizeof(void*))) {
+ // we cannot return an error in this function
+ abort(); // LCOV_EXCL_LINE
+ }
+ iter->stack_capacity = newcap;
+ }
+ iter->stack[iter->stack_size] = children;
+ iter->stack_size++;
iter->node = children;
iter->counter++;
}
}
// otherwise, create iterator and hand over to other function
- CxIterator iter = cxIterator(src, elem_size, num, false);
+ CxIterator iter = cxIterator(src, elem_size, num);
return cx_tree_add_iter(cxIteratorRef(iter), num, sfunc,
cfunc, cdata, failed, root,
loc_parent, loc_children, loc_last_child,
if (node == NULL) return 1; // LCOV_EXCL_LINE
cx_tree_zero_pointers(node, cx_tree_node_layout(tree));
tree->root = node;
- tree->size = 1;
+ tree->collection.size = 1;
return 0;
}
int result = cx_tree_add(data, tree->search, tree->node_create,
tree, &node, tree->root, cx_tree_node_layout(tree));
if (0 == result) {
- tree->size++;
+ tree->collection.size++;
} else {
- cxFree(tree->allocator, node);
+ cxFree(tree->collection.allocator, node);
}
return result;
}
void *failed;
ins += cx_tree_add_iter(iter, n, tree->search, tree->node_create,
tree, &failed, tree->root, cx_tree_node_layout(tree));
- tree->size += ins;
+ tree->collection.size += ins;
if (ins < n) {
- cxFree(tree->allocator, failed);
+ cxFree(tree->collection.allocator, failed);
}
return ins;
}
assert(search_func != NULL);
assert(search_data_func != NULL);
- CxTree *tree = cxMalloc(allocator, sizeof(CxTree));
+ CxTree *tree = cxZalloc(allocator, sizeof(CxTree));
if (tree == NULL) return NULL; // LCOV_EXCL_LINE
tree->cl = &cx_tree_default_class;
- tree->allocator = allocator;
+ tree->collection.allocator = allocator;
tree->node_create = create_func;
tree->search = search_func;
tree->search_data = search_data_func;
- tree->simple_destructor = NULL;
- tree->advanced_destructor = (cx_destructor_func2) cxFree;
- tree->destructor_data = (void *) allocator;
+ tree->collection.advanced_destructor = (cx_destructor_func2) cxFree;
+ tree->collection.destructor_data = (void *) allocator;
tree->loc_parent = loc_parent;
tree->loc_children = loc_children;
tree->loc_last_child = loc_last_child;
tree->loc_prev = loc_prev;
tree->loc_next = loc_next;
- tree->root = NULL;
- tree->size = 0;
return tree;
}
if (tree->root != NULL) {
cxTreeClear(tree);
}
- cxFree(tree->allocator, tree);
+ cxFree(tree->collection.allocator, tree);
}
CxTree *cxTreeCreateWrapped(const CxAllocator *allocator, void *root,
}
assert(root != NULL);
- CxTree *tree = cxMalloc(allocator, sizeof(CxTree));
+ CxTree *tree = cxZalloc(allocator, sizeof(CxTree));
if (tree == NULL) return NULL; // LCOV_EXCL_LINE
tree->cl = &cx_tree_default_class;
// set the allocator anyway, just in case...
- tree->allocator = allocator;
- tree->node_create = NULL;
- tree->search = NULL;
- tree->search_data = NULL;
- tree->simple_destructor = NULL;
- tree->advanced_destructor = NULL;
- tree->destructor_data = NULL;
+ tree->collection.allocator = allocator;
tree->loc_parent = loc_parent;
tree->loc_children = loc_children;
tree->loc_last_child = loc_last_child;
tree->loc_prev = loc_prev;
tree->loc_next = loc_next;
tree->root = root;
- tree->size = cxTreeSubtreeSize(tree, root);
+ tree->collection.size = cxTreeSubtreeSize(tree, root);
return tree;
}
void cxTreeSetParent(CxTree *tree, void *parent, void *child) {
size_t loc_parent = tree->loc_parent;
if (tree_parent(child) == NULL) {
- tree->size++;
+ tree->collection.size++;
}
cx_tree_link(parent, child, cx_tree_node_layout(tree));
}
void cxTreeAddChildNode(CxTree *tree, void *parent, void *child) {
cx_tree_link(parent, child, cx_tree_node_layout(tree));
- tree->size++;
+ tree->collection.size++;
}
int cxTreeAddChild(CxTree *tree, void *parent, const void *data) {
if (node == NULL) return 1; // LCOV_EXCL_LINE
cx_tree_zero_pointers(node, cx_tree_node_layout(tree));
cx_tree_link(parent, node, cx_tree_node_layout(tree));
- tree->size++;
+ tree->collection.size++;
return 0;
}
size_t cxTreeInsertArray(CxTree *tree, const void *data, size_t elem_size, size_t n) {
if (n == 0) return 0;
if (n == 1) return 0 == cxTreeInsert(tree, data) ? 1 : 0;
- CxIterator iter = cxIterator(data, elem_size, n, false);
+ CxIterator iter = cxIterator(data, elem_size, n);
return cxTreeInsertIter(tree, cxIteratorRef(iter), n);
}
}
size_t cxTreeSize(CxTree *tree) {
- return tree->size;
+ return tree->collection.size;
}
size_t cxTreeDepth(CxTree *tree) {
if (loc_last_child >= 0) tree_last_child(node) = NULL;
// the tree now has one member less
- tree->size--;
+ tree->collection.size--;
return 0;
}
void cxTreeRemoveSubtree(CxTree *tree, void *node) {
if (node == tree->root) {
tree->root = NULL;
- tree->size = 0;
+ tree->collection.size = 0;
return;
}
size_t subtree_size = cxTreeSubtreeSize(tree, node);
cx_tree_unlink(node, cx_tree_node_layout(tree));
- tree->size -= subtree_size;
+ tree->collection.size -= subtree_size;
}
int cxTreeDestroyNode(
) {
int result = cxTreeRemoveNode(tree, node, relink_func);
if (result == 0) {
- if (tree->simple_destructor) {
- tree->simple_destructor(node);
- }
- if (tree->advanced_destructor) {
- tree->advanced_destructor(tree->destructor_data, node);
- }
+ cx_invoke_destructor(tree, node);
return 0;
} else {
return result;
);
cx_foreach(void *, child, iter) {
if (iter.exiting) {
- if (tree->simple_destructor) {
- tree->simple_destructor(child);
- }
- if (tree->advanced_destructor) {
- tree->advanced_destructor(tree->destructor_data, child);
- }
+ cx_invoke_destructor(tree, child);
}
}
- tree->size -= iter.counter;
+ tree->collection.size -= iter.counter;
if (node == tree->root) {
tree->root = NULL;
}
*/
#import "toolkit.h"
-#import "../ui/tree.h"
+#import "../ui/list.h"
@interface ListDataSource : NSObject <NSTableViewDataSource>
- (id) init:(NSArray<NSTableColumn*>*) columns var:(UiVar*)var getvalue:(ui_getvaluefunc2) getvaluefunc getvaluedata:(void*)userdata;
@end
+
+@interface ArrayDataSource : NSObject <NSTableViewDataSource>
+
+@property NSMutableArray<NSString*> *data;
+
+- (id)init:(char**)elements size:(size_t)nelm;
+
+@end
}
@end
+
+@implementation ArrayDataSource
+
+- (id)init:(char**)elements size:(size_t)nelm {
+ _data = [[NSMutableArray alloc]init];
+ for(int i=0;i<nelm;i++) {
+ NSString *s = [[NSString alloc]initWithUTF8String:elements[i]];
+ _data[i] = s;
+ }
+ return self;
+}
+
+- (NSInteger) numberOfRowsInTableView:(NSTableView *) tableView {
+ return _data.count;
+}
+
+- (id) tableView:(NSTableView *) tableView
+objectValueForTableColumn:(NSTableColumn *) tableColumn
+ row:(NSInteger) row
+{
+ return _data[row];
+}
+
+@end
static char* create_linkbutton_jsonvalue(const char *label, const char *uri, UiBool include_null, UiBool visited, UiBool set_visited) {
CxJsonValue *obj = cxJsonCreateObj(NULL);
if(label) {
- cxJsonObjPutString(obj, CX_STR("label"), label);
+ cxJsonObjPutString(obj, cx_str("label"), label);
} else if(include_null) {
- cxJsonObjPutLiteral(obj, CX_STR("label"), CX_JSON_NULL);
+ cxJsonObjPutLiteral(obj, cx_str("label"), CX_JSON_NULL);
}
if(uri) {
- cxJsonObjPutString(obj, CX_STR("uri"), uri);
+ cxJsonObjPutString(obj, cx_str("uri"), uri);
} else if(include_null) {
- cxJsonObjPutLiteral(obj, CX_STR("uri"), CX_JSON_NULL);
+ cxJsonObjPutLiteral(obj, cx_str("uri"), CX_JSON_NULL);
}
if(set_visited) {
- cxJsonObjPutLiteral(obj, CX_STR("visited"), visited ? CX_JSON_TRUE : CX_JSON_FALSE);
+ cxJsonObjPutLiteral(obj, cx_str("visited"), visited ? CX_JSON_TRUE : CX_JSON_FALSE);
}
CxJsonWriter writer = cxJsonWriterCompact();
#include "Container.h"
+typedef struct UiImage {
+ void *nsimage;
+ unsigned int ref;
+} UiImage;
+
void ui_icon_init(void);
NSImage* ui_cocoa_named_icon(const char *name);
return [NSImage imageNamed:imageName];
}
+static UIIMAGE create_image(NSImage *image) {
+ UiImage *img = malloc(sizeof(UiImage));
+ img->nsimage = (__bridge_retained void*)image;
+ img->ref = 1;
+ return img;
+}
void ui_image_ref(UIIMAGE img) {
- // TODO
+ UiImage *image = img;
+ image->ref++;
}
void ui_image_unref(UIIMAGE img) {
- // TODO
+ UiImage *image = img;
+ if(--image->ref == 0) {
+ CFRelease(image->nsimage);
+ free(img);
+ }
+}
+
+static int load_image(UiGeneric *obj, NSImage *img) {
+ UIIMAGE image = create_image(img);
+
+ if(obj->set) {
+ obj->set(obj, image, UI_IMAGE_OBJECT_TYPE);
+ ui_image_unref(image);
+ } else {
+ obj->value = image;
+ obj->type = UI_IMAGE_OBJECT_TYPE;
+ }
+
+ return 0;
+}
+
+int ui_image_load_file(UiGeneric *obj, const char *path) {
+ NSString *str = [[NSString alloc]initWithUTF8String:path];
+ NSImage *img = [[NSImage alloc]initWithContentsOfFile:str];
+ if(img == nil) {
+ return 1;
+ }
+ return load_image(obj, img);
+}
+
+int ui_image_load_data(UiGeneric *obj, const void *imgdata, size_t size) {
+ NSData *data = [NSData dataWithBytes:(void*)imgdata length:size];
+ NSImage *img = [[NSImage alloc] initWithData:data];
+ if(img == nil) {
+ return 1;
+ }
+ return load_image(obj, img);
}
#import "toolkit.h"
#import "Container.h"
-#import "../ui/tree.h"
+#import "../ui/list.h"
#import "ListDataSource.h"
add_listdelegate(obj, tableview, args);
+ char **static_elements = args->static_elements;
+ size_t static_nelm = args->static_nelm;
UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST);
if(var) {
UiList *list = var->value;
tableview.dataSource = dataSource;
[tableview reloadData];
+ objc_setAssociatedObject(tableview, "ui_datasource", dataSource, OBJC_ASSOCIATION_RETAIN);
+ } else if(static_elements && static_nelm) {
+ NSTableColumn *column = [[NSTableColumn alloc] initWithIdentifier:@"column"];
+ [tableview addTableColumn:column];
+
+ ArrayDataSource *dataSource = [[ArrayDataSource alloc]init:static_elements size:static_nelm];
+ tableview.dataSource = dataSource;
+ [tableview reloadData];
+
objc_setAssociatedObject(tableview, "ui_datasource", dataSource, OBJC_ASSOCIATION_RETAIN);
}
ListDataSource *dataSource = [[ListDataSource alloc] init:cols var:var getvalue:getvalue getvaluedata:getvaluedata];
if(model) {
- dataSource.model = ui_model_copy(obj->ctx, model);
+ dataSource.model = model;
}
tableview.dataSource = dataSource;
@end
-UIWIDGET ui_combobox_create(UiObject* obj, UiListArgs *args) {
+UIWIDGET ui_dropdown_create(UiObject* obj, UiListArgs *args) {
NSComboBox *dropdown = [[NSComboBox alloc] init];
dropdown.editable = NO;
#include "../common/menu.h"
#include "../common/toolbar.h"
#include "../common/threadpool.h"
+#include "../common/app.h"
#import "image.h"
#import "menu.h"
static int app_argc;
static const char **app_argv;
-static ui_callback startup_func;
-static void *startup_data;
-static ui_callback open_func;
-static void *open_data;
-static ui_callback exit_func;
-static void *exit_data;
-
static UiBool exit_on_shutdown;
/* ------------------- App Init / Event Loop functions ------------------- */
return application_name;
}
-void ui_onstartup(ui_callback f, void *userdata) {
- startup_func = f;
- startup_data = userdata;
-}
-
-void ui_onopen(ui_callback f, void *userdata) {
- open_func = f;
- open_data = userdata;
-}
-
-void ui_onexit(ui_callback f, void *userdata) {
- exit_func = f;
- exit_data = userdata;
-}
-
void ui_app_exit_on_shutdown(UiBool exitapp) {
exit_on_shutdown = exitapp;
}
e.document = NULL;
e.eventdata = NULL;
e.intval = 0;
- if(startup_func) {
- startup_func(&e, startup_data);
- }
+ uic_application_startup(&e);
}
void ui_cocoa_onopen(const char *file) {
e.document = NULL;
e.eventdata = NULL;
e.intval = 0;
- if(open_func) {
- open_func(&e, open_data);
- }
+ uic_application_open(&e);
}
void ui_cocoa_onexit(void) {
e.document = NULL;
e.eventdata = NULL;
e.intval = 0;
- if(exit_func) {
- exit_func(&e, exit_data);
- }
+ uic_application_exit(&e);
}
void ui_main(void) {
return obj;
}
-UiObject* ui_window(const char *title, void *window_data) {
+UiObject* ui_window(const char *title) {
UiObject *obj = create_window(title, FALSE, FALSE, FALSE);
- obj->window = window_data;
return obj;
}
-UiObject* ui_simple_window(const char *title, void *window_data) {
+UiObject* ui_simple_window(const char *title) {
UiObject *obj = create_window(title, TRUE, FALSE, FALSE);
- obj->window = window_data;
return obj;
}
-UiObject* ui_sidebar_window(const char *title, void *window_data) {
+UiObject* ui_sidebar_window(const char *title) {
UiObject *obj = create_window(title, FALSE, TRUE, FALSE);
- obj->window = window_data;
return obj;
}
args->rbutton4 = strdup(label);
}
-void ui_dialogwindow_args_set_lbutton1_states(UiDialogWindowArgs *args, const int *states) {
- // TODO
+void ui_dialogwindow_args_set_lbutton1_states(UiDialogWindowArgs *args, const int *states, int numstates) {
+ args->lbutton1_states = calloc(numstates+1, sizeof(int));
+ memcpy((void*)args->lbutton1_states, states, numstates * sizeof(int));
+ ((int*)args->lbutton1_states)[numstates] = -1;
}
-void ui_dialogwindow_args_set_lbutton2_states(UiDialogWindowArgs *args, const int *states) {
- // TODO
+void ui_dialogwindow_args_set_lbutton2_states(UiDialogWindowArgs *args, const int *states, int numstates) {
+ args->lbutton2_states = calloc(numstates+1, sizeof(int));
+ memcpy((void*)args->lbutton2_states, states, numstates * sizeof(int));
+ ((int*)args->lbutton2_states)[numstates] = -1;
}
-void ui_dialogwindow_args_set_rbutton3_states(UiDialogWindowArgs *args, const int *states) {
- // TODO
+void ui_dialogwindow_args_set_rbutton3_states(UiDialogWindowArgs *args, const int *states, int numstates) {
+ args->rbutton3_states = calloc(numstates+1, sizeof(int));
+ memcpy((void*)args->rbutton3_states, states, numstates * sizeof(int));
+ ((int*)args->rbutton3_states)[numstates] = -1;
}
-void ui_dialogwindow_args_set_rbutton4_states(UiDialogWindowArgs *args, const int *states) {
- // TODO
+void ui_dialogwindow_args_set_rbutton4_states(UiDialogWindowArgs *args, const int *states, int numstates) {
+ args->rbutton4_states = calloc(numstates+1, sizeof(int));
+ memcpy((void*)args->rbutton4_states, states, numstates * sizeof(int));
+ ((int*)args->rbutton4_states)[numstates] = -1;
}
void ui_dialogwindow_args_set_default_button(UiDialogWindowArgs *args, int button) {
free((void*)args->lbutton2);
free((void*)args->rbutton3);
free((void*)args->rbutton4);
- free((void*)args->lbutton1_groups);
- free((void*)args->lbutton2_groups);
- free((void*)args->rbutton3_groups);
- free((void*)args->rbutton4_groups);
+ free((void*)args->lbutton1_states);
+ free((void*)args->lbutton2_states);
+ free((void*)args->rbutton3_states);
+ free((void*)args->rbutton4_states);
free(args);
}
args->onclickdata = onclickdata;
}
-void ui_toolbar_item_args_set_groups(UiToolbarItemArgs *args, int *states, int numstates) {
- args->groups = calloc(numstates+1, sizeof(int));
- memcpy((void*)args->groups, states, numstates * sizeof(int));
- ((int*)args->groups)[numstates] = -1;
+void ui_toolbar_item_args_set_states(UiToolbarItemArgs *args, int *states, int numstates) {
+ args->states = calloc(numstates+1, sizeof(int));
+ memcpy((void*)args->states, states, numstates * sizeof(int));
+ ((int*)args->states)[numstates] = -1;
}
void ui_toolbar_item_args_free(UiToolbarItemArgs *args) {
free((void*)args->label);
free((void*)args->icon);
free((void*)args->tooltip);
- free((void*)args->groups);
+ free((void*)args->states);
free(args);
}
args->onchangedata = onchangedata;
}
-void ui_toolbar_toggleitem_args_set_groups(UiToolbarToggleItemArgs *args,int *states, int numstates) {
- args->groups = calloc(numstates+1, sizeof(int));
- memcpy((void*)args->groups, states, numstates * sizeof(int));
- ((int*)args->groups)[numstates] = -1;
+void ui_toolbar_toggleitem_args_set_states(UiToolbarToggleItemArgs *args,int *states, int numstates) {
+ args->states = calloc(numstates+1, sizeof(int));
+ memcpy((void*)args->states, states, numstates * sizeof(int));
+ ((int*)args->states)[numstates] = -1;
}
void ui_toolbar_toggleitem_args_free(UiToolbarToggleItemArgs *args) {
free((void*)args->icon);
free((void*)args->tooltip);
free((void*)args->varname);
- free((void*)args->groups);
+ free((void*)args->states);
free(args);
}
args->onclickdata = onclickdata;
}
-void ui_button_args_set_groups(UiButtonArgs *args, int *states, int numstates) {
- args->groups = calloc(numstates+1, sizeof(int));
- memcpy((void*)args->groups, states, numstates * sizeof(int));
- ((int*)args->groups)[numstates] = -1;
+void ui_button_args_set_states(UiButtonArgs *args, int *states, int numstates) {
+ args->states = calloc(numstates+1, sizeof(int));
+ memcpy((void*)args->states, states, numstates * sizeof(int));
+ ((int*)args->states)[numstates] = -1;
}
void ui_button_args_free(UiButtonArgs *args) {
free((void*)args->label);
free((void*)args->icon);
free((void*)args->tooltip);
- free((void*)args->groups);
+ free((void*)args->states);
free(args);
}
args->value = value;
}
-void ui_toggle_args_set_enablegroup(UiToggleArgs *args, int group) {
- args->enable_group = group;
+void ui_toggle_args_set_enablestate(UiToggleArgs *args, int state) {
+ args->enable_state = state;
}
-void ui_toggle_args_set_groups(UiToggleArgs *args, int *states, int numstates) {
- args->groups = calloc(numstates+1, sizeof(int));
- memcpy((void*)args->groups, states, numstates * sizeof(int));
- ((int*)args->groups)[numstates] = -1;
+void ui_toggle_args_set_states(UiToggleArgs *args, int *states, int numstates) {
+ args->states = calloc(numstates+1, sizeof(int));
+ memcpy((void*)args->states, states, numstates * sizeof(int));
+ ((int*)args->states)[numstates] = -1;
}
void ui_toggle_args_free(UiToggleArgs *args) {
free((void*)args->icon);
free((void*)args->tooltip);
free((void*)args->varname);
- free((void*)args->groups);
+ free((void*)args->states);
free(args);
}
args->value = value;
}
-void ui_linkbutton_args_set_groups(UiLinkButtonArgs *args, int *states, int numstates) {
- args->groups = calloc(numstates+1, sizeof(int));
- memcpy((void*)args->groups, states, numstates * sizeof(int));
- ((int*)args->groups)[numstates] = -1;
+void ui_linkbutton_args_set_states(UiLinkButtonArgs *args, int *states, int numstates) {
+ args->states = calloc(numstates+1, sizeof(int));
+ memcpy((void*)args->states, states, numstates * sizeof(int));
+ ((int*)args->states)[numstates] = -1;
}
void ui_linkbutton_args_free(UiLinkButtonArgs *args) {
free((void*)args->label);
free((void*)args->uri);
free((void*)args->varname);
- free((void*)args->groups);
+ free((void*)args->states);
free(args);
}
args->contextmenu = menubuilder;
}
-void ui_list_args_set_groups(UiListArgs *args, int *states, int numstates) {
- args->groups = calloc(numstates+1, sizeof(int));
- memcpy((void*)args->groups, states, numstates * sizeof(int));
- ((int*)args->groups)[numstates] = -1;
+void ui_list_args_set_states(UiListArgs *args, int *states, int numstates) {
+ args->states = calloc(numstates+1, sizeof(int));
+ memcpy((void*)args->states, states, numstates * sizeof(int));
+ ((int*)args->states)[numstates] = -1;
}
void ui_list_args_free(UiListArgs *args) {
}
free(args->static_elements);
}
- free((void*)args->groups);
+ free((void*)args->states);
free(args);
}
free((void*)args->style_class);
free((void*)args->varname);
free((void*)args->sublists);
- free((void*)args->groups);
+ free((void*)args->states);
free(args);
}
args->value = value;
}
-void ui_textarea_args_set_groups(UiTextAreaArgs *args, int *states, int numstates) {
- args->groups = calloc(numstates+1, sizeof(int));
- memcpy((void*)args->groups, states, numstates * sizeof(int));
- ((int*)args->groups)[numstates] = -1;
+void ui_textarea_args_set_states(UiTextAreaArgs *args, int *states, int numstates) {
+ args->states = calloc(numstates+1, sizeof(int));
+ memcpy((void*)args->states, states, numstates * sizeof(int));
+ ((int*)args->states)[numstates] = -1;
}
void ui_textarea_args_free(UiTextAreaArgs *args) {
free((void*)args->name);
free((void*)args->style_class);
free((void*)args->varname);
- free((void*)args->groups);
+ free((void*)args->states);
free(args);
}
args->value = value;
}
-void ui_textfield_args_set_groups(UiTextFieldArgs *args, int *states, int numstates) {
- args->groups = calloc(numstates+1, sizeof(int));
- memcpy((void*)args->groups, states, numstates * sizeof(int));
- ((int*)args->groups)[numstates] = -1;
+void ui_textfield_args_set_states(UiTextFieldArgs *args, int *states, int numstates) {
+ args->states = calloc(numstates+1, sizeof(int));
+ memcpy((void*)args->states, states, numstates * sizeof(int));
+ ((int*)args->states)[numstates] = -1;
}
void ui_textfield_args_free(UiTextFieldArgs *args) {
free((void*)args->name);
free((void*)args->style_class);
free((void*)args->varname);
- free((void*)args->groups);
+ free((void*)args->states);
free(args);
}
args->rangevalue = value;
}
-void ui_spinbox_args_set_groups(UiSpinBoxArgs *args, int *states, int numstates) {
- args->groups = calloc(numstates+1, sizeof(int));
- memcpy((void*)args->groups, states, numstates * sizeof(int));
- ((int*)args->groups)[numstates] = -1;
+void ui_spinbox_args_set_states(UiSpinBoxArgs *args, int *states, int numstates) {
+ args->states = calloc(numstates+1, sizeof(int));
+ memcpy((void*)args->states, states, numstates * sizeof(int));
+ ((int*)args->states)[numstates] = -1;
}
void ui_spinbox_args_free(UiSpinBoxArgs *args) {
free((void*)args->name);
free((void*)args->style_class);
free((void*)args->varname);
- free((void*)args->groups);
+ free((void*)args->states);
free(args);
}
args->value = value;
}
-void ui_webview_args_set_groups(UiWebviewArgs *args, int *states, int numstates) {
- args->groups = calloc(numstates+1, sizeof(int));
- memcpy((void*)args->groups, states, numstates * sizeof(int));
- ((int*)args->groups)[numstates] = -1;
+void ui_webview_args_set_states(UiWebviewArgs *args, int *states, int numstates) {
+ args->states = calloc(numstates+1, sizeof(int));
+ memcpy((void*)args->states, states, numstates * sizeof(int));
+ ((int*)args->states)[numstates] = -1;
}
void ui_webview_args_free(UiWebviewArgs *args) {
free((void*)args->name);
free((void*)args->style_class);
free((void*)args->varname);
- free((void*)args->groups);
+ free((void*)args->states);
free(args);
}
#include "../ui/entry.h"
#include "../ui/menu.h"
#include "../ui/toolbar.h"
-#include "../ui/tree.h"
+#include "../ui/list.h"
#include "../ui/text.h"
#include "../ui/webview.h"
#include "../ui/widget.h"
UIEXPORT void ui_dialogwindow_args_set_lbutton2(UiDialogWindowArgs *args, const char *label);
UIEXPORT void ui_dialogwindow_args_set_rbutton3(UiDialogWindowArgs *args, const char *label);
UIEXPORT void ui_dialogwindow_args_set_rbutton4(UiDialogWindowArgs *args, const char *label);
-UIEXPORT void ui_dialogwindow_args_set_lbutton1_states(UiDialogWindowArgs *args, const int *states);
-UIEXPORT void ui_dialogwindow_args_set_lbutton2_states(UiDialogWindowArgs *args, const int *states);
-UIEXPORT void ui_dialogwindow_args_set_rbutton3_states(UiDialogWindowArgs *args, const int *states);
-UIEXPORT void ui_dialogwindow_args_set_rbutton4_states(UiDialogWindowArgs *args, const int *states);
+UIEXPORT void ui_dialogwindow_args_set_lbutton1_states(UiDialogWindowArgs *args, const int *states, int numstates);
+UIEXPORT void ui_dialogwindow_args_set_lbutton2_states(UiDialogWindowArgs *args, const int *states, int numstates);
+UIEXPORT void ui_dialogwindow_args_set_rbutton3_states(UiDialogWindowArgs *args, const int *states, int numstates);
+UIEXPORT void ui_dialogwindow_args_set_rbutton4_states(UiDialogWindowArgs *args, const int *states, int numstates);
UIEXPORT void ui_dialogwindow_args_set_default_button(UiDialogWindowArgs *args, int button);
UIEXPORT void ui_dialogwindow_args_set_width(UiDialogWindowArgs *args, int width);
UIEXPORT void ui_dialogwindow_args_set_height(UiDialogWindowArgs *args, int height);
UIEXPORT void ui_toolbar_item_args_set_tooltip(UiToolbarItemArgs *args, const char *tooltip);
UIEXPORT void ui_toolbar_item_args_set_onclick(UiToolbarItemArgs *args, ui_callback callback);
UIEXPORT void ui_toolbar_item_args_set_onclickdata(UiToolbarItemArgs *args, void *onclickdata);
-UIEXPORT void ui_toolbar_item_args_set_groups(UiToolbarItemArgs *args, int *states, int numstates);
+UIEXPORT void ui_toolbar_item_args_set_states(UiToolbarItemArgs *args, int *states, int numstates);
UIEXPORT void ui_toolbar_item_args_free(UiToolbarItemArgs *args);
UIEXPORT UiToolbarToggleItemArgs* ui_toolbar_toggleitem_args_new(void);
UIEXPORT void ui_toolbar_toggleitem_args_set_varname(UiToolbarToggleItemArgs *args, const char *varname);
UIEXPORT void ui_toolbar_toggleitem_args_set_onchange(UiToolbarToggleItemArgs *args, ui_callback callback);
UIEXPORT void ui_toolbar_toggleitem_args_set_onchangedata(UiToolbarToggleItemArgs *args, void *onchangedata);
-UIEXPORT void ui_toolbar_toggleitem_args_set_groups(UiToolbarToggleItemArgs *args, int *states, int numstates);
+UIEXPORT void ui_toolbar_toggleitem_args_set_states(UiToolbarToggleItemArgs *args, int *states, int numstates);
UIEXPORT void ui_toolbar_toggleitem_args_free(UiToolbarToggleItemArgs *args);
UIEXPORT UiToolbarMenuArgs* ui_toolbar_menu_args_new(void);
UIEXPORT void ui_container_args_set_margin_left(UiContainerArgs *args, int value);
UIEXPORT void ui_container_args_set_margin_right(UiContainerArgs *args, int value);
UIEXPORT void ui_container_args_set_margin_top(UiContainerArgs *args, int value);
-UIEXPORT void ui_container_args_set_margin_right(UiContainerArgs *args, int value);
+UIEXPORT void ui_container_args_set_margin_bottom(UiContainerArgs *args, int value);
UIEXPORT void ui_container_args_set_colspan(UiContainerArgs *args, int colspan);
UIEXPORT void ui_container_args_set_rowspan(UiContainerArgs *args, int rowspan);
UIEXPORT void ui_container_args_set_def_hexpand(UiContainerArgs *args, UiBool value);
UIEXPORT void ui_button_args_set_labeltype(UiButtonArgs *args, int labeltype);
UIEXPORT void ui_button_args_set_onclick(UiButtonArgs *args, ui_callback callback);
UIEXPORT void ui_button_args_set_onclickdata(UiButtonArgs *args, void *onclickdata);
-UIEXPORT void ui_button_args_set_groups(UiButtonArgs *args, int *states, int numstates);
+UIEXPORT void ui_button_args_set_states(UiButtonArgs *args, int *states, int numstates);
UIEXPORT void ui_button_args_free(UiButtonArgs *args);
UIEXPORT UiToggleArgs* ui_toggle_args_new(void);
UIEXPORT void ui_toggle_args_set_onchangedata(UiToggleArgs *args, void *onchangedata);
UIEXPORT void ui_toggle_args_set_varname(UiToggleArgs *args, const char *varname);
UIEXPORT void ui_toggle_args_set_value(UiToggleArgs *args, UiInteger *value);
-UIEXPORT void ui_toggle_args_set_enablegroup(UiToggleArgs *args, int group);
-UIEXPORT void ui_toggle_args_set_groups(UiToggleArgs *args, int *states, int numstates);
+UIEXPORT void ui_toggle_args_set_enablestate(UiToggleArgs *args, int state);
+UIEXPORT void ui_toggle_args_set_states(UiToggleArgs *args, int *states, int numstates);
UIEXPORT void ui_toggle_args_free(UiToggleArgs *args);
UIEXPORT UiLinkButtonArgs* ui_linkbutton_args_new(void);
UIEXPORT void ui_linkbutton_args_set_onclickdata(UiLinkButtonArgs *args, void *userdata);
UIEXPORT void ui_linkbutton_args_set_nofollow(UiLinkButtonArgs *args, UiBool value);
UIEXPORT void ui_linkbutton_args_set_type(UiLinkButtonArgs *args, UiLinkType type);
-UIEXPORT void ui_linkbutton_args_set_groups(UiLinkButtonArgs *args, int *states, int numstates);
+UIEXPORT void ui_linkbutton_args_set_states(UiLinkButtonArgs *args, int *states, int numstates);
UIEXPORT void ui_linkbutton_args_free(UiLinkButtonArgs *args);
UIEXPORT UiListArgs* ui_list_args_new(void);
UIEXPORT void ui_list_args_set_onsavedata(UiListArgs *args, void *userdata);
UIEXPORT void ui_list_args_set_multiselection(UiListArgs *args, UiBool multiselection);
UIEXPORT void ui_list_args_set_contextmenu(UiListArgs *args, UiMenuBuilder *menubuilder);
-UIEXPORT void ui_list_args_set_groups(UiListArgs *args, int *states, int numstates);
+UIEXPORT void ui_list_args_set_states(UiListArgs *args, int *states, int numstates);
UIEXPORT void ui_list_args_free(UiListArgs *args);
UIEXPORT UiSourceListArgs* ui_sourcelist_args_new(void);
UIEXPORT void ui_textarea_args_set_onchangedata(UiTextAreaArgs *args, void *onchangedata);
UIEXPORT void ui_textarea_args_set_varname(UiTextAreaArgs *args, const char *varname);
UIEXPORT void ui_textarea_args_set_value(UiTextAreaArgs *args, UiText *value);
-UIEXPORT void ui_textarea_args_set_groups(UiTextAreaArgs *args, int *states, int numstates);
+UIEXPORT void ui_textarea_args_set_states(UiTextAreaArgs *args, int *states, int numstates);
UIEXPORT void ui_textarea_args_free(UiTextAreaArgs *args);
UIEXPORT UiTextFieldArgs* ui_textfield_args_new(void);
UIEXPORT void ui_textfield_args_set_onactivatedata(UiTextFieldArgs *args, void *onactivatedata);
UIEXPORT void ui_textfield_args_set_varname(UiTextFieldArgs *args, const char *varname);
UIEXPORT void ui_textfield_args_set_value(UiTextFieldArgs *args, UiString *value);
-UIEXPORT void ui_textfield_args_set_groups(UiTextFieldArgs *args, int *states, int numstates);
+UIEXPORT void ui_textfield_args_set_states(UiTextFieldArgs *args, int *states, int numstates);
UIEXPORT void ui_textfield_args_free(UiTextFieldArgs *args);
UIEXPORT UiSpinBoxArgs* ui_spinbox_args_new(void);
UIEXPORT void ui_spinbox_args_set_intvalue(UiSpinBoxArgs *args, UiInteger *value);
UIEXPORT void ui_spinbox_args_set_doublevalue(UiSpinBoxArgs *args, UiDouble *value);
UIEXPORT void ui_spinbox_args_set_rangevalue(UiSpinBoxArgs *args, UiRange *value);
-UIEXPORT void ui_spinbox_args_set_groups(UiSpinBoxArgs *args, int *states, int numstates);
+UIEXPORT void ui_spinbox_args_set_states(UiSpinBoxArgs *args, int *states, int numstates);
UIEXPORT void ui_spinbox_args_free(UiSpinBoxArgs *args);
UIEXPORT UiWebviewArgs* ui_webview_args_new(void);
UIEXPORT void ui_webview_args_set_style_class(UiWebviewArgs *args, const char *classname);
UIEXPORT void ui_webview_args_set_varname(UiWebviewArgs *args, const char *varname);
UIEXPORT void ui_webview_args_set_value(UiWebviewArgs *args, UiGeneric *value);
-UIEXPORT void ui_webview_args_set_groups(UiWebviewArgs *args, int *states, int numstates);
+UIEXPORT void ui_webview_args_set_states(UiWebviewArgs *args, int *states, int numstates);
UIEXPORT void ui_webview_args_free(UiWebviewArgs *args);
#ifdef __cplusplus
memset(ctx, 0, sizeof(UiContext));
ctx->mp = mp;
ctx->allocator = mp->allocator;
- ctx->destroy_handler = cxArrayListCreate(ctx->allocator, NULL, sizeof(UiDestroyHandler), 16);
+ ctx->destroy_handler = cxArrayListCreate(ctx->allocator, sizeof(UiDestroyHandler), 16);
ctx->obj = toplevel;
ctx->vars = cxHashMapCreate(mp->allocator, CX_STORE_POINTERS, 16);
- ctx->documents = cxLinkedListCreate(mp->allocator, cx_cmp_ptr, CX_STORE_POINTERS);
- ctx->group_widgets = cxLinkedListCreate(mp->allocator, cx_cmp_ptr, sizeof(UiGroupWidget));
- ctx->groups = cxArrayListCreate(mp->allocator, cx_cmp_int, sizeof(int), 32);
+ ctx->documents = cxLinkedListCreate(mp->allocator, CX_STORE_POINTERS);
+ ctx->state_widgets = cxLinkedListCreate(mp->allocator, sizeof(UiStateWidget));
+ ctx->states = cxArrayListCreate(mp->allocator, sizeof(int), 32);
+ cxSetCompareFunc(ctx->states, cx_cmp_int);
ctx->attach_document = uic_context_attach_document;
ctx->detach_document2 = uic_context_detach_document;
}
void uic_context_prepare_close(UiContext *ctx) {
- cxListClear(ctx->groups);
- cxListClear(ctx->group_widgets);
+ cxListClear(ctx->states);
+ cxListClear(ctx->state_widgets);
}
void uic_context_attach_document(UiContext *ctx, void *document) {
+ if(ctx->single_document_mode) {
+ if(ctx->document) {
+ uic_context_detach_document(ctx, ctx->document);
+ }
+ }
+
cxListAdd(ctx->documents, document);
ctx->document = document;
void uic_context_detach_all(UiContext *ctx) {
// copy list
- CxList *ls = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS);
+ CxList *ls = cxLinkedListCreate(cxDefaultAllocator, CX_STORE_POINTERS);
CxIterator i = cxListIterator(ctx->documents);
cx_foreach(void *, doc, i) {
cxListAdd(ls, doc);
return ctx_getvar(ctx, key);
}
+UiVar* uic_get_var_t(UiContext *ctx,const char *name, UiVarType type) {
+ UiVar *var = uic_get_var(ctx, name);
+ if(var && var->type == type) {
+ return var;
+ }
+ return NULL;
+}
+
UiVar* uic_create_var(UiContext *ctx, const char *name, UiVarType type) {
UiVar *var = uic_get_var(ctx, name);
if(var) {
// public API
+void* ui_context_get_document(UiContext *ctx) {
+ return ctx->document;
+}
+
+void ui_context_single_attachment_mode(UiContext *ctx, UiBool enable) {
+ ctx->single_document_mode = enable;
+}
+
void ui_attach_document(UiContext *ctx, void *document) {
uic_context_attach_document(ctx, document);
}
}
-void ui_set_group(UiContext *ctx, int group) {
- if(!cxListIndexValid(ctx->groups, cxListFind(ctx->groups, &group))) {
- cxListAdd(ctx->groups, &group);
+void ui_set_state(UiContext *ctx, int state) {
+ if(!cxListIndexValid(ctx->states, cxListFind(ctx->states, &state))) {
+ cxListAdd(ctx->states, &state);
}
// enable/disable group widgets
- uic_check_group_widgets(ctx);
+ uic_check_state_widgets(ctx);
}
-void ui_unset_group(UiContext *ctx, int group) {
- int i = cxListFind(ctx->groups, &group);
+void ui_unset_state(UiContext *ctx, int state) {
+ int i = cxListFind(ctx->states, &state);
if(i != -1) {
- cxListRemove(ctx->groups, i);
+ cxListRemove(ctx->states, i);
}
// enable/disable group widgets
- uic_check_group_widgets(ctx);
+ uic_check_state_widgets(ctx);
}
-int* ui_active_groups(UiContext *ctx, int *ngroups) {
- *ngroups = cxListSize(ctx->groups);
- return cxListAt(ctx->groups, 0);
+int* ui_active_states(UiContext *ctx, int *nstates) {
+ *nstates = cxListSize(ctx->states);
+ return cxListAt(ctx->states, 0);
}
-void uic_check_group_widgets(UiContext *ctx) {
+void uic_check_state_widgets(UiContext *ctx) {
int ngroups = 0;
- int *groups = ui_active_groups(ctx, &ngroups);
+ int *groups = ui_active_states(ctx, &ngroups);
- CxIterator i = cxListIterator(ctx->group_widgets);
- cx_foreach(UiGroupWidget *, gw, i) {
- char *check = calloc(1, gw->numgroups);
+ CxIterator i = cxListIterator(ctx->state_widgets);
+ cx_foreach(UiStateWidget *, gw, i) {
+ char *check = calloc(1, gw->numstates);
for(int i=0;i<ngroups;i++) {
- for(int k=0;k<gw->numgroups;k++) {
- if(groups[i] == gw->groups[k]) {
+ for(int k=0;k<gw->numstates;k++) {
+ if(groups[i] == gw->states[k]) {
check[k] = 1;
}
}
}
int enable = 1;
- for(int i=0;i<gw->numgroups;i++) {
+ for(int i=0;i<gw->numstates;i++) {
if(check[i] == 0) {
enable = 0;
break;
}
}
-void ui_widget_set_groups(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, ...) {
+void ui_widget_set_states(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, ...) {
if(enable == NULL) {
enable = (ui_enablefunc)ui_set_enabled;
}
- // get groups
- CxList *groups = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), 16);
+ // get states
+ CxList *states = cxArrayListCreate(cxDefaultAllocator, sizeof(int), 16);
va_list ap;
va_start(ap, enable);
- int group;
- while((group = va_arg(ap, int)) != -1) {
- cxListAdd(groups, &group);
+ int state;
+ while((state = va_arg(ap, int)) != -1) {
+ cxListAdd(states, &state);
}
va_end(ap);
- uic_add_group_widget(ctx, widget, enable, groups);
+ uic_add_state_widget(ctx, widget, enable, states);
- cxListFree(groups);
+ cxListFree(states);
}
-void ui_widget_set_groups2(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, const int *groups, int ngroups) {
+void ui_widget_set_states2(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, const int *states, int nstates) {
if(enable == NULL) {
enable = (ui_enablefunc)ui_set_enabled;
}
- CxList *ls = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), ngroups);
- for(int i=0;i<ngroups;i++) {
- cxListAdd(ls, groups+i);
+ CxList *ls = cxArrayListCreate(cxDefaultAllocator, sizeof(int), nstates);
+ for(int i=0;i<nstates;i++) {
+ cxListAdd(ls, states+i);
}
- uic_add_group_widget(ctx, widget, enable, ls);
+ uic_add_state_widget(ctx, widget, enable, ls);
cxListFree(ls);
}
void ui_widget_set_visibility_states(UiContext *ctx, UIWIDGET widget, const int *states, int nstates) {
- ui_widget_set_groups2(ctx, widget, (ui_enablefunc)ui_set_visible, states, nstates);
+ ui_widget_set_states2(ctx, widget, (ui_enablefunc)ui_set_visible, states, nstates);
}
-size_t uic_group_array_size(const int *groups) {
+size_t uic_state_array_size(const int *states) {
int i;
- for(i=0;groups[i] >= 0;i++) { }
+ for(i=0;states[i] >= 0;i++) { }
return i;
}
-void uic_add_group_widget(UiContext *ctx, void *widget, ui_enablefunc enable, CxList *groups) {
- uic_add_group_widget_i(ctx, widget, enable, cxListAt(groups, 0), cxListSize(groups));
+void uic_add_state_widget(UiContext *ctx, void *widget, ui_enablefunc enable, CxList *states) {
+ uic_add_state_widget_i(ctx, widget, enable, cxListAt(states, 0), cxListSize(states));
}
-void uic_add_group_widget_i(UiContext *ctx, void *widget, ui_enablefunc enable, const int *groups, size_t numgroups) {
+void uic_add_state_widget_i(UiContext *ctx, void *widget, ui_enablefunc enable, const int *states, size_t numstates) {
const CxAllocator *a = ctx->allocator;
- UiGroupWidget gw;
+ UiStateWidget gw;
gw.widget = widget;
gw.enable = enable;
- gw.numgroups = numgroups;
- gw.groups = cxCalloc(a, numgroups, sizeof(int));
+ gw.numstates = numstates;
+ gw.states = cxCalloc(a, numstates, sizeof(int));
- // copy groups
- if(groups) {
- memcpy(gw.groups, groups, gw.numgroups * sizeof(int));
+ // copy states
+ if(states) {
+ memcpy(gw.states, states, gw.numstates * sizeof(int));
}
- cxListAdd(ctx->group_widgets, &gw);
+ cxListAdd(ctx->state_widgets, &gw);
}
-void uic_remove_group_widget(UiContext *ctx, void *widget) {
- (void)cxListFindRemove(ctx->group_widgets, widget);
+void uic_remove_state_widget(UiContext *ctx, void *widget) {
+ (void)cxListFindRemove(ctx->state_widgets, widget);
}
UIEXPORT void *ui_allocator(UiContext *ctx) {
void ui_set_destructor(void *mem, ui_destructor_func destr) {
cxMempoolSetDestructor(mem, (cx_destructor_func)destr);
}
+
+void ui_var_set_int(UiContext *ctx, const char *name, int64_t value) {
+ UiInteger *i = ui_get_int_var(ctx, name);
+ if(i) {
+ ui_set(i, value);
+ }
+}
+
+int64_t ui_var_get_int(UiContext *ctx, const char *name) {
+ UiInteger *i = ui_get_int_var(ctx, name);
+ if(i) {
+ return ui_get(i);
+ }
+ return 0;
+}
+
+void ui_var_set_double(UiContext *ctx, const char *name, double value) {
+ UiDouble *d = ui_get_double_var(ctx, name);
+ if(d) {
+ ui_set(d, value);
+ }
+}
+
+double ui_var_get_double(UiContext *ctx, const char *name) {
+ UiDouble *d = ui_get_double_var(ctx, name);
+ if(d) {
+ return ui_get(d);
+ }
+ return 0;
+}
+
+void ui_var_set_string(UiContext *ctx, const char *name, char *value) {
+ UiString *s = ui_get_string_var(ctx, name);
+ if(s) {
+ ui_set(s, value);
+ }
+}
+
+char* ui_var_get_string(UiContext *ctx, const char *name) {
+ UiString *s = ui_get_string_var(ctx, name);
+ if(s) {
+ return ui_get(s);
+ }
+ return NULL;
+}
+
+UIEXPORT void ui_var_add_observer(UiContext *ctx, const char *varname, ui_callback f, void *data) {
+ UiVar *var = uic_get_var(ctx, varname);
+ if(!var) {
+ return;
+ }
+
+ switch(var->type) {
+ case UI_VAR_INTEGER: {
+ UiInteger *v = var->value;
+ v->observers = ui_add_observer(v->observers, f, data);
+ break;
+ }
+ case UI_VAR_DOUBLE: {
+ UiDouble *v = var->value;
+ v->observers = ui_add_observer(v->observers, f, data);
+ break;
+ }
+ case UI_VAR_RANGE: {
+ UiRange *v = var->value;
+ v->observers = ui_add_observer(v->observers, f, data);
+ break;
+ }
+ case UI_VAR_STRING: {
+ UiString *v = var->value;
+ v->observers = ui_add_observer(v->observers, f, data);
+ break;
+ }
+ case UI_VAR_TEXT: {
+ UiText *v = var->value;
+ v->observers = ui_add_observer(v->observers, f, data);
+ break;
+ }
+ case UI_VAR_LIST: {
+ UiList *v = var->value;
+ v->observers = ui_add_observer(v->observers, f, data);
+ break;
+ }
+ }
+}
+
+UiInteger* ui_get_int_var(UiContext *ctx, const char *name) {
+ UiVar *var = uic_get_var_t(ctx, name, UI_VAR_INTEGER);
+ return var ? var->value : NULL;
+}
+
+UiDouble* ui_get_double_var(UiContext *ctx, const char *name) {
+ UiVar *var = uic_get_var_t(ctx, name, UI_VAR_DOUBLE);
+ return var ? var->value : NULL;
+}
+
+UiString* ui_get_string_var(UiContext *ctx, const char *name) {
+ UiVar *var = uic_get_var_t(ctx, name, UI_VAR_STRING);
+ return var ? var->value : NULL;
+}
+
+UiText* ui_get_text_var(UiContext *ctx, const char *name) {
+ UiVar *var = uic_get_var_t(ctx, name, UI_VAR_TEXT);
+ return var ? var->value : NULL;
+}
+
+UiRange* ui_get_range_var(UiContext *ctx, const char *name) {
+ UiVar *var = uic_get_var_t(ctx, name, UI_VAR_RANGE);
+ return var ? var->value : NULL;
+}
+
+UIEXPORT UiList* ui_get_list_var(UiContext *ctx, const char *name) {
+ UiVar *var = uic_get_var_t(ctx, name, UI_VAR_LIST);
+ return var ? var->value : NULL;
+}
+
+UiGeneric* ui_get_generic_var(UiContext *ctx, const char *name) {
+ UiVar *var = uic_get_var_t(ctx, name, UI_VAR_GENERIC);
+ return var ? var->value : NULL;
+}
typedef struct UiVar UiVar;
typedef struct UiListPtr UiListPtr;
typedef struct UiListVar UiListVar;
-typedef struct UiGroupWidget UiGroupWidget;
+typedef struct UiStateWidget UiStateWidget;
typedef struct UiDestroyHandler UiDestroyHandler;
typedef enum UiVarType {
CxMap *vars;
- CxList *groups; // int list
- CxList *group_widgets; // UiGroupWidget list
+ CxList *states; // int list
+ CxList *state_widgets; // UiGroupWidget list
void (*attach_document)(UiContext *ctx, void *document);
void (*detach_document2)(UiContext *ctx, void *document);
GtkAccelGroup *accel_group;
#endif
#endif
-
-
+
+ // allow only one document to be attached
+ // attaching a document will automatically detach the current document
+ UiBool single_document_mode;
ui_callback close_callback;
void *close_data;
UiBool bound;
};
-struct UiGroupWidget {
+struct UiStateWidget {
void *widget;
ui_enablefunc enable;
- int *groups;
- int numgroups;
+ int *states;
+ int numstates;
};
struct UiDestroyHandler {
void uic_context_detach_all(UiContext *ctx);
UiVar* uic_get_var(UiContext *ctx, const char *name);
+UiVar* uic_get_var_t(UiContext *ctx, const char *name, UiVarType type);
UiVar* uic_create_var(UiContext *ctx, const char *name, UiVarType type);
UiVar* uic_create_value_var(UiContext *ctx, void *value);
void* uic_create_value(UiContext *ctx, UiVarType type);
void uic_reg_var(UiContext *ctx, const char *name, UiVarType type, void *value);
-size_t uic_group_array_size(const int *groups);
-void uic_check_group_widgets(UiContext *ctx);
-void uic_add_group_widget(UiContext *ctx, void *widget, ui_enablefunc enable, CxList *groups);
-void uic_add_group_widget_i(UiContext *ctx, void *widget, ui_enablefunc enable, const int *groups, size_t numgroups);
-void uic_remove_group_widget(UiContext *ctx, void *widget);
+size_t uic_state_array_size(const int *states);
+void uic_check_state_widgets(UiContext *ctx);
+void uic_add_state_widget(UiContext *ctx, void *widget, ui_enablefunc enable, CxList *states);
+void uic_add_state_widget_i(UiContext *ctx, void *widget, ui_enablefunc enable, const int *states, size_t numstates);
+void uic_remove_state_widget(UiContext *ctx, void *widget);
#ifdef __cplusplus
}
}
void uic_menu_init(void) {
- global_builder.current = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS);
+ global_builder.current = cxLinkedListCreate(cxDefaultAllocator, CX_STORE_POINTERS);
current_builder = &global_builder;
}
return s ? strdup(s) : NULL;
}
-int* uic_copy_groups(const int* groups, size_t *ngroups) {
- *ngroups = 0;
- if (!groups) {
+int* uic_copy_states(const int* states, size_t *nstates) {
+ *nstates = 0;
+ if (!states) {
return NULL;
}
size_t n;
- for (n = 0; groups[n] > -1; n++) { }
+ for (n = 0; states[n] > -1; n++) { }
- if (ngroups > 0) {
+ if (nstates > 0) {
int* newarray = calloc(n+1, sizeof(int));
- memcpy(newarray, groups, n * sizeof(int));
+ memcpy(newarray, states, n * sizeof(int));
newarray[n] = -1;
- *ngroups = n;
+ *nstates = n;
return newarray;
}
return NULL;
item->icon = nl_strdup(args->icon);
item->userdata = args->onclickdata;
item->callback = args->onclick;
- item->groups = uic_copy_groups(args->groups, &item->ngroups);
+ item->states = uic_copy_states(args->states, &item->nstates);
add_item((UiMenuItemI*)item);
}
item->varname = nl_strdup(args->varname);
item->userdata = args->onchangedata;
item->callback = args->onchange;
- item->groups = uic_copy_groups(args->groups, &item->ngroups);
+ item->states = uic_copy_states(args->nstates, &item->nstates);
add_item((UiMenuItemI*)item);
}
item->varname = nl_strdup(args->varname);
item->userdata = args->onchangedata;
item->callback = args->onchange;
- item->groups = uic_copy_groups(args->groups, &item->ngroups);
+ item->states = uic_copy_states(args->nstates, &item->nstates);
add_item((UiMenuItemI*)item);
}
UiMenuBuilder *builder = malloc(sizeof(UiMenuBuilder));
builder->menus_begin = NULL;
builder->menus_end = NULL;
- builder->current = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS);
+ builder->current = cxLinkedListCreate(cxDefaultAllocator, CX_STORE_POINTERS);
builder->ref = 1;
current_builder = builder;
*out_builder = builder;
}
case UI_MENU_ITEM: {
UiMenuItem *i = (UiMenuItem*)item;
- free(i->groups);
+ free(i->states);
free(i->label);
free(i->icon);
break;
}
case UI_MENU_CHECK_ITEM: {
UiMenuCheckItem *i = (UiMenuCheckItem*)item;
- free(i->groups);
+ free(i->states);
free(i->label);
free(i->icon);
free(i->varname);
}
case UI_MENU_RADIO_ITEM: {
UiMenuRadioItem *i = (UiMenuRadioItem*)item;
- free(i->groups);
+ free(i->states);
free(i->label);
free(i->icon);
free(i->varname);
char *label;
char *icon;
void *userdata;
- int *groups;
- size_t ngroups;
+ int *states;
+ size_t nstates;
};
struct UiMenuCheckItem {
char *varname;
ui_callback callback;
void *userdata;
- int *groups;
- size_t ngroups;
+ int *states;
+ size_t nstates;
};
struct UiMenuRadioItem {
char *varname;
ui_callback callback;
void *userdata;
- int *groups;
- size_t ngroups;
+ int *states;
+ size_t nstates;
};
struct UiMenuItemList {
void uic_add_menu_to_stack(UiMenu* menu);
-int* uic_copy_groups(const int* groups, size_t *ngroups);
+int* uic_copy_states(const int* states, size_t *nstates);
void uic_set_tmp_eventdata(void *eventdata, int type);
void* uic_get_tmp_eventdata(void);
void ui_register_object_creation_callback(ui_object_callback func, void *userdata) {
if(!creation_callbacks) {
- creation_callbacks = cxLinkedListCreateSimple(sizeof(objcallback));
+ creation_callbacks = cxLinkedListCreate(NULL, sizeof(objcallback));
}
objcallback cb = { func, userdata };
cxListAdd(creation_callbacks, &cb);
void ui_register_object_destruction_callback(ui_object_callback func, void *userdata) {
if(!destruction_callbacks) {
- destruction_callbacks = cxLinkedListCreateSimple(sizeof(objcallback));
+ destruction_callbacks = cxLinkedListCreate(NULL, sizeof(objcallback));
}
objcallback cb = { func, userdata };
cxListAdd(destruction_callbacks, &cb);
void* ui_object_get(UiObject *obj, const char *key) {
UiObjectPrivate *p = (UiObjectPrivate*)obj;
- if(p->ext) {
- return cxMapGet(p->ext, key);
- }
+ return p->ext ? cxMapGet(p->ext, key) : NULL;
}
COMMON_OBJ += object$(OBJ_EXT)
COMMON_OBJ += container$(OBJ_EXT)
COMMON_OBJ += types$(OBJ_EXT)
+COMMON_OBJ += app$(OBJ_EXT)
COMMON_OBJ += properties$(OBJ_EXT)
COMMON_OBJ += menu$(OBJ_EXT)
COMMON_OBJ += toolbar$(OBJ_EXT)
-COMMON_OBJ += ucx_properties$(OBJ_EXT)
COMMON_OBJ += threadpool$(OBJ_EXT)
COMMON_OBJ += condvar$(OBJ_EXT)
COMMON_OBJ += args$(OBJ_EXT)
COMMON_OBJ += wrapper$(OBJ_EXT)
COMMON_OBJ += utils$(OBJ_EXT)
+COMMON_OBJ += message$(OBJ_EXT)
TOOLKITOBJS += $(COMMON_OBJ:%=$(COMMON_OBJPRE)uic_%)
TOOLKITSOURCE += $(COMMON_OBJ:%$(OBJ_EXT)=common/%.c)
#include <cx/buffer.h>
#include <cx/hash_map.h>
-#include "ucx_properties.h"
+#include <cx/properties.h>
static CxMap *application_properties;
static CxMap *language;
static char *locales_dir;
static char *pixmaps_dir;
+static UiBool use_xdg_config_home = TRUE;
+
#endif
+static UiBool load_on_startup = TRUE;
+static void *properties_data = NULL;
+static size_t properties_data_length = 0;
+
+void ui_load_properties_file_on_startup(UiBool enable) {
+ load_on_startup = enable;
+}
+
+void ui_set_properties_data(const char *str, size_t len) {
+ if(str && len > 0) {
+ properties_data = malloc(len);
+ memcpy(properties_data, str, len);
+ } else {
+ properties_data = NULL;
+ properties_data_length = 0;
+ }
+}
char* ui_getappdir(void) {
if(ui_appname() == NULL) {
#define UI_ENV_HOME "USERPROFILE"
#endif
+#define UI_XDG_CONFIG_HOME_VAR "XDG_CONFIG_HOME"
+
char* ui_configfile(const char *name) {
const char *appname = ui_appname();
if(!appname) {
}
CxBuffer buf;
- cxBufferInit(&buf, NULL, 128, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ cxBufferInit(&buf, cxDefaultAllocator, NULL, 128, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
// add base dir
char *homeenv = getenv(UI_ENV_HOME);
cxBufferPutString(&buf, "Library/Application Support/");
#elif defined(_WIN32)
// on Windows the app dir is $USERPROFILE/AppData/Local/$APPNAME/
- cxBufferPutString(&buf, "AppData\\Local\\");
+ cxBufferPutString(&buf, "AppData\\Roaming\\");
#else
- // app dir is $HOME/.$APPNAME/
- cxBufferPut(&buf, '.');
+ if(use_xdg_config_home) {
+ // app dir is $HOME/.config/$APPNAME/
+ char *xdghome = getenv(UI_XDG_CONFIG_HOME_VAR);
+ size_t xdghomelen = xdghome ? strlen(xdghome) : 0;
+ if(xdghome && xdghomelen > 0) {
+ cxBufferSeek(&buf, 0, SEEK_SET);
+ cxBufferPutString(&buf, xdghome);
+ if(xdghome[xdghomelen-1] != '/') {
+ cxBufferPut(&buf, '/');
+ }
+ } else {
+ cxBufferPutString(&buf, ".config/");
+ }
+ } else {
+ cxBufferPut(&buf, '.');
+ }
+
#endif
cxBufferPutString(&buf, appname);
cxBufferPut(&buf, UI_PATH_SEPARATOR);
#endif
}
+static int load_properties(FILE *file, CxMap *map) {
+ CxProperties prop;
+ cxPropertiesInitDefault(&prop);
+ char buf[8192];
+ size_t r;
+ CxPropertiesStatus status = CX_PROPERTIES_NO_ERROR;
+ while((r = fread(buf, 1, 8192, file)) > 0) {
+ cxPropertiesFilln(&prop, buf, r);
+ cxstring key;
+ cxstring value;
+ while((status = cxPropertiesNext(&prop, &key, &value)) == CX_PROPERTIES_NO_ERROR) {
+ cxMapPut(map, key, cx_strdup(value).ptr);
+ }
+ if(status > CX_PROPERTIES_OK) {
+ break;
+ }
+ }
+ return status <= CX_PROPERTIES_NO_DATA ? 0 : 1;
+}
+
void uic_load_app_properties() {
application_properties = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 128);
application_properties->collection.simple_destructor = free;
+ if(!load_on_startup) {
+ if(properties_data) {
+ CxProperties prop;
+ cxPropertiesInitDefault(&prop);
+
+ CxPropertiesStatus status = CX_PROPERTIES_NO_ERROR;
+ cxPropertiesFilln(&prop, properties_data, properties_data_length);
+
+ cxstring key;
+ cxstring value;
+ while((status = cxPropertiesNext(&prop, &key, &value)) == CX_PROPERTIES_NO_ERROR) {
+ cxMapPut(application_properties, key, cx_strdup(value).ptr);
+ }
+
+ if(status > CX_PROPERTIES_NO_DATA) {
+ fprintf(stderr, "Error: cannot load properties: %d\n", (int)status);
+ } else {
+ cxMapRehash(application_properties);
+ }
+ }
+ return;
+ }
+
if(!ui_appname()) {
// applications without name cannot load app properties
return;
if(!dir) {
return;
}
+ size_t len = strlen(dir);
+ if(len < 2) {
+ return;
+ }
if(ui_mkdir(dir)) {
+ if(errno == ENOENT) {
+ char *parent = strdup(dir);
+ for(int i=len-2;i>=0;i--) {
+ if(parent[i] == '/') {
+ parent[i] = 0;
+ break;
+ }
+ }
+ // try creating the parent
+ int err = ui_mkdir(parent);
+ if(err) {
+ fprintf(stderr, "Error: Cannot create directory %s: %s\n", parent, strerror(errno));
+ free(parent);
+ free(dir);
+ return;
+ }
+ free(parent);
+
+ // try dir again
+ if(!ui_mkdir(dir)) {
+ errno = EEXIST; // success
+ }
+ }
+
if(errno != EEXIST) {
- fprintf(stderr, "Ui Error: Cannot create directory %s\n", dir);
+ fprintf(stderr, "Error: Cannot create directory %s: %s\n", dir, strerror(errno));
free(dir);
return;
}
return;
}
- if(ucx_properties_load(application_properties, file)) {
- fprintf(stderr, "Ui Error: Cannot load application properties.\n");
+ if(load_properties(file, application_properties)) {
+ fprintf(stderr, "Error: Cannot load application properties.\n");
}
fclose(file);
}
int ret = 0;
- if(ucx_properties_store(application_properties, file)) {
- fprintf(stderr, "Ui Error: Cannot store application properties.\n");
- ret = 1;
+ CxMapIterator i = cxMapIterator(application_properties);
+ cx_foreach(CxMapEntry *, entry, i) {
+ fprintf(file, "%.*s = %s\n", (int)entry->key->len, (char*)entry->key->data, (char*)entry->value);
}
+ cxMapRehash(application_properties);
+
fclose(file);
free(path);
static char* uic_concat_path(const char *base, const char *p, const char *ext) {
size_t baselen = strlen(base);
- CxBuffer *buf = cxBufferCreate(NULL, 32, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ CxBuffer *buf = cxBufferCreate(cxDefaultAllocator, NULL, 32, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
if(baselen > 0) {
cxBufferWrite(base, 1, baselen, buf);
if(base[baselen - 1] != '/') {
return 1;
}
- if(ucx_properties_load(lang, file)) {
- fprintf(stderr, "Ui Error: Cannot parse language file: %s.\n", path);
+ if(load_properties(file, lang)) {
+ fprintf(stderr, "Error: Cannot parse language file: %s.\n", path);
}
fclose(file);
UiThreadpool* threadpool_new(int min, int max) {
UiThreadpool *pool = malloc(sizeof(UiThreadpool));
- pool->queue = NULL;
- pool->queue_len = 0;
+ pool->queue = ui_queue_create();
pool->num_idle = 0;
pool->min_threads = min;
pool->max_threads = max;
- pthread_mutex_init(&pool->queue_lock, NULL);
- pthread_mutex_init(&pool->avlbl_lock, NULL);
- pthread_cond_init(&pool->available, NULL);
-
return pool;
}
int threadpool_start(UiThreadpool *pool) {
+ pool->nthreads = pool->min_threads;
+ pool->threads = calloc(pool->max_threads, sizeof(pthread_t));
/* create pool threads */
- for(int i=0;i<pool->min_threads;i++) {
- pthread_t t;
- if (pthread_create(&t, NULL, threadpool_func, pool) != 0) {
+ for(int i=0;i<pool->nthreads;i++) {
+ if (pthread_create(&pool->threads[i], NULL, threadpool_func, pool) != 0) {
fprintf(stderr, "uic: threadpool_start: pthread_create failed: %s", strerror(errno));
return 1;
}
return 0;
}
+int threadpool_join(UiThreadpool *pool) {
+ int err = 0;
+ for(int i=0;i<pool->nthreads;i++) {
+ if(pthread_join(pool->threads[i], NULL)) {
+ err = 1;
+ }
+ }
+ return err;
+}
+
void* threadpool_func(void *data) {
UiThreadpool *pool = (UiThreadpool*)data;
}
threadpool_job* threadpool_get_job(UiThreadpool *pool) {
- pthread_mutex_lock(&pool->queue_lock);
-
- threadpool_job *job = NULL;
- pool->num_idle++;
- while(job == NULL) {
- if(pool->queue_len == 0) {
- pthread_cond_wait(&pool->available, &pool->queue_lock);
- continue;
- } else {
- pool_queue_t *q = pool->queue;
- job = q->job;
- pool->queue = q->next;
- pool->queue_len--;
- free(q);
- }
- }
- pool->num_idle--;
-
- pthread_mutex_unlock(&pool->queue_lock);
+ threadpool_job *job = ui_queue_get_wait(pool->queue);
return job;
}
-void threadpool_run(UiThreadpool *pool, job_callback_f func, void *data) {
- // TODO: handle errors
-
+void threadpool_run(UiThreadpool *pool, job_callback_f func, void *data) {
threadpool_job *job = malloc(sizeof(threadpool_job));
job->callback = func;
job->data = data;
-
- pthread_mutex_lock(&pool->queue_lock);
- threadpool_enqueue_job(pool, job);
-
- int create_thread = 0;
- int destroy_thread = 0;
- int diff = pool->queue_len - pool->num_idle;
-
- //if(pool->queue_len == 1) {
- pthread_cond_signal(&pool->available);
- //}
-
- pthread_mutex_unlock(&pool->queue_lock);
-}
-
-void threadpool_enqueue_job(UiThreadpool *pool, threadpool_job *job) {
- pool_queue_t *q = malloc(sizeof(pool_queue_t));
- q->job = job;
- q->next = NULL;
-
- if(pool->queue == NULL) {
- pool->queue = q;
- } else {
- pool_queue_t *last_elem = pool->queue;
- while(last_elem->next != NULL) {
- last_elem = last_elem->next;
- }
- last_elem->next = q;
- }
- pool->queue_len++;
+ ui_queue_put(pool->queue, job);
}
-
-
-
UiThreadpool* ui_threadpool_create(int nthreads) {
UiThreadpool *pool = threadpool_new(nthreads, nthreads);
threadpool_start(pool); // TODO: check return value
threadpool_run(pool, ui_threadpool_job_func, job);
}
+/* --------------------------------- Queue --------------------------------- */
+
+UiQueue* ui_queue_create(void) {
+ UiQueue *queue = calloc(1, sizeof(UiQueue));
+ pthread_mutex_init(&queue->lock, NULL);
+ pthread_mutex_init(&queue->avlbl_lock, NULL);
+ pthread_cond_init(&queue->available, NULL);
+ return queue;
+}
+
+void ui_queue_free(UiQueue *queue) {
+ // The queue must be empty, we could free UiQueueElm,
+ // but not the payload data
+ pthread_mutex_destroy(&queue->lock);
+ pthread_mutex_destroy(&queue->avlbl_lock);
+ pthread_cond_destroy(&queue->available);
+ free(queue);
+}
+
+void ui_queue_put(UiQueue *queue, void *data) {
+ // create queue element
+ UiQueueElm *elm = malloc(sizeof(UiQueueElm));
+ elm->data = data;
+ elm->next = NULL;
+
+ pthread_mutex_lock(&queue->lock);
+
+ // put queue element at the end of the linked list
+ if(queue->elements) {
+ UiQueueElm *end = queue->elements;
+ while(end->next) {
+ end = end->next;
+ }
+ end->next = elm;
+ } else {
+ queue->elements = elm;
+ }
+ queue->length++;
+
+ // signal new available data
+ pthread_cond_signal(&queue->available);
+
+ pthread_mutex_unlock(&queue->lock);
+}
+
+void* ui_queue_get_wait(UiQueue *queue) {
+ pthread_mutex_lock(&queue->lock);
+
+ void *data = NULL;
+ while(data == NULL) {
+ if(queue->length == 0) {
+ pthread_cond_wait(&queue->available, &queue->lock);
+ continue;
+ } else {
+ UiQueueElm *q = queue->elements;
+ data = q->data;
+ queue->elements = q->next;
+ queue->length--;
+ free(q);
+ }
+ }
+
+ pthread_mutex_unlock(&queue->lock);
+ return data;
+}
#endif
extern "C" {
#endif
+typedef struct UiQueueElm UiQueueElm;
+typedef struct UiQueue UiQueue;
typedef struct UiJob {
UiObject *obj;
void *finish_data;
} UiJob;
+struct UiQueueElm {
+ void *data;
+ UiQueueElm *next;
+};
+
+struct UiQueue {
+ UiQueueElm *elements;
+ size_t length;
+ pthread_mutex_t lock;
+ pthread_mutex_t avlbl_lock;
+ pthread_cond_t available;
+};
typedef struct _threadpool_job threadpool_job;
typedef void*(*job_callback_f)(void *data);
typedef struct _pool_queue pool_queue_t;
struct UiThreadpool {
- pthread_mutex_t queue_lock;
- pthread_mutex_t avlbl_lock;
- pthread_cond_t available;
- pool_queue_t *queue;
- uint32_t queue_len;
- uint32_t num_idle;
- int min_threads;
- int max_threads;
+ UiQueue *queue;
+ uint32_t num_idle;
+ int min_threads;
+ int max_threads;
+ pthread_t *threads;
+ int nthreads;
};
struct _threadpool_job {
UiThreadpool* threadpool_new(int min, int max);
int threadpool_start(UiThreadpool *pool);
+int threadpool_join(UiThreadpool *pool);
void* threadpool_func(void *data);
threadpool_job* threadpool_get_job(UiThreadpool *pool);
void threadpool_run(UiThreadpool *pool, job_callback_f func, void *data);
-void threadpool_enqueue_job(UiThreadpool *pool, threadpool_job *job);
+
+UiQueue* ui_queue_create(void);
+void ui_queue_free(UiQueue *queue);
+void ui_queue_put(UiQueue *queue, void *data);
+void* ui_queue_get_wait(UiQueue *queue);
#ifdef __cplusplus
}
void uic_toolbar_init(void) {\r
toolbar_items = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);\r
for(int i=0;i<8;i++) {\r
- toolbar_defaults[i] = cxLinkedListCreateSimple(CX_STORE_POINTERS);\r
+ toolbar_defaults[i] = cxLinkedListCreate(NULL, CX_STORE_POINTERS);\r
}\r
}\r
\r
newargs.tooltip = nl_strdup(args->tooltip);\r
newargs.onclick = args->onclick;\r
newargs.onclickdata = args->onclickdata;\r
- newargs.groups = uic_copy_groups(args->groups, ngroups);\r
- newargs.visibility_states = uic_copy_groups(args->visibility_states, nvstates);\r
+ newargs.states = uic_copy_states(args->states, ngroups);\r
+ newargs.visibility_states = uic_copy_states(args->visibility_states, nvstates);\r
return newargs;\r
}\r
\r
void ui_toolbar_item_create(const char* name, UiToolbarItemArgs *args) {\r
UiToolbarItem* item = malloc(sizeof(UiToolbarItem));\r
item->item.type = UI_TOOLBAR_ITEM;\r
- item->args = itemargs_copy(args, &item->ngroups, &item->nvstates);\r
+ item->args = itemargs_copy(args, &item->nstates, &item->nvstates);\r
cxMapPut(toolbar_items, name, item);\r
}\r
\r
newargs.varname = nl_strdup(args->varname);\r
newargs.onchange = args->onchange;\r
newargs.onchangedata = args->onchangedata;\r
- newargs.groups = uic_copy_groups(args->groups, ngroups);\r
- newargs.visibility_states = uic_copy_groups(args->visibility_states, nvstates);\r
+ newargs.states = uic_copy_states(args->states, ngroups);\r
+ newargs.visibility_states = uic_copy_states(args->visibility_states, nvstates);\r
return newargs;\r
}\r
\r
void ui_toolbar_toggleitem_create(const char* name, UiToolbarToggleItemArgs *args) {\r
UiToolbarToggleItem* item = malloc(sizeof(UiToolbarToggleItem));\r
item->item.type = UI_TOOLBAR_TOGGLEITEM;\r
- item->args = toggleitemargs_copy(args, &item->ngroups, &item->nvstates);\r
+ item->args = toggleitemargs_copy(args, &item->nstates, &item->nvstates);\r
cxMapPut(toolbar_items, name, item);\r
}\r
\r
newargs.label = nl_strdup(args->label);\r
newargs.icon = nl_strdup(args->icon);\r
newargs.tooltip = nl_strdup(args->tooltip);\r
- newargs.visibility_states = uic_copy_groups(args->visibility_states, nvstates);\r
+ newargs.visibility_states = uic_copy_states(args->visibility_states, nvstates);\r
return newargs;\r
}\r
\r
struct UiToolbarItem {\r
UiToolbarItemI item;\r
UiToolbarItemArgs args;\r
- size_t ngroups;\r
+ size_t nstates;\r
size_t nvstates;\r
};\r
\r
struct UiToolbarToggleItem {\r
UiToolbarItemI item;\r
UiToolbarToggleItemArgs args;\r
- size_t ngroups;\r
+ size_t nstates;\r
size_t nvstates;\r
};\r
\r
#include <cx/list.h>
#include <cx/array_list.h>
-#include "../ui/tree.h"
+#include "../ui/list.h"
#include "types.h"
#include "context.h"
#include "../ui/image.h"
/* --------------------------- UiList --------------------------- */
void uic_ucx_list_init(UiContext *ctx, UiList *list, void *unused) {
- list->data = cxArrayListCreate(ctx->mp->allocator, NULL, CX_STORE_POINTERS, 32);
+ list->data = cxArrayListCreate(ctx->mp->allocator, CX_STORE_POINTERS, 32);
list->first = ui_list_first;
list->next = ui_list_next;
list->get = ui_list_get;
UiModel* ui_model(UiContext *ctx, ...) {
UiModel *info = ui_calloc(ctx, 1, sizeof(UiModel));
+ info->ctx = ctx;
va_list ap;
va_start(ap, ctx);
- CxList *cols = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(UiColumn), 32);
+ CxList *cols = cxArrayListCreate(cxDefaultAllocator, sizeof(UiColumn), 32);
int type;
while((type = va_arg(ap, int)) != -1) {
char *name = va_arg(ap, char*);
#define UI_MODEL_DEFAULT_ALLOC_SIZE 16
+
+static void model_notify_observer(UiModel *model, int insert, int delete) {
+ UiModelChangeObserver *obs = model->observer;
+ while(obs) {
+ obs->update(model, obs->userdata, insert, delete);
+ obs = obs->next;
+ }
+}
+
UiModel* ui_model_new(UiContext *ctx) {
UiModel *info = ui_calloc(ctx, 1, sizeof(UiModel));
+ info->ctx = ctx;
info->alloc = UI_MODEL_DEFAULT_ALLOC_SIZE;
info->types = ui_calloc(ctx, UI_MODEL_DEFAULT_ALLOC_SIZE, sizeof(UiModelType));
info->titles = ui_calloc(ctx, UI_MODEL_DEFAULT_ALLOC_SIZE, sizeof(char*));
return info;
}
-void ui_model_add_column(UiContext *ctx, UiModel *model, UiModelType type, const char *title, int width) {
+void ui_model_add_column(UiModel *model, UiModelType type, const char *title, int width) {
+ UiContext *ctx = model->ctx;
if(model->columns >= model->alloc) {
model->alloc += UI_MODEL_DEFAULT_ALLOC_SIZE;
model->types = ui_realloc(ctx, model->types, model->alloc * sizeof(UiModelType));
model->titles = ui_realloc(ctx, model->titles, model->alloc * sizeof(char*));
model->columnsize = ui_realloc(ctx, model->columnsize, model->alloc * sizeof(int));
}
- model->types[model->columns] = type;
- model->titles[model->columns] = ui_strdup(ctx, title);
- model->columnsize[model->columns] = width;
+ int index = model->columns;
+ model->types[index] = type;
+ model->titles[index] = ui_strdup(ctx, title);
+ model->columnsize[index] = width;
+
+ model_notify_observer(model, index, -1);
+
model->columns++;
}
const CxAllocator* a = ctx ? ctx->allocator : cxDefaultAllocator;
UiModel* newmodel = cxMalloc(a, sizeof(UiModel));
+ newmodel->ctx = ctx;
*newmodel = *model;
newmodel->types = cxCalloc(a, model->columns, sizeof(UiModelType));
return newmodel;
}
-void ui_model_free(UiContext *ctx, UiModel *mi) {
- const CxAllocator* a = ctx ? ctx->allocator : cxDefaultAllocator;
+void ui_model_ref(UiModel *model) {
+ model->ref++;
+}
+
+void ui_model_unref(UiModel *model) {
+ if(--model->ref == 0) {
+ ui_model_free(model);
+ }
+}
+
+void ui_model_add_observer(UiModel *model, ui_model_update_func update, void *data) {
+ UiModelChangeObserver *observer = ui_malloc(model->ctx, sizeof(UiModelChangeObserver));
+ observer->update = update;
+ observer->userdata = data;
+ observer->next = NULL;
+
+ if(model->observer) {
+ UiModelChangeObserver *last = model->observer;
+ while(last->next) {
+ last = last->next;
+ }
+ last->next = observer;
+ } else {
+ model->observer = observer;
+ }
+}
+
+void ui_model_remove_observer(UiModel *model, void *data) {
+ if(model->observer) {
+ UiModelChangeObserver *obs = model->observer;
+ UiModelChangeObserver *prev = NULL;
+ while(obs) {
+ if(obs->userdata == data) {
+ // remove
+ if(prev) {
+ prev->next = obs->next;
+ } else {
+ model->observer = obs->next;
+ }
+ // free
+ ui_free(model->ctx, obs);
+ break;
+ }
+ prev = obs;
+ obs = obs->next;
+ }
+ }
+}
+
+void ui_model_free(UiModel *mi) {
+ UiContext *ctx = mi->ctx;
+ const CxAllocator* a = ctx->allocator;
for(int i=0;i<mi->columns;i++) {
ui_free(ctx, mi->titles[i]);
}
+ UiModelChangeObserver *obs = mi->observer;
+ while(obs) {
+ UiModelChangeObserver *n = obs->next;
+ cxFree(a, obs);
+ obs = n;
+ }
cxFree(a, mi->types);
cxFree(a, mi->titles);
cxFree(a, mi->columnsize);
}
UIEXPORT void ui_listselection_free(UiListSelection selection) {
- if (selection.rows) {
- free(selection.rows);
- }
+ free(selection.rows);
}
UIEXPORT UiStr ui_str(char *cstr) {
#include "utils.h"
#include "properties.h"
+#include <stdio.h>
+#include <string.h>
+
#include <cx/string.h>
+#include <cx/buffer.h>
UiPathElm* ui_default_pathelm_func(const char* full_path, size_t len, size_t* ret_nelm, void* data) {
cxstring *pathelms;
- size_t nelm = cx_strsplit_a(cxDefaultAllocator, cx_strn(full_path, len), CX_STR("/"), 4096, &pathelms);
+ size_t nelm = cx_strsplit_a(cxDefaultAllocator, cx_strn(full_path, len), cx_str("/"), 4096, &pathelms);
if (nelm == 0) {
*ret_nelm = 0;
}
}
}
+
+// from UCX json.c
+static cxmutstr escape_string(cxstring str, bool escape_slash) {
+ // note: this function produces the string without enclosing quotes
+ // the reason is that we don't want to allocate memory just for that
+ CxBuffer buf = {0};
+
+ bool all_printable = true;
+ for (size_t i = 0; i < str.length; i++) {
+ unsigned char c = str.ptr[i];
+ bool escape = c < 0x20 || c == '\\' || c == '"'
+ || (escape_slash && c == '/');
+
+ if (all_printable && escape) {
+ size_t capa = str.length + 32;
+ char *space = cxMallocDefault(capa);
+ if (space == NULL) return cx_mutstrn(NULL, 0);
+ cxBufferInit(&buf, NULL, space, capa, CX_BUFFER_AUTO_EXTEND);
+ cxBufferWrite(str.ptr, 1, i, &buf);
+ all_printable = false;
+ }
+ if (escape) {
+ cxBufferPut(&buf, '\\');
+ if (c == '\"') {
+ cxBufferPut(&buf, '\"');
+ } else if (c == '\n') {
+ cxBufferPut(&buf, 'n');
+ } else if (c == '\t') {
+ cxBufferPut(&buf, 't');
+ } else if (c == '\r') {
+ cxBufferPut(&buf, 'r');
+ } else if (c == '\\') {
+ cxBufferPut(&buf, '\\');
+ } else if (c == '/') {
+ cxBufferPut(&buf, '/');
+ } else if (c == '\f') {
+ cxBufferPut(&buf, 'f');
+ } else if (c == '\b') {
+ cxBufferPut(&buf, 'b');
+ } else {
+ char code[6];
+ snprintf(code, sizeof(code), "u%04x", (unsigned int) c);
+ cxBufferPutString(&buf, code);
+ }
+ } else if (!all_printable) {
+ cxBufferPut(&buf, c);
+ }
+ }
+ cxmutstr ret;
+ if (all_printable) {
+ // don't copy the string when we don't need to escape anything
+ ret = cx_mutstrn((char*)str.ptr, str.length);
+ } else {
+ ret = cx_mutstrn(buf.space, buf.size);
+ }
+ cxBufferDestroy(&buf);
+ return ret;
+}
+
+cxmutstr ui_escape_string(cxstring str) {
+ return escape_string(str, FALSE);
+}
#include "../ui/toolkit.h"
#include "../ui/text.h"
+#include <cx/string.h>
+
#ifdef __cplusplus
extern "C" {
#endif
*/
void ui_get_window_default_width(int *width, int *height);
+cxmutstr ui_escape_string(cxstring str);
+
#ifdef __cplusplus
}
#define UIC_WRAPPER_H
#include "../ui/toolkit.h"
-#include "../ui/tree.h"
+#include "../ui/list.h"
#ifdef __cplusplus
extern "C" {
UIWIDGET ui_button_create(UiObject *obj, UiButtonArgs *args) {
GtkWidget *button = ui_create_button(obj, args->label, args->icon, args->tooltip, args->onclick, args->onclickdata, 0, FALSE);
ui_set_name_and_style(button, args->name, args->style_class);
- ui_set_widget_groups(obj->ctx, button, args->groups);
+ ui_set_widget_states(obj->ctx, button, args->states);
UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
UiLayout layout = UI_ARGS2LAYOUT(args);
ct->add(ct, button, &layout);
static void ui_togglebutton_enable_state_callback(GtkToggleButton *widget, UiEventData *event) {
if(gtk_toggle_button_get_active(widget)) {
- ui_set_group(event->obj->ctx, event->value);
+ ui_set_state(event->obj->ctx, event->value);
} else {
- ui_unset_group(event->obj->ctx, event->value);
+ ui_unset_state(event->obj->ctx, event->value);
}
}
event->observers = NULL;
event->callback = NULL;
event->userdata = NULL;
+ event->customint1 = 0;
+ event->customint2 = 0;
g_signal_connect(
widget,
args->value,
args->onchange,
args->onchangedata,
- args->enable_group);
+ args->enable_state);
ui_set_name_and_style(widget, args->name, args->style_class);
- ui_set_widget_groups(obj->ctx, widget, args->groups);
+ ui_set_widget_states(obj->ctx, widget, args->states);
UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
UiLayout layout = UI_ARGS2LAYOUT(args);
static void ui_checkbox_enable_state(GtkCheckButton *widget, UiEventData *event) {
if(gtk_check_button_get_active(widget)) {
- ui_set_group(event->obj->ctx, event->value);
+ ui_set_state(event->obj->ctx, event->value);
} else {
- ui_unset_group(event->obj->ctx, event->value);
+ ui_unset_state(event->obj->ctx, event->value);
}
}
args->onchange,
args->onchangedata,
(ui_toggled_func)ui_checkbox_enable_state,
- args->enable_group);
+ args->enable_state);
ui_set_name_and_style(widget, args->name, args->style_class);
- ui_set_widget_groups(obj->ctx, widget, args->groups);
+ ui_set_widget_states(obj->ctx, widget, args->states);
UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
UiLayout layout = UI_ARGS2LAYOUT(args);
UiVarEventData *event)
{
GtkSwitch *sw = GTK_SWITCH (gobject);
- gboolean active = gtk_switch_get_active (sw);
+ gboolean active = gtk_switch_get_active(sw);
UiEvent e;
e.obj = event->obj;
e.window = e.obj->window;
e.eventdata = NULL;
e.eventdatatype = 0;
+ e.intval = active;
e.set = ui_get_setop();
if(event->callback) {
UiInteger *i = event->var->value;
ui_notify_evt(i->observers, &e);
}
+ if(event->customint1 > 0) {
+ if(active) {
+ ui_set_state(e.obj->ctx, event->customint1);
+ } else {
+ ui_unset_state(e.obj->ctx, event->customint1);
+ }
+ }
}
UIWIDGET ui_switch_create(UiObject* obj, UiToggleArgs *args) {
GtkWidget *widget = gtk_switch_new();
ui_set_name_and_style(widget, args->name, args->style_class);
- ui_set_widget_groups(obj->ctx, widget, args->groups);
+ ui_set_widget_states(obj->ctx, widget, args->states);
UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER);
if(var) {
}
UiVarEventData *event = malloc(sizeof(UiVarEventData));
+ memset(event, 0, sizeof(UiVarEventData));
event->obj = obj;
event->callback = args->onchange;
event->userdata = args->onchangedata;
event->var = var;
event->observers = NULL;
+ event->customint1 = args->enable_state;
g_signal_connect(
widget,
"destroy",
G_CALLBACK(ui_destroy_vardata),
event);
-
+
UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
UiLayout layout = UI_ARGS2LAYOUT(args);
ct->add(ct, widget, &layout);
#define RADIOBUTTON_GET_ACTIVE(button) gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button))
#endif
-static void radiobutton_toggled(void *widget, UiEventData *event) {
+static void radiobutton_toggled(void *widget, UiVarEventData *event) {
+ gboolean active = RADIOBUTTON_GET_ACTIVE(widget);
+
UiEvent e;
e.obj = event->obj;
e.window = event->obj->window;
e.document = event->obj->ctx->document;
e.eventdata = NULL;
e.eventdatatype = 0;
- e.intval = RADIOBUTTON_GET_ACTIVE(widget);
+ e.intval = active;
e.set = ui_get_setop();
- event->callback(&e, event->userdata);
+
+ if(event->callback) {
+ event->callback(&e, event->userdata);
+ }
+
+ if(active && event->var) {
+ UiInteger *i = event->var->value;
+
+ e.intval = i->get(i);
+ ui_notify_evt(i->observers, &e);
+ }
}
typedef struct UiRadioButtonData {
- UiInteger *value;
- UiVarEventData *eventdata;
+ UiObject *obj;
+ UiVar *var;
UiBool first;
} UiRadioButtonData;
static void destroy_radiobutton(GtkWidget *w, UiRadioButtonData *data) {
if(data->first) {
- ui_destroy_vardata(w, data->eventdata);
- g_slist_free(data->value->obj);
- data->value->obj = NULL;
- data->value->get = NULL;
- data->value->set = NULL;
- } else {
- free(data->eventdata);
+ UiInteger *value = data->var->value;
+ ui_destroy_boundvar(data->obj->ctx, data->var);
+ g_slist_free(value->obj);
}
free(data);
}
GtkWidget *rbutton = RADIOBUTTON_NEW(rg, args->label);
ui_set_name_and_style(rbutton, args->name, args->style_class);
- ui_set_widget_groups(obj->ctx, rbutton, args->groups);
+ ui_set_widget_states(obj->ctx, rbutton, args->states);
+
+ UiVarEventData *event = malloc(sizeof(UiVarEventData));
+ event->obj = obj;
+ event->var = var;
+ event->observers = NULL;
+ event->callback = args->onchange;
+ event->userdata = args->onchangedata;
+ event->customint1 = 0;
+ event->customint2 = 0;
+
if(rgroup) {
#if GTK_MAJOR_VERSION >= 4
if(rg) {
ui_radiobutton_set(rgroup, rgroup->value);
- UiVarEventData *event = malloc(sizeof(UiVarEventData));
- event->obj = obj;
- event->var = var;
- event->observers = NULL;
- event->callback = NULL;
- event->userdata = NULL;
-
UiRadioButtonData *rbdata = malloc(sizeof(UiRadioButtonData));
- rbdata->value = rgroup;
- rbdata->eventdata = event;
+ rbdata->obj = obj;
+ rbdata->var = var;
rbdata->first = first;
- g_signal_connect(
- rbutton,
- "toggled",
- G_CALLBACK(ui_radio_obs),
- event);
g_signal_connect(
rbutton,
"destroy",
G_CALLBACK(destroy_radiobutton),
rbdata);
}
-
- if(args->onchange) {
- UiEventData *event = malloc(sizeof(UiEventData));
- event->obj = obj;
- event->userdata = args->onchangedata;
- event->callback = args->onchange;
- event->value = 0;
- event->customdata = NULL;
- event->customint = 0;
- g_signal_connect(
- rbutton,
- "toggled",
- G_CALLBACK(radiobutton_toggled),
- event);
- g_signal_connect(
- rbutton,
- "destroy",
- G_CALLBACK(ui_destroy_userdata),
- event);
- }
+ g_signal_connect(
+ rbutton,
+ "toggled",
+ G_CALLBACK(radiobutton_toggled),
+ event);
+ g_signal_connect(
+ rbutton,
+ "destroy",
+ G_CALLBACK(ui_destroy_userdata),
+ event);
UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
UiLayout layout = UI_ARGS2LAYOUT(args);
return rbutton;
}
-void ui_radio_obs(GtkToggleButton *widget, UiVarEventData *event) {
- UiInteger *i = event->var->value;
-
- UiEvent e;
- e.obj = event->obj;
- e.window = event->obj->window;
- e.document = event->obj->ctx->document;
- e.eventdata = NULL;
- e.eventdatatype = 0;
- e.intval = i->get(i);
-
- ui_notify_evt(i->observers, &e);
-}
-
#if GTK_MAJOR_VERSION >= 4
int64_t ui_radiobutton_get(UiInteger *value) {
int selection = 0;
static char* create_linkbutton_jsonvalue(const char *label, const char *uri, gboolean include_null, gboolean visited, gboolean set_visited) {
CxJsonValue *obj = cxJsonCreateObj(NULL);
if(label) {
- cxJsonObjPutString(obj, CX_STR("label"), label);
+ cxJsonObjPutString(obj, cx_str("label"), label);
} else if(include_null) {
- cxJsonObjPutLiteral(obj, CX_STR("label"), CX_JSON_NULL);
+ cxJsonObjPutLiteral(obj, cx_str("label"), CX_JSON_NULL);
}
if(uri) {
- cxJsonObjPutString(obj, CX_STR("uri"), uri);
+ cxJsonObjPutString(obj, cx_str("uri"), uri);
} else if(include_null) {
- cxJsonObjPutLiteral(obj, CX_STR("uri"), CX_JSON_NULL);
+ cxJsonObjPutLiteral(obj, cx_str("uri"), CX_JSON_NULL);
}
if(set_visited) {
- cxJsonObjPutLiteral(obj, CX_STR("visited"), visited ? CX_JSON_TRUE : CX_JSON_FALSE);
+ cxJsonObjPutLiteral(obj, cx_str("visited"), visited ? CX_JSON_TRUE : CX_JSON_FALSE);
}
CxJsonWriter writer = cxJsonWriterCompact();
CxBuffer buf;
- cxBufferInit(&buf, NULL, 128, NULL, CX_BUFFER_AUTO_EXTEND);
+ cxBufferInit(&buf, NULL, NULL, 128, CX_BUFFER_AUTO_EXTEND);
cxJsonWrite(&buf, obj, (cx_write_func)cxBufferWrite, &writer);
cxJsonValueFree(obj);
cxBufferTerminate(&buf);
}
ui_set_name_and_style(button, args->name, args->style_class);
- ui_set_widget_groups(obj->ctx, button, args->groups);
+ ui_set_widget_states(obj->ctx, button, args->states);
UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
UiLayout layout = UI_ARGS2LAYOUT(args);
ct->add(ct, button, &layout);
return add;
}
+/* -------------------- Custom Container -------------------- */
+
+void ui_custom_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) {
+ UiCustomContainer *custom = (UiCustomContainer*)ct;
+ custom->add(custom->obj, ct->widget, widget, custom->userdata);
+}
+
+void ui_custom_container_create(UiObject *obj, UIWIDGET widget, ui_addwidget_func add_child, void *userdata) {
+ UiCustomContainer *container = cxZalloc(obj->ctx->allocator, sizeof(UiCustomContainer));
+ container->container.add = ui_custom_container_add;
+ container->container.widget = widget;
+ container->add = add_child;
+ container->userdata = userdata;
+ uic_object_push_container(obj, (UiContainerX*)container);
+}
/* -------------------- Box Container -------------------- */
UiContainerX* ui_box_container(UiObject *obj, GtkWidget *box, UiSubContainerType type) {
#else
gtk_box_pack_start(GTK_BOX(ct->widget), widget, expand, fill, 0);
#endif
-
- ct->current = widget;
}
UiContainerX* ui_grid_container(
gtk_grid_attach(GTK_GRID(ct->widget), widget, grid->x, grid->y, colspan, rowspan);
grid->x += colspan;
-
- grid->container.current = widget;
}
#endif
#ifdef UI_GTK2
void ui_frame_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) {
widget = ui_gtk_set_margin(widget, layout->margin, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom);
FRAME_SET_CHILD(ct->widget, widget);
- ct->current = widget;
}
UiContainerX* ui_expander_container(UiObject *obj, GtkWidget *expander) {
void ui_expander_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) {
widget = ui_gtk_set_margin(widget, layout->margin, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom);
EXPANDER_SET_CHILD(ct->widget, widget);
- ct->current = widget;
}
void ui_scrolledwindow_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) {
widget = ui_gtk_set_margin(widget, layout->margin, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom);
// TODO: check if the widget implements GtkScrollable
SCROLLEDWINDOW_SET_CHILD(ct->widget, widget);
- ct->current = widget;
}
UiContainerX* ui_scrolledwindow_container(UiObject *obj, GtkWidget *scrolledwindow) {
}
widget = ui_gtk_set_margin(widget, layout->margin, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom);
data->add_tab(ct->widget, -1, layout->label, widget);
-
- ct->current = widget;
}
#ifdef UI_GTK2
GtkWidget *box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
hb->centerbox = box;
UI_HEADERBAR_SET_TITLE_WIDGET(ct->widget, box);
+ UI_HEADERBAR_SHOW_TITLE_WIDGET(ct->widget, TRUE);
+ UI_HEADERBAR_SETTINGS(ct->widget);
}
BOX_ADD(hb->centerbox, widget);
}
UiSplitPane* ui_create_splitpane_data(GtkWidget *pane, UiOrientation orientation, int max, int init) {
UiSplitPane *ct = malloc(sizeof(UiSplitPane));
+ memset(ct, 0, sizeof(UiSplitPane));
ct->current_pane = pane;
ct->orientation = orientation;
ct->max = max;
ct->initial_position = init;
- ct->children = cxArrayListCreateSimple(CX_STORE_POINTERS, 4);
+ ct->children = cxArrayListCreate(NULL, CX_STORE_POINTERS, 4);
return ct;
}
static void update_itemlist(UiList *list, int c) {
UiGtkItemListContainer *ct = list->obj;
- CxMap *new_items = cxHashMapCreateSimple(CX_STORE_POINTERS);
+ CxMap *new_items = cxHashMapCreate(NULL, CX_STORE_POINTERS, 32);
new_items->collection.advanced_destructor = remove_item;
new_items->collection.destructor_data = ct;
container->create_ui = args->create_ui;
container->userdata = args->userdata;
container->subcontainer = args->subcontainer;
- container->current_items = cxHashMapCreateSimple(CX_STORE_POINTERS);
+ container->current_items = cxHashMapCreate(NULL, CX_STORE_POINTERS, 32);
container->current_items->collection.advanced_destructor = remove_item;
container->current_items->collection.destructor_data = container;
container->margin = args->sub_margin;
UiContainerX container;
GtkWidget *widget;
UIMENU menu;
- GtkWidget *current; // TODO: remove
void (*add)(UiContainerPrivate*, GtkWidget*, UiLayout *layout);
UiLayout layout;
int close;
};
+typedef struct UiCustomContainer {
+ UiContainerPrivate container;
+ UiObject *obj;
+ ui_addwidget_func add;
+ void *userdata;
+} UiCustomContainer;
+
typedef struct UiBoxContainer {
UiContainerPrivate container;
UiSubContainerType type;
UiDnD* ui_create_dnd(void) {
UiDnD *dnd = malloc(sizeof(UiDnD));
memset(dnd, 0, sizeof(UiDnD));
- dnd->providers = cxArrayListCreateSimple(sizeof(void*), 16);
+ dnd->providers = cxArrayListCreate(NULL, sizeof(void*), 16);
dnd->selected_action = 0;
dnd->delete = FALSE;
return dnd;
#endif
GtkWidget *spin = gtk_spin_button_new_with_range(min, max, args->step);
ui_set_name_and_style(spin, args->name, args->style_class);
- ui_set_widget_groups(obj->ctx, spin, args->groups);
+ ui_set_widget_states(obj->ctx, spin, args->states);
if(args->width > 0) {
gtk_widget_set_size_request(spin, args->width, -1);
}
UiVarEventData *event = malloc(sizeof(UiVarEventData));
+ memset(event, 0, sizeof(UiVarEventData));
event->obj = obj;
event->var = var;
event->observers = obs;
widget,
"draw",
G_CALLBACK(draw_callback),
- NULL);
+ drawingarea);
#endif
g_signal_connect(
- widget,
- "destroy",
- G_CALLBACK(destroy_drawingarea),
- drawingarea);
+ widget,
+ "destroy",
+ G_CALLBACK(destroy_drawingarea),
+ drawingarea);
return widget;
}
enum UiToolbarPos pos)
{
GtkWidget *button = ui_create_button(obj, item->args.label, item->args.icon, item->args.tooltip, item->args.onclick, item->args.onclickdata, 0, FALSE);
- ui_set_widget_groups(obj->ctx, button, item->args.groups);
+ ui_set_widget_states(obj->ctx, button, item->args.states);
ui_set_widget_visibility_states(obj->ctx, button, item->args.visibility_states);
WIDGET_ADD_CSS_CLASS(button, "flat");
headerbar_add(headerbar, box, button, pos);
enum UiToolbarPos pos)
{
GtkWidget *button = gtk_toggle_button_new();
- ui_set_widget_groups(obj->ctx, button, item->args.groups);
+ ui_set_widget_states(obj->ctx, button, item->args.states);
ui_set_widget_visibility_states(obj->ctx, button, item->args.visibility_states);
WIDGET_ADD_CSS_CLASS(button, "flat");
ui_setup_togglebutton(obj, button, item->args.label, item->args.icon, item->args.tooltip, item->args.varname, NULL, item->args.onchange, item->args.onchangedata, 0);
#define UI_HEADERBAR_PACK_START(h, w) adw_header_bar_pack_start(ADW_HEADER_BAR(h), w)
#define UI_HEADERBAR_PACK_END(h, w) adw_header_bar_pack_end(ADW_HEADER_BAR(h), w)
#define UI_HEADERBAR_SET_TITLE_WIDGET(h, w) adw_header_bar_set_title_widget(ADW_HEADER_BAR(h), w)
+#define UI_HEADERBAR_SHOW_TITLE_WIDGET(h, b) adw_header_bar_set_show_title(ADW_HEADER_BAR(h), b)
+#define UI_HEADERBAR_SETTINGS(h) adw_header_bar_set_centering_policy(ADW_HEADER_BAR(h), ADW_CENTERING_POLICY_LOOSE)
#else
#define UI_HEADERBAR GtkHeaderBar*
#define UI_HEADERBAR_CAST(h) GTK_HEADER_BAR(h)
#define UI_HEADERBAR_PACK_START(h, w) gtk_header_bar_pack_start(GTK_HEADER_BAR(h), w)
#define UI_HEADERBAR_PACK_END(h, w) gtk_header_bar_pack_end(GTK_HEADER_BAR(h), w)
+#define UI_HEADERBAR_SETTINGS(h)
#if GTK_MAJOR_VERSION >= 4
#define UI_HEADERBAR_SET_TITLE_WIDGET(h, w) gtk_header_bar_set_title_widget(GTK_HEADER_BAR(h), w)
+#define UI_HEADERBAR_SHOW_TITLE_WIDGET(h, b)
#else
#define UI_HEADERBAR_SET_TITLE_WIDGET(h, w) gtk_header_bar_set_custom_title(GTK_HEADER_BAR(h), w)
+#define UI_HEADERBAR_SHOW_TITLE_WIDGET(h, b)
#endif
#endif
#endif
void ui_image_init(void) {
- image_map = cxHashMapCreateSimple(CX_STORE_POINTERS);
+ image_map = cxHashMapCreate(NULL, CX_STORE_POINTERS, 16);
icon_theme = ICONTHEME_GET_DEFAULT();
}
#include "container.h"
#include "menu.h"
+#include "widget.h"
#include "../common/context.h"
#include "../common/object.h"
GtkWidget *drawingarea = gtk_drawing_area_new();
GtkWidget *toplevel;
GtkWidget *widget = drawingarea;
-
- gtk_widget_set_size_request(drawingarea, 100, 100);
+
+ int width = args->width;
+ int height = args->height;
+ if(width == 0 && height == 0) {
+ width = 100;
+ height = 100;
+ }
+ ui_widget_size_request(drawingarea, width, height);
#if GTK_MAJOR_VERSION < 4
GtkWidget *eventbox = gtk_event_box_new();
memset(tableview, 0, sizeof(UiListView));
tableview->obj = obj;
tableview->model = args->model;
+ tableview->multiselection = args->multiselection;
tableview->onactivate = args->onactivate;
tableview->onactivatedata = args->onactivatedata;
tableview->onselection = args->onselection;
tableview->onsave = args->onsave;
tableview->onsavedata = args->onsavedata;
+#if GTK_CHECK_VERSION(4, 0, 0)
+ tableview->coldata.listview = tableview;
+ tableview->coldata.column = 0;
+#endif
+
if(args->getvalue2) {
tableview->getvalue = args->getvalue2;
tableview->getvaluedata = args->getvalue2data;
static void column_factory_setup(GtkListItemFactory *factory, GtkListItem *item, gpointer userdata) {
UiColData *col = userdata;
UiModel *model = col->listview->model;
- UiModelType type = model->types[col->model_column];
+ UiModelType type = model->types[col->column];
if(type == UI_ICON_TEXT || type == UI_ICON_TEXT_FREE) {
GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
GtkWidget *image = gtk_image_new();
UiColData *col = userdata;
UiList *list = col->listview->var ? col->listview->var->value : NULL;
UiListView *listview = col->listview;
+ int datacolumn = listview->columns[col->column];
ObjWrapper *obj = gtk_list_item_get_item(item);
UiModel *model = col->listview->model;
- UiModelType type = model->types[col->model_column];
+ UiModelType type = model->types[col->column];
// cache the GtkListItem
CxHashKey row_key = cx_hash_key(&obj->i, sizeof(int));
UiRowItems *row = cxMapGet(listview->bound_rows, row_key);
if(row) {
- if(row->items[col->model_column] == NULL) {
+ if(row->items[col->column] == NULL) {
row->bound++;
}
} else {
cxMapPut(listview->bound_rows, row_key, row);
row->bound = 1;
}
- row->items[col->model_column] = item;
+ row->items[col->column] = item;
UiBool freevalue = FALSE;
- void *data = listview->getvalue(list, obj->data, obj->i, col->data_column, listview->getvaluedata, &freevalue);
+ void *data = listview->getvalue(list, obj->data, obj->i, datacolumn, listview->getvaluedata, &freevalue);
GtkWidget *child = gtk_list_item_get_child(item);
PangoAttrList *attributes = NULL;
}
}
- int style_col = col->data_column;
+ int style_col = datacolumn;
if(type == UI_ICON_TEXT || type == UI_ICON_TEXT_FREE) {
style_col++; // col->data_column is the icon, we need the next col for the label
}
}
case UI_ICON_TEXT_FREE: {
- void *data2 = listview->getvalue(list, obj->data, obj->i, col->data_column+1, listview->getvaluedata, &freevalue);
+ void *data2 = listview->getvalue(list, obj->data, obj->i, datacolumn+1, listview->getvaluedata, &freevalue);
if(type == UI_ICON_TEXT_FREE) {
freevalue = TRUE;
}
if(entry) {
entry->listview = col->listview;
entry->row = obj->i;
- entry->col = col->data_column;
+ entry->col = datacolumn;
entry->previous_value = strdup(data);
}
ENTRY_SET_TEXT(child, data);
}
}
-static void column_factory_unbind(GtkSignalListItemFactory *self, GtkListItem *item, UiColData *col) {
+static void column_factory_unbind(GtkSignalListItemFactory *self, GtkListItem *item, UiColData *col) {
ObjWrapper *obj = gtk_list_item_get_item(item);
UiListView *listview = col->listview;
CxHashKey row_key = cx_hash_key(&obj->i, sizeof(int));
UiRowItems *row = cxMapGet(listview->bound_rows, row_key);
if(row) {
- row->items[col->model_column] = NULL;
+ row->items[col->column] = NULL;
row->bound--;
if(row->bound == 0) {
cxMapRemove(listview->bound_rows, row_key);
}
listview->numcolumns = 1;
- listview->columns = malloc(sizeof(UiColData));
- listview->columns->listview = listview;
- listview->columns->data_column = 0;
- listview->columns->model_column = 0;
+ listview->columns = malloc(sizeof(int));
+ listview->columns[0] = 0;
listview->bound_rows = cxHashMapCreate(NULL, CX_STORE_POINTERS, 128);
listview->bound_rows->collection.simple_destructor = (cx_destructor_func)free;
GtkListItemFactory *factory = gtk_signal_list_item_factory_new();
- g_signal_connect(factory, "setup", G_CALLBACK(column_factory_setup), listview->columns);
- g_signal_connect(factory, "bind", G_CALLBACK(column_factory_bind), listview->columns);
+ g_signal_connect(factory, "setup", G_CALLBACK(column_factory_setup), &listview->coldata);
+ g_signal_connect(factory, "bind", G_CALLBACK(column_factory_bind), &listview->coldata);
+ g_signal_connect(factory, "unbind", G_CALLBACK(column_factory_unbind), &listview->coldata);
GtkSelectionModel *selection_model = create_selection_model(listview, ls, args->multiselection);
GtkWidget *view = gtk_list_view_new(GTK_SELECTION_MODEL(selection_model), factory);
return scroll_area;
}
-UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) {
+UIWIDGET ui_dropdown_create(UiObject *obj, UiListArgs *args) {
// to simplify things and share code with ui_tableview_create, we also
// use a UiModel for the listview
UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1);
}
listview->numcolumns = 1;
- listview->columns = malloc(sizeof(UiColData));
- listview->columns->listview = listview;
- listview->columns->data_column = 0;
- listview->columns->model_column = 0;
+ listview->columns = malloc(sizeof(int));
+ listview->columns[0] = 0;
listview->bound_rows = cxHashMapCreate(NULL, CX_STORE_POINTERS, 128);
listview->bound_rows->collection.simple_destructor = (cx_destructor_func)free;
GtkListItemFactory *factory = gtk_signal_list_item_factory_new();
- g_signal_connect(factory, "setup", G_CALLBACK(column_factory_setup), listview->columns);
- g_signal_connect(factory, "bind", G_CALLBACK(column_factory_bind), listview->columns);
+ g_signal_connect(factory, "setup", G_CALLBACK(column_factory_setup), &listview->coldata);
+ g_signal_connect(factory, "bind", G_CALLBACK(column_factory_bind), &listview->coldata);
GtkWidget *view = gtk_drop_down_new(G_LIST_MODEL(ls), NULL);
gtk_drop_down_set_factory(GTK_DROP_DOWN(view), factory);
list->obj = listview;
list->update = ui_listview_update2;
- list->getselection = ui_combobox_getselection;
- list->setselection = ui_combobox_setselection;
+ list->getselection = ui_dropdown_getselection;
+ list->setselection = ui_dropdown_setselection;
ui_update_liststore(ls, list);
} else if (args->static_elements && args->static_nelm > 0) {
gtk_selection_model_select_item(model, index, TRUE);
}
-void ui_combobox_select(UIWIDGET dropdown, int index) {
+void ui_dropdown_select(UIWIDGET dropdown, int index) {
gtk_drop_down_set_selected(GTK_DROP_DOWN(dropdown), index);
}
+static void add_column(UiListView *tableview, int index) {
+ UiModel *model = tableview->model;
+
+ UiColData *col = malloc(sizeof(UiColData));
+ col->listview = tableview;
+ col->column = index;
+
+ GtkListItemFactory *factory = gtk_signal_list_item_factory_new();
+ g_signal_connect(factory, "setup", G_CALLBACK(column_factory_setup), col);
+ g_signal_connect(factory, "bind", G_CALLBACK(column_factory_bind), col);
+ g_object_set_data_full(G_OBJECT(factory), "coldata", col, (GDestroyNotify)free);
+
+ GtkColumnViewColumn *column = gtk_column_view_column_new(model->titles[index], factory);
+ gtk_column_view_column_set_resizable(column, true);
+ gtk_column_view_insert_column(GTK_COLUMN_VIEW(tableview->widget), index, column);
+
+ int size = model->columnsize[index];
+ if(size > 0) {
+ gtk_column_view_column_set_fixed_width(column, size);
+ } else if(size < 0) {
+ gtk_column_view_column_set_expand(column, TRUE);
+ }
+}
+
UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) {
GListStore *ls = g_list_store_new(G_TYPE_OBJECT);
//g_list_store_append(ls, v1);
// create columns from UiModel
UiModel *model = args->model;
- int columns = model ? model->columns : 0;
+ int columns = 0;
+ if(model) {
+ columns = model->columns;
+ ui_model_add_observer(model, ui_listview_update_model, tableview);
+ }
- tableview->columns = calloc(columns, sizeof(UiColData));
+ tableview->columns = calloc(columns, sizeof(int));
tableview->numcolumns = columns;
tableview->bound_rows = cxHashMapCreate(NULL, CX_STORE_POINTERS, 128);
int addi = 0;
for(int i=0;i<columns;i++) {
- tableview->columns[i].listview = tableview;
- tableview->columns[i].model_column = i;
- tableview->columns[i].data_column = i+addi;
+ tableview->columns[i] = i+addi;
if(model->types[i] == UI_ICON_TEXT || model->types[i] == UI_ICON_TEXT_FREE) {
// icon+text has 2 data columns
addi++;
}
- GtkListItemFactory *factory = gtk_signal_list_item_factory_new();
- UiColData *col = &tableview->columns[i];
- g_signal_connect(factory, "setup", G_CALLBACK(column_factory_setup), col);
- g_signal_connect(factory, "bind", G_CALLBACK(column_factory_bind), col);
-
- GtkColumnViewColumn *column = gtk_column_view_column_new(model->titles[i], factory);
- gtk_column_view_column_set_resizable(column, true);
- gtk_column_view_append_column(GTK_COLUMN_VIEW(view), column);
-
- int size = model->columnsize[i];
- if(size > 0) {
- gtk_column_view_column_set_fixed_width(column, size);
- } else if(size < 0) {
- gtk_column_view_column_set_expand(column, TRUE);
- }
+ add_column(tableview, i);
}
// bind listview to list
return scroll_area;
}
+void ui_listview_update_model(UiModel *model, void *userdata, int insert_index, int delete_index) {
+ UiListView *listview = userdata;
+ if(insert_index >= listview->numcolumns) {
+ listview->numcolumns = insert_index+1;
+ listview->columns = realloc(listview->columns, listview->numcolumns * sizeof(UiColData));
+ }
+
+ gtk_column_view_set_model(GTK_COLUMN_VIEW(listview->widget), NULL);
+ cxMapClear(listview->bound_rows);
+
+ if(insert_index) {
+ int prev = 0;
+ if(insert_index > 0) {
+ prev = listview->columns[insert_index-1];
+ }
+ listview->columns[insert_index] = prev+1;
+ add_column(listview, insert_index);
+
+ if(insert_index+1 < listview->numcolumns) {
+ // the data index of trailing columns must be adjusted
+ UiModelType type = model->types[insert_index];
+ int add = 1;
+ if(type == UI_ICON_TEXT || type == UI_ICON_TEXT_FREE) {
+ add++;
+ }
+ for(int i=insert_index+1;i<listview->numcolumns;i++) {
+ listview->columns[i] += add;
+ }
+ }
+ } // TODO: delete_index
+
+ GListStore *ls = g_list_store_new(G_TYPE_OBJECT);
+ GtkSelectionModel *selection_model = create_selection_model(listview, ls, listview->multiselection);
+ gtk_column_view_set_model(GTK_COLUMN_VIEW(listview->widget), selection_model);
+ listview->selectionmodel = selection_model;
+ listview->liststore = ls;
+
+ if(listview->var) {
+ UiList *list = listview->var->value;
+ ui_list_update(list);
+ }
+}
+
static UiListSelection selectionmodel_get_selection(GtkSelectionModel *model) {
UiListSelection sel = { 0, NULL };
GtkBitset *bitset = gtk_selection_model_get_selection(model);
view->selection.count = 0;
view->selection.rows = NULL;
- CX_ARRAY_DECLARE(int, newselection);
- cx_array_initialize(newselection, 8);
-
size_t nitems = g_list_model_get_n_items(G_LIST_MODEL(view->liststore));
+ int *newselection = calloc(nitems, sizeof(int));
+ int selection_size = 0;
for(size_t i=0;i<nitems;i++) {
if(gtk_selection_model_is_selected(view->selectionmodel, i)) {
int s = (int)i;
- cx_array_simple_add(newselection, s);
+ newselection[selection_size++] = s;
}
}
- if(newselection_size > 0) {
- view->selection.count = newselection_size;
+ if(selection_size > 0) {
+ view->selection.count = selection_size;
view->selection.rows = newselection;
} else {
free(newselection);
void ui_listview_update2(UiList *list, int i) {
UiListView *view = list->obj;
+ view->current_row = -1;
if(i < 0) {
+ cxMapClear(view->bound_rows);
ui_update_liststore(view->liststore, list);
} else {
void *value = list->get(list, i);
CxHashKey row_key = cx_hash_key(&i, sizeof(int));
UiRowItems *row = cxMapGet(view->bound_rows, row_key);
if(row) {
+ UiColData coldata;
+ coldata.listview = view;
for(int c=0;c<view->numcolumns;c++) {
if(row->items[c] != NULL) {
- column_factory_bind(NULL, row->items[c], &view->columns[c]);
+ coldata.column = c;
+ column_factory_bind(NULL, row->items[c], &coldata);
}
}
}
ui_setop_enable(FALSE);
}
-UiListSelection ui_combobox_getselection(UiList *list) {
+UiListSelection ui_dropdown_getselection(UiList *list) {
UiListView *view = list->obj;
guint selection = gtk_drop_down_get_selected(GTK_DROP_DOWN(view->widget));
UiListSelection sel = { 0, NULL };
return sel;
}
-void ui_combobox_setselection(UiList *list, UiListSelection selection) {
+void ui_dropdown_setselection(UiList *list, UiListSelection selection) {
ui_setop_enable(TRUE);
UiListView *view = list->obj;
if(selection.count > 0) {
}
break;
}
+ case UI_STRING_EDITABLE: {
+ g_value_init(&value, G_TYPE_STRING);
+ g_value_set_string(&value, data);
+ if(freevalue) {
+ free(data);
+ }
+ break;
+ }
+ case UI_BOOL_EDITABLE: {
+ g_value_init(&value, G_TYPE_BOOLEAN);
+ intptr_t b = (intptr_t)data;
+ g_value_set_boolean(&value, b != 0 ? TRUE : FALSE);
+ if(freevalue) {
+ free(data);
+ }
+ break;
+ }
}
gtk_list_store_set_value(store, iter, c, &value);
types[c] = G_TYPE_OBJECT;
types[++c] = G_TYPE_STRING;
}
+ case UI_STRING_EDITABLE: types[c] = G_TYPE_STRING; break;
+ case UI_BOOL_EDITABLE: types[c] = G_TYPE_BOOLEAN; break;
}
}
int s = 0;
// create treeview
GtkWidget *view = gtk_tree_view_new();
ui_set_name_and_style(view, args->name, args->style_class);
- ui_set_widget_groups(obj->ctx, view, args->groups);
+ ui_set_widget_states(obj->ctx, view, args->states);
GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(NULL, renderer, "text", 0, NULL);
gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
//g_object_unref(path);
}
-void ui_combobox_select(UIWIDGET dropdown, int index) {
+void ui_dropdown_select(UIWIDGET dropdown, int index) {
gtk_combo_box_set_active(GTK_COMBO_BOX(dropdown), index);
}
UiListSelection ui_listview_getselection(UiList *list) {
UiListView *view = list->obj;
- UiListSelection selection = ui_listview_selection(
+ UiListSelection selection = ui_listview_get_selection(
gtk_tree_view_get_selection(GTK_TREE_VIEW(view->widget)),
NULL);
return selection;
-/* --------------------------- ComboBox --------------------------- */
+/* --------------------------- Dropdown --------------------------- */
-UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) {
+UIWIDGET ui_dropdown_create(UiObject *obj, UiListArgs *args) {
GtkWidget *combobox = gtk_combo_box_new();
if(args->width > 0) {
gtk_widget_set_size_request(combobox, args->width, -1);
}
ui_set_name_and_style(combobox, args->name, args->style_class);
- ui_set_widget_groups(obj->ctx, combobox, args->groups);
+ ui_set_widget_states(obj->ctx, combobox, args->states);
UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
UiLayout layout = UI_ARGS2LAYOUT(args);
ct->add(ct, combobox, &layout);
if(var) {
listview->var = var;
list->update = ui_combobox_modelupdate;
- list->getselection = ui_combobox_getselection;
- list->setselection = ui_combobox_setselection;
+ list->getselection = ui_dropdown_getselection;
+ list->setselection = ui_dropdown_setselection;
list->obj = listview;
list->update(list, -1);
} else if(args->static_nelm > 0) {
g_object_unref(store);
}
-UiListSelection ui_combobox_getselection(UiList *list) {
+UiListSelection ui_dropdown_getselection(UiList *list) {
UiListView *combobox = list->obj;
UiListSelection ret;
ret.rows = malloc(sizeof(int*));
return ret;
}
-void ui_combobox_setselection(UiList *list, UiListSelection selection) {
+void ui_dropdown_setselection(UiList *list, UiListSelection selection) {
ui_setop_enable(TRUE);
UiListView *combobox = list->obj;
if(selection.count > 0) {
GtkTreeViewColumn *column,
UiTreeEventData *event)
{
- UiListSelection selection = ui_listview_selection(
+ UiListSelection selection = ui_listview_get_selection(
gtk_tree_view_get_selection(treeview),
event);
GtkTreeSelection *treeselection,
UiTreeEventData *event)
{
- UiListSelection selection = ui_listview_selection(treeselection, event);
+ UiListSelection selection = ui_listview_get_selection(treeselection, event);
UiEvent e;
e.obj = event->obj;
}
}
-UiListSelection ui_listview_selection(
+UiListSelection ui_listview_get_selection(
GtkTreeSelection *selection,
UiTreeEventData *event)
{
if(v->var) {
ui_destroy_boundvar(v->obj->ctx, v->var);
}
+ if(v->model) {
+ ui_model_remove_observer(v->model, v);
+ ui_model_unref(v->model);
+ }
if(v->elements) {
for(int i=0;i<v->nelm;i++) {
free(v->elements[i]);
uisublist.numitems = 0;
uisublist.header = sublist->header ? strdup(sublist->header) : NULL;
uisublist.separator = sublist->separator;
- uisublist.widgets = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+ uisublist.widgets = cxLinkedListCreate(NULL, CX_STORE_POINTERS);
uisublist.listbox = uilistbox;
uisublist.userdata = sublist->userdata;
uisublist.index = cxListSize(sublists);
UiList *list = uisublist.var->value;
list->obj = sublist_ptr;
list->update = ui_listbox_list_update;
+ list->getselection = ui_listbox_list_getselection;
+ list->setselection = ui_listbox_list_setselection;
}
}
SCROLLEDWINDOW_SET_CHILD(scroll_area, listbox);
ui_set_name_and_style(listbox, args->name, args->style_class);
- ui_set_widget_groups(obj->ctx, listbox, args->groups);
+ ui_set_widget_states(obj->ctx, listbox, args->states);
UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
UiLayout layout = UI_ARGS2LAYOUT(args);
ct->add(ct, scroll_area, &layout);
uilistbox->onactivatedata = args->onactivatedata;
uilistbox->onbuttonclick = args->onbuttonclick;
uilistbox->onbuttonclickdata = args->onbuttonclickdata;
- uilistbox->sublists = cxArrayListCreateSimple(sizeof(UiListBoxSubList), 4);
+ uilistbox->sublists = cxArrayListCreate(NULL, sizeof(UiListBoxSubList), 4);
uilistbox->sublists->collection.advanced_destructor = (cx_destructor_func2)sublist_destroy;
uilistbox->sublists->collection.destructor_data = obj;
uilistbox->first_row = NULL;
UiList *list = var->value;
list->obj = uilistbox;
list->update = ui_listbox_dynamic_update;
+ list->getselection = ui_listbox_dynamic_getselection;
+ list->setselection = ui_listbox_dynamic_setselection;
ui_listbox_dynamic_update(list, -1);
}
}
cxListFree(uilistbox->sublists);
- CxList *new_sublists = cxArrayListCreateSimple(sizeof(UiListBoxSubList), list->count(list));
+ CxList *new_sublists = cxArrayListCreate(NULL, sizeof(UiListBoxSubList), list->count(list));
uilistbox->sublists = new_sublists;
UiSubList *sublist = list->first(list);
ui_listbox_update(uilistbox, 0, cxListSize(uilistbox->sublists));
}
+void ui_listbox_dynamic_setselection(UiList *list, UiListSelection sel) {
+ UiListBox *uilistbox = list->obj;
+ gtk_list_box_unselect_all(uilistbox->listbox);
+ if(sel.count > 0) {
+ int index = sel.rows[0];
+ if(index >= 0) {
+ GtkListBoxRow *row = gtk_list_box_get_row_at_index(uilistbox->listbox, index);
+ if(row) {
+ gtk_list_box_select_row(uilistbox->listbox, row);
+ }
+ }
+ }
+}
+
+UiListSelection ui_listbox_dynamic_getselection(UiList *list) {
+ UiListSelection sel = { 0, NULL };
+ UiListBox *uilistbox = list->obj;
+ GtkListBoxRow *row = gtk_list_box_get_selected_row(uilistbox->listbox);
+ if(row) {
+ sel.count = 1;
+ sel.rows = malloc(sizeof(int));
+ sel.rows[0] = gtk_list_box_row_get_index(row);
+ }
+ return sel;
+}
+
void ui_listbox_update(UiListBox *listbox, int from, int to) {
CxIterator i = cxListIterator(listbox->sublists);
size_t pos = 0;
ui_sourcelist_update_finished();
}
+void ui_listbox_list_setselection(UiList *list, UiListSelection sel) {
+ UiListBoxSubList *sublist = list->obj;
+ UiListBox *uilistbox = sublist->listbox;
+ gtk_list_box_unselect_all(uilistbox->listbox);
+ if(sel.count > 0) {
+ int index = sel.rows[0];
+ if(index >= 0 && index < sublist->numitems) {
+ int global_index = sublist->startpos + index;
+ GtkListBoxRow *row = gtk_list_box_get_row_at_index(uilistbox->listbox, global_index);
+ if(row) {
+ gtk_list_box_select_row(uilistbox->listbox, row);
+ }
+ }
+ }
+}
+
+UiListSelection ui_listbox_list_getselection(UiList *list) {
+ UiListSelection sel = { 0, NULL };
+ UiListBoxSubList *sublist = list->obj;
+ UiListBox *uilistbox = sublist->listbox;
+ GtkListBoxRow *row = gtk_list_box_get_selected_row(uilistbox->listbox);
+ if(row) {
+ int index = gtk_list_box_row_get_index(row);
+ size_t startpos = sublist->startpos;
+ if(index >= startpos && index < startpos+sublist->numitems) {
+ sel.count = 1;
+ sel.rows = malloc(sizeof(int));
+ sel.rows[0] = index - startpos;
+ }
+ }
+ return sel;
+}
+
void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user_data) {
UiEventDataExt *data = g_object_get_data(G_OBJECT(row), "ui-listbox-row-eventdata");
if(!data) {
* POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef TREE_H
-#define TREE_H
+#ifndef LIST_H
+#define LIST_H
-#include "../ui/tree.h"
+#include "../ui/list.h"
#include "toolkit.h"
#include <cx/array_list.h>
extern "C" {
#endif
+typedef struct UiListView UiListView;
typedef struct UiColData UiColData;
#if GTK_CHECK_VERSION(4, 10, 0)
} UiRowItems;
#endif
-typedef struct UiListView {
+struct UiColData {
+ UiListView *listview;
+ int column;
+};
+
+struct UiListView {
UiObject *obj;
GtkWidget *widget;
UiVar *var;
UiModel *model;
+ UiBool multiselection;
ui_getvaluefunc2 getvalue;
void *getvaluedata;
ui_getstylefunc getstyle;
CxMap *bound_rows;
GListStore *liststore;
GtkSelectionModel *selectionmodel;
- UiColData *columns;
+ int *columns;
int numcolumns;
+ UiColData coldata;
PangoAttrList *current_row_attributes;
#else
int style_offset;
void *onsavedata;
UiListSelection selection;
-} UiListView;
-
-struct UiColData {
- UiListView *listview;
- int model_column;
- int data_column;
};
typedef struct UiTreeEventData {
void ui_update_liststore(GListStore *liststore, UiList *list);
void ui_update_liststore_static(GListStore *liststore, char **elm, size_t nelm);
+void ui_listview_update_model(UiModel *model, void *userdata, int insert_index, int delete_index);
void ui_listview_update2(UiList *list, int i);
UiListSelection ui_listview_getselection2(UiList *list);
void ui_listview_setselection2(UiList *list, UiListSelection selection);
GtkWidget* ui_get_tree_widget(UIWIDGET widget);
+void ui_listview_update_model(UiModel *model, void *userdata, int insert_index, int delete_index);
void ui_listview_update(UiList *list, int i);
UiListSelection ui_listview_getselection(UiList *list);
void ui_listview_setselection(UiList *list, UiListSelection selection);
void ui_listview_selection_event(
GtkTreeSelection *treeselection,
UiTreeEventData *event);
-UiListSelection ui_listview_selection(
+UiListSelection ui_listview_get_selection(
GtkTreeSelection *selection,
UiTreeEventData *event);
int ui_tree_path_list_index(GtkTreePath *path);
GtkWidget* ui_create_combobox(UiObject *obj, UiModel *model, UiVar *var, char **elm, size_t nelm, ui_callback f, void *udata);
void ui_combobox_change_event(GtkComboBox *widget, UiEventData *e);
void ui_combobox_modelupdate(UiList *list, int i);
-UiListSelection ui_combobox_getselection(UiList *list);
-void ui_combobox_setselection(UiList *list, UiListSelection selection);
+UiListSelection ui_dropdown_getselection(UiList *list);
+void ui_dropdown_setselection(UiList *list, UiListSelection selection);
void ui_listbox_dynamic_update(UiList *list, int i);
+void ui_listbox_dynamic_setselection(UiList *list, UiListSelection sel);
+UiListSelection ui_listbox_dynamic_getselection(UiList *list);
+
void ui_listbox_update(UiListBox *listbox, int from, int to);
void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist, size_t listbox_insert_index);
void ui_listbox_list_update(UiList *list, int i);
+void ui_listbox_list_setselection(UiList *list, UiListSelection sel);
+UiListSelection ui_listbox_list_getselection(UiList *list);
void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user_data);
}
#endif
-#endif /* TREE_H */
+#endif /* LIST_H */
gtk_menu_shell_append(GTK_MENU_SHELL(parent), widget);
- if(i->groups) {
- CxList *groups = cxArrayListCreateSimple(sizeof(int), i->ngroups);
- cxListAddArray(groups, i->groups, i->ngroups);
- uic_add_group_widget(obj->ctx, widget, (ui_enablefunc)ui_set_enabled, groups);
- cxListFree(groups);
+ if(i->states) {
+ CxList *states = cxArrayListCreate(NULL, sizeof(int), i->nstates);
+ cxListAddArray(states, i->states, i->nstates);
+ uic_add_state_widget(obj->ctx, widget, (ui_enablefunc)ui_set_enabled, states);
+ cxListFree(states);
}
}
// binding object is a list of all connected UiActiveMenuItemList.
CxList *bindings = list->obj;
if(!bindings) {
- bindings = cxLinkedListCreate(var->from_ctx->mp->allocator, NULL, CX_STORE_POINTERS);
+ bindings = cxLinkedListCreate(var->from_ctx->mp->allocator, CX_STORE_POINTERS);
list->obj = bindings;
}
cxListAdd(bindings, ls);
g_action_map_add_action(obj->ctx->action_map, G_ACTION(action));
g_object_unref(action);
- if(i->groups) {
- CxList *groups = cxArrayListCreateSimple(sizeof(int), i->ngroups);
- cxListAddArray(groups, i->groups, i->ngroups);
- uic_add_group_widget(obj->ctx, action, (ui_enablefunc)action_enable, groups);
+ if(i->states) {
+ CxList *groups = cxArrayListCreate(NULL, sizeof(int), i->nstates);
+ cxListAddArray(groups, i->states, i->nstates);
+ uic_add_state_widget(obj->ctx, action, (ui_enablefunc)action_enable, groups);
cxListFree(groups);
}
}
static void stateful_action_notify_group(UiMenuRadioGroup *group, UiInteger *i) {
+ int intval = ui_get(i);
+
UiEvent event;
event.obj = group->obj;
event.window = event.obj->window;
event.document = event.obj->ctx->document;
event.eventdata = NULL;
event.eventdatatype = 0;
- event.intval = (int)i->value;
event.set = ui_get_setop();
CxIterator iter = cxListIterator(group->callbacks);
cx_foreach(UiCallbackData *, cb, iter) {
+ event.intval = intval == iter.index;
if(cb->callback) {
cb->callback(&event, cb->userdata);
}
}
+ event.intval = intval;
UiObserver *obs = i->observers;
while(obs) {
if(obs->callback) {
static UiMenuRadioGroup* create_radio_group(UiObject *obj, UiMenuRadioItem *item, GSimpleAction *action) {
UiMenuRadioGroup *group = cxZalloc(obj->ctx->allocator, sizeof(UiMenuRadioGroup));
- group->callbacks = cxArrayListCreate(obj->ctx->allocator, NULL, sizeof(UiCallbackData), 8);
+ group->callbacks = cxArrayListCreate(obj->ctx->allocator, sizeof(UiCallbackData), 8);
group->var = uic_create_var(ui_global_context(), item->varname, UI_VAR_INTEGER);
group->obj = obj;
group->action = action;
UiInteger *i = group->var->value;
CxList *bindings = i->obj;
if(!bindings) {
- bindings = cxLinkedListCreate(group->var->from_ctx->mp->allocator, NULL, CX_STORE_POINTERS);
+ bindings = cxLinkedListCreate(group->var->from_ctx->mp->allocator, CX_STORE_POINTERS);
i->obj = bindings;
i->set = ui_action_set_state;
i->get = ui_action_get_state;
// binding object is a list of all connected UiActiveMenuItemList.
CxList *bindings = list->obj;
if(!bindings) {
- bindings = cxLinkedListCreate(var->from_ctx->mp->allocator, NULL, CX_STORE_POINTERS);
+ bindings = cxLinkedListCreate(var->from_ctx->mp->allocator, CX_STORE_POINTERS);
list->obj = bindings;
}
cxListAdd(bindings, ls);
#include "text.h"
#include "container.h"
+#include "widget.h"
#include <cx/printf.h>
int sel = gtk_text_buffer_get_selection_bounds (buf, &begin, &end);
if(sel != textview->last_selection_state) {
if(sel) {
- ui_set_group(textview->ctx, UI_GROUP_SELECTION);
+ ui_set_state(textview->ctx, UI_GROUP_SELECTION);
} else {
- ui_unset_group(textview->ctx, UI_GROUP_SELECTION);
+ ui_unset_state(textview->ctx, UI_GROUP_SELECTION);
}
}
textview->last_selection_state = sel;
GtkWidget *text_area = gtk_text_view_new();
ui_set_name_and_style(text_area, args->name, args->style_class);
- ui_set_widget_groups(obj->ctx, text_area, args->groups);
+ ui_set_widget_states(obj->ctx, text_area, args->states);
gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text_area), GTK_WRAP_WORD_CHAR);
g_signal_connect(
GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS
SCROLLEDWINDOW_SET_CHILD(scroll_area, text_area);
- if(args->width > 0 || args->height > 0) {
- int width = args->width;
- int height = args->height;
- if(width == 0) {
- width = -1;
- }
- if(height == 0) {
- height = -1;
- }
- gtk_widget_set_size_request(scroll_area, width, height);
- }
+ ui_widget_size_request(scroll_area, args->width, args->height);
// font and padding
//PangoFontDescription *font;
static UIWIDGET create_textfield(UiObject *obj, UiBool frameless, UiBool password, UiTextFieldArgs *args) {
GtkWidget *textfield = gtk_entry_new();
ui_set_name_and_style(textfield, args->name, args->style_class);
- ui_set_widget_groups(obj->ctx, textfield, args->groups);
+ ui_set_widget_states(obj->ctx, textfield, args->states);
UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_STRING);
// TODO: move to common
static UiPathElm* default_pathelm_func(const char* full_path, size_t len, size_t* ret_nelm, void* data) {
cxstring *pathelms;
- size_t nelm = cx_strsplit_a(cxDefaultAllocator, cx_strn(full_path, len), CX_STR("/"), 4096, &pathelms);
+ size_t nelm = cx_strsplit_a(cxDefaultAllocator, cx_strn(full_path, len), cx_str("/"), 4096, &pathelms);
if (nelm == 0) {
*ret_nelm = 0;
pathtf->stack = gtk_stack_new();
gtk_widget_set_name(pathtf->stack, "path-textfield-box");
+ if(args->width > 0) {
+ gtk_widget_set_size_request(pathtf->stack, args->width, -1);
+ }
+
UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
UiLayout layout = UI_ARGS2LAYOUT(args);
ct->add(ct, pathtf->stack, &layout);
gtk_box_append(GTK_BOX(pathtf->hbox), button);
- if(i+1 < pathtf->current_nelm && cx_strcmp(cx_strn(elm->name, elm->name_len), CX_STR("/"))) {
+ if(i+1 < pathtf->current_nelm && cx_strcmp(cx_strn(elm->name, elm->name_len), cx_str("/"))) {
GtkWidget *path_separator = gtk_label_new("/");
gtk_widget_add_css_class(path_separator, "pathbar-button-inactive");
gtk_box_append(GTK_BOX(pathtf->hbox), path_separator);
G_CALLBACK(ui_path_textfield_destroy),
pathtf);
+ if(args->width > 0) {
+ gtk_widget_set_size_request(eventbox, args->width, -1);
+ }
+
UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
UiLayout layout = UI_ARGS2LAYOUT(args);
ct->add(ct, eventbox, &layout);
}
gtk_tool_item_set_is_important(button, TRUE);
- ui_set_widget_ngroups(obj->ctx, GTK_WIDGET(button), item->args.groups, item->ngroups);
+ ui_set_widget_nstates(obj->ctx, GTK_WIDGET(button), item->args.states, item->nstates);
if(item->args.onclick) {
UiEventData *event = cxMalloc(
if(item->args.tooltip) {
gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(button), item->args.tooltip);
}
- ui_set_widget_ngroups(obj->ctx, GTK_WIDGET(button), item->args.groups, item->ngroups);
+ ui_set_widget_nstates(obj->ctx, GTK_WIDGET(button), item->args.states, item->nstates);
UiVar* var = uic_widget_var(obj->ctx, obj->ctx, NULL, item->args.varname, UI_VAR_INTEGER);
if(var) {
}
}
- UiVarEventData *event = cxMalloc(
+ UiVarEventData *event = cxZalloc(
obj->ctx->allocator,
sizeof(UiVarEventData));
event->obj = obj;
if(item->args.icon) {
ui_button_set_icon_name(button, item->args.icon);
}
- ui_set_widget_groups(obj->ctx, button, item->args.groups);
+ ui_set_widget_states(obj->ctx, button, item->args.states);
gtk_header_bar_pack_start(hb, button);
#include "../common/menu.h"
#include "../common/toolbar.h"
#include "../common/threadpool.h"
+#include "../common/app.h"
#include <cx/string.h>
#include <cx/printf.h>
static const char *application_name;
-static ui_callback startup_func;
-static void *startup_data;
-static ui_callback open_func;
-void *open_data;
-static ui_callback exit_func;
-void *exit_data;
-
static ui_callback appclose_fnc;
static void *appclose_udata;
return application_name;
}
-void ui_onstartup(ui_callback f, void *userdata) {
- startup_func = f;
- startup_data = userdata;
-}
-
-void ui_onopen(ui_callback f, void *userdata) {
- open_func = f;
- open_data = userdata;
-}
-
-void ui_onexit(ui_callback f, void *userdata) {
- exit_func = f;
- exit_data = userdata;
-}
-
void ui_app_exit_on_shutdown(UiBool exitapp) {
exit_on_shutdown = exitapp;
}
#ifdef UI_APPLICATION
static void app_startup(GtkApplication* app, gpointer userdata) {
- if(startup_func) {
- startup_func(NULL, startup_data);
- }
+ uic_application_startup(NULL);
}
static void app_activate(GtkApplication* app, gpointer userdata) {
}
static void app_shutdown(GtkApplication *app, gpointer userdata) {
- if(exit_func) {
- exit_func(NULL, exit_data);
- }
+ uic_application_exit(NULL);
ui_app_save_settings();
}
free(appid.ptr);
#else
- if(startup_func) {
- startup_func(NULL, startup_data);
- }
+ uic_application_startup(NULL);
gtk_main();
- if(exit_func) {
- exit_func(NULL, exit_data);
- }
+ uic_application_exit(NULL);
ui_app_save_settings();
#endif
if(exit_on_shutdown) {
#ifndef UI_GTK2
void ui_app_quit() {
- g_application_quit(G_APPLICATION(app));
+ g_application_quit(G_APPLICATION(app)); // TODO: fix, does not work
}
GtkApplication* ui_get_application() {
void ui_show(UiObject *obj) {
gboolean visible = gtk_widget_is_visible(obj->widget);
- uic_check_group_widgets(obj->ctx);
+ uic_check_state_widgets(obj->ctx);
#if GTK_MAJOR_VERSION >= 4
gtk_window_present(GTK_WINDOW(obj->widget));
#elif GTK_MAJOR_VERSION <= 3
gtk_widget_set_visible(widget, visible);
#else
if(visible) {
- gtk_widget_set_no_show_all(widget, FALSE);
gtk_widget_show_all(widget);
} else {
+ gtk_widget_set_no_show_all(widget, FALSE);
gtk_widget_hide(widget);
}
#endif
#if GTK_MAJOR_VERSION == 4
static const char *ui_gtk_css =
+".ui-entry-box {\n"
+" background-color: alpha(currentColor, 0.1);"
+" border-radius: 6px;"
+" padding: 7px;"
+"}\n"
"#path-textfield-box {\n"
" background-color: alpha(currentColor, 0.1);"
" border-radius: 6px;"
}
if(style_classes) {
cxstring *cls = NULL;
- size_t numClasses = cx_strsplit_a(cxDefaultAllocator, cx_str(style_classes), CX_STR(" "), 128, &cls);
+ size_t numClasses = cx_strsplit_a(cxDefaultAllocator, cx_str(style_classes), cx_str(" "), 128, &cls);
for(int i=0;i<numClasses;i++) {
cxmutstr m = cx_strdup(cls[i]);
#if GTK_MAJOR_VERSION >= 4
}
}
-void ui_set_widget_groups(UiContext *ctx, GtkWidget *widget, const int *groups) {
- if(!groups) {
+void ui_set_widget_states(UiContext *ctx, GtkWidget *widget, const int *states) {
+ if(!states) {
return;
}
- size_t ngroups = uic_group_array_size(groups);
- ui_set_widget_ngroups(ctx, widget, groups, ngroups);
+ size_t nstates = uic_state_array_size(states);
+ ui_set_widget_nstates(ctx, widget, states, nstates);
}
-void ui_set_widget_ngroups(UiContext *ctx, GtkWidget *widget, const int *groups, size_t ngroups) {
- if(ngroups > 0) {
- uic_add_group_widget_i(ctx, widget, (ui_enablefunc)ui_set_enabled, groups, ngroups);
+void ui_set_widget_nstates(UiContext *ctx, GtkWidget *widget, const int *states, size_t nstates) {
+ if(nstates > 0) {
+ uic_add_state_widget_i(ctx, widget, (ui_enablefunc)ui_set_enabled, states, nstates);
ui_set_enabled(widget, FALSE);
}
}
if(!states) {
return;
}
- size_t nstates = uic_group_array_size(states);
+ size_t nstates = uic_state_array_size(states);
ui_set_widget_nvisibility_states(ctx, widget, states, nstates);
}
void ui_set_widget_nvisibility_states(UiContext *ctx, GtkWidget *widget, const int *states, size_t ngroups) {
if(ngroups > 0) {
- uic_add_group_widget_i(ctx, widget, (ui_enablefunc)ui_set_visible, states, ngroups);
+ uic_add_state_widget_i(ctx, widget, (ui_enablefunc)ui_set_visible, states, ngroups);
ui_set_visible(widget, FALSE);
}
}
UiObserver **observers;
ui_callback callback;
void *userdata;
+ int customint1;
+ int customint2;
} UiVarEventData;
typedef enum UiOrientation UiOrientation;
int ui_get_scalefactor();
void ui_set_name_and_style(GtkWidget *widget, const char *name, const char *style);
-void ui_set_widget_groups(UiContext *ctx, GtkWidget *widget, const int *groups);
-void ui_set_widget_ngroups(UiContext *ctx, GtkWidget *widget, const int *groups, size_t ngroups);
+void ui_set_widget_states(UiContext *ctx, GtkWidget *widget, const int *states);
+void ui_set_widget_nstates(UiContext *ctx, GtkWidget *widget, const int *states, size_t nstates);
void ui_set_widget_visibility_states(UiContext *ctx, GtkWidget *widget, const int *states);
void ui_set_widget_nvisibility_states(UiContext *ctx, GtkWidget *widget, const int *states, size_t ngroups);
#include "container.h"
#include "webview.h"
+#include "widget.h"
#ifdef UI_WEBVIEW
ui_set_name_and_style(webview, args->name, args->style_class);
+ ui_widget_size_request(webview, args->width, args->height);
+
UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_GENERIC);
if(var) {
WebViewData *data = malloc(sizeof(WebViewData));
}
}
- ui_set_widget_groups(obj->ctx, webview, args->groups);
+ ui_set_widget_states(obj->ctx, webview, args->states);
UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
UiLayout layout = UI_ARGS2LAYOUT(args);
ct->add(ct, webview, &layout);
#include "../common/object.h"
-UIEXPORT UIWIDGET ui_customwidget_create(UiObject *obj, ui_createwidget_func create_widget, void *userdata, UiWidgetArgs *args) {
+void ui_widget_size_request(UIWIDGET w, int width, int height) {
+ if(width > 0 || height > 0) {
+ if(width == 0) {
+ width = -1;
+ }
+ if(height == 0) {
+ height = -1;
+ }
+ gtk_widget_set_size_request(w, width, height);
+ }
+}
+
+
+UIWIDGET ui_customwidget_create(UiObject *obj, ui_createwidget_func create_widget, void *userdata, UiWidgetArgs *args) {
UIWIDGET widget = create_widget(obj, args, userdata);
UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
extern "C" {
#endif
-
+/*
+ * Sets a widget width/height.
+ *
+ * If wdith or height is 0, the dimension is not changed
+ */
+void ui_widget_size_request(UIWIDGET w, int width, int height);
#ifdef __cplusplus
ui_set_property("ui.window.splitview.pos", buf);
}
-static UiObject* create_window(const char *title, void *window_data, UiBool sidebar, UiBool splitview, UiBool simple) {
+static UiObject* create_window(const char *title, UiBool sidebar, UiBool splitview, UiBool simple) {
UiObject *obj = uic_object_new_toplevel();
#ifdef UI_LIBADWAITA
obj->widget = gtk_window_new(GTK_WINDOW_TOPLEVEL);
#endif
- obj->window = window_data;
-
#if GTK_CHECK_VERSION(4, 0, 0)
obj->ctx->action_map = G_ACTION_MAP(obj->widget);
#endif
obj);
#endif
+ int splitview_pos = 0;
+ if(splitview) {
+ const char *splitview_pos_str = ui_get_property("ui.window.splitview.pos");
+ splitview_pos= splitview_window_default_pos;
+ if(splitview_pos < 0) {
+ splitview_pos = window_width / 2;
+ }
+ if(splitview_pos_str && splitview_window_use_prop) {
+ int sv_pos = atoi(splitview_pos_str);
+ if(sv_pos > 0) {
+ splitview_pos = sv_pos;
+ }
+ }
+ }
+
GtkWidget *vbox = ui_gtk_vbox_new(0);
#ifdef UI_LIBADWAITA
GtkWidget *toolbar_view = adw_toolbar_view_new();
G_CALLBACK(save_window_splitview_pos),
NULL);
- const char *splitview_pos_str = ui_get_property("ui.window.splitview.pos");
- int pos = splitview_window_default_pos;
- if(pos < 0) {
- pos = window_width / 2;
- }
- if(splitview_pos_str && splitview_window_use_prop) {
- int splitview_pos = atoi(splitview_pos_str);
- if(splitview_pos > 0) {
- pos = splitview_pos;
- }
- }
- gtk_paned_set_position(GTK_PANED(content), pos);
+ gtk_paned_set_position(GTK_PANED(content), splitview_pos);
GtkWidget *right_panel = adw_toolbar_view_new();
GtkWidget *right_vbox = ui_gtk_vbox_new(0);
if(!simple) {
ui_fill_headerbar(obj, headerbar_sidebar, headerbar_main, headerbar_right);
}
-#elif GTK_MAJOR_VERSION >= 4
- GtkWidget *content_box = ui_gtk_vbox_new(0);
- WINDOW_SET_CONTENT(obj->widget, vbox);
- if(!simple) {
- if(uic_get_menu_list()) {
- GtkWidget *mb = ui_create_menubar(obj);
- if(mb) {
- BOX_ADD(vbox, mb);
- }
- }
- }
- if(sidebar) {
- GtkWidget *paned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
- GtkWidget *sidebar_vbox = ui_gtk_vbox_new(0);
- gtk_paned_set_start_child(GTK_PANED(paned), sidebar_vbox);
- gtk_paned_set_end_child(GTK_PANED(paned), content_box);
- BOX_ADD_EXPAND(GTK_BOX(vbox), paned);
- g_object_set_data(G_OBJECT(obj->widget), "ui_sidebar", sidebar_vbox);
- } else {
- BOX_ADD_EXPAND(GTK_BOX(vbox), content_box);
- }
#else
if(!simple) {
// menu
if(uic_get_menu_list()) {
GtkWidget *mb = ui_create_menubar(obj);
if(mb) {
- gtk_box_pack_start(GTK_BOX(vbox), mb, FALSE, FALSE, 0);
+ BOX_ADD(vbox, mb);
}
}
// toolbar
+#if GTK_MAJOR_VERSION >= 4
+ // TODO: gtk4 toolbar
+#else
if(uic_toolbar_isenabled()) {
GtkWidget *tb = ui_create_toolbar(obj);
if(tb) {
- gtk_box_pack_start(GTK_BOX(vbox), tb, FALSE, FALSE, 0);
+ BOX_ADD(vbox, tb);
}
}
-
+#endif
//GtkWidget *hb = ui_create_headerbar(obj);
//gtk_window_set_titlebar(GTK_WINDOW(obj->widget), hb);
}
GtkWidget *content_box = ui_gtk_vbox_new(0);
WINDOW_SET_CONTENT(obj->widget, vbox);
- if(sidebar) {
+ if(sidebar || splitview) {
GtkWidget *paned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
- GtkWidget *sidebar_vbox = ui_gtk_vbox_new(0);
- gtk_paned_add1(GTK_PANED(paned), sidebar_vbox);
- gtk_paned_add2(GTK_PANED(paned), content_box);
+ if(sidebar) {
+ GtkWidget *sidebar_vbox = ui_gtk_vbox_new(0);
+ PANED_SET_CHILD1(paned, sidebar_vbox);
+ g_object_set_data(G_OBJECT(obj->widget), "ui_sidebar", sidebar_vbox);
+ gtk_paned_set_position(GTK_PANED(paned), 200);
+ }
+
+ if(splitview) {
+ GtkWidget *content_paned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
+ gtk_paned_set_position(GTK_PANED(content_paned), splitview_pos);
+ PANED_SET_CHILD2(paned, content_paned);
+
+ GtkWidget *right_content_box = ui_gtk_vbox_new(0);
+ PANED_SET_CHILD1(content_paned, content_box);
+ PANED_SET_CHILD2(content_paned, right_content_box);
+
+ g_object_set_data(G_OBJECT(obj->widget), "ui_window_splitview", content_paned);
+ g_object_set_data(G_OBJECT(obj->widget), "ui_left_panel", content_box);
+ g_object_set_data(G_OBJECT(obj->widget), "ui_right_panel", right_content_box);
+ } else {
+ PANED_SET_CHILD2(paned, content_box);
+ }
+
BOX_ADD_EXPAND(GTK_BOX(vbox), paned);
- g_object_set_data(G_OBJECT(obj->widget), "ui_sidebar", sidebar_vbox);
- gtk_paned_set_position (GTK_PANED(paned), 200);
} else {
BOX_ADD_EXPAND(GTK_BOX(vbox), content_box);
}
}
-UiObject* ui_window(const char *title, void *window_data) {
- return create_window(title, window_data, FALSE, FALSE, FALSE);
+UiObject* ui_window(const char *title) {
+ return create_window(title, FALSE, FALSE, FALSE);
}
-UiObject *ui_sidebar_window(const char *title, void *window_data) {
- return create_window(title, window_data, TRUE, FALSE, FALSE);
+UiObject *ui_sidebar_window(const char *title) {
+ return create_window(title, TRUE, FALSE, FALSE);
}
-UIEXPORT UiObject *ui_splitview_window(const char *title, UiBool sidebar) {
- return create_window(title, NULL, sidebar, TRUE, FALSE);
+UiObject *ui_splitview_window(const char *title, UiBool sidebar) {
+ return create_window(title, sidebar, TRUE, FALSE);
}
-UiObject* ui_simple_window(const char *title, void *window_data) {
- return create_window(title, window_data, FALSE, FALSE, TRUE);
+UiObject* ui_simple_window(const char *title) {
+ return create_window(title, FALSE, FALSE, TRUE);
}
void ui_window_size(UiObject *obj, int width, int height) {
XtManageChild(button);
ui_container_add(ctn, button);
- ui_set_widget_groups(obj->ctx, button, args->groups);
+ ui_set_widget_groups(obj->ctx, button, args->states);
if(args->onclick) {
UiEventData *eventdata = malloc(sizeof(UiEventData));
XtManageChild(button);
ui_container_add(ctn, button);
- ui_set_widget_groups(obj->ctx, button, args->groups);
+ ui_set_widget_groups(obj->ctx, button, args->states);
- ui_bind_togglebutton(obj, button, args->varname, args->value, args->onchange, args->onchangedata, args->enable_group);
+ ui_bind_togglebutton(obj, button, args->varname, args->value, args->onchange, args->onchangedata, args->enable_state);
XmStringFree(label);
return button;
XtManageChild(button);
ui_container_add(ctn, button);
- ui_set_widget_groups(obj->ctx, button, args->groups);
+ ui_set_widget_groups(obj->ctx, button, args->states);
- ui_bind_togglebutton(obj, button, args->varname, args->value, args->onchange, args->onchangedata, args->enable_group);
+ ui_bind_togglebutton(obj, button, args->varname, args->value, args->onchange, args->onchangedata, args->enable_state);
XmStringFree(label);
return button;
if(event->value > 0) {
// button in configured to enable/disable states
if(tb->set) {
- ui_set_group(event->obj->ctx, event->value);
+ ui_set_state(event->obj->ctx, event->value);
} else {
- ui_unset_group(event->obj->ctx, event->value);
+ ui_unset_state(event->obj->ctx, event->value);
}
}
if(event->value > 0) {
// button in configured to enable/disable states
if(tb->set) {
- ui_set_group(event->obj->ctx, event->value);
+ ui_set_state(event->obj->ctx, event->value);
} else {
- ui_unset_group(event->obj->ctx, event->value);
+ ui_unset_state(event->obj->ctx, event->value);
}
}
if(!rb) {
// first button in the radiobutton group
// create a list for all buttons and use the list as value obj
- rb = cxArrayListCreateSimple(CX_STORE_POINTERS, 4);
+ rb = cxArrayListCreate(NULL, CX_STORE_POINTERS, 4);
value->obj = rb;
value->get = ui_radiobutton_get;
value->set = ui_radiobutton_set;
XtManageChild(button);
ui_container_add(ctn, button);
- ui_set_widget_groups(obj->ctx, button, args->groups);
+ ui_set_widget_groups(obj->ctx, button, args->states);
UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER);
if(var) {
if(!rb) {
// first button in the radiobutton group
// create a list for all buttons and use the list as value obj
- rb = cxArrayListCreateSimple(CX_STORE_POINTERS, 4);
+ rb = cxArrayListCreate(NULL, CX_STORE_POINTERS, 4);
value->obj = rb;
value->get = ui_radiobutton_get;
value->set = ui_radiobutton_set;
event->userdata = args->onchangedata;
event->observers = NULL;
event->var = var;
- event->value = args->enable_group;
+ event->value = args->enable_state;
XtAddCallback(
button,
XmNvalueChangedCallback,
tabview->select = ui_motif_tabview_select;
tabview->add = ui_motif_tabview_add_tab;
tabview->remove = ui_motif_tabview_remove;
- tabview->tabs = cxArrayListCreate(obj->ctx->allocator, cx_cmp_ptr, sizeof(UiTab), 8);
+ tabview->tabs = cxArrayListCreate(obj->ctx->allocator, sizeof(UiTab), 8);
tabview->current_index = -1;
UiTabViewContainer *ct = ui_malloc(obj->ctx, sizeof(UiTabViewContainer));
XtManageChild(spinbox);
ui_container_add(ctn, spinbox);
- ui_set_widget_groups(obj->ctx, spinbox, args->groups);
+ ui_set_widget_groups(obj->ctx, spinbox, args->states);
WidgetList children;
Cardinal num_children;
listview->onselection = args->onselection;
listview->onselectiondata = args->onselectiondata;
+ char **static_elements = args->static_elements;
+ size_t static_nelm = args->static_nelm;
if(var) {
UiList *list = var->value;
list->obj = listview;
list->getselection = ui_listview_getselection;
list->setselection = ui_listview_setselection;
ui_listview_update(list, 0);
+ } else if(static_elements && static_nelm > 0) {
+ XmStringTable items = calloc(static_nelm, sizeof(XmString));
+ for(int i=0;i<static_nelm;i++) {
+ items[i] = XmStringCreateLocalized(static_elements[i]);
+ }
+ XtVaSetValues(
+ listview->widget,
+ XmNitems, items,
+ XmNitemCount,
+ static_nelm,
+ NULL);
+ for (int i=0;i<static_nelm;i++) {
+ XmStringFree(items[i]);
+ }
+ free(items);
+ listview->getvalue = getvalue_wrapper;
+ listview->getvaluedata = ui_strmodel_getvalue;
}
XtAddCallback(
XtAddCallback(
widget,
XmNextendedSelectionCallback,
- (XtCallbackProc)ui_listview_selection,
+ (XtCallbackProc)ui_listview_selection_changed,
listview);
XtAddCallback(
widget,
XmNsingleSelectionCallback,
- (XtCallbackProc)ui_listview_selection,
+ (XtCallbackProc)ui_listview_selection_changed,
listview);
return widget;
}
+void ui_listview_select(UIWIDGET listview, int index) {
+ XmListDeselectAllItems(listview);
+ XmListSelectPos(listview, index+1, False);
+}
+
+int ui_listview_selection(UIWIDGET listview) {
+ int *selpositions = NULL;
+ int numpos = 0;
+ XtVaGetValues(listview, XmNselectedPositions, &selpositions, XmNselectedPositionCount, &numpos, NULL);
+ return numpos > 0 ? selpositions[0] : -1;
+}
+
void ui_listview_destroy(Widget w, UiListView *listview, XtPointer d) {
- // TODO
+ ui_listselection_free(listview->current_selection);
+ if(listview->model) {
+ ui_model_remove_observer(listview->model, listview);
+ ui_model_unref(listview->model);
+ }
+ free(listview);
}
static void list_callback(UiObject *obj, UiListSelection sel, ui_callback callback, void *userdata) {
}
}
-void ui_listview_selection(Widget w, UiListView *listview, XmListCallbackStruct *cb) {
+void ui_listview_selection_changed(Widget w, UiListView *listview, XmListCallbackStruct *cb) {
listview_save_selection(listview, cb);
if(listview->onselection) {
list_callback(listview->obj, listview->current_selection, listview->onselection, listview->onselectiondata);
/* ------------------------------- Drop Down ------------------------------- */
-static void ui_dropdown_selection(
+static void ui_dropdown_selection_changed(
Widget w,
UiListView *listview,
XmComboBoxCallbackStruct *cb)
}
}
-UIWIDGET ui_combobox_create(UiObject* obj, UiListArgs *args) {
+UIWIDGET ui_dropdown_create(UiObject* obj, UiListArgs *args) {
Arg xargs[16];
int n = 0;
listview->onselection = args->onselection;
listview->onselectiondata = args->onselectiondata;
+ char **static_elements = args->static_elements;
+ size_t static_nelm = args->static_nelm;
if(var) {
UiList *list = var->value;
list->obj = listview;
list->getselection = ui_dropdown_getselection;
list->setselection = ui_dropdown_setselection;
ui_listview_update(list, 0);
+ } else if(static_elements && static_nelm > 0) {
+ XmStringTable items = calloc(static_nelm, sizeof(XmString));
+ for(int i=0;i<static_nelm;i++) {
+ items[i] = XmStringCreateLocalized(static_elements[i]);
+ }
+ XtVaSetValues(
+ listview->widget,
+ XmNitems, items,
+ XmNitemCount,
+ static_nelm,
+ NULL);
+ for (int i=0;i<static_nelm;i++) {
+ XmStringFree(items[i]);
+ }
+ free(items);
+ listview->getvalue = getvalue_wrapper;
+ listview->getvaluedata = ui_strmodel_getvalue;
}
XtAddCallback(
XtAddCallback(
widget,
XmNselectionCallback,
- (XtCallbackProc)ui_dropdown_selection,
+ (XtCallbackProc)ui_dropdown_selection_changed,
listview);
return widget;
}
return sel;
}
+
+void ui_dropdown_select(UIWIDGET dropdown, int index) {
+ XtVaSetValues(dropdown, XmNselectedPosition, index, NULL);
+}
+
+int ui_dropdown_selection(UIWIDGET dropdown) {
+ int pos = -1;
+ XtVaGetValues(dropdown, XmNselectedPosition, &pos, NULL);
+}
#define LIST_H
#include "toolkit.h"
-#include "../ui/tree.h"
+#include "../ui/list.h"
#include "../common/context.h"
#ifdef __cplusplus
void ui_listview_destroy(Widget w, UiListView *listview, XtPointer d);
void ui_listview_activate(Widget w, UiListView *listview, XmListCallbackStruct *cb);
-void ui_listview_selection(Widget w, UiListView *listview, XmListCallbackStruct *cb);
+void ui_listview_selection_changed(Widget w, UiListView *listview, XmListCallbackStruct *cb);
void ui_listview_update(UiList *list, int i);
UiListSelection ui_listview_getselection(UiList *list);
#include "menu.h"
#include "button.h"
#include "toolkit.h"
-#include "stock.h"
#include "container.h"
#include "../common/context.h"
#include "../common/menu.h"
eventdata);
}
- ui_set_widget_groups(obj->ctx, mitem, it->groups);
+ ui_set_widget_groups(obj->ctx, mitem, it->states);
}
void add_menuseparator_widget(Widget p, int i, UiMenuItemI *item, UiObject *obj) {
ui_bind_togglebutton(obj, checkbox, it->varname, NULL, it->callback, it->userdata, 0);
- ui_set_widget_groups(obj->ctx, checkbox, it->groups);
+ ui_set_widget_groups(obj->ctx, checkbox, it->states);
}
void add_radioitem_widget(Widget p, int index, UiMenuItemI *item, UiObject *obj) {
// binding object is a list of all connected UiActiveMenuItemList.
CxList *bindings = list->obj;
if(!bindings) {
- bindings = cxLinkedListCreate(ls->var->from_ctx->mp->allocator, NULL, CX_STORE_POINTERS);
+ bindings = cxLinkedListCreate(ls->var->from_ctx->mp->allocator, CX_STORE_POINTERS);
list->obj = bindings;
}
cxListAdd(bindings, ls);
MOTIF_OBJPRE = $(OBJ_DIR)$(MOTIF_SRC_DIR)
MOTIFOBJ = toolkit.o
-MOTIFOBJ += stock.o
MOTIFOBJ += window.o
MOTIFOBJ += widget.o
MOTIFOBJ += container.o
static cxmutstr concat_path_s(cxstring base, cxstring path) {
if(!path.ptr) {
- path = CX_STR("");
+ path = cx_str("");
}
int add_separator = 0;
cxmutstr url;
if(add_separator) {
- url = cx_strcat(3, base, CX_STR("/"), path);
+ url = cx_strcat(3, base, cx_str("/"), path);
} else {
url = cx_strcat(2, base, path);
}
int sel = left < right ? 1 : 0;
if(sel != textarea->last_selection_state) {
if(sel) {
- ui_set_group(textarea->obj->ctx, UI_GROUP_SELECTION);
+ ui_set_state(textarea->obj->ctx, UI_GROUP_SELECTION);
} else {
- ui_unset_group(textarea->obj->ctx, UI_GROUP_SELECTION);
+ ui_unset_state(textarea->obj->ctx, UI_GROUP_SELECTION);
}
}
textarea->last_selection_state = sel;
XtManageChild(textfield);
ui_container_add(ctn, textfield);
- ui_set_widget_groups(obj->ctx, textfield, args->groups);
+ ui_set_widget_groups(obj->ctx, textfield, args->states);
UiEventDataExt *eventdata = malloc(sizeof(UiEventDataExt));
memset(eventdata, 0, sizeof(UiEventDataExt));
#include "toolbar.h"
#include "button.h"
-#include "stock.h"
#include "list.h"
#include <cx/hash_map.h>
#include "toolkit.h"
#include "toolbar.h"
#include "container.h"
-#include "stock.h"
#include "../common/menu.h"
#include "../common/toolbar.h"
#include "../common/document.h"
#include "../common/properties.h"
+#include "../common/app.h"
#include <cx/buffer.h>
#include <X11/Intrinsic.h>
static Widget active_window;
static const char *application_name;
-static ui_callback startup_func;
-static void *startup_data;
-static ui_callback open_func;
-static void *open_data;
-static ui_callback exit_func;
-static void *exit_data;
-
static ui_callback appclose_fnc;
static void *appclose_udata;
return display;
}
-void ui_onstartup(ui_callback f, void *userdata) {
- startup_func = f;
- startup_data = userdata;
-}
-
-void ui_onopen(ui_callback f, void *userdata) {
- open_func = f;
- open_data = userdata;
-}
-
-void ui_onexit(ui_callback f, void *userdata) {
- exit_func = f;
- exit_data = userdata;
-}
-
void ui_app_exit_on_shutdown(UiBool exitapp) {
exit_on_shutdown = exitapp;
}
void ui_main() {
- if(startup_func) {
- startup_func(NULL, startup_data);
- }
+ uic_application_startup(NULL);
XtAppMainLoop(app);
- if(exit_func) {
- exit_func(NULL, exit_data);
- }
+ uic_application_exit(NULL);
uic_store_app_properties();
if(exit_on_shutdown) {
exit(0);
}
void ui_show(UiObject *obj) {
- uic_check_group_widgets(obj->ctx);
+ uic_check_state_widgets(obj->ctx);
if(!XtIsRealized(obj->widget)) {
XtRealizeWidget(obj->widget);
obj->ref++;
if(!groups) {
return;
}
- size_t ngroups = uic_group_array_size(groups);
+ size_t ngroups = uic_state_array_size(groups);
ui_set_widget_ngroups(ctx, widget, groups, ngroups);
}
void ui_set_widget_ngroups(UiContext *ctx, Widget widget, const int *groups, size_t ngroups) {
if(ngroups > 0) {
- uic_add_group_widget_i(ctx, widget, (ui_enablefunc)ui_set_enabled, groups, ngroups);
+ uic_add_state_widget_i(ctx, widget, (ui_enablefunc)ui_set_enabled, groups, ngroups);
ui_set_enabled(widget, FALSE);
}
}
}
-static UiObject* create_window(const char *title, void *window_data, Boolean simple) {
+static UiObject* create_window(const char *title, Boolean simple) {
CxMempool *mp = cxMempoolCreateSimple(256);
const CxAllocator *a = mp->allocator;
UiObject *obj = uic_object_new_toplevel();
- obj->window = window_data;
obj->destroy = ui_window_widget_destroy;
int window_width = window_default_width;
return obj;
}
-UiObject* ui_window(const char *title, void *window_data) {
- return create_window(title, window_data, FALSE);
+UiObject* ui_window(const char *title) {
+ return create_window(title, FALSE);
}
-UiObject* ui_simple_window(const char *title, void *window_data) {
- return create_window(title, window_data, TRUE);
+UiObject* ui_simple_window(const char *title) {
+ return create_window(title, TRUE);
}
void ui_window_size(UiObject *obj, int width, int height) {
bool fill = layout.fill;
if(hasStretchedWidget && fill) {
fill = false;
- fprintf(stderr, "UiError: container has 2 filled widgets");
+ fprintf(stderr, "UiError: container has 2 filled widgets\n");
+ }
+
+ if(singleChild) {
+ fill = true;
+ if(hasChild) {
+ fprintf(stderr, "UiError: single child container already has a child\n");
+ }
}
uic_layout_setup_margin(&layout);
hasStretchedWidget = true;
}
current = widget;
+ hasChild = true;
}
UIWIDGET ui_box(UiObject *obj, UiContainerArgs *args, QBoxLayout::Direction dir) {
}
+/* ------------------------------ Frame ------------------------------ */
+
+UIWIDGET ui_frame_create(UiObject *obj, UiFrameArgs *args) {
+ UiContainerPrivate *ctn = (UiContainerPrivate*)ui_obj_container(obj);
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+
+ bool createBox = true;
+ bool singleChild = false;
+ QBoxLayout::Direction dir = QBoxLayout::TopToBottom;
+ QGroupBox *widget = new QGroupBox();
+ if(args->label) {
+ widget->setTitle(args->label);
+ }
+
+ switch(args->subcontainer) {
+ default: break;
+ case UI_CONTAINER_HBOX: {
+ dir = QBoxLayout::LeftToRight;
+ break;
+ }
+ case UI_CONTAINER_GRID: {
+ createBox = false;
+ break;
+ }
+ case UI_CONTAINER_NO_SUB: {
+ singleChild = true;
+ }
+ }
+
+ if(createBox) {
+ QBoxLayout *box = new QBoxLayout(dir);
+ widget->setLayout(box);
+
+ UiBoxContainer *container = new UiBoxContainer(box);
+ ui_obj_add_container(obj, container);
+
+ container->singleChild = singleChild;
+ } else {
+ QGridLayout *grid = new QGridLayout();
+ widget->setLayout(grid);
+
+ ui_obj_add_container(obj, new UiGridContainer(
+ grid,
+ args->padding,
+ args->columnspacing,
+ args->rowspacing,
+ false,
+ false,
+ false,
+ false));
+ }
+
+
+ ctn->add(widget, layout);
+
+ return widget;
+}
+
/* ---------------------------- UiSidebar ---------------------------- */
UIWIDGET ui_sidebar_create(UiObject *obj, UiSidebarArgs *args) {
#include <QTabWidget>
#include <QStackedWidget>
#include <QSplitter>
+#include <QGroupBox>
#define ui_obj_container(obj) (UiContainerPrivate*)((UiContainerX*)obj->container_end)->container
public:
QBoxLayout *box;
bool hasStretchedWidget = false;
+ bool singleChild = false;
+ bool hasChild = false;
QSpacerItem *space;
QBoxLayout::Direction direction;
#include <QTreeView>
#include <QTreeWidgetItem>
#include <QListView>
+#include <QComboBox>
extern "C" void* ui_strmodel_getvalue(void *elm, int column) {
return column == 0 ? elm : NULL;
return NULL;
}
-UIWIDGET ui_listview_create(UiObject* obj, UiListArgs *args) {
+UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) {
UiContainerPrivate *ctn = ui_obj_container(obj);
QListView *view = new QListView();
return view;
}
-UIWIDGET ui_table_create(UiObject* obj, UiListArgs *args) {
+UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) {
UiContainerPrivate *ctn = ui_obj_container(obj);
QTreeView *view = new QTreeView();
return view;
}
+
+UIWIDGET ui_dropdown_create(UiObject *obj, UiListArgs *args) {
+ UiContainerPrivate *ctn = ui_obj_container(obj);
+
+ QComboBox *view = new QComboBox();
+ UiLayout layout = UI_ARGS2LAYOUT(args);
+ ctn->add(view, layout);
+
+ ui_getvaluefunc2 getvalue = nullptr;
+ void *getvaluedata = nullptr;
+ if(args->getvalue2) {
+ getvalue = args->getvalue2;
+ getvaluedata = args->getvalue2data;
+ } else if(args->getvalue) {
+ getvalue = getvalue_wrapper;
+ getvaluedata = (void*)args->getvalue;
+ } else {
+ getvalue = getvalue_wrapper;
+ getvaluedata = (void*)ui_strmodel_getvalue;
+ }
+
+ UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST);
+
+ ListModel *model = new ListModel(obj, view, var, getvalue, getvaluedata);
+ view->setModel(model);
+
+ if(var) {
+ UiList *list = (UiList*)var->value;
+ list->update = ui_listmodel_update;
+ list->getselection = ui_listmodel_getselection;
+ list->setselection = ui_listmodel_setselection;
+ list->obj = model;
+ }
+
+ model->setActivationCallback(args->onactivate, args->onactivatedata);
+ model->setSelectionCallback(args->onselection, args->onselectiondata);
+
+ return view;
+}
#ifndef TREE_H
#define TREE_H
-#include "../ui/tree.h"
+#include "../ui/list.h"
#include "model.h"
#include <QTableView>
#include "../common/menu.h"
#include "../ui/properties.h"
#include "../ui/window.h"
-#include "stock.h"
#include "container.h"
}
if(states) {
- size_t nstates = uic_group_array_size(states);
- uic_add_group_widget_i(obj->ctx, action, (ui_enablefunc)ui_action_enable, states, nstates);
+ size_t nstates = uic_state_array_size(states);
+ uic_add_state_widget_i(obj->ctx, action, (ui_enablefunc)ui_action_enable, states, nstates);
action->setEnabled(false);
}
void add_menuitem_widget(QMenu *parent, int i, UiMenuItemI *item, UiObject *obj) {
UiMenuItem *it = (UiMenuItem*)item;
- UiAction *action = create_action(obj, it->icon, it->label, it->callback, it->userdata, it->groups);
+ UiAction *action = create_action(obj, it->icon, it->label, it->callback, it->userdata, it->states);
parent->addAction(action);
QObject::connect(action, SIGNAL(triggered()), action, SLOT(trigger()));
}
void add_checkitem_widget(QMenu *parent, int i, UiMenuItemI *item, UiObject *obj) {
UiMenuCheckItem *it = (UiMenuCheckItem*)item;
- UiAction *action = create_action(obj, it->icon, it->label, it->callback, it->userdata, it->groups);
+ UiAction *action = create_action(obj, it->icon, it->label, it->callback, it->userdata, it->states);
parent->addAction(action);
action->setCheckable(true);
action->prepare_event = ui_checkableaction_prepare_event;
void add_radioitem_widget(QMenu *parent, int index, UiMenuItemI *item, UiObject *obj) {
UiMenuRadioItem *it = (UiMenuRadioItem*)item;
- UiAction *action = create_action(obj, it->icon, it->label, it->callback, it->userdata, it->groups);
+ UiAction *action = create_action(obj, it->icon, it->label, it->callback, it->userdata, it->states);
parent->addAction(action);
action->setCheckable(true);
action->prepare_event = ui_actiongroup_prepare_event;
#include "model.h"
-ListModel::ListModel(UiObject *obj, QListView *view, UiVar *var, ui_getvaluefunc2 getvalue, void *getvaluedata){
+ListModel::ListModel(UiObject *obj, QListView *view, UiVar *var, ui_getvaluefunc2 getvalue, void *getvaluedata) {
this->obj = obj;
- this->view = view;
+ this->listview = view;
+ this->combobox = nullptr;
+ this->var = var;
+ this->getvalue = getvalue;
+ this->getvaluedata = getvaluedata;
+ this->onactivate = nullptr;
+ this->onactivatedata = nullptr;
+ this->onselection = nullptr;
+ this->onselectiondata = nullptr;
+}
+
+ListModel::ListModel(UiObject *obj, QComboBox *view, UiVar *var, ui_getvaluefunc2 getvalue, void *getvaluedata) {
+ this->obj = obj;
+ this->combobox = view;
+ this->listview = nullptr;
this->var = var;
this->getvalue = getvalue;
this->getvaluedata = getvaluedata;
}
void ListModel::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) {
- UiListSelection sel = ui_selection_model_to_selection(view->selectionModel());
+ UiListSelection sel;
+ if(listview) {
+ sel = ui_selection_model_to_selection(listview->selectionModel());
+ } else {
+ // TODO
+ }
UiEvent event;
event.obj = obj;
QModelIndex index = model->index(sel.rows[i]);
selection.select(index, index);
}
- model->view->selectionModel()->select(selection, QItemSelectionModel::ClearAndSelect);
+ if(model->listview) {
+ model->listview->selectionModel()->select(selection, QItemSelectionModel::ClearAndSelect);
+ } else if(model->combobox) {
+ // TODO
+ }
}
UiListSelection ui_listmodel_getselection(UiList *list) {
ListModel *model = (ListModel*)list->obj;
- return ui_selection_model_to_selection(model->view->selectionModel());
+ if(model->listview) {
+ return ui_selection_model_to_selection(model->listview->selectionModel());
+ } else {
+ UiListSelection sel = { 0, NULL };
+ return sel;
+ }
}
#define MODEL_H
#include "toolkit.h"
-#include "../ui/tree.h"
+#include "../ui/list.h"
#include "../common/context.h"
#include <QListView>
#include <QTreeView>
+#include <QComboBox>
#include <QAbstractListModel>
#include <QAbstractTableModel>
#include <QAbstractItemModel>
public:
UiObject *obj;
UiVar *var;
- QListView *view;
+ QListView *listview;
+ QComboBox *combobox;
ListModel(UiObject *obj, QListView *view, UiVar *var, ui_getvaluefunc2 getvalue, void *getvaluedata);
+ ListModel(UiObject *obj, QComboBox *view, UiVar *var, ui_getvaluefunc2 getvalue, void *getvaluedata);
void setActivationCallback(ui_callback f, void *userdata);
void setSelectionCallback(ui_callback f, void *userdata);
SOURCES += window.cpp
SOURCES += menu.cpp
SOURCES += toolbar.cpp
-SOURCES += stock.cpp
SOURCES += container.cpp
SOURCES += text.cpp
SOURCES += model.cpp
HEADERS += window.h
HEADERS += menu.h
HEADERS += toolbar.h
-HEADERS += stock.h
HEADERS += container.h
HEADERS += text.h
HEADERS += model.h
#include "toolbar.h"
#include "menu.h"
-#include "stock.h"
static void add_items(UiObject *obj, QToolBar *toolbar, CxList *defaults, CxMap *items);
static void create_item(UiObject *obj, QToolBar *toolbar, UiToolbarItemI *i);
#include "toolkit.h"
#include "window.h"
-#include "stock.h"
#include "../common/document.h"
#include "../common/properties.h"
#include "../common/menu.h"
#include "../common/toolbar.h"
+#include "../common/app.h"
static const char *application_name;
-static ui_callback startup_func;
-static void *startup_data;
-static ui_callback open_func;
-static void *open_data;
-static ui_callback exit_func;
-static void *exit_data;
-
static int is_toplevel_realized = 0;
static int app_argc;
return application_name;
}
-void ui_onstartup(ui_callback f, void *userdata) {
- startup_func = f;
- startup_data = userdata;
-}
-
-void ui_onopen(ui_callback f, void *userdata) {
- open_func = f;
- open_data = userdata;
-}
-
-void ui_onexit(ui_callback f, void *userdata) {
- exit_func = f;
- exit_data = userdata;
-}
-
void ui_app_exit_on_shutdown(UiBool exitapp) {
exit_on_shutdown = exitapp;
}
void ui_main() {
- if(startup_func) {
- startup_func(NULL, startup_data);
- }
+ uic_application_startup(NULL);
application->exec();
- if(exit_func) {
- exit_func(NULL, exit_data);
- }
+ uic_application_exit(NULL);
uic_store_app_properties();
delete application;
#include <QDockWidget>
#include <QMessageBox>
-static UiObject* create_window(const char *title, void *window_data, bool simple, bool sidebar = false) {
+static UiObject* create_window(const char *title, bool simple, bool sidebar = false) {
UiObject *obj = uic_object_new_toplevel();
- obj->window = window_data;
obj->next = NULL;
QMainWindow *window = new QMainWindow();
return obj;
}
-UiObject* ui_window(const char *title, void *window_data) {
- return create_window(title, window_data, false);
+UiObject* ui_window(const char *title) {
+ return create_window(title, false);
}
-UiObject* ui_simplewindow(char *title, void *window_data) {
- return create_window(title, window_data, true);
+UiObject* ui_simple_window(const char *title) {
+ return create_window(title, true);
}
-UiObject *ui_sidebar_window(const char *title, void *window_data) {
- return create_window(title, window_data, false, true);
+UiObject* ui_sidebar_window(const char *title) {
+ return create_window(title, false, true);
}
void ui_dialog_create(UiObject *parent, UiDialogArgs *args) {
ui_callback onclick;
void *onclickdata;
- const int *groups;
+ const int *states;
} UiButtonArgs;
typedef struct UiToggleArgs {
const char *varname;
ui_callback onchange;
void *onchangedata;
- int enable_group;
+ int enable_state;
- const int *groups;
+ const int *states;
} UiToggleArgs;
typedef struct UiLinkButtonArgs {
UiBool nofollow;
UiLinkType type;
- const int *groups;
+ const int *states;
} UiLinkButtonArgs;
#define ui_button(obj, ...) ui_button_create(obj, &(UiButtonArgs){ __VA_ARGS__ } )
#define ui_left_panel0(obj) for(ui_left_panel_create(obj, &(UiSidebarArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj))
#define ui_right_panel0(obj) for(ui_right_panel_create(obj, &(UiSidebarArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj))
-
#define ui_vbox_w(obj, w, ...) for(w = ui_vbox_create(obj, &(UiContainerArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
#define ui_hbox_w(obj, w, ...) for(w = ui_hbox_create(obj, &(UiContainerArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
#define ui_grid_w(obj, w, ...) for(w = ui_grid_create(obj, &(UiContainerArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
#define ui_tabview_w(obj, w, ...) for(w = ui_tabview_create(obj, &(UiTabViewArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
#define ui_scrolledwindow_w(obj, w, ...) for(w = ui_scrolledwindow_create(obj, &(UiFrameArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_custom_container(ob, widget, addfunc, data) for(ui_custom_container_create(obj, widget, addfunc, data);ui_container_finish(obj);ui_container_begin_close(obj))
+
#define ui_hsplitpane(obj, ...) for(ui_hsplitpane_create(obj, &(UiSplitPaneArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
#define ui_vsplitpane(obj, ...) for(ui_vsplitpane_create(obj, &(UiSplitPaneArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
#define ui_hsplitpane0(obj) for(ui_hsplitpane_create(obj, &(UiSplitPaneArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj))
UIEXPORT void ui_newline(UiObject *obj);
+typedef void(*ui_addwidget_func)(UiObject *obj, UIWIDGET parent, UIWIDGET child, void *userdata);
+UIEXPORT void ui_custom_container_create(UiObject *obj, UIWIDGET widget, ui_addwidget_func add_child, void *userdata);
+
// TODO
UIEXPORT UiTabbedPane* ui_tabbed_document_view(UiObject *obj);
UIEXPORT UiObject* ui_document_tab(UiTabbedPane *view);
ui_callback onchange;
void* onchangedata;
- const int *groups;
+ const int *states;
} UiSpinBoxArgs;
int margin_bottom;
int colspan;
int rowspan;
+ int width;
+ int height;
const char *name;
const char *style_class;
ui_callback onclick;
void* onclickdata;
- const int* groups;
+ const int* states;
} UiMenuItemArgs;
typedef struct UiMenuToggleItemArgs {
ui_callback onchange;
void* onchangedata;
- const int* groups;
+ const int* nstates;
} UiMenuToggleItemArgs;
typedef struct UiMenuItemListArgs {
extern "C" {
#endif
+void ui_load_properties_file_on_startup(UiBool enable);
+void ui_set_properties_data(const char *str, size_t len);
+
const char* ui_get_property(const char *name);
void ui_set_property(const char *name, const char *value);
const char* ui_set_default_property(const char *name, const char *value);
ui_callback onchange;
void *onchangedata;
- const int *groups;
+ const int *states;
} UiTextAreaArgs;
typedef struct UiTextFieldArgs {
ui_callback onactivate;
void *onactivatedata;
- const int *groups;
+ const int *states;
} UiTextFieldArgs;
typedef struct UiPathElmRet {
int margin_bottom;
int colspan;
int rowspan;
+ int width;
const char *name;
const char *style_class;
ui_callback onclick;
void* onclickdata;
- const int *groups;
+ const int *states;
const int *visibility_states;
} UiToolbarItemArgs;
ui_callback onchange;
void *onchangedata;
- const int *groups;
+ const int *states;
const int *visibility_states;
} UiToolbarToggleItemArgs;
#endif
+#elif UI_SERVER
+
+typedef struct UiWidget UiWidget;
+
+#define UIWIDGET UiWidget*
+#define UIWINDOW UiWidget*
+#define UIMENU void*
+
#endif
#ifndef TRUE
UIEXPORT UiContext* ui_document_context(void *doc);
+UIEXPORT void* ui_context_get_document(UiContext *ctx);
+UIEXPORT void ui_context_single_attachment_mode(UiContext *ctx, UiBool enable);
UIEXPORT void ui_attach_document(UiContext *ctx, void *document);
UIEXPORT void ui_detach_document(UiContext *ctx, void *document);
-UIEXPORT void ui_widget_set_groups(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, ...);
-UIEXPORT void ui_widget_set_groups2(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, const int *groups, int ngroups);
+UIEXPORT void ui_widget_set_states(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, ...);
+UIEXPORT void ui_widget_set_states2(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, const int *states, int nstates);
UIEXPORT void ui_widget_set_visibility_states(UiContext *ctx, UIWIDGET widget, const int *states, int nstates);
-UIEXPORT void ui_set_group(UiContext *ctx, int group);
-UIEXPORT void ui_unset_group(UiContext *ctx, int group);
-UIEXPORT int* ui_active_groups(UiContext *ctx, int *ngroups);
+UIEXPORT void ui_set_state(UiContext *ctx, int state);
+UIEXPORT void ui_unset_state(UiContext *ctx, int state);
+UIEXPORT int* ui_active_states(UiContext *ctx, int *nstates);
UIEXPORT void* ui_allocator(UiContext *ctx);
UIEXPORT void* ui_cx_mempool(UiContext *ctx);
UIEXPORT void ui_var_set_string(UiContext *ctx, const char *name, char *value);
UIEXPORT char* ui_var_get_string(UiContext *ctx, const char *name);
+UIEXPORT UiInteger* ui_get_int_var(UiContext *ctx, const char *name);
+UIEXPORT UiDouble* ui_get_double_var(UiContext *ctx, const char *name);
+UIEXPORT UiString* ui_get_string_var(UiContext *ctx, const char *name);
+UIEXPORT UiText* ui_get_text_var(UiContext *ctx, const char *name);
+UIEXPORT UiRange* ui_get_range_var(UiContext *ctx, const char *name);
+UIEXPORT UiList* ui_get_list_var(UiContext *ctx, const char *name);
+UIEXPORT UiGeneric* ui_get_generic_var(UiContext *ctx, const char *name);
+
+UIEXPORT void ui_var_add_observer(UiContext *ctx, const char *varname, ui_callback f, void *data);
+
UIEXPORT UiObserver* ui_observer_new(ui_callback f, void *data);
UIEXPORT UiObserver* ui_obsvlist_add(UiObserver *list, UiObserver *observer);
UIEXPORT UiObserver* ui_add_observer(UiObserver *list, ui_callback f, void *data);
#include "menu.h"
#include "toolbar.h"
#include "window.h"
-#include "stock.h"
#include "button.h"
#include "text.h"
#include "properties.h"
-#include "tree.h"
+#include "list.h"
#include "graphics.h"
#include "entry.h"
#include "range.h"
int margin_bottom;
int colspan;
int rowspan;
+ int width;
+ int height;
const char *name;
const char *style_class;
UiGeneric *value;
const char *varname;
- const int* groups;
+ const int* states;
} UiWebviewArgs;
#define ui_webview(obj, ...) ui_webview_create(obj, &(UiWebviewArgs){ __VA_ARGS__ } )
typedef UIWIDGET(*ui_createwidget_func)(UiObject *obj, UiWidgetArgs *args, void *userdata);
#elif defined(UI_WIN32)
typedef UIWIDGET(*ui_createwidget_func)(UiObject *obj, UiWidgetArgs *args, void *userdata);
+#elif defined(UI_SERVER)
+typedef UIWIDGET(*ui_createwidget_func)(UiObject *obj, UiWidgetArgs *args, void *userdata);
#endif
UIEXPORT UIWIDGET ui_customwidget_create(UiObject *obj, ui_createwidget_func create_widget, void *userdata, UiWidgetArgs *args);
};\r
\r
struct W32WidgetClass {\r
- void (*eventproc)(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);\r
+ int (*eventproc)(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);\r
void (*show)(W32Widget *widget, BOOLEAN show);\r
void (*enable)(W32Widget *widget, BOOLEAN enable);\r
W32Size (*get_preferred_size)(W32Widget *widget);\r
const char *lbutton2;
const char *rbutton3;
const char *rbutton4;
- const int *lbutton1_groups;
- const int *lbutton2_groups;
- const int *rbutton3_groups;
- const int *rbutton4_groups;
+ const int *lbutton1_states;
+ const int *lbutton2_states;
+ const int *rbutton3_states;
+ const int *rbutton4_states;
int default_button;
int width;
int height;
void *onclickdata;
} UiDialogWindowArgs;
-UIEXPORT UiObject *ui_window(const char *title, void *window_data);
-UIEXPORT UiObject *ui_sidebar_window(const char *title, void *window_data);
+UIEXPORT UiObject *ui_window(const char *title);
+UIEXPORT UiObject *ui_sidebar_window(const char *title);
UIEXPORT UiObject *ui_splitview_window(const char *title, UiBool sidebar);
-UIEXPORT UiObject *ui_simple_window(const char *title, void *window_data);
+UIEXPORT UiObject *ui_simple_window(const char *title);
UIEXPORT UiObject *ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs *args);
#define ui_dialog_window(parent, ...) ui_dialog_window_create(parent, &(UiDialogWindowArgs){ __VA_ARGS__ });