]> uap-core.de Git - note.git/commitdiff
update to ucx version 4.0 main
authorOlaf Wintermann <olaf.wintermann@gmail.com>
Thu, 1 Jan 2026 17:10:55 +0000 (18:10 +0100)
committerOlaf Wintermann <olaf.wintermann@gmail.com>
Thu, 1 Jan 2026 17:10:55 +0000 (18:10 +0100)
119 files changed:
application/attachment.c
application/editor.c
application/gtk-text.c
application/nbconfig.c
application/note.c
application/notebook.c
application/store.c
application/tests/test-editor.c
application/window.c
dbutils/Makefile
dbutils/class.c
dbutils/db.c
dbutils/dbutils.c
dbutils/dbutils/db.h
dbutils/dbutils/dbutils.h
dbutils/dbutils/json.h [new file with mode: 0644]
dbutils/field.c
dbutils/field.h
dbutils/json.c [new file with mode: 0644]
dbutils/json.h [new file with mode: 0644]
dbutils/object.c
libidav/config.c
libidav/crypto.c
libidav/davqlexec.c
libidav/davqlparser.c
libidav/methods.c
libidav/pwdstore.c
libidav/resource.c
libidav/session.c
libidav/utils.c
libidav/versioning.c
libidav/webdav.c
libidav/xml.c
ucx/allocator.c
ucx/array_list.c
ucx/buffer.c
ucx/compare.c
ucx/cx/allocator.h
ucx/cx/array_list.h
ucx/cx/buffer.h
ucx/cx/collection.h
ucx/cx/common.h
ucx/cx/compare.h
ucx/cx/hash_key.h
ucx/cx/hash_map.h
ucx/cx/iterator.h
ucx/cx/json.h
ucx/cx/kv_list.h
ucx/cx/linked_list.h
ucx/cx/list.h
ucx/cx/map.h
ucx/cx/mempool.h
ucx/cx/printf.h
ucx/cx/properties.h
ucx/cx/streams.h
ucx/cx/string.h
ucx/cx/test.h
ucx/cx/tree.h
ucx/hash_key.c
ucx/hash_map.c
ucx/iterator.c
ucx/json.c
ucx/kv_list.c
ucx/linked_list.c
ucx/list.c
ucx/map.c
ucx/properties.c
ucx/string.c
ucx/tree.c
ui/cocoa/GridLayout.m
ui/cocoa/ListDataSource.h
ui/cocoa/ListDataSource.m
ui/cocoa/button.m
ui/cocoa/image.h
ui/cocoa/image.m
ui/cocoa/list.m
ui/common/args.c
ui/common/args.h
ui/common/context.c
ui/common/context.h
ui/common/menu.c
ui/common/message.c
ui/common/object.c
ui/common/properties.c
ui/common/toolbar.c
ui/common/types.c
ui/common/utils.c
ui/gtk/button.c
ui/gtk/container.c
ui/gtk/container.h
ui/gtk/dnd.c
ui/gtk/entry.c
ui/gtk/headerbar.h
ui/gtk/icon.c
ui/gtk/list.c
ui/gtk/list.h
ui/gtk/menu.c
ui/gtk/menu.h
ui/gtk/text.c
ui/gtk/toolbar.c
ui/gtk/toolbar.h
ui/gtk/toolkit.c
ui/gtk/toolkit.h
ui/gtk/window.c
ui/motif/button.c
ui/motif/container.c
ui/motif/menu.c
ui/motif/pathbar.c
ui/ui/toolkit.h
ui/win32/button.c
ui/win32/grid.c
ui/win32/list.c
ui/win32/menu.c
ui/win32/menu.h
ui/win32/toolkit.c
ui/win32/toolkit.h
ui/win32/window.c
ui/win32/window.h
ui/winui/text.cpp

index 00ad2702164bab2526ef41d8e8a333a10940c705..fc2ce681e02e1d33d06015aa18d83cfd5240f93e 100644 (file)
@@ -125,7 +125,7 @@ cxmutstr attachment_file_load(const CxAllocator *a, const char *path) {
     }
     
     CxBuffer buf;
-    cxBufferInit(&buf, NULL, 1024*128, a, CX_BUFFER_AUTO_EXTEND);
+    cxBufferInit(&buf, a, NULL, 1024*128, CX_BUFFER_AUTO_EXTEND);
     
     cx_stream_copy(f, &buf, (cx_read_func)fread, (cx_write_func)cxBufferWrite);
     fclose(f);
index 00da427a65111e652f2433781f771dd9216dd525..9dbcbd1ca6e36965799e886e3cf31b5b10ae0aeb 100644 (file)
@@ -188,7 +188,7 @@ static int md_enter_span(MD_SPANTYPE type, void* detail, void* userdata) {
         MD_SPAN_IMG_DETAIL *img = detail;
         if(img) {
             cxstring imgsrc = cx_strn(img->src.text, img->src.size);
-            if(cx_strprefix(imgsrc, CX_STR("http://")) || cx_strprefix(imgsrc, CX_STR("https://"))) {
+            if(cx_strprefix(imgsrc, "http://") || cx_strprefix(imgsrc, "https://")) {
                 // copy url in case it is http/https
                 node->link = cx_strdup_a(data->a, imgsrc);
             } else {
@@ -244,7 +244,7 @@ static int md_text(MD_TEXTTYPE type, const MD_CHAR* text, MD_SIZE size, void* us
         }
         
         if(prev_text_node) {
-            cxmutstr content = cx_strcat_a(data->a, 2, prev_text_node->text, cx_strn(text, size));
+            cxmutstr content = cx_strcat_a(data->a, CX_NULLSTR, 2, prev_text_node->text, cx_strn(text, size));
             cxFree(data->a, prev_text_node->text.ptr);
             prev_text_node->text = content;
             return 0;
@@ -287,7 +287,7 @@ MDDoc* parse_markdown(cxstring markdown) {
     MDParserData data;
     data.doc = doc;
     data.p_current = NULL;
-    data.text_stack = cxArrayListCreateSimple(CX_STORE_POINTERS, 16);
+    data.text_stack = cxArrayListCreate(NULL, CX_STORE_POINTERS, 16);
     data.list_depth = 0;
     data.a = a;
     
@@ -441,8 +441,8 @@ static void linearize_paragraph(CxBuffer *buf, CxList *sections, MDPara *p) {
 
 MDDocLinear mddoc_linearization(MDDoc *doc) {
     CxBuffer buf;
-    cxBufferInit(&buf, NULL, doc->size, NULL, CX_BUFFER_AUTO_EXTEND);
-    CxList *sections = cxArrayListCreateSimple(sizeof(MDDocStyleSection), 64);
+    cxBufferInit(&buf, NULL, NULL, doc->size, CX_BUFFER_AUTO_EXTEND);
+    CxList *sections = cxArrayListCreate(NULL, sizeof(MDDocStyleSection), 64);
     
     MDPara *p = doc->content;
     while(p) {
index bd25a2de59f00fd99306166979bd038f4c622441..266d334ef4a3fdb222bf17cacb656961ec22138f 100644 (file)
@@ -114,7 +114,7 @@ static void editor_realize(GtkWidget *unused, NoteEditor *editor) {
 }
 
 void editor_global_init() {
-    markdown_tags = cxHashMapCreateSimple(sizeof(MDTag));
+    markdown_tags = cxHashMapCreate(NULL, sizeof(MDTag), 16);
     
     cxMapPut(markdown_tags, EDITOR_STYLE_PARAGRAPH, &((MDTag){NULL, NULL}));
     cxMapPut(markdown_tags, EDITOR_STYLE_HEADING1, &((MDTag){"# ", NULL}));
@@ -677,8 +677,8 @@ void editor_init_textbuf(UiText *text) {
     }
     
     BufferEmbeddedObjects *embedded_objects = malloc(sizeof(BufferEmbeddedObjects));
-    embedded_objects->objects = cxArrayListCreateSimple(CX_STORE_POINTERS, 8);
-    embedded_objects->objects->collection.simple_destructor = (cx_destructor_func)embedded_widget_free;
+    embedded_objects->objects = cxArrayListCreate(NULL, CX_STORE_POINTERS, 8);
+    cxSetDestructor(embedded_objects->objects, (cx_destructor_func)embedded_widget_free);
     g_object_set_data_full(G_OBJECT(buf), "embedded", embedded_objects, (GDestroyNotify)buffer_embedded_objects_free);
 }
 
@@ -844,7 +844,7 @@ void editor_apply_styles(Note *note, UIWIDGET textview, UiText *text, CxList /*
     
     // anything that needs to be inserted into the textbuffer must be inserted
     // after all other styling operations
-    CxList *insert_attachments = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+    CxList *insert_attachments = cxLinkedListCreate(NULL, CX_STORE_POINTERS);
     
     CxIterator i = cxListIterator(styles);
     cx_foreach(MDDocStyleSection*, sec, i) {
@@ -898,7 +898,7 @@ void editor_apply_styles(Note *note, UIWIDGET textview, UiText *text, CxList /*
  * value: GtkTextTag* 
  */
 static CxMap* tags2map(GSList *tags) {
-    CxMap *map = cxHashMapCreateSimple(CX_STORE_POINTERS);
+    CxMap *map = cxHashMapCreate(NULL, CX_STORE_POINTERS, 16);
     int anon_tag = 0;
     char buf[32];
     while(tags) {
@@ -922,7 +922,7 @@ static CxMap* tags2map(GSList *tags) {
  * The values are not cloned
  */
 static CxMap* map_clone(CxMap *map) {
-    CxMap *newmap = cxHashMapCreateSimple(CX_STORE_POINTERS);
+    CxMap *newmap = cxHashMapCreate(NULL, CX_STORE_POINTERS, cxMapSize(map));
     CxMapIterator i = cxMapIterator(map);
     cx_foreach(CxMapEntry *, entry, i) {
         cxMapPut(newmap, *entry->key, entry->value);
@@ -945,11 +945,11 @@ static void map_subtract(CxMap *map, CxMap *sub) {
  */
 cxmutstr editor_get_markdown(UiText *text, const CxAllocator *a) { 
     CxBuffer out;
-    cxBufferInit(&out, NULL, 1024, a, CX_BUFFER_AUTO_EXTEND);
+    cxBufferInit(&out, a, NULL, 1024, CX_BUFFER_AUTO_EXTEND);
     
     GtkTextBuffer *buffer = text->data1;
     
-    CxMap *prev_tags = cxHashMapCreateSimple(CX_STORE_POINTERS);
+    CxMap *prev_tags = cxHashMapCreate(NULL, CX_STORE_POINTERS, 64);
     
     GtkTextChildAnchor *anchor = NULL;
     GtkTextChildAnchor *prevAnchor = NULL;
index c61a108b61142c43e341c15b39e38e93144eca15..3f96d28d145d0e13b03347c52063a61cdf6df0ba 100644 (file)
@@ -421,9 +421,9 @@ void notebook_config_dialog(void) {
     wdata->tab3_repo_encryption_key = ui_list_new(obj->ctx, NULL);
     wdata->tab3_repo_isencrypted = ui_int_new(obj->ctx, NULL);
     
-    wdata->repositories = cxArrayListCreate(a, NULL, CX_STORE_POINTERS, 32);
-    wdata->groups = cxArrayListCreate(a, NULL, CX_STORE_POINTERS, 32);
-    wdata->notebooks = cxArrayListCreate(a, NULL, CX_STORE_POINTERS, 32);
+    wdata->repositories = cxArrayListCreate(a, CX_STORE_POINTERS, 32);
+    wdata->groups = cxArrayListCreate(a, CX_STORE_POINTERS, 32);
+    wdata->notebooks = cxArrayListCreate(a, CX_STORE_POINTERS, 32);
     wdata->notebook_parents = cxHashMapCreate(a, CX_STORE_POINTERS, 32);
     
     // fill data
index 372bc4d02f2174287debcc561d3391c89d21903e..0e65d6db6f820a2c0d72bda52927796ebb159b79 100644 (file)
@@ -172,7 +172,7 @@ void note_load_content(UiObject *obj, NotebookModel *notebook, Note *note) {
 
 void note_add_attachment(Note *note, Attachment *attachment) {
     if(!note->resource->attachments) {
-        note->resource->attachments = cxArrayListCreate(note->model->note_allocator, NULL, CX_STORE_POINTERS, 8);
+        note->resource->attachments = cxArrayListCreate(note->model->note_allocator, CX_STORE_POINTERS, 8);
     }
     cxListAdd(note->resource->attachments, attachment);
 }
index 79fdca6d29f851ea92e079425d51e99e795717c8..37a9e9f9360b3156d2161ea96a00d1fd237998d9 100644 (file)
@@ -46,7 +46,6 @@ NotebookModel* notebookmodel_create() {
     
 static void notelist_select_note(NotebookModel *model, Note *note) {
     CxList *list = model->notes->data; // UiList uses CxList internally
-    list->collection.cmpfunc = cx_cmp_ptr;
     int index = (int)cxListFind(list, note);
     if(cxListIndexValid(list, index)) {
         ui_list_setselection(model->notes, index);
index 432391113865f82409c46c4eff5c5f2c7c0b65a6..f061cb2892e836838c58f5eac88722c7ee811eb1 100644 (file)
@@ -407,7 +407,7 @@ void note_store_reload() {
             Resource *parent = cxMapGet(collection_map, parent_key);
             if(parent) {
                 if(!parent->children) {
-                    parent->children = cxArrayListCreate(a, NULL, CX_STORE_POINTERS, 8);
+                    parent->children = cxArrayListCreate(a, CX_STORE_POINTERS, 8);
                 }
                 cxListAdd(parent->children, col);
             }
index a0b274be2bf33b4cd73fe9daaaeff1f85b0c38d0..bb9c6cdd79b90e84ca1d8ddfe2873a709ea38cf9 100644 (file)
@@ -37,46 +37,46 @@ CX_TEST(test_parse_markdown_para) {
     MDDoc *doc;
             
     CX_TEST_DO {
-        doc = parse_markdown(CX_STR("simple paragraph"));
+        doc = parse_markdown(cx_str("simple paragraph"));
         CX_TEST_ASSERT(doc);
         CX_TEST_ASSERT(doc->content);
         CX_TEST_ASSERT(doc->content->type == MD_BLOCK_P);
         CX_TEST_ASSERT(doc->content->next == NULL);
         CX_TEST_ASSERT(doc->content->content);
         CX_TEST_ASSERT(doc->content->content->text.ptr != NULL);
-        CX_TEST_ASSERT(!cx_strcmp(cx_strcast(doc->content->content->text), CX_STR("simple paragraph")));
+        CX_TEST_ASSERT(!cx_strcmp(cx_strcast(doc->content->content->text), cx_str("simple paragraph")));
         mddoc_free(doc);
         
-        doc = parse_markdown(CX_STR("paragraph 0\n\nparagraph 1"));
+        doc = parse_markdown(cx_str("paragraph 0\n\nparagraph 1"));
         CX_TEST_ASSERT(doc);
         MDPara *p0 = doc->content;
         CX_TEST_ASSERT(p0);
         CX_TEST_ASSERT(p0->type == MD_BLOCK_P);
         CX_TEST_ASSERT(p0->content);
         CX_TEST_ASSERT(p0->content->text.ptr != NULL);
-        CX_TEST_ASSERT(!cx_strcmp(cx_strcast(p0->content->text), CX_STR("paragraph 0")));
+        CX_TEST_ASSERT(!cx_strcmp(cx_strcast(p0->content->text), cx_str("paragraph 0")));
         MDPara *p1 = p0->next;
         CX_TEST_ASSERT(p1);
         CX_TEST_ASSERT(p1->type == MD_BLOCK_P);
         CX_TEST_ASSERT(p1->content);
         CX_TEST_ASSERT(p1->content->text.ptr != NULL);
-        CX_TEST_ASSERT(!cx_strcmp(cx_strcast(p1->content->text), CX_STR("paragraph 1")));
+        CX_TEST_ASSERT(!cx_strcmp(cx_strcast(p1->content->text), cx_str("paragraph 1")));
         mddoc_free(doc);
         
-        doc = parse_markdown(CX_STR("# heading\n\n    code1\n    code2\n"));
+        doc = parse_markdown(cx_str("# heading\n\n    code1\n    code2\n"));
         CX_TEST_ASSERT(doc);
         p0 = doc->content;
         CX_TEST_ASSERT(p0);
         CX_TEST_ASSERT(p0->type == MD_BLOCK_H);
         CX_TEST_ASSERT(p0->content);
         CX_TEST_ASSERT(p0->content->text.ptr != NULL);
-        CX_TEST_ASSERT(!cx_strcmp(cx_strcast(p0->content->text), CX_STR("heading")));
+        CX_TEST_ASSERT(!cx_strcmp(cx_strcast(p0->content->text), cx_str("heading")));
         p1 = p0->next;
         CX_TEST_ASSERT(p1);
         CX_TEST_ASSERT(p1->type == MD_BLOCK_CODE);
         CX_TEST_ASSERT(p1->content);
         CX_TEST_ASSERT(p1->content->text.ptr != NULL);
-        CX_TEST_ASSERT(!cx_strcmp(cx_strcast(p1->content->text), CX_STR("code1\ncode2\n")));
+        CX_TEST_ASSERT(!cx_strcmp(cx_strcast(p1->content->text), cx_str("code1\ncode2\n")));
         mddoc_free(doc);
     }
 }
@@ -92,7 +92,7 @@ CX_TEST(test_parse_markdown_formatting_simple) {
     MDNode *t3;
     
     CX_TEST_DO {
-        doc = parse_markdown(CX_STR("test **bold** end"));
+        doc = parse_markdown(cx_str("test **bold** end"));
         CX_TEST_ASSERT(doc);
         CX_TEST_ASSERT(doc->content);
         p0 = doc->content;
@@ -101,18 +101,18 @@ CX_TEST(test_parse_markdown_formatting_simple) {
         t0 = p0->content;
         CX_TEST_ASSERT(t0);
         CX_TEST_ASSERT(t0->text.ptr != NULL);
-        CX_TEST_ASSERT(!cx_strcmp(cx_strcast(t0->text), CX_STR("test ")));
+        CX_TEST_ASSERT(!cx_strcmp(cx_strcast(t0->text), cx_str("test ")));
         t1 = t0->next;
         CX_TEST_ASSERT(t1);
         CX_TEST_ASSERT(t1->type == MD_SPAN_STRONG);
-        CX_TEST_ASSERT(!cx_strcmp(mdnode_get_text(t1), CX_STR("bold")));
+        CX_TEST_ASSERT(!cx_strcmp(mdnode_get_text(t1), cx_str("bold")));
         t2 = t1->next;
         CX_TEST_ASSERT(t2);
         CX_TEST_ASSERT(t2->text.ptr != NULL);
-        CX_TEST_ASSERT(!cx_strcmp(cx_strcast(t2->text), CX_STR(" end")));
+        CX_TEST_ASSERT(!cx_strcmp(cx_strcast(t2->text), cx_str(" end")));
         mddoc_free(doc);
         
-        doc = parse_markdown(CX_STR("**bold start** end"));
+        doc = parse_markdown(cx_str("**bold start** end"));
         CX_TEST_ASSERT(doc);
         CX_TEST_ASSERT(doc->content);
         p0 = doc->content;
@@ -121,14 +121,14 @@ CX_TEST(test_parse_markdown_formatting_simple) {
         t0 = p0->content;
         CX_TEST_ASSERT(t0);
         CX_TEST_ASSERT(t0->type == MD_SPAN_STRONG);
-        CX_TEST_ASSERT(!cx_strcmp(mdnode_get_text(t0), CX_STR("bold start")));
+        CX_TEST_ASSERT(!cx_strcmp(mdnode_get_text(t0), cx_str("bold start")));
         t1 = t0->next;
         CX_TEST_ASSERT(t1);
         CX_TEST_ASSERT(t1->text.ptr != NULL);
-        CX_TEST_ASSERT(!cx_strcmp(cx_strcast(t1->text), CX_STR(" end")));
+        CX_TEST_ASSERT(!cx_strcmp(cx_strcast(t1->text), cx_str(" end")));
         mddoc_free(doc);
         
-        doc = parse_markdown(CX_STR("start **bold end**"));
+        doc = parse_markdown(cx_str("start **bold end**"));
         CX_TEST_ASSERT(doc);
         CX_TEST_ASSERT(doc->content);
         p0 = doc->content;
@@ -137,35 +137,35 @@ CX_TEST(test_parse_markdown_formatting_simple) {
         t0 = p0->content;
         CX_TEST_ASSERT(t0);
         CX_TEST_ASSERT(t0->text.ptr != NULL);
-        CX_TEST_ASSERT(!cx_strcmp(cx_strcast(t0->text), CX_STR("start ")));
+        CX_TEST_ASSERT(!cx_strcmp(cx_strcast(t0->text), cx_str("start ")));
         t1 = t0->next;
         CX_TEST_ASSERT(t1);
         CX_TEST_ASSERT(t1->type == MD_SPAN_STRONG);
-        CX_TEST_ASSERT(!cx_strcmp(mdnode_get_text(t1), CX_STR("bold end")));
+        CX_TEST_ASSERT(!cx_strcmp(mdnode_get_text(t1), cx_str("bold end")));
         mddoc_free(doc);
         
-        doc = parse_markdown(CX_STR("hello `code` hello *emphasis*"));
+        doc = parse_markdown(cx_str("hello `code` hello *emphasis*"));
         CX_TEST_ASSERT(doc);
         CX_TEST_ASSERT(doc->content);
         p0 = doc->content;
         CX_TEST_ASSERT(p0->type == MD_BLOCK_P);
         t0 = p0->content;
         CX_TEST_ASSERT(t0->text.ptr);
-        CX_TEST_ASSERT(!cx_strcmp(cx_strcast(t0->text), CX_STR("hello ")));
+        CX_TEST_ASSERT(!cx_strcmp(cx_strcast(t0->text), cx_str("hello ")));
         t1 = t0->next;
         CX_TEST_ASSERT(t1);
         CX_TEST_ASSERT(t1->type == MD_SPAN_CODE);
-        CX_TEST_ASSERT(!cx_strcmp(mdnode_get_text(t1), CX_STR("code")));
+        CX_TEST_ASSERT(!cx_strcmp(mdnode_get_text(t1), cx_str("code")));
         t2 = t1->next;
         CX_TEST_ASSERT(t2);
         CX_TEST_ASSERT(t2->text.ptr);
-        CX_TEST_ASSERT(!cx_strcmp(cx_strcast(t2->text), CX_STR(" hello ")));
+        CX_TEST_ASSERT(!cx_strcmp(cx_strcast(t2->text), cx_str(" hello ")));
         t3 = t2->next;
         CX_TEST_ASSERT(t3);
         CX_TEST_ASSERT(t3->type == MD_SPAN_EM);
-        CX_TEST_ASSERT(!cx_strcmp(mdnode_get_text(t3), CX_STR("emphasis")));
+        CX_TEST_ASSERT(!cx_strcmp(mdnode_get_text(t3), cx_str("emphasis")));
         
-        doc = parse_markdown(CX_STR("test *begin __bold__ text* end"));
+        doc = parse_markdown(cx_str("test *begin __bold__ text* end"));
         CX_TEST_ASSERT(doc);
         CX_TEST_ASSERT(doc->content);
         p0 = doc->content;
@@ -173,7 +173,7 @@ CX_TEST(test_parse_markdown_formatting_simple) {
         t0 = p0->content;
         CX_TEST_ASSERT(t0);
         CX_TEST_ASSERT(t0->text.ptr);
-        CX_TEST_ASSERT(!cx_strcmp(cx_strcast(t0->text), CX_STR("test ")));
+        CX_TEST_ASSERT(!cx_strcmp(cx_strcast(t0->text), cx_str("test ")));
         t1 = t0->next;
         CX_TEST_ASSERT(t1);
         CX_TEST_ASSERT(!t1->text.ptr);
@@ -181,20 +181,20 @@ CX_TEST(test_parse_markdown_formatting_simple) {
         t2 = t1->next;
         CX_TEST_ASSERT(t2);
         CX_TEST_ASSERT(t2->text.ptr);
-        CX_TEST_ASSERT(!cx_strcmp(cx_strcast(t2->text), CX_STR(" end")));
+        CX_TEST_ASSERT(!cx_strcmp(cx_strcast(t2->text), cx_str(" end")));
         MDNode *em_t0 = t1->children_begin;
         CX_TEST_ASSERT(em_t0);
         CX_TEST_ASSERT(em_t0->text.ptr);
-        CX_TEST_ASSERT(!cx_strcmp(cx_strcast(em_t0->text), CX_STR("begin ")));
+        CX_TEST_ASSERT(!cx_strcmp(cx_strcast(em_t0->text), cx_str("begin ")));
         MDNode *em_t1 = em_t0->next;
         CX_TEST_ASSERT(em_t1);
         CX_TEST_ASSERT(em_t1->type == MD_SPAN_STRONG);
-        CX_TEST_ASSERT(!cx_strcmp(mdnode_get_text(em_t1), CX_STR("bold")));
+        CX_TEST_ASSERT(!cx_strcmp(mdnode_get_text(em_t1), cx_str("bold")));
         MDNode *em_t2 = em_t1->next;
         CX_TEST_ASSERT(em_t2);
         CX_TEST_ASSERT(!em_t2->next);
         CX_TEST_ASSERT(em_t2->text.ptr);
-        CX_TEST_ASSERT(!cx_strcmp(cx_strcast(em_t2->text), CX_STR(" text")));
+        CX_TEST_ASSERT(!cx_strcmp(cx_strcast(em_t2->text), cx_str(" text")));
     }
 }
 
@@ -210,7 +210,7 @@ CX_TEST(test_parse_markdown_formatting_nested) {
     MDNode *t4;
     
     CX_TEST_DO {
-        doc = parse_markdown(CX_STR("test *begin __bold__ end*"));
+        doc = parse_markdown(cx_str("test *begin __bold__ end*"));
         CX_TEST_ASSERT(doc);
         CX_TEST_ASSERT(doc->content);
         p0 = doc->content;
@@ -219,7 +219,7 @@ CX_TEST(test_parse_markdown_formatting_nested) {
         t0 = p0->content;
         
         CX_TEST_ASSERT(t0);
-        CX_TEST_ASSERT(!cx_strcmp(cx_strcast(t0->text), CX_STR("test ")));
+        CX_TEST_ASSERT(!cx_strcmp(cx_strcast(t0->text), cx_str("test ")));
         t1 = t0->next;
         CX_TEST_ASSERT(t1);
         CX_TEST_ASSERT(t1->text.ptr == NULL);
@@ -227,15 +227,15 @@ CX_TEST(test_parse_markdown_formatting_nested) {
         CX_TEST_ASSERT(t1->next == NULL);
         t2 = t1->children_begin;
         CX_TEST_ASSERT(t2);
-        CX_TEST_ASSERT(!cx_strcmp(cx_strcast(t2->text), CX_STR("begin ")));
+        CX_TEST_ASSERT(!cx_strcmp(cx_strcast(t2->text), cx_str("begin ")));
         t3 = t2->next;
         CX_TEST_ASSERT(t3);
         CX_TEST_ASSERT(t3->text.ptr == NULL);
         CX_TEST_ASSERT(t3->type == MD_SPAN_STRONG);
-        CX_TEST_ASSERT(!cx_strcmp(mdnode_get_text(t3), CX_STR("bold")));
+        CX_TEST_ASSERT(!cx_strcmp(mdnode_get_text(t3), cx_str("bold")));
         t4 = t3->next;
         CX_TEST_ASSERT(t4);
-        CX_TEST_ASSERT(!cx_strcmp(cx_strcast(t4->text), CX_STR(" end")));
+        CX_TEST_ASSERT(!cx_strcmp(cx_strcast(t4->text), cx_str(" end")));
         mddoc_free(doc);
         
     }
@@ -253,7 +253,7 @@ CX_TEST(test_parse_markdown_list) {
     MDNode *li1text_bold;
     
     CX_TEST_DO {
-        doc = parse_markdown(CX_STR("Test Paragraph\n\n - List1\n - List2 __bold__\n\nend"));
+        doc = parse_markdown(cx_str("Test Paragraph\n\n - List1\n - List2 __bold__\n\nend"));
         CX_TEST_ASSERT(doc);
         
         // paragraph structure
@@ -278,20 +278,20 @@ CX_TEST(test_parse_markdown_list) {
         li0text = li0->children_begin;
         CX_TEST_ASSERT(li0text);
         CX_TEST_ASSERT(li0text->text.ptr != NULL);
-        CX_TEST_ASSERT(!cx_strcmp(mdnode_get_text(li0text), CX_STR("List1")));
+        CX_TEST_ASSERT(!cx_strcmp(mdnode_get_text(li0text), cx_str("List1")));
         
         li1text = li1->children_begin;
         CX_TEST_ASSERT(li1text);
         CX_TEST_ASSERT(li1text->text.ptr != NULL);
-        CX_TEST_ASSERT(!cx_strcmp(mdnode_get_text(li1text), CX_STR("List2 ")));
+        CX_TEST_ASSERT(!cx_strcmp(mdnode_get_text(li1text), cx_str("List2 ")));
         li1text_bold = li1text->next;
         CX_TEST_ASSERT(li1text_bold);
         CX_TEST_ASSERT(li1text_bold->type == MD_SPAN_STRONG);
-        CX_TEST_ASSERT(!cx_strcmp(mdnode_get_text(li1text_bold), CX_STR("bold")));
+        CX_TEST_ASSERT(!cx_strcmp(mdnode_get_text(li1text_bold), cx_str("bold")));
         
         // check end paragraph
         CX_TEST_ASSERT(p2->content != NULL);
-        CX_TEST_ASSERT(!cx_strcmp(mdnode_get_text(p2->content), CX_STR("end")));
+        CX_TEST_ASSERT(!cx_strcmp(mdnode_get_text(p2->content), cx_str("end")));
     }
 }
 
@@ -310,17 +310,17 @@ static int section_style_sort(MDDocStyleSection *s1, MDDocStyleSection *s2) {
 }
 
 CX_TEST(test_mddoc_linearization) {
-    MDDoc *doc = parse_markdown(CX_STR("# heading 1\n\ntest *begin __bold__ text* end"));
+    MDDoc *doc = parse_markdown(cx_str("# heading 1\n\ntest *begin __bold__ text* end"));
     
     CX_TEST_DO {
         MDDocLinear md = mddoc_linearization(doc);
         // don't compare strings with cx_strcmp because currently the last paragraph
         // is terminated with 2 line breaks, but this behavior can change in the future
-        CX_TEST_ASSERT(cx_strprefix(cx_strcast(md.content), CX_STR("heading 1\n\ntest begin bold text end")));
+        CX_TEST_ASSERT(cx_strprefix(cx_strcast(md.content), cx_str("heading 1\n\ntest begin bold text end")));
         
         CX_TEST_ASSERT(md.styles);
         CX_TEST_ASSERT(cxListSize(md.styles) == 4);
-        md.styles->collection.cmpfunc = (cx_compare_func)section_style_sort;
+        cxSetCompareFunc(md.styles, (cx_compare_func)section_style_sort);
         cxListSort(md.styles); // sort list, because there is no defined order for the style sections list
         MDDocStyleSection *styles = cxListAt(md.styles, 0);
         CX_TEST_ASSERT(str_eq(styles[0].style, EDITOR_STYLE_HEADING1));
index 84d272e1121098e853d155d9d9abd6c91e4331c3..12b7d11535322df75a651a2f7fe17ea60cff2d71 100644 (file)
@@ -129,12 +129,12 @@ MainWindow* window_init_data(UiObject *obj) {
     wdata->obj = obj;
     
     wdata->notelist_isvisible = TRUE;
-    wdata->navstack = cxArrayListCreateSimple(sizeof(NavStack), 32);
+    wdata->navstack = cxArrayListCreate(NULL, sizeof(NavStack), 32);
     
     wdata->notebooks = ui_list_new(obj->ctx, NULL);
     update_sublists(obj->ctx, wdata->notebooks);
     
-    wdata->notebook_cache = cxHashMapCreateSimple(CX_STORE_POINTERS);
+    wdata->notebook_cache = cxHashMapCreate(NULL, CX_STORE_POINTERS, 32);
     
     return wdata;
 }
@@ -174,7 +174,7 @@ void update_sublists(UiContext *ctx, UiList *sublists) {
     CxList *list = sublists->data;
     
     // save previous elements
-    CxList *delete_list = cxArrayListCreateSimple(CX_STORE_POINTERS, cxListSize(list));
+    CxList *delete_list = cxArrayListCreate(NULL, CX_STORE_POINTERS, cxListSize(list));
     CxIterator i = cxListIterator(list);
     cx_foreach(UiSubList *, sublist, i) {
         cxListAdd(delete_list, sublist);
index caaa05615b18edd002d8c482869291cab260f97b..8a4d1aa6419385b8d2e750218e3b296adfb77b28 100644 (file)
@@ -35,6 +35,7 @@ SRC += field.c
 SRC += db.c
 SRC += object.c
 SRC += sqlite.c
+SRC += json.c
 
 OBJ = $(SRC:%.c=../build/dbutils/%$(OBJ_EXT))
 
index 65dec0d7b68aa20c4f562448662fbbcf55eb2552..fcd2751a862fcdb83b65084514fbc038f4040631 100644 (file)
@@ -46,11 +46,11 @@ DBUClass* dbuClassCreate(const char *name) {
     memset(cls, 0, sizeof(DBUClass));
     
     cls->name = cx_strdup(cx_str(name));
-    cls->fields = cxHashMapCreateSimple(CX_STORE_POINTERS);
+    cls->fields = cxHashMapCreate(NULL, CX_STORE_POINTERS, 32);
     cls->fields->collection.simple_destructor = (cx_destructor_func)field_destructor;
-    cls->obj_fields = cxHashMapCreateSimple(CX_STORE_POINTERS);
+    cls->obj_fields = cxHashMapCreate(NULL, CX_STORE_POINTERS, 16);
     cls->obj_fields->collection.simple_destructor = (cx_destructor_func)field_destructor;
-    cls->foreign_keys = cxHashMapCreateSimple(sizeof(DBUForeignKeyField));
+    cls->foreign_keys = cxHashMapCreate(NULL, sizeof(DBUForeignKeyField), 16);
     
     return cls;
 }
index 6f6b99878481ad76279041bbb0709b8349597de4..a2a0d89fbc282d2cad1657bc54f8931863cd7278 100644 (file)
@@ -39,7 +39,28 @@ int dbuConnectionExec(DBUConnection *conn, const char *sql) {
     return err;
 }
 
+int dbuConnectionIsActive(DBUConnection *conn) {
+    return conn->isActive ? conn->isActive(conn) : 1;
+}
+
+void dbuConnectionFree(DBUConnection *conn) {
+    conn->free(conn);
+}
 
+DBUQuery* dbuConnectionQuery(DBUConnection *conn, const CxAllocator *a) {
+    return conn->createQuery(conn, a);
+}
+
+DBUQuery* dbuQueryCreate(DBUConnection *conn, const CxAllocator *a, const char *sql) {
+    DBUQuery *query = dbuConnectionQuery(conn, a);
+    if(query) {
+        if(dbuQuerySetSQL(query, sql)) {
+            dbuQueryFree(query);
+            return NULL;
+        }
+    }
+    return query;
+}
 
 int dbuQuerySetSQL(DBUQuery *q, const char *sql) {
     return q->setSQL(q, sql);
@@ -82,6 +103,47 @@ void dbuQueryFree(DBUQuery *q) {
 }
 
 
+int dbuResultHasData(DBUResult *result) {
+    return result->hasData(result);
+}
+
+int dbuResultIsOk(DBUResult *result) {
+    return result->isOk(result);
+}
+
+int dbuResultNextRow(DBUResult *result) {
+    return result->nextRow(result);
+}
+
+cxstring dbuResultGetText(DBUResult *result, int field) {
+    return result->getText(result, field);
+}
+
+int64_t dbuResultGetInt(DBUResult *result, int field) {
+    if(result->optional_getInt) {
+        return result->optional_getInt(result, field);
+    } else {
+        cxstring s = result->getText(result, 0);
+        int64_t value = 0;
+        cx_strtoi64(s, &value, 10);
+        return value;
+    }
+}
+
+DBUBytes dbuResultGetBinary(DBUResult *result, int field) {
+    if(result->optional_getBinary) {
+        return result->optional_getBinary(result, field);
+    } else {
+        cxstring bin = result->getText(result, field);
+        return (DBUBytes) { (const unsigned char*)bin.ptr, bin.length };
+    }
+}
+
+void dbuResultFree(DBUResult *result) {
+    result->free(result);
+}
+
+
 
 int dbuResultAsInt32(DBUResult *r, int32_t *result) {
     int64_t i;
@@ -138,11 +200,15 @@ int dbuResultAsString(DBUResult *r, char **result) {
 }
 
 int dbuResultAsCxString(DBUResult *r, cxmutstr *result) {
+    return dbuResultAsCxStringA(r, cxDefaultAllocator, result);
+}
+
+int dbuResultAsCxStringA(DBUResult *r, const CxAllocator *a, cxmutstr *result) {
     if(r->hasData(r)) {
         if(r->fieldType(r, 0) != DBU_FIELD_NULL) {
             cxstring s = r->getText(r, 0);
             if(s.ptr) {
-                *result = cx_strdup(s);
+                *result = cx_strdup_a(a, s);
                 r->free(r);
                 return 0;
             }
@@ -151,3 +217,164 @@ int dbuResultAsCxString(DBUResult *r, cxmutstr *result) {
     r->free(r);
     return -1;
 }
+
+static DBUQuery* execInt64ParamQuery(DBUConnection *conn, const char *sql, int64_t param) {
+    DBUQuery *q = conn->createQuery(conn, cxDefaultAllocator);
+    if(dbuQuerySetSQL(q, sql)) {
+        dbuQueryFree(q);
+        return NULL;
+    }
+    dbuQuerySetParamInt64(q, 1, param);
+    if(dbuQueryExec(q)) {
+        dbuQueryFree(q);
+        return NULL;
+    }
+    return q;
+}
+
+static DBUQuery* execStringParamQuery(DBUConnection *conn, const char *sql, cxstring param) {
+    DBUQuery *q = conn->createQuery(conn, cxDefaultAllocator);
+    if(dbuQuerySetSQL(q, sql)) {
+        dbuQueryFree(q);
+        return NULL;
+    }
+    dbuQuerySetParamString(q, 1, param);
+    if(dbuQueryExec(q)) {
+        dbuQueryFree(q);
+        return NULL;
+    }
+    return q;
+}
+
+DBUResult* dbuSqlExec(DBUConnection *conn, const CxAllocator *a, const char *sql) {
+    DBUQuery *q = dbuQueryCreate(conn, a, sql);
+    if(!q) {
+        return NULL;
+    }
+    if(dbuQueryExec(q)) {
+        dbuQueryFree(q);
+        return NULL;
+    }
+    DBUResult *result = dbuQueryGetResult(q);
+    dbuQueryFree(q);
+    return result;
+}
+
+DBUResult* dbuSqlExecParamInt32(DBUConnection *conn, const CxAllocator *a, const char *sql, int32_t param) {
+    DBUQuery *q = execInt64ParamQuery(conn, sql, param);
+    if(!q) return NULL;
+    DBUResult *r = dbuQueryGetResult(q);
+    dbuQueryFree(q);
+    return r;
+}
+
+DBUResult* dbuSqlExecParamInt64(DBUConnection *conn, const CxAllocator *a, const char *sql, int64_t param) {
+    DBUQuery *q = execInt64ParamQuery(conn, sql, param);
+    if(!q) return NULL;
+    DBUResult *r = dbuQueryGetResult(q);
+    dbuQueryFree(q);
+    return r;
+}
+
+DBUResult* dbuSqlExecParamUInt32(DBUConnection *conn, const CxAllocator *a, const char *sql, uint32_t param) {
+    DBUQuery *q = execInt64ParamQuery(conn, sql, param);
+    if(!q) return NULL;
+    DBUResult *r = dbuQueryGetResult(q);
+    dbuQueryFree(q);
+    return r;
+}
+
+DBUResult* dbuSqlExecParamUInt64(DBUConnection *conn, const CxAllocator *a, const char *sql, uint32_t param) {
+    DBUQuery *q = execInt64ParamQuery(conn, sql, param);
+    if(!q) return NULL;
+    DBUResult *r = dbuQueryGetResult(q);
+    dbuQueryFree(q);
+    return r;
+}
+
+DBUResult* dbuSqlExecParamString(DBUConnection *conn, const CxAllocator *a, const char *sql, const char *param) {
+    DBUQuery *q = execStringParamQuery(conn, sql, cx_str(param));
+    if(!q) return NULL;
+    DBUResult *r = dbuQueryGetResult(q);
+    dbuQueryFree(q);
+    return r;
+}
+
+DBUResult* dbuSqlExecParamCxString(DBUConnection *conn, const CxAllocator *a, const char *sql, cxstring param) {
+    DBUQuery *q = execStringParamQuery(conn, sql, param);
+    if(!q) return NULL;
+    DBUResult *r = dbuQueryGetResult(q);
+    dbuQueryFree(q);
+    return r;
+}
+
+DBUResult* dbuSqlExecParamCxMutStr(DBUConnection *conn, const CxAllocator *a, const char *sql, cxmutstr param) {
+    DBUQuery *q = execStringParamQuery(conn, sql, cx_strcast(param));
+    if(!q) return NULL;
+    DBUResult *r = dbuQueryGetResult(q);
+    dbuQueryFree(q);
+    return r;
+}
+
+
+int dbuSimpleGetInt64WithIntParam(DBUConnection *conn, const char *sql, int64_t param, int64_t *result) {
+    DBUQuery *q = execInt64ParamQuery(conn, sql, param);
+    if(!q) return 1;
+    int err = dbuQueryExec(q);
+    dbuQueryFree(q);
+    if(!err) {
+        DBUResult *r = dbuQueryGetResult(q);
+        err = dbuResultAsInt64(r, result);
+    }
+    return err;
+}
+
+int dbuSimpleGetInt64WithStringParam(DBUConnection *conn, const char *sql, cxstring param, int64_t *result) {
+    DBUQuery *q = execStringParamQuery(conn, sql, param);
+    if(!q) return 1;
+    DBUResult *r = dbuQueryGetResult(q);
+    dbuQueryFree(q);
+    return dbuResultAsInt64(r, result);;
+}
+
+int dbuSimpleGetStringWithIntParam(DBUConnection *conn, const char *sql, int64_t param, const CxAllocator *a, cxmutstr *result) {
+    DBUQuery *q = execInt64ParamQuery(conn, sql, param);
+    if(!q) return 1;
+    DBUResult *r = dbuQueryGetResult(q);
+    dbuQueryFree(q);
+    return dbuResultAsCxStringA(r, a, result);
+}
+
+int dbuSimpleGetStringWithStringParam(DBUConnection *conn, const char *sql, cxstring param, const CxAllocator *a, cxmutstr *result) {
+    DBUQuery *q = execStringParamQuery(conn, sql, param);
+    if(!q) return 1;
+    DBUResult *r = dbuQueryGetResult(q);
+    dbuQueryFree(q);
+    return dbuResultAsCxStringA(r, a, result);
+}
+
+CxList* dbuSimpleGetListWithIntParam(DBUConnection *conn, const char *sql, int64_t param, const CxAllocator *a, DBUClass *type) {
+    DBUQuery *q = conn->createQuery(conn, cxDefaultAllocator);
+    if(dbuQuerySetSQL(q, sql)) {
+        dbuQueryFree(q);
+        return NULL;
+    }
+    dbuQuerySetParamInt64(q, 1, param);
+    DBUObjectBuilder *builder = dbuObjectBuilder(type, q, a);
+    CxList *list = dbuObjectBuilderGetList(builder);
+    dbuObjectBuilderDestroy(builder);
+    return list;
+}
+
+CxList* dbuSimpleGetListWithStringParam(DBUConnection *conn, const char *sql, cxstring param, const CxAllocator *a, DBUClass *type) {
+    DBUQuery *q = conn->createQuery(conn, cxDefaultAllocator);
+    if(dbuQuerySetSQL(q, sql)) {
+        dbuQueryFree(q);
+        return NULL;
+    }
+    dbuQuerySetParamString(q, 1, param);
+    DBUObjectBuilder *builder = dbuObjectBuilder(type, q, a);
+    CxList *list = dbuObjectBuilderGetList(builder);
+    dbuObjectBuilderDestroy(builder);
+    return list;
+}
index 68b013e158a1eeb01fc3da34b11a15b40de2212f..df7392b1d35e4f91a81aff3e927d2392c58e02b5 100644 (file)
@@ -34,7 +34,7 @@
 
 DBUContext* dbuContextCreate(void) {
     DBUContext *ctx = malloc(sizeof(DBUContext));
-    ctx->classes = cxHashMapCreateSimple(CX_STORE_POINTERS);
+    ctx->classes = cxHashMapCreate(NULL, CX_STORE_POINTERS, 32);
     ctx->classes->collection.simple_destructor = (cx_destructor_func)dbuClassFree;
     return ctx;
 }
index 8a04a080ce7b20fc14ff1e385fa6020bb716c3c2..3162704ef2e496f20e4755d20eb3a865c8716c2a 100644 (file)
@@ -97,6 +97,10 @@ struct DBUResult {
 };
 
 int dbuConnectionExec(DBUConnection *conn, const char *sql);
+int dbuConnectionIsActive(DBUConnection *conn);
+void dbuConnectionFree(DBUConnection *conn);
+DBUQuery* dbuConnectionQuery(DBUConnection *conn, const CxAllocator *a);
+DBUQuery* dbuQueryCreate(DBUConnection *conn, const CxAllocator *a, const char *sql);
 
 int dbuQuerySetSQL(DBUQuery *q, const char *sql);
 int dbuQuerySetParamString(DBUQuery *q, int index, cxstring str);
@@ -109,6 +113,14 @@ int dbuQueryExec(DBUQuery *q);
 DBUResult* dbuQueryGetResult(DBUQuery *q);
 void dbuQueryFree(DBUQuery *q);
 
+int dbuResultHasData(DBUResult *result);
+int dbuResultIsOk(DBUResult *result);
+int dbuResultNextRow(DBUResult *result);
+cxstring dbuResultGetText(DBUResult *result, int field);
+int64_t dbuResultGetInt(DBUResult *result, int field);
+DBUBytes dbuResultGetBinary(DBUResult *result, int field);
+void dbuResultFree(DBUResult *result);
+
 
 #define dbuResultAsValue(r, result) \
     _Generic(result, \
@@ -125,6 +137,7 @@ int dbuResultAsInt64(DBUResult *r, int64_t *result);
 int dbuResultAsUInt64(DBUResult *r, uint64_t *result);
 int dbuResultAsString(DBUResult *r, char **result);
 int dbuResultAsCxString(DBUResult *r, cxmutstr *result);
+int dbuResultAsCxStringA(DBUResult *r, const CxAllocator *a, cxmutstr *result);
 
 DBUObjectBuilder* dbuObjectBuilder(DBUClass *type, DBUQuery *query, const CxAllocator *a);
 void dbuObjectBuilderSetDenseResult(DBUObjectBuilder *builder, bool dense);
@@ -139,7 +152,34 @@ void dbuObjectBuilderDestroy(DBUObjectBuilder *builder);
 // TODO: implement
 int dbuObjectBuilderAddSubquery(DBUObjectBuilder *builder, DBUClass *type, DBUQuery *subquery);
 
-
+#define dbuSqlExecParam(conn, a, sql, param) \
+    _Generic(param, \
+    int32_t    : dbuSqlExecParamInt32, \
+    int64_t    : dbuSqlExecParamInt64, \
+    uint32_t   : dbuSqlExecParamUInt32, \
+    uint64_t   : dbuSqlExecParamUInt64, \
+    char*      : dbuSqlExecParamString, \
+    const char*: dbuSqlExecParamString, \
+    cxstring   : dbuSqlExecParamCxString, \
+    cxmutstr   : dbuSqlExecParamCxMutStr)(conn, a, sql, param)
+
+DBUResult* dbuSqlExec(DBUConnection *conn, const CxAllocator *a, const char *sql);
+DBUResult* dbuSqlExecParamInt32(DBUConnection *conn, const CxAllocator *a, const char *sql, int32_t param);
+DBUResult* dbuSqlExecParamInt64(DBUConnection *conn, const CxAllocator *a, const char *sql, int64_t param);
+DBUResult* dbuSqlExecParamUInt32(DBUConnection *conn, const CxAllocator *a, const char *sql, uint32_t param);
+DBUResult* dbuSqlExecParamUInt64(DBUConnection *conn, const CxAllocator *a, const char *sql, uint32_t param);
+DBUResult* dbuSqlExecParamString(DBUConnection *conn, const CxAllocator *a, const char *sql, const char *param);
+DBUResult* dbuSqlExecParamCxString(DBUConnection *conn, const CxAllocator *a, const char *sql, cxstring param);
+DBUResult* dbuSqlExecParamCxMutStr(DBUConnection *conn, const CxAllocator *a, const char *sql, cxmutstr param);
+
+
+
+int dbuSimpleGetInt64WithIntParam(DBUConnection *conn, const char *sql, int64_t param, int64_t *result);
+int dbuSimpleGetInt64WithStringParam(DBUConnection *conn, const char *sql, cxstring param, int64_t *result);
+int dbuSimpleGetStringWithIntParam(DBUConnection *conn, const char *sql, int64_t param, const CxAllocator *a, cxmutstr *result);
+int dbuSimpleGetStringWithStringParam(DBUConnection *conn, const char *sql, cxstring param, const CxAllocator *a, cxmutstr *result);
+CxList* dbuSimpleGetListWithIntParam(DBUConnection *conn, const char *sql, int64_t param, const CxAllocator *a, DBUClass *type);
+CxList* dbuSimpleGetListWithStringParam(DBUConnection *conn, const char *sql, cxstring param, const CxAllocator *a, DBUClass *type);
 
 #ifdef __cplusplus
 }
index e11ac4c1edf8300cdc4e9c8e7e7f47929ee310eb..c75740149ff683402f5a33337caa88e8d97fa364 100644 (file)
@@ -54,6 +54,12 @@ typedef char* DBUObject;
 
 typedef int(*DBUFieldDefInitFunc)(DBUField *f, const CxAllocator *a, DBUObject obj);
 typedef int(*DBUFieldInitFunc)(DBUField *f, const CxAllocator *a, DBUObject obj, const char *value, size_t length);
+typedef bool(*DBUFieldToBoolFunc)(DBUField *f, DBUObject obj);
+typedef int64_t(*DBUFieldToInt64Func)(DBUField *f, DBUObject obj);
+typedef uint64_t(*DBUFieldToUInt64Func)(DBUField *f, DBUObject obj);
+typedef uint64_t(*DBUFieldToUInt64Func)(DBUField *f, DBUObject obj);
+typedef cxmutstr(*DBUFieldToStringFunc)(DBUField *f, DBUObject obj, const CxAllocator *a);
+typedef double(*DBUFieldToDoubleFunc)(DBUField *f, DBUObject obj);
 
 struct DBUContext {
     /*
@@ -136,6 +142,39 @@ struct DBUForeignKeyField {
     DBUClass *cls;
 };
 
+/*
+ * list abstraction for accessing elements
+ */
+typedef struct DBUAbstractList DBUAbstractList;
+struct DBUAbstractList {
+    /*
+     * List pointer (for example a C array or CxList*)
+     */
+    void *list;
+    /*
+     * Get the list size
+     */
+    size_t (*length)(DBUAbstractList *ls);
+    /*
+     * Get the element at the specified index
+     */
+    void* (*get)(DBUAbstractList *ls, size_t index);
+    /*
+     * Get an DBUField for an list element. The DBUField pointer is only
+     * valid until another elementField call.
+     */
+    DBUField* (*elementField)(DBUAbstractList *ls, void *elm);
+    /*
+     * Optional function for getting an list iterator
+     */
+    CxIterator (*iterator)(DBUAbstractList *ls);
+    /*
+     * Free the DBUAbstractList abstraction layer. The actual list is
+     * not freed.
+     */
+    void (*free)(DBUAbstractList *ls);
+};
+
 /*
  * abstract field
  */
@@ -145,6 +184,11 @@ struct DBUField {
      */
     cxmutstr name;
     
+    /*
+     * field object type
+     */
+    DBUClass *objType;
+    
     /*
      * called, if the field is null (optional)
      */
@@ -180,6 +224,64 @@ struct DBUField {
      */
     int (*initObjValue)(DBUField *f, const CxAllocator *a, DBUObject obj, void *child);
     
+    
+    /*
+     * get the field value as a boolean
+     * 
+     * This should only be implemented, if the field is a boolean
+     */
+    bool (*toBool)(DBUField *f, DBUObject obj);
+    
+    /*
+     * get the field value as an int64 (optional)
+     * 
+     * This should only be implemented, if the field is a signed integer
+     */
+    int64_t (*toInt64)(DBUField *f, DBUObject obj);
+    
+    /*
+     * get the field value as an int64 (optional)
+     * 
+     * This should only be implemented, if the field is a unsigned integer
+     */
+    uint64_t (*toUInt64)(DBUField *f, DBUObject obj);
+    
+    /*
+     * get the field value as a string(optional)
+     * 
+     * This should only be implemented, if the field is represented as a string
+     */
+    cxmutstr (*toString)(DBUField *f, DBUObject obj, const CxAllocator *a);
+    
+    /*
+     * get the field value as a binary (optional)
+     * 
+     * This should only be implemented, if the field contains binary data
+     */
+    cxmutstr (*toBinary)(DBUField *f, DBUObject obj, const CxAllocator *a);
+    
+    /*
+     * get the field value as double (optional)
+     * 
+     * This should only be implemented, if the field is a float/double
+     */
+    double (*toDouble)(DBUField *f, DBUObject obj);
+    
+    /*
+     * get the field value as object (optional)
+     * 
+     * This should only be implemented, if the field has a compley type
+     * and objType is set.
+     */
+    DBUObject (*toObject)(DBUField *f, DBUObject obj);
+    
+    /*
+     * get the field value as a list/iterator (optional)
+     * 
+     * This should only be implemented, if the field is a list.
+     */
+    DBUAbstractList* (*toList)(DBUField *f, DBUObject obj);
+    
     /*
      * destructor (optional)
      * 
diff --git a/dbutils/dbutils/json.h b/dbutils/dbutils/json.h
new file mode 100644 (file)
index 0000000..0617ef8
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef LIBDBU_JSON_H
+#define LIBDBU_JSON_H
+
+#include <cx/json.h>
+#include "dbutils.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    
+CxJsonValue* dbuObjectToJson(DBUClass *type, void *obj, const CxAllocator *a);
+cxmutstr dbuObjectToJsonString(DBUClass *type, void *obj, const CxAllocator *a);
+
+CxJsonValue* dbuListToJson(DBUClass *type, CxList *list, const CxAllocator *a);
+CxJsonValue* dbuArrayToJson(DBUClass *type, void **objArray, size_t size, const CxAllocator *a);
+
+cxmutstr dbuListToJsonString(DBUClass *type, CxList *list, const CxAllocator *a);
+cxmutstr dbuArrayToJsonString(DBUClass *type, void **objArray, size_t size, const CxAllocator *a);
+
+CxJsonValue* dbuObjectToJson2(DBUClass *type, void *obj, const CxAllocator *a, bool setNull, bool forceString);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBDBU_JSON_H */
+
index e2d480467b087941792eb3914b134e170301a743..bebf2211216628ccbe088657d03f9a5be31ada7f 100644 (file)
@@ -177,6 +177,11 @@ static int field_init_int8(
     return 0;
 }
 
+static int64_t field_get_int8(DBUOffsetField *f, DBUObject obj) {
+    int8_t *i = (int8_t*)(obj + f->offset);
+    return *i;
+}
+
 static int field_init_uint8(
         DBUOffsetField *f,
         const CxAllocator *a,
@@ -195,6 +200,11 @@ static int field_init_uint8(
     return 0;
 }
 
+static uint64_t field_get_uint8(DBUOffsetField *f, DBUObject obj) {
+    uint8_t *i = (uint8_t*)(obj + f->offset);
+    return *i;
+}
+
 static int field_init_int16(
         DBUOffsetField *f,
         const CxAllocator *a,
@@ -213,6 +223,11 @@ static int field_init_int16(
     return 0;
 }
 
+static int64_t field_get_int16(DBUOffsetField *f, DBUObject obj) {
+    int16_t *i = (int16_t*)(obj + f->offset);
+    return *i;
+}
+
 static int field_init_uint16(
         DBUOffsetField *f,
         const CxAllocator *a,
@@ -231,6 +246,11 @@ static int field_init_uint16(
     return 0;
 }
 
+static uint64_t field_get_uint16(DBUOffsetField *f, DBUObject obj) {
+    uint16_t *i = (uint16_t*)(obj + f->offset);
+    return *i;
+}
+
 static int field_init_int32(
         DBUOffsetField *f,
         const CxAllocator *a,
@@ -249,6 +269,11 @@ static int field_init_int32(
     return 0;
 }
 
+static int64_t field_get_int32(DBUOffsetField *f, DBUObject obj) {
+    int32_t *i = (int32_t*)(obj + f->offset);
+    return *i;
+}
+
 static int field_init_uint32(
         DBUOffsetField *f,
         const CxAllocator *a,
@@ -267,6 +292,12 @@ static int field_init_uint32(
     return 0;
 }
 
+static uint64_t field_get_uint32(DBUOffsetField *f, DBUObject obj) {
+    uint32_t *i = (uint32_t*)(obj + f->offset);
+    return *i;
+}
+
+
 static int field_init_int64(
         DBUOffsetField *f,
         const CxAllocator *a,
@@ -282,6 +313,11 @@ static int field_init_int64(
     return 0;
 }
 
+static int64_t field_get_int64(DBUOffsetField *f, DBUObject obj) {
+    int64_t *i = (int64_t*)(obj + f->offset);
+    return *i;
+}
+
 static int field_init_uint64(
         DBUOffsetField *f,
         const CxAllocator *a,
@@ -297,6 +333,11 @@ static int field_init_uint64(
     return 0;
 }
 
+static uint64_t field_get_uint64(DBUOffsetField *f, DBUObject obj) {
+    uint64_t *i = (uint64_t*)(obj + f->offset);
+    return *i;
+}
+
 static int field_init_size(
         DBUOffsetField *f,
         const CxAllocator *a,
@@ -308,10 +349,15 @@ static int field_init_size(
     if(!str2uint(value, &i)) {
         return 1;
     }
-    *(uint64_t*)(obj+f->offset) = i;
+    *(size_t*)(obj+f->offset) = i;
     return 0;
 }
 
+static uint64_t field_get_size(DBUOffsetField *f, DBUObject obj) {
+    size_t *i = (size_t*)(obj + f->offset);
+    return *i;
+}
+
 static int field_init_ssize(
         DBUOffsetField *f,
         const CxAllocator *a,
@@ -327,6 +373,11 @@ static int field_init_ssize(
     return 0;
 }
 
+static int64_t field_get_ssize(DBUOffsetField *f, DBUObject obj) {
+    ssize_t *i = (ssize_t*)(obj + f->offset);
+    return *i;
+}
+
 static int field_init_bool(
         DBUOffsetField *f,
         const CxAllocator *a,
@@ -345,6 +396,11 @@ static int field_init_bool(
     return 0;
 }
 
+static bool field_get_bool(DBUOffsetField *f, DBUObject obj) {
+    bool *i = (bool*)(obj + f->offset);
+    return *i;
+}
+
 static int field_init_float(
         DBUOffsetField *f,
         const CxAllocator *a,
@@ -361,6 +417,11 @@ static int field_init_float(
     return 0;
 }
 
+static double field_get_float(DBUOffsetField *f, DBUObject obj) {
+    float *d = (float*)(obj + f->offset);
+    return *d;
+}
+
 static int field_init_double(
         DBUOffsetField *f,
         const CxAllocator *a,
@@ -377,6 +438,10 @@ static int field_init_double(
     return 0;
 }
 
+static double field_get_double(DBUOffsetField *f, DBUObject obj) {
+    double *d = (double*)(obj + f->offset);
+    return *d;
+}
 
 static int field_init_str(
         DBUOffsetField *f,
@@ -393,6 +458,12 @@ static int field_init_str(
     return 0;
 }
 
+
+static cxmutstr field_get_str(DBUOffsetField *f, DBUObject obj, const CxAllocator *a) {
+    char **s = (char**)(obj + f->offset);
+    return cx_strdup_a(a, cx_str(*s));
+}
+
 static int field_init_cxmutstr(
         DBUOffsetField *f,
         const CxAllocator *a,
@@ -408,6 +479,11 @@ static int field_init_cxmutstr(
     return 0;
 }
 
+static cxmutstr field_get_cxstr(DBUOffsetField *f, DBUObject obj, const CxAllocator *a) {
+    cxmutstr *s = (cxmutstr*)(obj + f->offset);
+    return cx_strdup_a(a, *s);
+}
+
 static int field_init_str_size(
         DBUObjLenField *f,
         const CxAllocator *a,
@@ -424,6 +500,12 @@ static int field_init_str_size(
     return 0;
 }
 
+static cxmutstr field_get_str_len(DBUObjLenField *f, DBUObject obj, const CxAllocator *a) {
+    char *ptr = *(char**)(obj+f->offset_obj);
+    size_t len = *(size_t*)(obj+f->offset_len);
+    return cx_strdup_a(a, cx_strn(ptr, len));
+}
+
 static int field_init_str_intlen(
         DBUObjLenField *f,
         const CxAllocator *a,
@@ -440,6 +522,13 @@ static int field_init_str_intlen(
     return 0;
 }
 
+static cxmutstr field_get_str_intlen(DBUObjLenField *f, DBUObject obj, const CxAllocator *a) {
+    char *ptr = *(char**)(obj+f->offset_obj);
+    int len = *(int*)(obj+f->offset_len);
+    return cx_strdup_a(a, cx_strn(ptr, len));
+}
+
+
 static int field_init_bytes_size(
         DBUObjLenField *f,
         const CxAllocator *a,
@@ -494,7 +583,7 @@ static DBUField* create_offset_def_field(
     return (DBUField*)field;
 }
 
-static void add_offset_def_field(
+static DBUField* add_offset_def_field(
         DBUClass *cls, 
         const char *name, 
         off_t offset,
@@ -503,10 +592,12 @@ static void add_offset_def_field(
         bool nonnull, 
         union DBUDefValue def)
 {
-    dbuClassAddField(cls, name, create_offset_def_field(offset, def_init, init, nonnull, def));
+    DBUField *field = create_offset_def_field(offset, def_init, init, nonnull, def);
+    dbuClassAddField(cls, name, field);
+    return field;
 }
 
-static void add_offset_fk_field(
+static DBUField* add_offset_fk_field(
         DBUClass *cls, 
         const char *name, 
         off_t offset,
@@ -514,7 +605,9 @@ static void add_offset_fk_field(
         DBUFieldInitFunc init,
         bool nonnull)
 {
-    dbuClassAddFKField(cls, name, create_offset_def_field(offset, NULL, init, nonnull, (union DBUDefValue){ 0 }), fkcls);
+    DBUField *field = create_offset_def_field(offset, NULL, init, nonnull, (union DBUDefValue){ 0 });
+    dbuClassAddFKField(cls, name, field, fkcls);
+    return field;
 }
 
 static void add_bool(
@@ -530,6 +623,7 @@ static void add_bool(
     field->field.query_length = false;
     field->offset = offset;
     field->def.def = 0;
+    field->field.toInt64 = (DBUFieldToInt64Func)field_get_bool;
     dbuClassAddField(cls, name, (DBUField*)field);
 }
 
@@ -548,24 +642,19 @@ static DBUField* create_offset_str_field(
     return (DBUField*)field;
 }
 
-static void add_offset_str_field(
+static DBUField* add_offset_str_field(
         DBUClass *cls, 
         const char *name,
         DBUFieldInitFunc init,
         off_t offset,
         bool nonnull)
 {
-    DBUOffsetField *field = calloc(1, sizeof(DBUOffsetField));
-    field->field.initDefaultValue = NULL;
-    field->field.initValue = (DBUFieldInitFunc)init;
-    field->field.nonnull = nonnull;
-    field->field.query_length = true;
-    field->offset = offset;
-    field->def.def = 0;
-    dbuClassAddField(cls, name, create_offset_str_field(init, offset, nonnull));
+    DBUField *f = create_offset_str_field(init, offset, nonnull);
+    dbuClassAddField(cls, name, f);
+    return f;
 }
 
-static void add_objlen_field(
+static DBUField* add_objlen_field(
         DBUClass *cls, 
         const char *name, 
         DBUFieldInitFunc init,
@@ -582,40 +671,53 @@ static void add_objlen_field(
     field->offset_obj = offset;
     field->offset_len = size_offset;
     dbuClassAddField(cls, name, (DBUField*)field);
+    return (DBUField*)field;
 }
 
 
 
 
 DBUField* dbuFieldCreateInt32(off_t offset) {
-    return create_offset_def_field(offset, NULL, (DBUFieldInitFunc)field_init_int32, false, (union DBUDefValue){ 0 });
+    DBUField *f = create_offset_def_field(offset, NULL, (DBUFieldInitFunc)field_init_int32, false, (union DBUDefValue){ 0 });
+    f->toInt64 = (DBUFieldToInt64Func)field_get_int32;
+    return f;
 }
 
 DBUField* dbuFieldCreateUInt32(off_t offset) {
-    return create_offset_def_field(offset, NULL, (DBUFieldInitFunc)field_init_uint32, false, (union DBUDefValue){ 0 });
+    DBUField *f = create_offset_def_field(offset, NULL, (DBUFieldInitFunc)field_init_uint32, false, (union DBUDefValue){ 0 });
+    f->toUInt64 = (DBUFieldToUInt64Func)field_get_uint32;
+    return f;
 }
 
 DBUField* dbuFieldCreateInt64(off_t offset) {
-    return create_offset_def_field(offset, NULL, (DBUFieldInitFunc)field_init_int64, false, (union DBUDefValue){ 0 });
+    DBUField *f = create_offset_def_field(offset, NULL, (DBUFieldInitFunc)field_init_int64, false, (union DBUDefValue){ 0 });
+    f->toInt64 = (DBUFieldToInt64Func)field_get_int64;
+    return f;
 }
 
 DBUField* dbuFieldCreateUInt64(off_t offset) {
-    return create_offset_def_field(offset, NULL, (DBUFieldInitFunc)field_init_uint64, false, (union DBUDefValue){ 0 });
+    DBUField *f = create_offset_def_field(offset, NULL, (DBUFieldInitFunc)field_init_uint64, false, (union DBUDefValue){ 0 });
+    f->toUInt64 = (DBUFieldToUInt64Func)field_get_uint64;
+    return f;
 }
 
 DBUField* dbuFieldCreateString(off_t offset) {
-    return create_offset_str_field((DBUFieldInitFunc)field_init_str, offset, false);
+    DBUField *f = create_offset_str_field((DBUFieldInitFunc)field_init_str, offset, false);
+    f->toString = (DBUFieldToStringFunc)field_get_str;
+    return f;
 }
 
 DBUField* dbuFieldCreateCxMutStr(off_t offset) {
-    return create_offset_str_field((DBUFieldInitFunc)field_init_cxmutstr, offset, false);
+    DBUField *f = create_offset_str_field((DBUFieldInitFunc)field_init_cxmutstr, offset, false);
+    f->toString = (DBUFieldToStringFunc)field_get_cxstr;
+    return f;
 }
 
 /* --------------------            PUBLIC            -------------------- */
 
 
 void dbuClassAddInt(DBUClass *cls, const char *name, off_t offset, bool nonnull) {
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -623,10 +725,11 @@ void dbuClassAddInt(DBUClass *cls, const char *name, off_t offset, bool nonnull)
             (DBUFieldInitFunc)field_init_int,
             nonnull,
             (union DBUDefValue){ 0 });
+    f->toInt64 = (DBUFieldToInt64Func)field_get_int32;
 }
 
 void dbuClassAddUInt(DBUClass *cls, const char *name, off_t offset, bool nonnull) {
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -634,10 +737,11 @@ void dbuClassAddUInt(DBUClass *cls, const char *name, off_t offset, bool nonnull
             (DBUFieldInitFunc)field_init_uint,
             nonnull,
             (union DBUDefValue){ 0 });
+    f->toUInt64 = (DBUFieldToUInt64Func)field_get_uint32;
 }
 
 void dbuClassAddInt8(DBUClass *cls, const char *name, off_t offset, bool nonnull) {
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -645,10 +749,11 @@ void dbuClassAddInt8(DBUClass *cls, const char *name, off_t offset, bool nonnull
             (DBUFieldInitFunc)field_init_int8,
             nonnull,
             (union DBUDefValue){ 0 });
+    f->toInt64 = (DBUFieldToInt64Func)field_get_int8;
 }
 
 void dbuClassAddUInt8(DBUClass *cls, const char *name, off_t offset, bool nonnull) {
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -656,10 +761,11 @@ void dbuClassAddUInt8(DBUClass *cls, const char *name, off_t offset, bool nonnul
             (DBUFieldInitFunc)field_init_uint8,
             nonnull,
             (union DBUDefValue){ 0 });
+    f->toUInt64 = (DBUFieldToUInt64Func)field_get_uint8;
 }
 
 void dbuClassAddInt16(DBUClass *cls, const char *name, off_t offset, bool nonnull) {
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -667,10 +773,11 @@ void dbuClassAddInt16(DBUClass *cls, const char *name, off_t offset, bool nonnul
             (DBUFieldInitFunc)field_init_int16,
             nonnull,
             (union DBUDefValue){ 0 });
+    f->toInt64 = (DBUFieldToInt64Func)field_get_int16;
 }
 
 void dbuClassAddUInt16(DBUClass *cls, const char *name, off_t offset, bool nonnull) {
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -678,10 +785,11 @@ void dbuClassAddUInt16(DBUClass *cls, const char *name, off_t offset, bool nonnu
             (DBUFieldInitFunc)field_init_uint16,
             nonnull,
             (union DBUDefValue){ 0 });
+    f->toUInt64 = (DBUFieldToUInt64Func)field_get_uint16;
 }
 
 void dbuClassAddInt32(DBUClass *cls, const char *name, off_t offset, bool nonnull) {
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -689,10 +797,11 @@ void dbuClassAddInt32(DBUClass *cls, const char *name, off_t offset, bool nonnul
             (DBUFieldInitFunc)field_init_int32,
             nonnull,
             (union DBUDefValue){ 0 });
+    f->toInt64 = (DBUFieldToInt64Func)field_get_int32;
 }
 
 void dbuClassAddUInt32(DBUClass *cls, const char *name, off_t offset, bool nonnull) {
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -700,10 +809,11 @@ void dbuClassAddUInt32(DBUClass *cls, const char *name, off_t offset, bool nonnu
             (DBUFieldInitFunc)field_init_uint32,
             nonnull,
             (union DBUDefValue){ 0 });
+    f->toUInt64 = (DBUFieldToUInt64Func)field_get_uint32;
 }
 
 void dbuClassAddInt64(DBUClass *cls, const char *name, off_t offset, bool nonnull) {
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -711,10 +821,11 @@ void dbuClassAddInt64(DBUClass *cls, const char *name, off_t offset, bool nonnul
             (DBUFieldInitFunc)field_init_int64,
             nonnull,
             (union DBUDefValue){ 0 });
+    f->toInt64 = (DBUFieldToInt64Func)field_get_int64;
 }
 
 void dbuClassAddUInt64(DBUClass *cls, const char *name, off_t offset, bool nonnull) {
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -722,10 +833,11 @@ void dbuClassAddUInt64(DBUClass *cls, const char *name, off_t offset, bool nonnu
             (DBUFieldInitFunc)field_init_uint64,
             nonnull,
             (union DBUDefValue){ 0 });
+    f->toUInt64 = (DBUFieldToUInt64Func)field_get_uint64;
 }
 
 void dbuClassAddSize(DBUClass *cls, const char *name, off_t offset, bool nonnull) {
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -733,10 +845,11 @@ void dbuClassAddSize(DBUClass *cls, const char *name, off_t offset, bool nonnull
             (DBUFieldInitFunc)field_init_size,
             nonnull,
             (union DBUDefValue){ 0 });
+    f->toUInt64 = (DBUFieldToUInt64Func)field_get_size;
 }
 
 void dbuClassAddSSize(DBUClass *cls, const char *name, off_t offset, bool nonnull) {
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -744,6 +857,7 @@ void dbuClassAddSSize(DBUClass *cls, const char *name, off_t offset, bool nonnul
             (DBUFieldInitFunc)field_init_ssize,
             nonnull,
             (union DBUDefValue){ 0 });
+    f->toInt64 = (DBUFieldToInt64Func)field_get_ssize;
 }
 
 void dbuClassAddBool(DBUClass *cls, const char *name, off_t offset, bool nonnull) {
@@ -755,7 +869,7 @@ void dbuClassAddBool(DBUClass *cls, const char *name, off_t offset, bool nonnull
 }
 
 void dbuClassAddFloat(DBUClass *cls, const char *name, off_t offset, bool nonnull) {
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -763,10 +877,11 @@ void dbuClassAddFloat(DBUClass *cls, const char *name, off_t offset, bool nonnul
             (DBUFieldInitFunc)field_init_float,
             nonnull,
             (union DBUDefValue){ 0 });
+    f->toDouble = (DBUFieldToDoubleFunc)field_get_float;
 }
 
 void dbuClassAddDouble(DBUClass *cls, const char *name, off_t offset, bool nonnull) {
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -774,132 +889,145 @@ void dbuClassAddDouble(DBUClass *cls, const char *name, off_t offset, bool nonnu
             (DBUFieldInitFunc)field_init_double,
             nonnull,
             (union DBUDefValue){ 0 });
+    f->toDouble = (DBUFieldToDoubleFunc)field_get_double;
 }
 
 void dbuClassAddString(DBUClass *cls, const char *name, off_t offset, bool nonnull) {
-    add_offset_str_field(
+    DBUField *f = add_offset_str_field(
             cls, 
             name,
             (DBUFieldInitFunc)field_init_str,
             offset,
             nonnull);
+    f->toString = (DBUFieldToStringFunc)field_get_str;
 }
 
 void dbuClassAddCXMutStr(DBUClass *cls, const char *name, off_t offset, bool nonnull) {
-    add_offset_str_field(
+    DBUField *f = add_offset_str_field(
             cls, 
             name,
             (DBUFieldInitFunc)field_init_cxmutstr,
             offset,
             nonnull);
+    f->toString = (DBUFieldToStringFunc)field_get_cxstr;
 }
 
 void dbuClassAddStringSize(DBUClass *cls, const char *name, off_t offset, off_t size_offset, bool nonnull) {
-    add_objlen_field(
+    DBUField *f = add_objlen_field(
             cls, 
             name, 
             (DBUFieldInitFunc)field_init_str_size,
             offset,
             size_offset,
             nonnull);
+    f->toString = (DBUFieldToStringFunc)field_get_str_len;
 }
 
 void dbuClassAddStringIntLen(DBUClass *cls, const char *name, off_t offset, off_t int_offset, bool nonnull) {
-    add_objlen_field(
+    DBUField *f = add_objlen_field(
             cls, 
             name, 
             (DBUFieldInitFunc)field_init_str_intlen,
             offset,
             int_offset,
             nonnull);
+    f->toString = (DBUFieldToStringFunc)field_get_str_intlen;
 }
 
 void dbuClassAddBuf(DBUClass *cls, const char *name, off_t offset, off_t size_offset, bool nonnull) {
-    add_objlen_field(
+    DBUField *f = add_objlen_field(
             cls, 
             name, 
             (DBUFieldInitFunc)field_init_bytes_size,
             offset,
             size_offset,
             nonnull);
+    f->toBinary = (DBUFieldToStringFunc)field_get_str_len;
 }
 
 void dbuClassAddBufIntLen(DBUClass *cls, const char *name, off_t offset, off_t int_offset, bool nonnull) {
-    add_objlen_field(
+    DBUField *f = add_objlen_field(
             cls, 
             name, 
             (DBUFieldInitFunc)field_init_bytes_intlen,
             offset,
             int_offset,
             nonnull);
+    f->toBinary = (DBUFieldToStringFunc)field_get_str_intlen;
 }
 
 
 void dbuClassAddInt32FK(DBUClass *cls, const char *name, off_t offset, DBUClass *foreign_cls, bool nonnull) {
-    add_offset_fk_field(
+    DBUField *f = add_offset_fk_field(
             cls,
             name,
             offset,
             foreign_cls,
             (DBUFieldInitFunc)field_init_int32,
             nonnull);
+    f->toInt64 = (DBUFieldToInt64Func)field_get_int32;
 }
 
 void dbuClassAddUInt32FK(DBUClass *cls, const char *name, off_t offset, DBUClass *foreign_cls, bool nonnull){
-    add_offset_fk_field(
+    DBUField *f = add_offset_fk_field(
             cls,
             name,
             offset,
             foreign_cls,
             (DBUFieldInitFunc)field_init_uint32,
             nonnull);
+    f->toUInt64 = (DBUFieldToUInt64Func)field_get_uint32;
 }
 
 void dbuClassAddInt64FK(DBUClass *cls, const char *name, off_t offset, DBUClass *foreign_cls, bool nonnull){
-    add_offset_fk_field(
+    DBUField *f = add_offset_fk_field(
             cls,
             name,
             offset,
             foreign_cls,
             (DBUFieldInitFunc)field_init_int64,
             nonnull);
+    f->toInt64 = (DBUFieldToInt64Func)field_get_int64;
 }
 
 void dbuClassAddUInt64FK(DBUClass *cls, const char *name, off_t offset, DBUClass *foreign_cls, bool nonnull){
-    add_offset_fk_field(
+    DBUField *f = add_offset_fk_field(
             cls,
             name,
             offset,
             foreign_cls,
             (DBUFieldInitFunc)field_init_uint64,
             nonnull);
+    f->toUInt64 = (DBUFieldToUInt64Func)field_get_uint64;
 }
 
 void dbuClassAddStringFK(DBUClass *cls, const char *name, off_t offset, DBUClass *foreign_cls, bool nonnull){
-    add_offset_fk_field(
+    DBUField *f = add_offset_fk_field(
             cls, 
             name,
             offset,
             foreign_cls,
             (DBUFieldInitFunc)field_init_str,
             nonnull);
+    f->toString = (DBUFieldToStringFunc)field_get_str;
 }
 
 void dbuClassAddCXMutStrFK(DBUClass *cls, const char *name, off_t offset, DBUClass *foreign_cls, bool nonnull){
-    add_offset_fk_field(
+    DBUField *f = add_offset_fk_field(
             cls, 
             name,
             offset,
             foreign_cls,
             (DBUFieldInitFunc)field_init_cxmutstr,
             nonnull);
+    f->toString = (DBUFieldToStringFunc)field_get_cxstr;
 }
 
 
 void dbuClassAddIntDef(DBUClass *cls, const char *name, off_t offset, int def) {
     union DBUDefValue defvalue;
     defvalue.def = def;
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -907,12 +1035,13 @@ void dbuClassAddIntDef(DBUClass *cls, const char *name, off_t offset, int def) {
             (DBUFieldInitFunc)field_init_int,
             false,
             (union DBUDefValue){ 0 });
+    f->toInt64 = (DBUFieldToInt64Func)field_get_int32;
 }
 
 void dbuClassAddUIntDef(DBUClass *cls, const char *name, off_t offset, unsigned int def) {
     union DBUDefValue defvalue;
     defvalue.udef = def;
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -920,12 +1049,13 @@ void dbuClassAddUIntDef(DBUClass *cls, const char *name, off_t offset, unsigned
             (DBUFieldInitFunc)field_init_uint,
             false,
             (union DBUDefValue){ 0 });
+    f->toUInt64 = (DBUFieldToUInt64Func)field_get_uint32;
 }
 
 void dbuClassAddInt16Def(DBUClass *cls, const char *name, off_t offset, int16_t def) {
     union DBUDefValue defvalue;
     defvalue.def = def;
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -933,12 +1063,13 @@ void dbuClassAddInt16Def(DBUClass *cls, const char *name, off_t offset, int16_t
             (DBUFieldInitFunc)field_init_int16,
             false,
             (union DBUDefValue){ 0 });
+    f->toInt64 = (DBUFieldToInt64Func)field_get_int16;
 }
 
 void dbuClassAddUInt16Def(DBUClass *cls, const char *name, off_t offset, uint16_t def) {
     union DBUDefValue defvalue;
     defvalue.udef = def;
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -946,12 +1077,13 @@ void dbuClassAddUInt16Def(DBUClass *cls, const char *name, off_t offset, uint16_
             (DBUFieldInitFunc)field_init_uint16,
             false,
             (union DBUDefValue){ 0 });
+    f->toUInt64 = (DBUFieldToUInt64Func)field_get_uint16;
 }
 
 void dbuClassAddInt32Def(DBUClass *cls, const char *name, off_t offset, int32_t def) {
     union DBUDefValue defvalue;
     defvalue.def = def;
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -959,12 +1091,13 @@ void dbuClassAddInt32Def(DBUClass *cls, const char *name, off_t offset, int32_t
             (DBUFieldInitFunc)field_init_int32,
             false,
             (union DBUDefValue){ 0 });
+    f->toInt64 = (DBUFieldToInt64Func)field_get_int32;
 }
 
 void dbuClassAddUInt32Def(DBUClass *cls, const char *name, off_t offset, uint32_t def) {
     union DBUDefValue defvalue;
     defvalue.udef = def;
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -972,12 +1105,13 @@ void dbuClassAddUInt32Def(DBUClass *cls, const char *name, off_t offset, uint32_
             (DBUFieldInitFunc)field_init_uint32,
             false,
             (union DBUDefValue){ 0 });
+    f->toUInt64 = (DBUFieldToUInt64Func)field_get_uint32;
 }
 
 void dbuClassAddInt64Def(DBUClass *cls, const char *name, off_t offset, int64_t def) {
     union DBUDefValue defvalue;
     defvalue.def = def;
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -985,12 +1119,13 @@ void dbuClassAddInt64Def(DBUClass *cls, const char *name, off_t offset, int64_t
             (DBUFieldInitFunc)field_init_int64,
             false,
             (union DBUDefValue){ 0 });
+    f->toInt64 = (DBUFieldToInt64Func)field_get_int64;
 }
 
 void dbuClassAddUInt64Def(DBUClass *cls, const char *name, off_t offset, uint64_t def) {
     union DBUDefValue defvalue;
     defvalue.def = def;
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -998,12 +1133,13 @@ void dbuClassAddUInt64Def(DBUClass *cls, const char *name, off_t offset, uint64_
             (DBUFieldInitFunc)field_init_uint64,
             false,
             (union DBUDefValue){ 0 });
+    f->toUInt64 = (DBUFieldToUInt64Func)field_get_uint64;
 }
 
 void dbuClassAddSizeDef(DBUClass *cls, const char *name, off_t offset, size_t def) {
     union DBUDefValue defvalue;
     defvalue.udef = def;
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -1011,12 +1147,13 @@ void dbuClassAddSizeDef(DBUClass *cls, const char *name, off_t offset, size_t de
             (DBUFieldInitFunc)field_init_size,
             false,
             (union DBUDefValue){ 0 });
+    f->toUInt64 = (DBUFieldToUInt64Func)field_get_size;
 }
 
 void dbuClassAddSSizeDef(DBUClass *cls, const char *name, off_t offset, ssize_t def) {
     union DBUDefValue defvalue;
     defvalue.def = def;
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -1024,12 +1161,13 @@ void dbuClassAddSSizeDef(DBUClass *cls, const char *name, off_t offset, ssize_t
             (DBUFieldInitFunc)field_init_ssize,
             false,
             (union DBUDefValue){ 0 });
+    f->toInt64 = (DBUFieldToInt64Func)field_get_ssize;
 }
 
 void dbuClassAddFloatDef(DBUClass *cls, const char *name, off_t offset, float def) {
     union DBUDefValue defvalue;
     defvalue.fdef = def;
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -1037,12 +1175,13 @@ void dbuClassAddFloatDef(DBUClass *cls, const char *name, off_t offset, float de
             (DBUFieldInitFunc)field_init_float,
             false,
             (union DBUDefValue){ 0 });
+    f->toDouble = (DBUFieldToDoubleFunc)field_get_float;
 }
 
 void dbuClassAddDoubleDef(DBUClass *cls, const char *name, off_t offset, double def) {
     union DBUDefValue defvalue;
     defvalue.ddef = def;
-    add_offset_def_field(
+    DBUField *f = add_offset_def_field(
             cls,
             name,
             offset,
@@ -1050,6 +1189,7 @@ void dbuClassAddDoubleDef(DBUClass *cls, const char *name, off_t offset, double
             (DBUFieldInitFunc)field_init_double,
             false,
             (union DBUDefValue){ 0 });
+    f->toDouble = (DBUFieldToDoubleFunc)field_get_double;
 }
 
 
@@ -1061,10 +1201,17 @@ static int set_obj_ptr(DBUField *f, const CxAllocator *a, DBUObject obj, void *c
     return 0;
 }
 
+static DBUObject get_obj_ptr(DBUField *f, DBUObject obj) {
+    DBUOffsetField *field = (DBUOffsetField*)f;
+    return *(void**)(obj+field->offset);
+}
+
 void dbuClassAddObj(DBUClass *cls, const char *name, off_t offset, DBUClass *foreign_cls) {
     DBUOffsetField *field = malloc(sizeof(DBUOffsetField));
     memset(field, 0, sizeof(DBUOffsetField));
     field->field.initObjValue = set_obj_ptr;
+    field->field.toObject = get_obj_ptr;
+    field->field.objType = foreign_cls;
     field->offset = offset;
     field->def.def = 0;
     dbuClassAddObjField(cls, name, (DBUField*)field, foreign_cls);
@@ -1074,7 +1221,7 @@ static int linked_list_add(DBUField *f, const CxAllocator *a, DBUObject obj, voi
     DBUOffsetField *field = (DBUOffsetField*)f;
     CxList **list = (CxList**)(obj+field->offset);
     if(*list) {
-        *list = cxLinkedListCreate(a, NULL, CX_STORE_POINTERS);
+        *list = cxLinkedListCreate(a, CX_STORE_POINTERS);
         if(!(*list)) {
             return 1;
         }
@@ -1094,7 +1241,7 @@ static int linkedlist_add(DBUObjectResult *result, DBUObject parent, DBUClass *t
     DBUClass *cls = result->userdata2;
     CxList **list = (CxList**)(parent+field->offset);
     if(!*list) {
-        *list = cxLinkedListCreate(a, NULL, CX_STORE_POINTERS);
+        *list = cxLinkedListCreate(a, CX_STORE_POINTERS);
         if(!(*list)) {
             return 1;
         }
@@ -1103,6 +1250,45 @@ static int linkedlist_add(DBUObjectResult *result, DBUObject parent, DBUClass *t
     return 0;
 }
 
+static size_t dbu_cxlist_length(DBUAbstractList *list) {
+    DBUListImpl *ls = (DBUListImpl*)list;
+    return cxListSize(ls->list.list);
+}
+
+static void* dbu_cxlist_get(DBUAbstractList *list, size_t index) {
+    DBUListImpl *ls = (DBUListImpl*)list;
+    return cxListAt(ls->list.list, index);
+}
+
+static DBUField* listimpl_field(DBUAbstractList *list, void *elm) {
+    DBUListImpl *ls = (DBUListImpl*)list;
+    return &ls->field;
+}
+
+static CxIterator dbu_cxlist_iterator(DBUAbstractList *list) {
+    DBUListImpl *ls = (DBUListImpl*)list;
+    return cxListIterator(ls->list.list);
+}
+
+static DBUObject elmfield_toObject(DBUField *f, DBUObject obj) {
+    return obj;
+}
+
+static DBUAbstractList* cxlist_tolist(DBUField *f, DBUObject obj) {
+    DBUOffsetField *field = (DBUOffsetField*)f;
+    DBUListImpl *list = malloc(sizeof(DBUListImpl));
+    list->list.list = *(CxList**)(obj+field->offset);;
+    list->list.length = dbu_cxlist_length;
+    list->list.get = dbu_cxlist_get;
+    list->list.iterator = dbu_cxlist_iterator;
+    list->list.elementField = listimpl_field;
+    list->list.free = (void(*)(DBUAbstractList*))free;
+    memset(&list->field, 0, sizeof(DBUField));
+    list->field.toObject = elmfield_toObject;
+    list->field.objType = field->field.builder.userdata2;
+    return (DBUAbstractList*)list;
+}
+
 void dbuClassAddCxLinkedList(DBUClass *cls, const char *name, off_t offset, DBUClass *foreign_cls) {
     DBUOffsetField *field = malloc(sizeof(DBUOffsetField));
     memset(field, 0, sizeof(DBUOffsetField));
@@ -1112,7 +1298,10 @@ void dbuClassAddCxLinkedList(DBUClass *cls, const char *name, off_t offset, DBUC
     field->field.builder.userdata2 = foreign_cls;
     field->field.builder.create = linkedlist_create_obj;
     field->field.builder.add = linkedlist_add;
+    field->field.toList = cxlist_tolist;
+    // TODO: can we pass name to dbuClassAddObjField?
     dbuClassAddObjField(cls, NULL, (DBUField*)field, foreign_cls);
+    field->field.name = name ? cx_strdup(cx_str(name)) : (cxmutstr){NULL,0};
     //dbuClassAddParentObjField(foreign_cls, name, cls, (DBUField*)field);
 }
 
index 21d1843606ba365e7a4d136437f491641884c2d7..4cd6a51eba936957c0a7c2c2fabaf5d353ed0306 100644 (file)
@@ -61,6 +61,16 @@ typedef struct DBUObjLenField {
 } DBUObjLenField;
 
 
+
+/*
+ * can be used by all DBUAbstractList implementations
+ */
+typedef struct DBUListImpl {
+    DBUAbstractList list;
+    DBUField field;
+} DBUListImpl;
+
+
 DBUField* dbuFieldCreateInt32(off_t offset);
 
 DBUField* dbuFieldCreateUInt32(off_t offset);
diff --git a/dbutils/json.c b/dbutils/json.c
new file mode 100644 (file)
index 0000000..52cdc8a
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "json.h"
+
+CxJsonValue* dbuObjectToJson(DBUClass *type, void *obj, const CxAllocator *a) {
+    return dbuObjectToJson2(type, obj, a, false, false);
+}
+
+CxJsonValue* dbuObjectToJson2(DBUClass *type, void *obj, const CxAllocator *a, bool setNull, bool forceString) {
+    if(obj == NULL) {
+        return cxJsonCreateLiteral(a, CX_JSON_NULL);
+    }
+    if(!a) {
+        a = cxDefaultAllocator;
+    }
+    
+    CxJsonValue *value = cxJsonCreateObj(a);
+    if(!value) {
+        return NULL;
+    }
+    
+    // add all primitive values
+    CxMapIterator i = cxMapIteratorValues(type->fields);
+        for(int n=0;n<2;n++) {
+            cx_foreach(DBUField *, field, i) {
+            CxJsonValue *child = NULL;
+            if(field->toBool) {
+                bool b = field->toBool(field, obj);
+                child = cxJsonCreateLiteral(a, b ? CX_JSON_TRUE : CX_JSON_FALSE);
+            } else if(field->toInt64) {
+                int64_t i = field->toInt64(field, obj);
+                child = cxJsonCreateInteger(a, i);
+            } else if(field->toUInt64) {
+                uint64_t u = field->toUInt64(field, obj);
+                child = cxJsonCreateInteger(a, (int64_t)u);
+            } else if(field->toDouble) {
+                double d = field->toDouble(field, obj);
+                child = cxJsonCreateNumber(a, d);
+            } else if(field->toString) {
+                cxmutstr s = field->toString(field, obj, a);
+                if(s.ptr) {
+                    // we don't want to copy the string again, therefore
+                    // we can't use cxJsonCreateString
+                    child = cxMalloc(a, sizeof(CxJsonValue));
+                    if(!child) {
+                        cxFree(a, s.ptr);
+                        return NULL;
+                    }
+                    child->allocator = a;
+                    child->type = CX_JSON_STRING;
+                    child->string = s;
+                }
+            } else if(field->toBinary) {
+                // TODO
+            } else if(field->toObject) {
+                DBUObject child_obj = field->toObject(field, obj);
+                if(child_obj == NULL) {
+                    child = CX_JSON_NULL;
+                } else if(field->objType) {
+                    child = dbuObjectToJson2(field->objType, child_obj, a, setNull, forceString);
+                }
+            } else if(field->toList) {
+                DBUAbstractList *list = field->toList(field, obj);
+                child = cxJsonCreateArr(a, list->length(list));
+                if(child) {
+                    if(list->iterator) {
+                        CxIterator iter = list->iterator(list);
+                        cx_foreach(void *, elm, iter) {
+                            DBUField *f = list->elementField(list, elm);
+                            if(f->toObject) {
+                                DBUObject child_obj = f->toObject(field, elm);
+                                if(f->objType) {
+                                    CxJsonValue *array_elm = dbuObjectToJson2(f->objType, child_obj, a, setNull, forceString);
+                                    cxJsonArrAddValues(child, &array_elm, 1);
+                                }
+                            }
+                        }
+                    }
+                }
+            } else {
+                continue; // non-serializable field
+            }
+
+            if(!child) {
+                cxJsonValueFree(value);
+                return NULL;
+            }
+
+            if(cxJsonObjPut(value, field->name, child)) {
+                cxJsonValueFree(child);
+                cxJsonValueFree(value);
+                return NULL;
+            }
+        }
+        i = cxMapIteratorValues(type->obj_fields);
+    }
+    
+    return value;
+}
diff --git a/dbutils/json.h b/dbutils/json.h
new file mode 100644 (file)
index 0000000..debdad3
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DBU_JSON_H
+#define DBU_JSON_H
+
+#include "dbutils/json.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DBU_JSON_H */
+
index 0a54c30737d9cbe1ed4126c31a3d1e6ad024ab2a..6c25e662eef652671b472d3571790113057c45dd 100644 (file)
 
 
 DBUObjectBuilder* dbuObjectBuilder(DBUClass *type, DBUQuery *query, const CxAllocator *a) {
-    CxList *additionalQueries = cxLinkedListCreateSimple(sizeof(DBUBuilderQuery));
+    CxList *additionalQueries = cxLinkedListCreate(NULL, sizeof(DBUBuilderQuery));
     if(!additionalQueries) {
         return NULL;
     }
-    CxMap *subQueries = cxHashMapCreateSimple(CX_STORE_POINTERS);
+    CxMap *subQueries = cxHashMapCreate(NULL, CX_STORE_POINTERS, 8);
     if(!subQueries) {
         cxListFree(additionalQueries);
         return NULL;
@@ -105,7 +105,7 @@ static int list_result_add(DBUObjectResult *result, DBUObject parent, DBUClass *
 }
 
 CxList* dbuObjectBuilderGetList(DBUObjectBuilder *builder) {
-    CxList *result_list = cxArrayListCreate(builder->allocator, NULL, CX_STORE_POINTERS, 8);
+    CxList *result_list = cxArrayListCreate(builder->allocator, CX_STORE_POINTERS, 8);
     if(!result_list) {
         return NULL;
     }
@@ -127,7 +127,7 @@ CxList* dbuObjectBuilderGetList(DBUObjectBuilder *builder) {
 
 
 CxList* dbuObjectBuilderGetValueList(DBUObjectBuilder *builder) {
-    CxList *result_list = cxArrayListCreate(builder->allocator, NULL, builder->resultType->obj_size, 8);
+    CxList *result_list = cxArrayListCreate(builder->allocator, builder->resultType->obj_size, 8);
     if(!result_list) {
         return NULL;
     }
@@ -190,7 +190,7 @@ static int add_to_parent(DBUObjectResult *result, DBUObject parent, DBUClass *ty
 
 int dbuObjectExec(DBUObjectBuilder *builder, DBUObjectResult *objresult) {
     if(cxListSize(builder->additionalQueries) > 0 || builder->denseResult) {
-        builder->cache = cxHashMapCreateSimple(sizeof(DBUBuilderObjCache));
+        builder->cache = cxHashMapCreate(NULL, sizeof(DBUBuilderObjCache), 128);
         if(!builder->cache) {
             return 1;
         }
@@ -269,19 +269,18 @@ int dbuObjectExecuteQuery(DBUObjectBuilder *builder, DBUQuery *query, DBUClass *
     // list of all classes returned by the result
     // used when dense == true, to check, if a new result row contains
     // the same objects (primary keys haven't changed)
-    DBUResultType result_types_static[16];
     size_t result_types_capacity = 16;
     size_t result_types_size = 0;
-    DBUResultType *result_types = result_types_static;
-    CxArrayReallocator ar = cx_array_reallocator(NULL, result_types_static);
+    CX_ARRAY(DBUResultType, result_types);
+    cx_array_init(result_types, 16);
     
     DBUResultType mainResult = { .cls = cls };
-    cx_array_add(&result_types, &result_types_size, &result_types_capacity, sizeof(DBUResultType), &mainResult, &ar);
+    cx_array_add(result_types, mainResult);
     
     for(int i=0;i<numcols;i++) {
         cxstring fieldname = cx_str(result->fieldName(result, i));
         DBUField *field = NULL;
-        if(cx_strprefix(fieldname, CX_STR("__"))) {
+        if(cx_strprefix(fieldname, cx_str("__"))) {
             // columns starting with __ are reserved for marking table names
             // __<table>__<fieldname>
             // __<table>   (value ignored)
@@ -289,7 +288,7 @@ int dbuObjectExecuteQuery(DBUObjectBuilder *builder, DBUQuery *query, DBUClass *
             // after a column is marked as the start of the new table
             // all following columns will be treated as columns of this table
             cxstring tabname = cx_strsubs(fieldname, 2);
-            cxstring remaining = cx_strstr(tabname, CX_STR("__"));
+            cxstring remaining = cx_strstr(tabname, cx_str("__"));
             DBUClass *fcls = NULL;
             if(remaining.length > 2) {
                 tabname.length = remaining.ptr - tabname.ptr;
@@ -300,7 +299,7 @@ int dbuObjectExecuteQuery(DBUObjectBuilder *builder, DBUQuery *query, DBUClass *
             if(fcls) {
                 if(fcls != field_class) {
                     DBUResultType resultCls = { .cls = fcls };
-                    cx_array_add(&result_types, &result_types_size, &result_types_capacity, sizeof(DBUResultType), &resultCls, &ar);
+                    cx_array_add(result_types, resultCls);
                 }
                 if(remaining.length > 2) {
                     field = cxMapGet(fcls->fields, cx_strsubs(remaining, 2));
@@ -324,7 +323,7 @@ int dbuObjectExecuteQuery(DBUObjectBuilder *builder, DBUQuery *query, DBUClass *
                 main_pk_index = i;
             }
             if(field == field_class->primary_key) {
-                result_types[result_types_size-1].pk_col = i;
+                result_types.data[result_types.size-1].pk_col = i;
             }
             
             if(end_main_fields == numcols && field_class != cls) {
@@ -340,9 +339,9 @@ int dbuObjectExecuteQuery(DBUObjectBuilder *builder, DBUQuery *query, DBUClass *
     const CxAllocator *a = builder->allocator;
     
     // collect all foreign keys per row
-    CxList *fklist = cxArrayListCreateSimple(sizeof(DBUFK), 4);
+    CxList *fklist = cxArrayListCreate(NULL, sizeof(DBUFK), 4);
     fklist->collection.simple_destructor = (cx_destructor_func)dbufkelm_free;
-    
+      
      // get result
     int err = 0;
     while(result->hasData(result)) {       
@@ -353,12 +352,12 @@ int dbuObjectExecuteQuery(DBUObjectBuilder *builder, DBUQuery *query, DBUClass *
         int cls_index = 0;
         if(dense) {
             cxstring text = result->getText(result, main_pk_index);
-            cxmutstr prev_pk = result_types[0].prev_key;
+            cxmutstr prev_pk = result_types.data[0].prev_key;
             if(prev_pk.ptr && !cx_strcmp(text, cx_strcast(prev_pk))) {
-                obj = result_types[0].prev_obj;
+                obj = result_types.data[0].prev_obj;
                 addobj = false;
                 if(1 < result_types_size) {
-                    skip_fields = result_types[1].pk_col;
+                    skip_fields = result_types.data[1].pk_col;
                 }
             }
         }
@@ -371,13 +370,14 @@ int dbuObjectExecuteQuery(DBUObjectBuilder *builder, DBUQuery *query, DBUClass *
                 cls->init(obj, a);
             }
         }
-        result_types[0].prev_obj = obj;
+        result_types.data[0].prev_obj = obj;
             
         if(!obj) {
             break;
         }
         
         void *current_obj = obj;
+        void *parent_obj = obj;
         void *child_obj = NULL;
         DBUClass *current_cls = cls;
         for(int i=skip_fields;i<numcols;i++) {
@@ -392,12 +392,12 @@ int dbuObjectExecuteQuery(DBUObjectBuilder *builder, DBUQuery *query, DBUClass *
                 if(dense) {
                     // check if this object was already added
                     if(cls_index < result_types_size) {
-                        cxstring text = result->getText(result, result_types[cls_index].pk_col);
-                        cxmutstr prev_pk = result_types[cls_index].prev_key;
+                        cxstring text = result->getText(result, result_types.data[cls_index].pk_col);
+                        cxmutstr prev_pk = result_types.data[cls_index].prev_key;
                         if(prev_pk.ptr && !cx_strcmp(text, cx_strcast(prev_pk))) {
                             //printf("already added -> skip\n");
                             if(cls_index+1 < result_types_size) {
-                                i = result_types[cls_index+1].pk_col-1; // -1 because i++ by loop
+                                i = result_types.data[cls_index+1].pk_col-1; // -1 because i++ by loop
                                 //printf("next col %d\n", i);
                                 continue;
                             }
@@ -405,10 +405,21 @@ int dbuObjectExecuteQuery(DBUObjectBuilder *builder, DBUQuery *query, DBUClass *
                     }
                 }
                 
-                current_cls = field.cls;
                 if(field.field) {
                     // check if an object of this class can be added to the main obj
                     DBUField *obj_field = cxMapGet(cls->obj_fields, field.cls->name);
+                    if(!obj_field) {
+                        // priority 2: can this class be added to the current class?
+                        obj_field = cxMapGet(current_cls->obj_fields, field.cls->name);
+                        if(obj_field) {
+                            parent_obj = current_obj;
+                        }
+                    } else {
+                        parent_obj = obj;
+                    }
+                    
+                    current_cls = field.cls;
+                    
                     if(obj_field && obj_field->initObjValue) {
                         //printf("create child obj [%s]\n", field.cls->name.ptr);
                         child_obj = objresult->create(objresult, field.cls, a);
@@ -422,17 +433,17 @@ int dbuObjectExecuteQuery(DBUObjectBuilder *builder, DBUQuery *query, DBUClass *
                             field.cls->init(child_obj, a); // TODO: check return
                         }
 
-                        obj_field->initObjValue(obj_field, a, obj, child_obj);
+                        obj_field->initObjValue(obj_field, a, parent_obj, child_obj);
                     } else {
                         //printf("list field\n");
                         
                         DBUField *parent_field = NULL;
                         for(int c=0;c<result_types_size;c++) {
-                            DBUResultType parentType = result_types[c];
+                            DBUResultType parentType = result_types.data[c];
                             DBUField *f = cxMapGet(parentType.cls->obj_fields, field.cls->name);
                             if(f && f->builder.create) {
                                 current_obj = f->builder.create(&f->builder, field.cls, a);
-                                void *parent_obj = result_types[c].prev_obj;
+                                void *parent_obj = result_types.data[c].prev_obj;
                                 f->builder.add(&f->builder, parent_obj, field.cls, current_obj, fklist, a);
                                 break;
                             }
@@ -443,7 +454,7 @@ int dbuObjectExecuteQuery(DBUObjectBuilder *builder, DBUQuery *query, DBUClass *
                     }
                 }
                 
-                result_types[cls_index].prev_obj = current_obj;
+                result_types.data[cls_index].prev_obj = current_obj;
             }
             
             DBUField *type_field = field.field;
@@ -455,6 +466,7 @@ int dbuObjectExecuteQuery(DBUObjectBuilder *builder, DBUQuery *query, DBUClass *
                 } else {
                     cxstring text = result->getText(result, i);
                     //printf("getText(%d) = %s\n", i, text.ptr);
+                    //printf("init obj: [%d] %p   {%s}\n", i, current_obj, text.ptr);
                     field.field->initValue(field.field, a, current_obj, text.ptr, text.length);
                     
                     // if obj caching is enabled and the current field contains
@@ -503,9 +515,9 @@ int dbuObjectExecuteQuery(DBUObjectBuilder *builder, DBUQuery *query, DBUClass *
         
         if(dense) {
             for(int i=0;i<result_types_size;i++) {
-                cxstring text = result->getText(result, result_types[i].pk_col);
-                free(result_types[i].prev_key.ptr);
-                result_types[i].prev_key = cx_strdup(text);
+                cxstring text = result->getText(result, result_types.data[i].pk_col);
+                free(result_types.data[i].prev_key.ptr);
+                result_types.data[i].prev_key = cx_strdup(text);
             }
         }
         
@@ -517,11 +529,9 @@ int dbuObjectExecuteQuery(DBUObjectBuilder *builder, DBUQuery *query, DBUClass *
     cxListFree(fklist);
     
     for(int i=0;i<result_types_size;i++) {
-        free(result_types[i].prev_key.ptr);
-    }
-    if(result_types != result_types_static) {
-        free(result_types);
+        free(result_types.data[i].prev_key.ptr);
     }
+    free(result_types.data);
     
     result->free(result);
     
index 715f98b032c7d058435c1c112b40ca2050b2ba46..a055f67cf361c2f4d0c81411245618addf84ef78 100644 (file)
@@ -285,7 +285,7 @@ CxBuffer* dav_config2buf(DavConfig *config) {
         return NULL;
     }
     
-    CxBuffer *buf = cxBufferCreate(NULL, textLen, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS);
+    CxBuffer *buf = cxBufferCreate(cxDefaultAllocator, NULL, textLen, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS);
     cxBufferWrite(xmlText, 1, textLen, buf);
     xmlFree(xmlText);
     return buf;
@@ -940,9 +940,9 @@ DavCfgRepository* dav_config_url2repo_s(DavConfig *config, cxstring url, cxmutst
     path->length = 0;
     
     int s;
-    if(cx_strprefix(url, CX_STR("http://"))) {
+    if(cx_strprefix(url, cx_str("http://"))) {
         s = 7;
-    } else if(cx_strprefix(url, CX_STR("https://"))) {
+    } else if(cx_strprefix(url, cx_str("https://"))) {
         s = 8;
     } else {
         s = 1;
@@ -963,10 +963,10 @@ DavCfgRepository* dav_config_url2repo_s(DavConfig *config, cxstring url, cxmutst
         // TODO: who is responsible for freeing this repository?
         // how can the callee know, if he has to call free()?
         repo = dav_repository_new(config);
-        repo->name.value = cx_strdup_a(config->mp->allocator, CX_STR(""));
+        repo->name.value = cx_strdup_a(config->mp->allocator, cx_str(""));
         if(url.ptr[url.length-1] == '/') {
             repo->url.value = cx_strdup_a(config->mp->allocator, url);
-            *path = cx_strdup(CX_STR("/"));
+            *path = cx_strdup(cx_str("/"));
         } else if (cx_strchr(url, '/').length > 0) {
             // TODO: fix the following workaround after
             //       fixing the inconsistent behavior of util_url_*()
@@ -975,7 +975,7 @@ DavCfgRepository* dav_config_url2repo_s(DavConfig *config, cxstring url, cxmutst
             *path = cx_strdup(util_url_path_s(url));
         } else {
             repo->url.value = cx_strdup(url);
-            *path = cx_strdup(CX_STR("/"));
+            *path = cx_strdup(cx_str("/"));
         }
     }
     
index fe3651a7fed9e1c38b876905b987ea1e4d0c75d9..bbf679ef9904e1448c9a011f26518cf958fc1c82 100644 (file)
@@ -1522,7 +1522,7 @@ void dav_sha256_final_free(DAV_SHA_CTX *ctx, unsigned char *buf) {
 }
 
 CxBuffer* aes_encrypt_buffer(CxBuffer *in, DavKey *key) {
-    CxBuffer *encbuf = cxBufferCreate(NULL, in->size, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    CxBuffer *encbuf = cxBufferCreate(cxDefaultAllocator, NULL, in->size, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     if(!encbuf) {
         return NULL;
     }
@@ -1549,7 +1549,7 @@ CxBuffer* aes_encrypt_buffer(CxBuffer *in, DavKey *key) {
 }
 
 CxBuffer* aes_decrypt_buffer(CxBuffer *in, DavKey *key) {
-    CxBuffer *decbuf = cxBufferCreate(NULL, in->size, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    CxBuffer *decbuf = cxBufferCreate(cxDefaultAllocator, NULL, in->size, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     if(!decbuf) {
         return NULL;
     }
index afe62d127711f0e37281d55c7e720eacea43a147..9320e96f38f02eeb71b2d75d9518210630886c53 100644 (file)
@@ -179,7 +179,7 @@ DavResult dav_statement_execv(DavSession *sn, DavQLStatement *st, va_list ap) {
 
 cxmutstr dav_format_string(const CxAllocator *a, cxstring fstr, DavQLArgList *ap, davqlerror_t *error) {
     CxBuffer buf;
-    cxBufferInit(&buf, NULL, 128, a, CX_BUFFER_AUTO_EXTEND);
+    cxBufferInit(&buf, a, NULL, 128, CX_BUFFER_AUTO_EXTEND);
     
     int placeholder = 0;
     for(int i=0;i<fstr.length;i++) {
@@ -262,7 +262,7 @@ static int fl_add_properties(DavSession *sn, const CxAllocator *a, CxMap *map, D
         property->name = name;
         property->value = NULL;
         
-        cxMapPut(map, cx_hash_key(expression->srctext.ptr, expression->srctext.length), property);
+        cxMapPut(map, expression->srctext, property);
     }
     
     if(expression->left) {
@@ -285,11 +285,11 @@ static CxBuffer* fieldlist2propfindrequest(DavSession *sn, const CxAllocator *a,
     
     CxIterator i = cxListIterator(fields);
     cx_foreach(DavQLField*, field, i) {
-        if(!cx_strcmp(field->name, CX_STR("*"))) {
+        if(!cx_strcmp(field->name, cx_str("*"))) {
             cxMapFree(properties);
             *isallprop = 1;
             return create_allprop_propfind_request();
-        } else if(!cx_strcmp(field->name, CX_STR("-"))) {
+        } else if(!cx_strcmp(field->name, cx_str("-"))) {
             cxMapFree(properties);
             return create_propfind_request(sn, NULL, "propfind", 0);
         } else {
@@ -302,7 +302,7 @@ static CxBuffer* fieldlist2propfindrequest(DavSession *sn, const CxAllocator *a,
     }
     
     CxMapIterator mi = cxMapIteratorValues(properties);
-    CxList *list = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+    CxList *list = cxLinkedListCreate(NULL, CX_STORE_POINTERS);
     cx_foreach(DavProperty*, value, mi) {
         cxListAdd(list, value);
     }
@@ -483,11 +483,11 @@ DavResult dav_exec_select(DavSession *sn, DavQLStatement *st, va_list ap) {
     cxMempoolRegister(mp, rqbuf, (cx_destructor_func)cxBufferFree);
     
     // compile field list
-    CxList *cfieldlist = cxLinkedListCreate(mp->allocator, NULL, CX_STORE_POINTERS);
+    CxList *cfieldlist = cxLinkedListCreate(mp->allocator, CX_STORE_POINTERS);
     if(st->fields) {
         CxIterator i = cxListIterator(st->fields);
         cx_foreach(DavQLField*, field, i) {
-            if(cx_strcmp(field->name, CX_STR("*")) && cx_strcmp(field->name, CX_STR("-"))) {
+            if(cx_strcmp(field->name, cx_str("*")) && cx_strcmp(field->name, cx_str("-"))) {
                 // compile field expression
                 CxBuffer *code = dav_compile_expr(
                         sn->context,
@@ -543,7 +543,7 @@ DavResult dav_exec_select(DavSession *sn, DavQLStatement *st, va_list ap) {
     // compile order criterion
     CxList *ordercr = NULL;
     if(st->orderby) {
-        ordercr = cxLinkedListCreate(mp->allocator, NULL, sizeof(DavOrderCriterion));
+        ordercr = cxLinkedListCreate(mp->allocator, sizeof(DavOrderCriterion));
         CxIterator i = cxListIterator(st->orderby);
         cx_foreach(DavQLOrderCriterion*, oc, i) {
             DavQLExpression *column = oc->column;
@@ -601,7 +601,7 @@ DavResult dav_exec_select(DavSession *sn, DavQLStatement *st, va_list ap) {
     
     DavResource *selroot = dav_resource_new(sn, path.ptr);
     
-    CxList *stack = cxLinkedListCreateSimple(sizeof(DavQLRes));
+    CxList *stack = cxLinkedListCreate(NULL, sizeof(DavQLRes));
     // initialize the stack with the requested resource
     DavQLRes res;
     res.resource = selroot;
@@ -609,7 +609,7 @@ DavResult dav_exec_select(DavSession *sn, DavQLStatement *st, va_list ap) {
     cxListInsert(stack, 0, &res);
     
     // reuseable response buffer
-    CxBuffer *rpbuf = cxBufferCreate(NULL, 4096, mp->allocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    CxBuffer *rpbuf = cxBufferCreate(mp->allocator, NULL, 4096, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     if(!rpbuf) {
         // TODO: cleanup
         cxMempoolFree(mp);
@@ -750,21 +750,21 @@ static int count_func_args(DavQLExpression *expr) {
 }
 
 int dav_identifier2resprop(cxstring src, davqlresprop_t *prop) {
-    if(!cx_strcmp(src, CX_STR("name"))) {
+    if(!cx_strcmp(src, cx_str("name"))) {
         *prop = DAVQL_RES_NAME;
-    } else if(!cx_strcmp(src, CX_STR("path"))) {
+    } else if(!cx_strcmp(src, cx_str("path"))) {
         *prop = DAVQL_RES_PATH;
-    } else if(!cx_strcmp(src, CX_STR("href"))) {
+    } else if(!cx_strcmp(src, cx_str("href"))) {
         *prop = DAVQL_RES_HREF;
-    } else if(!cx_strcmp(src, CX_STR("contentlength"))) {
+    } else if(!cx_strcmp(src, cx_str("contentlength"))) {
         *prop = DAVQL_RES_CONTENTLENGTH;
-    } else if(!cx_strcmp(src, CX_STR("contenttype"))) {
+    } else if(!cx_strcmp(src, cx_str("contenttype"))) {
         *prop = DAVQL_RES_CONTENTTYPE;
-    } else if(!cx_strcmp(src, CX_STR("creationdate"))) {
+    } else if(!cx_strcmp(src, cx_str("creationdate"))) {
         *prop = DAVQL_RES_CREATIONDATE;
-    } else if(!cx_strcmp(src, CX_STR("lastmodified"))) {
+    } else if(!cx_strcmp(src, cx_str("lastmodified"))) {
         *prop = DAVQL_RES_LASTMODIFIED;
-    } else if(!cx_strcmp(src, CX_STR("iscollection"))) {
+    } else if(!cx_strcmp(src, cx_str("iscollection"))) {
         *prop = DAVQL_RES_ISCOLLECTION;
     } else {
         return 0;
@@ -835,10 +835,10 @@ static int add_cmd(DavContext *ctx, const CxAllocator *a, CxBuffer *bcode, DavQL
                     return -1;
                 }
             } else if(!dav_identifier2resprop(src, &cmd.data.resprop)) {
-                if(!cx_strcmp(src, CX_STR("true"))) {
+                if(!cx_strcmp(src, cx_str("true"))) {
                     cmd.type = DAVQL_CMD_INT;
                     cmd.data.integer = 1;
-                } else if(!cx_strcmp(src, CX_STR("false"))) {
+                } else if(!cx_strcmp(src, cx_str("false"))) {
                     cmd.type = DAVQL_CMD_INT;
                     cmd.data.integer = 0;
                 } else {
@@ -1032,7 +1032,7 @@ static int add_cmd(DavContext *ctx, const CxAllocator *a, CxBuffer *bcode, DavQL
 }
 
 CxBuffer* dav_compile_expr(DavContext *ctx, const CxAllocator *a, DavQLExpression *lexpr, DavQLArgList  *ap) {
-    CxBuffer *bcode = cxBufferCreate(NULL, 512, a, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    CxBuffer *bcode = cxBufferCreate(a, NULL, 512, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     if(!bcode) {
         return NULL;
     }
index eb2dd464bc1ccc939bc0b0421870958061ccb24f..33c8a11ca6f2ffbaab623a587569bef6e5d4f7a1 100644 (file)
@@ -169,7 +169,7 @@ static int dav_debug_ql_expr_selected(DavQLExpression *expr) {
 
 static void dav_debug_ql_expr_print(DavQLExpression *expr) {
     if (dav_debug_ql_expr_selected(expr)) {
-        cxstring empty = CX_STR("(empty)");
+        cxstring empty = cx_str("(empty)");
         printf(
             "Text: %.*s\nType: %s\nOperator: %s\n",
             sfmtarg(expr->srctext),
@@ -422,7 +422,7 @@ static void dav_error_in_context(int errorcode, const char *errormsg,
     
     // we try to achieve two things: get as many information as possible
     // and recover the concrete source string (and not the token strings)
-    cxstring emptystring = CX_STR("");
+    cxstring emptystring = cx_str("");
     cxstring prev = token->prev ? (token->prev->prev ?
         token_sstr(token->prev->prev) : token_sstr(token->prev))
         : emptystring;
@@ -457,10 +457,10 @@ static void dav_error_in_context(int errorcode, const char *errormsg,
 static const char *special_token_symbols = ",()+-*/&|^~=!<>";
 
 static _Bool iskeyword(DavQLToken *token) {
-    cxstring keywords[] ={CX_STR("select"), CX_STR("set"), CX_STR("from"), CX_STR("at"), CX_STR("as"),
-        CX_STR("where"), CX_STR("anywhere"), CX_STR("like"), CX_STR("unlike"), CX_STR("and"),
-        CX_STR("or"), CX_STR("not"), CX_STR("xor"), CX_STR("with"), CX_STR("infinity"),
-        CX_STR("order"), CX_STR("by"), CX_STR("asc"), CX_STR("desc")
+    cxstring keywords[] ={cx_str("select"), cx_str("set"), cx_str("from"), cx_str("at"), cx_str("as"),
+        cx_str("where"), cx_str("anywhere"), cx_str("like"), cx_str("unlike"), cx_str("and"),
+        cx_str("or"), cx_str("not"), cx_str("xor"), cx_str("with"), cx_str("infinity"),
+        cx_str("order"), cx_str("by"), cx_str("asc"), cx_str("desc")
     };
     for (int i = 0 ; i < sizeof(keywords)/sizeof(cxstring) ; i++) {
         if (!cx_strcasecmp(token->value, keywords[i])) {
@@ -471,8 +471,8 @@ static _Bool iskeyword(DavQLToken *token) {
 }
 
 static _Bool islongoperator(DavQLToken *token) {
-    cxstring operators[] = {CX_STR("and"), CX_STR("or"), CX_STR("not"), CX_STR("xor"),
-        CX_STR("like"), CX_STR("unlike")
+    cxstring operators[] = {cx_str("and"), cx_str("or"), cx_str("not"), cx_str("xor"),
+        cx_str("like"), cx_str("unlike")
     };
     for (int i = 0 ; i < sizeof(operators)/sizeof(cxstring) ; i++) {
         if (!cx_strcasecmp(token->value, operators[i])) {
@@ -484,7 +484,7 @@ static _Bool islongoperator(DavQLToken *token) {
 
 static int dav_stmt_add_field(DavQLStatement *stmt, DavQLField *field) {
     if(!stmt->fields) {
-        stmt->fields = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+        stmt->fields = cxLinkedListCreate(NULL, CX_STORE_POINTERS);
         if(!stmt->fields) {
             stmt->errorcode = DAVQL_ERROR_OUT_OF_MEMORY;
             return 1;
@@ -645,7 +645,7 @@ static DavQLToken* dav_parse_tokenize(cxstring src) {
     
     alloc_token();
     token->tokenclass = DAVQL_TOKEN_END;
-    token->value = CX_STR("");
+    token->value = cx_str("");
     
     cx_linked_list_add((void**)&tokens_begin, (void**)&tokens_end, offsetof(DavQLToken, prev), offsetof(DavQLToken, next), token);
     return tokens_begin;
@@ -745,7 +745,7 @@ static int dav_parse_binary_expr(DavQLStatement* stmt, DavQLToken* token,
 
 static void fmt_args_add(DavQLStatement *stmt, void *data) {
     if(!stmt->args) {
-        stmt->args = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+        stmt->args = cxLinkedListCreate(NULL, CX_STORE_POINTERS);
     }
     cxListAdd(stmt->args, data);
 }
@@ -1508,7 +1508,7 @@ static int dav_parse_orderby_clause(DavQLStatement *stmt, DavQLToken *token) {
     DavQLOrderCriterion crit;
     
     if(!stmt->orderby) {
-        stmt->orderby = cxLinkedListCreateSimple(sizeof(DavQLOrderCriterion));
+        stmt->orderby = cxLinkedListCreate(NULL, sizeof(DavQLOrderCriterion));
         if(!stmt->orderby) {
             return 0;
         }
@@ -1837,7 +1837,7 @@ DavQLStatement* dav_parse_statement(cxstring srctext) {
 
 void dav_free_statement(DavQLStatement *stmt) {
     if(stmt->fields) {
-        cxDefineDestructor(stmt->fields, dav_free_field);
+        cxSetDestructor(stmt->fields, dav_free_field);
         cxListFree(stmt->fields);
     }
     
@@ -1849,7 +1849,7 @@ void dav_free_statement(DavQLStatement *stmt) {
     }
     
     if(stmt->orderby) {
-        cxDefineDestructor(stmt->orderby, dav_free_order_criterion);
+        cxSetDestructor(stmt->orderby, dav_free_order_criterion);
         cxListFree(stmt->orderby);
     }
     if(stmt->args) {
index 90e7597edd7f1b9f89db1553c5514fd07135d778..a76ed4cb8a9b39b8b0fef6ff88a7ed1ffd38583f 100644 (file)
@@ -74,7 +74,7 @@ CURLcode do_propfind_request(
     curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, cxBufferWrite);
     curl_easy_setopt(handle, CURLOPT_WRITEDATA, response);
     CxMap *respheaders = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 32);
-    cxDefineDestructor(respheaders, free);
+    cxSetDestructor(respheaders, free);
     util_capture_header(handle, respheaders);
     
     for(int i=0;i<maxretry;i++) {
@@ -103,7 +103,7 @@ CURLcode do_propfind_request(
          *    => the server handled our request and we can stop requesting
          */
         char *msdavexterror;
-        msdavexterror = cxMapGet(respheaders, cx_hash_key_str("x-msdavext_error"));
+        msdavexterror = cxMapGet(respheaders, "x-msdavext_error");
         int iishack =  depth == 1 &&
             msdavexterror && !strncmp(msdavexterror, "589831;", 7);
         
@@ -122,39 +122,39 @@ CURLcode do_propfind_request(
 }
 
 CxBuffer* create_allprop_propfind_request(void) {
-    CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    CxBuffer *buf = cxBufferCreate(cxDefaultAllocator, NULL, 512, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     cxstring s;
     
-    s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
+    s = cx_str("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
     cxBufferWrite(s.ptr, 1, s.length, buf);
     
-    s = CX_STR("<D:propfind xmlns:D=\"DAV:\">\n");
+    s = cx_str("<D:propfind xmlns:D=\"DAV:\">\n");
     cxBufferWrite(s.ptr, 1, s.length, buf);
     
-    s = CX_STR("<D:allprop/></D:propfind>\n");
+    s = cx_str("<D:allprop/></D:propfind>\n");
     cxBufferWrite(s.ptr, 1, s.length, buf);
     
     return buf;
 }
 
 CxBuffer* create_cryptoprop_propfind_request(void) {
-    CxBuffer *buf = cxBufferCreate(NULL, 256, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    CxBuffer *buf = cxBufferCreate(cxDefaultAllocator, NULL, 256, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     cxstring s;
     
-    s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
+    s = cx_str("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
     cxBufferWrite(s.ptr, 1, s.length, buf);
     
-    s = CX_STR("<D:propfind xmlns:D=\"DAV:\" xmlns:idav=\"" DAV_NS "\">\n");
+    s = cx_str("<D:propfind xmlns:D=\"DAV:\" xmlns:idav=\"" DAV_NS "\">\n");
     cxBufferWrite(s.ptr, 1, s.length, buf);
     
-    s = CX_STR("<D:prop><idav:crypto-prop/></D:prop></D:propfind>\n");
+    s = cx_str("<D:prop><idav:crypto-prop/></D:prop></D:propfind>\n");
     cxBufferWrite(s.ptr, 1, s.length, buf);
     
     return buf;
 }
 
 CxBuffer* create_propfind_request(DavSession *sn, CxList *properties, char *rootelm, DavBool nocrypt) {
-    CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    CxBuffer *buf = cxBufferCreate(cxDefaultAllocator, NULL, 512, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     cxstring s;
     
     int add_crypto_name = 1;
@@ -166,7 +166,7 @@ CxBuffer* create_propfind_request(DavSession *sn, CxList *properties, char *root
         CxIterator i = cxListIterator(properties);
         cx_foreach(DavProperty*, p, i) {
             if(strcmp(p->ns->name, "DAV:")) {
-                cxMapPut(namespaces, cx_hash_key_str(p->ns->prefix), p->ns);
+                cxMapPut(namespaces, p->ns->prefix, p->ns);
             }
 
             // if the properties list contains the idav properties crypto-name
@@ -190,63 +190,47 @@ CxBuffer* create_propfind_request(DavSession *sn, CxList *properties, char *root
     if(add_crypto_name && add_crypto_key && DAV_CRYPTO(sn) && !nocrypt) {
         idav_ns.prefix = "idav";
         idav_ns.name = DAV_NS;
-        cxMapPut(namespaces, cx_hash_key_str("idav"), &idav_ns);
+        cxMapPut(namespaces, "idav", &idav_ns);
     }
     
-    s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
-    cxBufferWrite(s.ptr, 1, s.length, buf);
+    cxBufferPutString(buf, "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
     
     // write root element and namespaces
     cx_bprintf(buf, "<D:%s xmlns:D=\"DAV:\"", rootelm);
     
     CxMapIterator mapi = cxMapIteratorValues(namespaces);
     cx_foreach(DavNamespace*, ns, mapi) {
-        s = CX_STR(" xmlns:");
-        cxBufferWrite(s.ptr, 1, s.length, buf);
-        s = cx_str(ns->prefix);
-        cxBufferWrite(s.ptr, 1, s.length, buf);
-        s = CX_STR("=\"");
-        cxBufferWrite(s.ptr, 1, s.length, buf);
-        s = cx_str(ns->name);
-        cxBufferWrite(s.ptr, 1, s.length, buf);
-        s = CX_STR("\"");
-        cxBufferWrite(s.ptr, 1, s.length, buf);
+        cxBufferPutString(buf, " xmlns:");
+        cxBufferPutString(buf, ns->prefix);
+        cxBufferPutString(buf, "=\"");
+        cxBufferPutString(buf, ns->name);
+        cxBufferPutString(buf, "\"");
     }
-    s = CX_STR(">\n");
-    cxBufferWrite(s.ptr, 1, s.length, buf);
+    cxBufferPutString(buf, ">\n");
     
     // default properties
-    s = CX_STR("<D:prop>\n");
-    cxBufferWrite(s.ptr, 1, s.length, buf);
-    
-    s = CX_STR("<D:creationdate />\n<D:getlastmodified />\n");
-    cxBufferWrite(s.ptr, 1, s.length, buf);
-    
-    s = CX_STR("<D:getcontentlength />\n<D:getcontenttype />\n");
-    cxBufferWrite(s.ptr, 1, s.length, buf);
+    cxBufferPutString(buf, "<D:prop>\n");
+    cxBufferPutString(buf, "<D:creationdate />\n<D:getlastmodified />\n");
+    cxBufferPutString(buf, "<D:getcontentlength />\n<D:getcontenttype />\n");
     
-    s = CX_STR("<D:resourcetype />\n");
-    cxBufferWrite(s.ptr, 1, s.length, buf);
+    cxBufferPutString(buf, "<D:resourcetype />\n");
     
     // crypto properties
     if(DAV_CRYPTO(sn) && !nocrypt) {
         if(add_crypto_name) {
             cxBufferPut(buf, '<');
             cxBufferPutString(buf, crypto_ns);
-            s = CX_STR(":crypto-name />\n");
-            cxBufferWrite(s.ptr, 1, s.length, buf);
+            cxBufferPutString(buf, ":crypto-name />\n");
         }
         if(add_crypto_key) {
             cxBufferPut(buf, '<');
             cxBufferPutString(buf, crypto_ns);
-            s = CX_STR(":crypto-key />\n");
-            cxBufferWrite(s.ptr, 1, s.length, buf);
+            cxBufferPutString(buf, ":crypto-key />\n");
         }
         if(add_crypto_hash) {
             cxBufferPut(buf, '<');
             cxBufferPutString(buf, crypto_ns);
-            s = CX_STR(":crypto-hash />\n");
-            cxBufferWrite(s.ptr, 1, s.length, buf);
+            cxBufferPutString(buf, ":crypto-hash />\n");
         }
     }
     
@@ -254,16 +238,11 @@ CxBuffer* create_propfind_request(DavSession *sn, CxList *properties, char *root
     if(properties) {
         CxIterator i = cxListIterator(properties);
         cx_foreach(DavProperty*, prop, i) {
-            s = CX_STR("<");
-            cxBufferWrite(s.ptr, 1, s.length, buf);
-            s = cx_str(prop->ns->prefix);
-            cxBufferWrite(s.ptr, 1, s.length, buf);
-            s = CX_STR(":");
-            cxBufferWrite(s.ptr, 1, s.length, buf);
-            s = cx_str(prop->name);
-            cxBufferWrite(s.ptr, 1, s.length, buf);
-            s = CX_STR(" />\n");
-            cxBufferWrite(s.ptr, 1, s.length, buf);
+            cxBufferPutString(buf, "<");
+            cxBufferPutString(buf, prop->ns->prefix);
+            cxBufferPutString(buf, ":");
+            cxBufferPutString(buf, prop->name);
+            cxBufferPutString(buf, " />\n");
         }
     }
     
@@ -275,36 +254,25 @@ CxBuffer* create_propfind_request(DavSession *sn, CxList *properties, char *root
 }
 
 CxBuffer* create_basic_propfind_request(void) {
-    CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    CxBuffer *buf = cxBufferCreate(cxDefaultAllocator, NULL, 512, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     cxstring s;
     
-    s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
-    cxBufferWrite(s.ptr, 1, s.length, buf);
+    cxBufferPutString(buf, "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
     
-    s = CX_STR("<D:propfind xmlns:D=\"DAV:\" xmlns:i=\"");
-    cxBufferWrite(s.ptr, 1, s.length, buf);  
-    s = CX_STR(DAV_NS);
-    cxBufferWrite(s.ptr, 1, s.length, buf);
-    s = CX_STR("\" >\n");
-    cxBufferWrite(s.ptr, 1, s.length, buf);
+    cxBufferPutString(buf, "<D:propfind xmlns:D=\"DAV:\" xmlns:i=\"");
+    cxBufferPutString(buf, DAV_NS);
+    cxBufferPutString(buf, "\" >\n");
     
     // properties
-    s = CX_STR("<D:prop>\n");
-    cxBufferWrite(s.ptr, 1, s.length, buf);
-    s = CX_STR("<D:resourcetype />\n");
-    cxBufferWrite(s.ptr, 1, s.length, buf);
-    s = CX_STR("<i:crypto-key />\n");
-    cxBufferWrite(s.ptr, 1, s.length, buf);
-    s = CX_STR("<i:crypto-name />\n");
-    cxBufferWrite(s.ptr, 1, s.length, buf);
-    s = CX_STR("<i:crypto-hash />\n");
-    cxBufferWrite(s.ptr, 1, s.length, buf);
-    s = CX_STR("</D:prop>\n");
-    cxBufferWrite(s.ptr, 1, s.length, buf);
+    cxBufferPutString(buf, "<D:prop>\n");
+    cxBufferPutString(buf, "<D:resourcetype />\n");
+    cxBufferPutString(buf, "<i:crypto-key />\n");
+    cxBufferPutString(buf, "<i:crypto-name />\n");
+    cxBufferPutString(buf, "<i:crypto-hash />\n");
+    cxBufferPutString(buf, "</D:prop>\n");
     
     // end
-    s = CX_STR("</D:propfind>\n");
-    cxBufferWrite(s.ptr, 1, s.length, buf);
+    cxBufferPutString(buf, "</D:propfind>\n");
     
     return buf;
 }
@@ -356,7 +324,7 @@ int get_propfind_response(PropfindParser *parser, ResponseTag *result) {
     char *crypto_name = NULL; // name set by crypto-name property
     char *crypto_key = NULL;
     
-    result->properties = cxLinkedListCreateSimple(CX_STORE_POINTERS); // xmlNode list
+    result->properties = cxLinkedListCreate(NULL, CX_STORE_POINTERS); // xmlNode list
     
     xmlNode *node = parser->current->children;
     while(node) {
@@ -389,7 +357,7 @@ int get_propfind_response(PropfindParser *parser, ResponseTag *result) {
                                 return -1;
                             }
                             status_str = cx_strsubsl(status_str, 9, 3);
-                            if(!cx_strcmp(status_str, CX_STR("200"))) {
+                            if(!cx_strcmp(status_str, cx_str("200"))) {
                                 ok = 1;
                             }
                         }
@@ -590,7 +558,7 @@ int parse_response_tag(DavResource *resource, xmlNode *node) {
     //DavResource *res = resource;
     DavResource *res = NULL;
     const char *href = NULL;
-    CxList *properties = cxLinkedListCreateSimple(CX_STORE_POINTERS); // xmlNode list
+    CxList *properties = cxLinkedListCreate(NULL, CX_STORE_POINTERS); // xmlNode list
     char *crypto_name = NULL; // name set by crypto-name property
     char *crypto_key = NULL;
     
@@ -639,7 +607,7 @@ int parse_response_tag(DavResource *resource, xmlNode *node) {
                                 return 1;
                             }
                             status_str = cx_strsubsl(status_str, 9, 3);
-                            if(!cx_strcmp(status_str, CX_STR("200"))) {
+                            if(!cx_strcmp(status_str, cx_str("200"))) {
                                 ok = 1;
                             }
                         }
@@ -822,11 +790,11 @@ CURLcode do_proppatch_request(
 }
 
 CxBuffer* create_proppatch_request(DavResourceData *data) {
-    CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    CxBuffer *buf = cxBufferCreate(cxDefaultAllocator, NULL, 512, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     cxstring s;
     
     CxMap *namespaces = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 8);
-    cxDefineDestructor(namespaces, free);
+    cxSetDestructor(namespaces, free);
 
     {
         char prefix[8];
@@ -836,7 +804,7 @@ CxBuffer* create_proppatch_request(DavResourceData *data) {
             cx_foreach(DavProperty*, p, i) {
                 if (strcmp(p->ns->name, "DAV:")) {
                     snprintf(prefix, 8, "x%d", pfxnum++);
-                    cxMapPut(namespaces, cx_hash_key_str(p->ns->name), strdup(prefix));
+                    cxMapPut(namespaces, p->ns->name, strdup(prefix));
                 }
             }
         }
@@ -845,55 +813,41 @@ CxBuffer* create_proppatch_request(DavResourceData *data) {
             cx_foreach(DavProperty*, p, i) {
                 if (strcmp(p->ns->name, "DAV:")) {
                     snprintf(prefix, 8, "x%d", pfxnum++);
-                    cxMapPut(namespaces, cx_hash_key_str(p->ns->name), strdup(prefix));
+                    cxMapPut(namespaces, p->ns->name, strdup(prefix));
                 }
             }
         }
     }
     
-    s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
-    cxBufferWrite(s.ptr, 1, s.length, buf);
+    cxBufferPutString(buf, "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
     
     // write root element and namespaces
-    s = CX_STR("<D:propertyupdate xmlns:D=\"DAV:\"");
-    cxBufferWrite(s.ptr, 1, s.length, buf);
+    cxBufferPutString(buf, "<D:propertyupdate xmlns:D=\"DAV:\"");
     CxMapIterator mapi = cxMapIterator(namespaces);
     cx_foreach(CxMapEntry*, entry, mapi) {
-        s = CX_STR(" xmlns:");
-        cxBufferWrite(s.ptr, 1, s.length, buf);
-        s = cx_str(entry->value);
-        cxBufferWrite(s.ptr, 1, s.length, buf);
-        s = CX_STR("=\"");
-        cxBufferWrite(s.ptr, 1, s.length, buf);
-        s = cx_strn(entry->key->data, entry->key->len);
-        cxBufferWrite(s.ptr, 1, s.length, buf);
-        s = CX_STR("\"");
-        cxBufferWrite(s.ptr, 1, s.length, buf);
+        cxBufferPutString(buf, " xmlns:");
+        cxBufferPutString(buf, cx_str(entry->value));
+        cxBufferPutString(buf, "=\"");
+        cxBufferPutString(buf, cx_strn(entry->key->data, entry->key->len));
+        cxBufferPutString(buf, "\"");
     }
-    s = CX_STR(">\n");
-    cxBufferWrite(s.ptr, 1, s.length, buf);
+    cxBufferPutString(buf, ">\n");
     
     if(data->set) {
-        s = CX_STR("<D:set>\n<D:prop>\n");
-        cxBufferWrite(s.ptr, 1, s.length, buf);
+        cxBufferPutString(buf, "<D:set>\n<D:prop>\n");
         CxIterator i = cxListIterator(data->set);
         cx_foreach(DavProperty*, property, i) {
-            char *prefix = cxMapGet(namespaces, cx_hash_key_str(property->ns->name));
+            char *prefix = cxMapGet(namespaces, property->ns->name);
             if(!prefix) {
                 prefix = "D";
             }
             
             // begin tag
-            s = CX_STR("<");
-            cxBufferWrite(s.ptr, 1, s.length, buf);
-            s = cx_str(prefix);
-            cxBufferWrite(s.ptr, 1, s.length, buf);
-            s = CX_STR(":");
-            cxBufferWrite(s.ptr, 1, s.length, buf);
-            s = cx_str(property->name);
-            cxBufferWrite(s.ptr, 1, s.length, buf);
-            s = CX_STR(">");
-            cxBufferWrite(s.ptr, 1, s.length, buf);
+            cxBufferPutString(buf, "<");
+            cxBufferPutString(buf, prefix);
+            cxBufferPutString(buf, ":");
+            cxBufferPutString(buf, property->name);
+            cxBufferPutString(buf, ">");
             
             // content
             DavXmlNode *content = property->value;
@@ -904,44 +858,30 @@ CxBuffer* create_proppatch_request(DavResourceData *data) {
             }
             
             // end tag
-            s = CX_STR("</");
-            cxBufferWrite(s.ptr, 1, s.length, buf);
-            s = cx_str(prefix);
-            cxBufferWrite(s.ptr, 1, s.length, buf);
-            s = CX_STR(":");
-            cxBufferWrite(s.ptr, 1, s.length, buf);
-            s = cx_str(property->name);
-            cxBufferWrite(s.ptr, 1, s.length, buf);
-            s = CX_STR(">\n");
-            cxBufferWrite(s.ptr, 1, s.length, buf);
+            cxBufferPutString(buf, "</");
+            cxBufferPutString(buf, prefix);
+            cxBufferPutString(buf, ":");
+            cxBufferPutString(buf, property->name);
+            cxBufferPutString(buf, ">\n");
         }
-        s = CX_STR("</D:prop>\n</D:set>\n");
-        cxBufferWrite(s.ptr, 1, s.length, buf);
+        cxBufferPutString(buf, "</D:prop>\n</D:set>\n");
     }
     if(data->remove) {
-        s = CX_STR("<D:remove>\n<D:prop>\n");
-        cxBufferWrite(s.ptr, 1, s.length, buf);
+        cxBufferPutString(buf, "<D:remove>\n<D:prop>\n");
         CxIterator i = cxListIterator(data->remove);
         cx_foreach(DavProperty*, property, i) {
-            char *prefix = cxMapGet(namespaces, cx_hash_key_str(property->ns->name));
+            char *prefix = cxMapGet(namespaces, property->ns->name);
             
-            s = CX_STR("<");
-            cxBufferWrite(s.ptr, 1, s.length, buf);
-            s = cx_str(prefix);
-            cxBufferWrite(s.ptr, 1, s.length, buf);
-            s = CX_STR(":");
-            cxBufferWrite(s.ptr, 1, s.length, buf);
-            s = cx_str(property->name);
-            cxBufferWrite(s.ptr, 1, s.length, buf);
-            s = CX_STR(" />\n");
-            cxBufferWrite(s.ptr, 1, s.length, buf);
+            cxBufferPutString(buf, "<");
+            cxBufferPutString(buf, prefix);
+            cxBufferPutString(buf, ":");
+            cxBufferPutString(buf, property->name);
+            cxBufferPutString(buf, " />\n");
         }
-        s = CX_STR("</D:prop>\n</D:remove>\n");
-        cxBufferWrite(s.ptr, 1, s.length, buf);
+        cxBufferPutString(buf, "</D:prop>\n</D:remove>\n");
     }
     
-    s = CX_STR("</D:propertyupdate>\n");
-    cxBufferWrite(s.ptr, 1, s.length, buf);
+    cxBufferPutString(buf, "</D:propertyupdate>\n");
     
     // cleanup namespace map
     cxMapFree(namespaces);
@@ -950,44 +890,34 @@ CxBuffer* create_proppatch_request(DavResourceData *data) {
 }
 
 CxBuffer* create_crypto_proppatch_request(DavSession *sn, DavKey *key, const char *name, const char *hash) {
-    CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    CxBuffer *buf = cxBufferCreate(cxDefaultAllocator, NULL, 512, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     cxstring s;
     
-    s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
-    cxBufferWrite(s.ptr, 1, s.length, buf);
+    cxBufferPutString(buf, "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
     
-    s = CX_STR("<D:propertyupdate xmlns:D=\"DAV:\" xmlns:idav=\"" DAV_NS "\">\n");
-    cxBufferWrite(s.ptr, 1, s.length, buf);
+    cxBufferPutString(buf, "<D:propertyupdate xmlns:D=\"DAV:\" xmlns:idav=\"" DAV_NS "\">\n");
     
-    s = CX_STR("<D:set>\n<D:prop>\n");
-    cxBufferWrite(s.ptr, 1, s.length, buf);
+    cxBufferPutString(buf, "<D:set>\n<D:prop>\n");
     
     if(DAV_ENCRYPT_NAME(sn)) {
-        s = CX_STR("<idav:crypto-name>");
-        cxBufferWrite(s.ptr, 1, s.length, buf);
+        cxBufferPutString(buf, "<idav:crypto-name>");
         char *crname = aes_encrypt(name, strlen(name), key);
         cxBufferPutString(buf, crname);
         free(crname);
-        s = CX_STR("</idav:crypto-name>\n");
-        cxBufferWrite(s.ptr, 1, s.length, buf);
+        cxBufferPutString(buf, "</idav:crypto-name>\n");
     }
     
-    s = CX_STR("<idav:crypto-key>");
-    cxBufferWrite(s.ptr, 1, s.length, buf);
+    cxBufferPutString(buf, "<idav:crypto-key>");
     cxBufferPutString(buf, key->name);
-    s = CX_STR("</idav:crypto-key>\n");
-    cxBufferWrite(s.ptr, 1, s.length, buf);
+    cxBufferPutString(buf, "</idav:crypto-key>\n");
     
     if(hash) {
-        s = CX_STR("<idav:crypto-hash>");
-        cxBufferWrite(s.ptr, 1, s.length, buf);
+        cxBufferPutString(buf, "<idav:crypto-hash>");
         cxBufferPutString(buf, hash);
-        s = CX_STR("</idav:crypto-hash>\n");
-        cxBufferWrite(s.ptr, 1, s.length, buf);
+        cxBufferPutString(buf, "</idav:crypto-hash>\n");
     }
     
-    s = CX_STR("</D:prop>\n</D:set>\n</D:propertyupdate>\n");
-    cxBufferWrite(s.ptr, 1, s.length, buf);
+    cxBufferPutString(buf, "</D:prop>\n</D:set>\n</D:propertyupdate>\n");
     
     return buf;
 }
@@ -1025,7 +955,7 @@ CURLcode do_put_request(DavSession *sn, char *lock, DavBool create, void *data,
     
     CxBuffer *buf = NULL;
     if(!read_func) {
-        buf = cxBufferCreate(data, length, cxDefaultAllocator, 0);
+        buf = cxBufferCreate(cxDefaultAllocator, data, length, 0);
         buf->size = length;
         data = buf;
         read_func = (dav_read_func)cxBufferRead;
@@ -1148,7 +1078,7 @@ CURLcode do_copy_move_request(DavSession *sn, char *dest, char *lock, DavBool co
         free(ltheader);
     }
     //cxstring deststr = ucx_sprintf("Destination: %s", dest);
-    cxmutstr deststr = cx_strcat(2, CX_STR("Destination: "), cx_str(dest));
+    cxmutstr deststr = cx_strcat(CX_NULLSTR, 2, cx_str("Destination: "), cx_str(dest));
     headers = curl_slist_append(headers, deststr.ptr);
     if(override) {
         headers = curl_slist_append(headers, "Overwrite: T");
@@ -1167,20 +1097,18 @@ CURLcode do_copy_move_request(DavSession *sn, char *dest, char *lock, DavBool co
 
 
 CxBuffer* create_lock_request(void) {
-    CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    CxBuffer *buf = cxBufferCreate(cxDefaultAllocator, NULL, 512, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     cxstring s;
     
-    s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
-    cxBufferWrite(s.ptr, 1, s.length, buf);
+    cxBufferPutString(buf, "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
     
-    s = CX_STR("<D:lockinfo xmlns:D=\"DAV:\">\n"
+    s = cx_str("<D:lockinfo xmlns:D=\"DAV:\">\n"
           "<D:lockscope><D:exclusive/></D:lockscope>\n"
           "<D:locktype><D:write/></D:locktype>\n"
           "<D:owner><D:href>http://davutils.org/libidav/</D:href></D:owner>\n");
-    cxBufferWrite(s.ptr, 1, s.length, buf);
+    cxBufferPutString(buf, s);
     
-    s = CX_STR("</D:lockinfo>\n");
-    cxBufferWrite(s.ptr, 1, s.length, buf);
+    cxBufferPutString(buf, "</D:lockinfo>\n");
     
     return buf;
 }
index 6a134d6007c55aedd89ee92a5483115a76257235..01d84380d722ae6a2e2c2e4df71911c76e0a7a32 100644 (file)
@@ -64,7 +64,7 @@ PwdStore* pwdstore_open(const char *file) {
         return NULL;
     }
     
-    CxBuffer *buf = cxBufferCreate(NULL, 2048, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    CxBuffer *buf = cxBufferCreate(cxDefaultAllocator, NULL, 2048, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     cx_stream_copy(in, buf, (cx_read_func)fread, (cx_write_func)cxBufferWrite);
     fclose(in);
     
@@ -75,8 +75,8 @@ PwdStore* pwdstore_open(const char *file) {
     
     PwdStore *p = malloc(sizeof(PwdStore));
     p->ids = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);
-    p->locations = cxLinkedListCreateSimple(CX_STORE_POINTERS);
-    p->noloc = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+    p->locations = cxLinkedListCreate(NULL, CX_STORE_POINTERS);
+    p->noloc = cxLinkedListCreate(NULL, CX_STORE_POINTERS);
     p->index = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);
     p->content = buf;
     p->key = NULL;
@@ -96,10 +96,10 @@ PwdStore* pwdstore_open(const char *file) {
 PwdStore* pwdstore_new(void) {
     PwdStore *p = calloc(1, sizeof(PwdStore));
     p->ids = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);
-    p->locations = cxLinkedListCreateSimple(CX_STORE_POINTERS);
-    p->noloc = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+    p->locations = cxLinkedListCreate(NULL, CX_STORE_POINTERS);
+    p->noloc = cxLinkedListCreate(NULL, CX_STORE_POINTERS);
     p->index = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);
-    p->content = cxBufferCreate(NULL, PWDS_HEADER_SIZE, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    p->content = cxBufferCreate(cxDefaultAllocator, NULL, PWDS_HEADER_SIZE, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     PWDS_MAGIC(p) = PWDS_MAGIC_CHAR;
     PWDS_VERSION(p) = 1;
     PWDS_ENC(p) = DAV_KEY_AES256;
@@ -128,8 +128,8 @@ PwdStore* pwdstore_clone(PwdStore *p) {
     
     PwdStore *newp = calloc(1, sizeof(PwdStore));
     newp->ids = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);
-    newp->locations = cxLinkedListCreateSimple(CX_STORE_POINTERS);
-    newp->noloc = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+    newp->locations = cxLinkedListCreate(NULL, CX_STORE_POINTERS);
+    newp->noloc = cxLinkedListCreate(NULL, CX_STORE_POINTERS);
     newp->index = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);
     newp->content = newbuffer;
     newp->key = key;
@@ -149,7 +149,7 @@ PwdStore* pwdstore_clone(PwdStore *p) {
         PwdIndexEntry *entry = e->value;
         CxList *locations = NULL;
         if(entry->locations) {
-            locations = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+            locations = cxLinkedListCreate(NULL, CX_STORE_POINTERS);
             CxIterator li = cxListIterator(entry->locations);
             cx_foreach(char *, location, li) {
                 cxListAdd(locations, strdup(location));
@@ -206,8 +206,8 @@ static int read_indexentry(PwdStore *p, CxBuffer *in) {
     }
       
     char *id = NULL;
-    CxList *locations = cxLinkedListCreateSimple(CX_STORE_POINTERS);
-    cxDefineDestructor(locations, free);
+    CxList *locations = cxLinkedListCreate(NULL, CX_STORE_POINTERS);
+    cxSetDestructor(locations, free);
     
     // get id (required)
     int ret = 0;
@@ -330,7 +330,7 @@ int pwdstore_getindex(PwdStore *s) {
     s->encoffset = PWDS_HEADER_SIZE + indexlen;
     
     // the index starts after the header
-    CxBuffer *index = cxBufferCreate(s->content->space+PWDS_HEADER_SIZE, indexlen, cxDefaultAllocator, 0);
+    CxBuffer *index = cxBufferCreate(cxDefaultAllocator, s->content->space+PWDS_HEADER_SIZE, indexlen, 0);
     index->size = indexlen;
     
     // read index
@@ -352,7 +352,7 @@ int pwdstore_decrypt(PwdStore *p) {
     
     // decrypt contet
     size_t encsz = p->content->size - p->encoffset;
-    CxBuffer *enc = cxBufferCreate(p->content->space + p->encoffset, encsz, cxDefaultAllocator, 0);
+    CxBuffer *enc = cxBufferCreate(cxDefaultAllocator, p->content->space + p->encoffset, encsz, 0);
     enc->size = encsz;
     enc->size = p->content->size - p->encoffset;
     CxBuffer *content = aes_decrypt_buffer(enc, p->key);
@@ -398,7 +398,7 @@ void pwdstore_free_entry(PwdEntry *e) {
 }
 
 void pwdstore_free(PwdStore* p) {
-    cxDefineDestructor(p->ids, pwdstore_free_entry);
+    cxSetDestructor(p->ids, pwdstore_free_entry);
     cxMapFree(p->ids);
     
     cxListFree(p->locations);
@@ -411,11 +411,11 @@ void pwdstore_free(PwdStore* p) {
 }
 
 int pwdstore_has_id(PwdStore *s, const char *id) {
-    return cxMapGet(s->index, cx_hash_key_str(id)) ? 1 : 0;
+    return cxMapGet(s->index, id) ? 1 : 0;
 }
 
 PwdEntry* pwdstore_get(PwdStore *p, const char *id) {
-    PwdEntry *e = cxMapGet(p->ids, cx_hash_key_str(id));
+    PwdEntry *e = cxMapGet(p->ids, id);
     if(e && e->user && e->password) {
         return e;
     } else {
@@ -428,11 +428,11 @@ void pwdstore_put(PwdStore *p, const char *id, const char *username, const char
     entry->id = strdup(id);
     entry->user = strdup(username);
     entry->password = strdup(password);
-    cxMapPut(p->ids, cx_hash_key_str(id), entry);
+    cxMapPut(p->ids, id, entry);
 }
 
 void pwdstore_put_index(PwdStore *p, char *id, CxList *locations) {
-    PwdIndexEntry *e = cxMapGet(p->index, cx_hash_key_str(id));
+    PwdIndexEntry *e = cxMapGet(p->index, id);
     if(e) {
         return;
     }
@@ -445,7 +445,7 @@ void pwdstore_put_index(PwdStore *p, char *id, CxList *locations) {
         newentry->locations = NULL;
         cxListAdd(p->noloc, newentry);
     }
-    cxMapPut(p->index, cx_hash_key_str(id), newentry);
+    cxMapPut(p->index, id, newentry);
 }
 
 void write_index_entry(CxBuffer *out, PwdIndexEntry *e) {
@@ -477,8 +477,8 @@ int pwdstore_store(PwdStore *p, const char *file) {
         return 1;
     }
     
-    CxBuffer *index = cxBufferCreate(NULL, 2048, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
-    CxBuffer *content = cxBufferCreate(NULL, 2048, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    CxBuffer *index = cxBufferCreate(cxDefaultAllocator, NULL, 2048, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    CxBuffer *content = cxBufferCreate(cxDefaultAllocator, NULL, 2048, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     
     // create index
     CxIterator i = cxListIterator(p->noloc);
@@ -547,7 +547,7 @@ int pwdstore_decrypt_secrets(PwdStore *secrets) {
     
     char *ps_password = NULL;
     if(secrets->unlock_cmd && strlen(secrets->unlock_cmd) > 0) {
-        CxBuffer *cmd_out = cxBufferCreate(NULL, 128, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+        CxBuffer *cmd_out = cxBufferCreate(cxDefaultAllocator, NULL, 128, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
         if(!util_exec_command(secrets->unlock_cmd, cmd_out)) {
             // command successful, get first line from output without newline
             // and use that as password for the secretstore
index b82473b3de8be26ecdb2ca5557f677b0de5a41e5..bf57861ca1276d5aa5bcfc0d65040f54ecabe20b 100644 (file)
@@ -360,7 +360,7 @@ cxmutstr dav_property_key_a(const CxAllocator *a, const char *ns, const char *na
     cxstring ns_str = cx_str(ns);
     cxstring name_str = cx_str(name);
     
-    return cx_strcat_a(a, 4, ns_str, CX_STR("\0"), name_str, CX_STR("\0"));
+    return cx_strcat_a(a, CX_NULLSTR, 4, ns_str, cx_str("\0"), name_str, cx_str("\0"));
 }
 
 
@@ -619,7 +619,7 @@ int dav_set_string_property(DavResource *res, char *name, char *value) {
 
 static int add2propertylist(const CxAllocator *a, CxList **list, DavProperty *property) {
     if(!*list) {
-        CxList *newlist = cxLinkedListCreate(a, NULL, CX_STORE_POINTERS);
+        CxList *newlist = cxLinkedListCreate(a, CX_STORE_POINTERS);
         if(!newlist) {
             return 1;
         }
@@ -788,7 +788,7 @@ int dav_load_prop(DavResource *res, DavPropName *properties, size_t numprop) {
     CxMempool *mp = cxMempoolCreateSimple(64);
     const CxAllocator *a = mp->allocator;
     
-    CxList *proplist = cxArrayListCreate(a, NULL, sizeof(DavProperty), numprop);
+    CxList *proplist = cxArrayListCreate(a, sizeof(DavProperty), numprop);
     for(size_t i=0;i<numprop;i++) {
         DavProperty p;
         p.name = properties[i].name;
@@ -870,7 +870,7 @@ int dav_store(DavResource *res) {
                         data->read,
                         data->seek);
             } else {
-                buf = cxBufferCreate(data->content, data->length, cxDefaultAllocator, 0);
+                buf = cxBufferCreate(cxDefaultAllocator, data->content, data->length, 0);
                 buf->size = data->length;
                 enc = aes_encrypter_new(
                         sn->key,
@@ -912,7 +912,7 @@ int dav_store(DavResource *res) {
             HashStream hstr;
             CxBuffer *iobuf = NULL;
             if(!data->read) {
-                iobuf = cxBufferCreate(data->content, data->length, cxDefaultAllocator, 0);
+                iobuf = cxBufferCreate(cxDefaultAllocator, data->content, data->length, 0);
                 iobuf->size = data->length;
                 init_hash_stream(
                         &hstr,
@@ -1045,7 +1045,7 @@ int dav_store(DavResource *res) {
     sn->error = DAV_OK;
     if(data->set || data->remove > 0) {
         CxBuffer *request = create_proppatch_request(data);
-        CxBuffer *response = cxBufferCreate(NULL, 1024, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+        CxBuffer *response = cxBufferCreate(cxDefaultAllocator, NULL, 1024, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
         //printf("request:\n%.*s\n\n", request->pos, request->space);
 
         CURLcode ret = do_proppatch_request(sn, locktoken, request, response);
@@ -1196,7 +1196,7 @@ int dav_delete(DavResource *res) {
     DavLock *lock = dav_get_lock(res->session, res->path);
     char *locktoken = lock ? lock->token : NULL;
     
-    CxBuffer *response = cxBufferCreate(NULL, 4096, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    CxBuffer *response = cxBufferCreate(cxDefaultAllocator, NULL, 4096, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     CURLcode ret = do_delete_request(res->session, locktoken, response);
     long status = 0;
     curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status);
@@ -1399,7 +1399,7 @@ int dav_lock_t(DavResource *res, time_t timeout) {
     util_set_url(sn, dav_resource_get_href(res));
     
     CxBuffer *request = create_lock_request();
-    CxBuffer *response = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    CxBuffer *response = cxBufferCreate(cxDefaultAllocator, NULL, 512, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     CURLcode ret = do_lock_request(sn, request, response, timeout);
     
     //printf("\nlock\n");
@@ -1476,7 +1476,7 @@ int resource_add_crypto_info(DavSession *sn, const char *href, const char *name,
     }
     
     CxBuffer *request = create_crypto_proppatch_request(sn, sn->key, name, hash);
-    CxBuffer *response = cxBufferCreate(NULL, 1024, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    CxBuffer *response = cxBufferCreate(cxDefaultAllocator, NULL, 1024, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     
     util_set_url(sn, href);
     // TODO: lock
@@ -1503,12 +1503,12 @@ DavXmlNode* create_crypto_prop(DavSession *sn, CxMap *properties) {
         return NULL;
     }
     
-    CxBuffer *content = cxBufferCreate(NULL, 2048, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    CxBuffer *content = cxBufferCreate(cxDefaultAllocator, NULL, 2048, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     
     // create an xml document containing all properties
     CxMap *nsmap = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 8);
-    cxDefineDestructor(nsmap, free);
-    cxMapPut(nsmap, cx_hash_key_str("DAV:"), strdup("D"));
+    cxSetDestructor(nsmap, free);
+    cxMapPut(nsmap, "DAV:", strdup("D"));
     
     cxBufferPutString(content, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
     cxBufferPutString(content, "<D:prop xmlns:D=\"DAV:\">\n");
index e7b983f7aab8faa955e1fb4b792f0b2d6ed02aff..bf66e7976355b16d4c6282bfe8821b290925a597 100644 (file)
@@ -65,7 +65,7 @@ DavSession* dav_session_new(DavContext *context, char *base_url) {
     sn->locks = NULL;
 
     // set proxy
-    DavProxy *proxy = cx_strprefix(url, CX_STR("https")) ? context->https_proxy
+    DavProxy *proxy = cx_strprefix(url, cx_str("https")) ? context->https_proxy
                                                   : context->http_proxy;
     
     if (proxy->url) {
@@ -395,8 +395,8 @@ char* dav_session_get_href(DavSession *sn, const char *path) {
         cxstring p = cx_str(path);
         CxBuffer href;
         CxBuffer pbuf;
-        cxBufferInit(&href, NULL, 256, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
-        cxBufferInit(&pbuf, NULL, 256, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+        cxBufferInit(&href, cxDefaultAllocator, NULL, 256, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+        cxBufferInit(&pbuf, cxDefaultAllocator, NULL, 256, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
         
         int start = 0;
         int begin = 0;
@@ -405,7 +405,7 @@ char* dav_session_get_href(DavSession *sn, const char *path) {
         char *cp = strdup(path);
         //printf("cp: %s\n", cp);
         while(strlen(cp) > 1) {
-            char *cached = cxMapGet(sn->pathcache, cx_hash_key_str(cp));
+            char *cached = cxMapGet(sn->pathcache, cp);
             if(cached) {
                 start = strlen(cp);
                 begin = start;
@@ -435,7 +435,7 @@ char* dav_session_get_href(DavSession *sn, const char *path) {
         CxBuffer *rqbuf = create_basic_propfind_request();
         
         cxstring remaining = cx_strsubs(p, start);
-        CxStrtokCtx elms = cx_strtok(remaining, CX_STR("/"), INT_MAX);
+        CxStrtokCtx elms = cx_strtok(remaining, cx_str("/"), INT_MAX);
         DavResource *res = root;
         cxBufferPutString(&pbuf, res->path);
         // iterate over all remaining path elements
@@ -568,7 +568,8 @@ static int create_lock_manager(DavSession *sn) {
     // create lock manager
     DavLockManager *locks = cxMalloc(sn->mp->allocator, sizeof(DavLockManager));
     locks->resource_locks = cxHashMapCreate(sn->mp->allocator, CX_STORE_POINTERS, 16);
-    locks->collection_locks = cxLinkedListCreate(sn->mp->allocator, dav_lock_cmp, CX_STORE_POINTERS);
+    locks->collection_locks = cxLinkedListCreate(sn->mp->allocator, CX_STORE_POINTERS);
+    cxSetCompareFunc(locks->collection_locks, dav_lock_cmp);
     sn->locks = locks;
     return 0;
 }
@@ -620,7 +621,7 @@ DavLock* dav_get_lock(DavSession *sn, const char *path) {
     
     cxstring p = cx_str(path);
     
-    DavLock *lock = cxMapGet(locks->resource_locks, cx_hash_key(p.ptr, p.length));
+    DavLock *lock = cxMapGet(locks->resource_locks, p);
     if(lock) {
         return lock;
     }
@@ -646,7 +647,7 @@ void dav_remove_lock(DavSession *sn, const char *path, DavLock *lock) {
         return;
     }
     
-    if(!cxMapRemove(locks->resource_locks, cx_hash_key_str(path))) {
+    if(!cxMapRemove(locks->resource_locks, path)) {
         return;
     }
 
index 767f52aceed7a5d20e602a325d3160a46a02c4c2..c24c772ad3633d6aef2738e9d8a76881efd4439b 100644 (file)
@@ -460,7 +460,7 @@ static size_t util_header_callback(char *buffer, size_t size,
     }
     cxmutstr value_cp = cx_strdup(cx_strtrim(value));
         
-    cxMapPut(map, cx_hash_key(key_cp.ptr, key_cp.length), value_cp.ptr);
+    cxMapPut(map, key_cp, value_cp.ptr);
     
     free(key_cp.ptr);
     
@@ -521,7 +521,7 @@ int util_path_isabsolut(const char *path) {
 char* util_path_normalize(const char *path) {
     size_t len = strlen(path);
     CxBuffer buf;
-    cxBufferInit(&buf, NULL, len+1, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    cxBufferInit(&buf, cxDefaultAllocator, NULL, len+1, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     
     if(path[0] == '/') {
         cxBufferPut(&buf, '/');
@@ -541,7 +541,7 @@ char* util_path_normalize(const char *path) {
             
             if(seg_len > 0) {
                 cxstring seg = cx_strn(seg_ptr, seg_len);
-                if(!cx_strcmp(seg, CX_STR(".."))) {
+                if(!cx_strcmp(seg, cx_str(".."))) {
                     for(int j=buf.pos;j>=0;j--) {
                         char t = j < buf.pos ? buf.space[j] : 0;
                         if(IS_PATH_SEPARATOR(t) || j == 0) {
@@ -552,7 +552,7 @@ char* util_path_normalize(const char *path) {
                             break;
                         }
                     }
-                } else if(!cx_strcmp(seg, CX_STR("."))) {
+                } else if(!cx_strcmp(seg, cx_str("."))) {
                     // ignore
                 } else {
                     if(add_separator) {
@@ -615,13 +615,13 @@ static char* create_relative_path(const char *abspath, const char *base) {
             }
         }
         
-        cxBufferInit(&out, NULL, dircount*3+path_len-last_dir, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+        cxBufferInit(&out, cxDefaultAllocator, NULL, dircount*3+path_len-last_dir, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
         
         for(size_t i=0;i<dircount;i++) {
             cxBufferPutString(&out, "../");
         }
     } else {
-        cxBufferInit(&out, NULL, path_len - last_dir, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+        cxBufferInit(&out, cxDefaultAllocator, NULL, path_len - last_dir, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     }
     
     cxBufferPutString(&out, abspath + last_dir + 1);
@@ -719,7 +719,7 @@ char* util_concat_path(const char *url_base, const char *p) {
     if(p) {
         path = cx_str((char*)p);
     } else {
-        path = CX_STR("");
+        path = cx_str("");
     }
     
     return util_concat_path_s(base, path).ptr;
@@ -727,7 +727,7 @@ char* util_concat_path(const char *url_base, const char *p) {
 
 cxmutstr util_concat_path_s(cxstring base, cxstring path) {
     if(!path.ptr) {
-        path = CX_STR("");
+        path = cx_str("");
     }
     
     int add_separator = 0;
@@ -743,9 +743,9 @@ cxmutstr util_concat_path_s(cxstring base, cxstring path) {
     
     cxmutstr url;
     if(add_separator) {
-        url = cx_strcat(3, base, CX_STR("/"), path);
+        url = cx_strcat(CX_NULLSTR, 3, base, cx_str("/"), path);
     } else {
-        url = cx_strcat(2, base, path);
+        url = cx_strcat(CX_NULLSTR, 2, base, path);
     }
     
     return url;
@@ -753,7 +753,7 @@ cxmutstr util_concat_path_s(cxstring base, cxstring path) {
 
 cxmutstr util_concat_path_ext(cxstring base, cxstring path, char separator) {
     if(!path.ptr) {
-        path = CX_STR("");
+        path = cx_str("");
     }
 
     int add_separator = 0;
@@ -769,9 +769,9 @@ cxmutstr util_concat_path_ext(cxstring base, cxstring path, char separator) {
 
     cxmutstr url;
     if(add_separator) {
-        url = cx_strcat(3, base, cx_strn(&separator, 1), path);
+        url = cx_strcat(CX_NULLSTR, 3, base, cx_strn(&separator, 1), path);
     } else {
-        url = cx_strcat(2, base, path);
+        url = cx_strcat(CX_NULLSTR, 2, base, path);
     }
 
     return url;
@@ -792,7 +792,7 @@ char* util_get_url(DavSession *sn, const char *href) {
     const char *base_path = util_url_path(sn->base_url);
     base.length -= strlen(base_path);
     
-    cxmutstr url = cx_strcat(2, base, href_str);
+    cxmutstr url = cx_strcat(CX_NULLSTR, 2, base, href_str);
     return url.ptr;
 }
 
@@ -809,7 +809,7 @@ char* util_path_to_url(DavSession *sn, const char *path) {
     }
     
     CxBuffer url;
-    cxBufferInit(&url, NULL, 256, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    cxBufferInit(&url, cxDefaultAllocator, NULL, 256, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     
     // add base url
     cxBufferWrite(sn->base_url, 1, strlen(sn->base_url), &url);
@@ -818,7 +818,7 @@ char* util_path_to_url(DavSession *sn, const char *path) {
     
     cxstring p = cx_strn(path, pathlen);
     
-    CxStrtokCtx tkctx = cx_strtok(p, CX_STR("/"), INT_MAX);
+    CxStrtokCtx tkctx = cx_strtok(p, cx_str("/"), INT_MAX);
     cxstring node;
     while(cx_strtok_next(&tkctx, &node)) {
         if(node.length > 0) {
@@ -1155,7 +1155,7 @@ char* util_random_str() {
     unsigned char *str = malloc(25);
     str[24] = '\0';
     
-    cxstring t = CX_STR(
+    cxstring t = cx_str(
             "01234567890"
             "abcdefghijklmnopqrstuvwxyz"
             "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
@@ -1224,7 +1224,7 @@ sstr_t util_getsubstr_until_token(sstr_t str, sstr_t token, sstr_t *sub) {
 
 cxmutstr util_readline(FILE *stream) {
     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);
     
     int c;
     while((c = fgetc(stream)) != EOF) {
@@ -1260,7 +1260,7 @@ char* util_password_input(char *prompt) {
     
     // read password input
     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);
     int c = 0;
     while((c = getpasswordchar()) != EOF) {
         if(c == '\n' || c == '\r') {
@@ -1335,7 +1335,7 @@ int util_exec_command(char *command, CxBuffer *outbuf) {
 char* util_hexstr(const unsigned char *data, size_t len) {
     size_t buflen = 2*len + 4;
     CxBuffer buf;
-    cxBufferInit(&buf, NULL, buflen + 1, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    cxBufferInit(&buf, cxDefaultAllocator, NULL, buflen + 1, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     for(int i=0;i<len;i++) {
         cx_bprintf(&buf, "%02x", data[i]);
     }
index 2a7c6c22b58a940cf0c8932cde160f818fe70aac..1ef47bfd5b268542fde098b0654a6f2d929a8f0c 100644 (file)
@@ -101,7 +101,7 @@ DavResource* dav_versiontree(DavResource *res, char *properties) {
     
     // create a version-tree request, which is almost the same as propfind
     CxBuffer *rqbuf = create_propfind_request(sn, proplist, "version-tree", 1);
-    CxBuffer *rpbuf = cxBufferCreate(NULL, 4096, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    CxBuffer *rpbuf = cxBufferCreate(cxDefaultAllocator, NULL, 4096, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     
     // do the request
     CURLcode ret = do_report_request(sn, rqbuf, rpbuf);
index ea5aac1f3ddc1e52671d16dd8a1c216625c96f9a..0a4afed2523d06b7f61ba216882f172f8ddd8e40 100644 (file)
@@ -49,8 +49,8 @@ DavContext* dav_context_new(void) {
     if(!context) {
         return NULL;
     }
-    context->sessions = cxLinkedListCreate(cxDefaultAllocator, cx_cmp_ptr, CX_STORE_POINTERS);
-    cxDefineDestructor(context->sessions, dav_session_destructor);
+    context->sessions = cxLinkedListCreate(cxDefaultAllocator, CX_STORE_POINTERS);
+    cxSetDestructor(context->sessions, dav_session_destructor);
     context->http_proxy = calloc(1, sizeof(DavProxy));
     if(!context->http_proxy) {
         dav_context_destroy(context);
@@ -249,14 +249,14 @@ int dav_add_namespace(DavContext *context, const char *prefix, const char *name)
 
 DavNamespace* dav_get_namespace(DavContext *context, const char *prefix) {
     dav_context_lock(context);
-    DavNamespace *ns = cxMapGet(context->namespaces, cx_hash_key_str(prefix));
+    DavNamespace *ns = cxMapGet(context->namespaces, prefix);
     dav_context_unlock(context);
     return ns;
 }
 
 DavNamespace* dav_get_namespace_s(DavContext *context, cxstring prefix) {
     dav_context_lock(context);
-    DavNamespace *ns = cxMapGet(context->namespaces, cx_hash_key(prefix.ptr, prefix.length));
+    DavNamespace *ns = cxMapGet(context->namespaces, prefix);
     dav_context_unlock(context);
     return ns;
 }
@@ -387,7 +387,7 @@ DavResource* dav_get(DavSession *sn, char *path, const char *properties) {
         proplist = parse_properties_string(sn->context, cx_str(properties));
     }
     CxBuffer *rqbuf = create_propfind_request(sn, proplist, "propfind", 0);
-    CxBuffer *rpbuf = cxBufferCreate(NULL, 4096, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    CxBuffer *rpbuf = cxBufferCreate(cxDefaultAllocator, NULL, 4096, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     
     //fwrite(rqbuf->space, 1, rqbuf->size, stdout);
     //printf("\n");
@@ -432,7 +432,7 @@ int dav_propfind(DavSession *sn, DavResource *root, CxBuffer *rqbuf) {
     CURL *handle = sn->handle;
     util_set_url(sn, dav_resource_get_href(root));
      
-    CxBuffer *rpbuf = cxBufferCreate(NULL, 4096, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    CxBuffer *rpbuf = cxBufferCreate(cxDefaultAllocator, NULL, 4096, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     DavResource *resource = root;
     CURLcode ret = do_propfind_request(sn, rqbuf, rpbuf);
     long status = 0;
@@ -454,7 +454,7 @@ int dav_propfind(DavSession *sn, DavResource *root, CxBuffer *rqbuf) {
 }
 
 CxList* parse_properties_string(DavContext *context, cxstring str) {
-    CxList *proplist = cxLinkedListCreateSimple(sizeof(DavProperty));
+    CxList *proplist = cxLinkedListCreate(NULL, sizeof(DavProperty));
     
     CxStrtokCtx tok = cx_strtok(str, cx_str(","), INT_MAX);
     cxstring s;
index cfea172ca9f93e4a371061f56b2b35cfaee6de18..9dbb15e7e6473169b2e1e1b4d6bd9bb25597fbf9 100644 (file)
@@ -63,7 +63,7 @@ DavXmlNode* dav_convert_xml(DavSession *sn, xmlNode *node) {
     ConvXmlElm ce;
     ce.node = node;
     ce.parent = NULL;
-    CxList *stack = cxLinkedListCreate(cxDefaultAllocator, NULL, sizeof(ConvXmlElm));
+    CxList *stack = cxLinkedListCreate(cxDefaultAllocator, sizeof(ConvXmlElm));
     if(!stack) {
         return NULL;
     }
index bb01129fcc08353adf63dcc3813d0032135c5430..c07406becc8f94f0384c190f2639b698a2a24c77 100644 (file)
@@ -61,14 +61,14 @@ unsigned long cx_system_page_size(void) {
 #endif
 
 static void *cx_malloc_stdlib(
-        cx_attr_unused void *d,
+        CX_UNUSED void *d,
         size_t n
 ) {
     return malloc(n);
 }
 
 static void *cx_realloc_stdlib(
-        cx_attr_unused void *d,
+        CX_UNUSED void *d,
         void *mem,
         size_t n
 ) {
@@ -76,7 +76,7 @@ static void *cx_realloc_stdlib(
 }
 
 static void *cx_calloc_stdlib(
-        cx_attr_unused void *d,
+        CX_UNUSED void *d,
         size_t nmemb,
         size_t size
 ) {
@@ -84,7 +84,7 @@ static void *cx_calloc_stdlib(
 }
 
 static void cx_free_stdlib(
-        cx_attr_unused void *d,
+        CX_UNUSED void *d,
         void *mem
 ) {
     free(mem);
index 102ec582c15056bec3c62431e192115d8514cf17..98b0ca707c7b722a70ca5ec9e1a57783774053ec 100644 (file)
  * 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
 
 /**
@@ -128,295 +62,146 @@ static size_t cx_array_grow_capacity(
     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;
-    }
-
-    // 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;
+    array->capacity = capacity;
+    if (array->size > capacity) {
+        array->size = capacity;
     }
-
-    // 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;
         }
+        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
@@ -430,8 +215,8 @@ static int cx_array_insert_sorted_impl(
         // 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++;
 
@@ -450,17 +235,17 @@ static int cx_array_insert_sorted_impl(
                 // 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++;
@@ -479,20 +264,21 @@ static int cx_array_insert_sorted_impl(
                 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
@@ -505,24 +291,24 @@ static int cx_array_insert_sorted_impl(
     }
 
     // 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;
@@ -535,7 +321,9 @@ static int cx_array_insert_sorted_impl(
                             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
                         }
                     }
                 }
@@ -545,13 +333,13 @@ static int cx_array_insert_sorted_impl(
                 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));
@@ -560,41 +348,136 @@ static int cx_array_insert_sorted_impl(
     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
+
+#if defined(WITH_QSORT_R) && defined(__APPLE__)
+// macOS uses a different comparefunc signature for qsort_r
+typedef struct QsortCmpFuncWrapper {
+    cx_compare_func2 fn;
+    void *context;
+} QsortCmpFuncWrapper;
+
+static int sort_comparefunc(void *context, const void *left, const void *right){
+    QsortCmpFuncWrapper *w = context;
+    return w->fn(left, right, w->context);
+}
+#endif
+
+void cx_array_qsort_c(void *array, size_t nmemb, size_t size,
+        cx_compare_func2 fn, void *context) {
+#ifdef WITH_QSORT_R
+#ifndef __APPLE__
+    qsort_r(array, nmemb, size, fn, context);
+#else
+    QsortCmpFuncWrapper wrapper;
+    wrapper.fn = fn;
+    wrapper.context = context;
+    qsort_r(array, nmemb, size, &wrapper, sort_comparefunc);
+#endif
+#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;
@@ -606,7 +489,7 @@ static size_t cx_array_binary_search_inf_impl(
     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) {
@@ -617,7 +500,7 @@ static size_t cx_array_binary_search_inf_impl(
     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;
     }
@@ -626,12 +509,12 @@ static size_t cx_array_binary_search_inf_impl(
     // 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;
@@ -648,60 +531,63 @@ static size_t cx_array_binary_search_inf_impl(
     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--;
         }
@@ -713,6 +599,39 @@ size_t cx_array_binary_search_sup(
     }
 }
 
+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
@@ -763,7 +682,6 @@ typedef struct {
     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) {
@@ -794,93 +712,64 @@ static size_t cx_arl_insert_array(
         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(
@@ -959,24 +848,20 @@ static size_t cx_arl_remove(
         );
     }
 
+    // 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;
@@ -1037,18 +922,18 @@ static size_t cx_arl_find_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);
@@ -1058,7 +943,7 @@ static size_t cx_arl_find_remove(
 
     // 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);
             }
@@ -1070,11 +955,11 @@ static size_t cx_arl_find_remove(
 }
 
 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
     );
 }
 
@@ -1082,12 +967,11 @@ static int cx_arl_compare(
         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;
             }
@@ -1200,7 +1084,6 @@ static cx_list_class cx_array_list_class = {
 
 CxList *cxArrayListCreate(
         const CxAllocator *allocator,
-        cx_compare_func comparator,
         size_t elem_size,
         size_t initial_capacity
 ) {
@@ -1210,8 +1093,7 @@ CxList *cxArrayListCreate(
 
     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
@@ -1222,8 +1104,5 @@ CxList *cxArrayListCreate(
         return NULL;
     } // LCOV_EXCL_STOP
 
-    // configure the reallocator
-    list->reallocator = cx_array_reallocator(allocator, NULL);
-
     return (CxList *) list;
 }
index a727ee4ae06709ceeb5d26469dfb713fd7e01d60..be3fad76217a7c4030bc67539f51cb885d146a1d 100644 (file)
@@ -45,11 +45,11 @@ static int buffer_copy_on_write(CxBuffer* buffer) {
 
 int cxBufferInit(
         CxBuffer *buffer,
+        const CxAllocator *allocator,
         void *space,
         size_t capacity,
-        const CxAllocator *allocator,
         int flags
-) {
+        ) {
     if (allocator == NULL) {
         allocator = cxDefaultAllocator;
     }
@@ -82,17 +82,17 @@ void cxBufferDestroy(CxBuffer *buffer) {
 }
 
 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
index 1fc036d7029b8545425f53010a05dc89e18e49b3..ae03987e193ba6861de9877538bcc8f705ae6e0f 100644 (file)
@@ -29,6 +29,7 @@
 #include "cx/compare.h"
 
 #include <math.h>
+#include <string.h>
 
 int cx_vcmp_int(int a, int b) {
     if (a == b) {
@@ -289,3 +290,12 @@ int cx_cmp_ptr(
         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);
+}
index 11750f374c308dd2c59c8fa694739be6e0aa5368..ff6b287ed3adb7335c2b82d63463f1766063a47b 100644 (file)
 
 #include "common.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  * The class definition for an allocator.
  */
@@ -142,7 +138,7 @@ typedef void (*cx_destructor_func2)(void *data, void *memory);
  * @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);
 
 /**
@@ -153,17 +149,13 @@ typedef void*(cx_clone_func)(void *target, const void *source,
  *
  * @return the system's memory page size in bytes
  */
-cx_attr_nodiscard
-CX_EXPORT unsigned long cx_system_page_size(void);
+CX_EXTERN CX_NODISCARD
+unsigned long cx_system_page_size(void);
 
 /**
- * Reallocate a previously allocated block and changes the pointer in-place,
- * if necessary.
- *
- * @note This will use stdlib reallocate and @em not the cxDefaultAllocator.
+ * Reallocate a previously allocated block.
  *
- * @par Error handling
- * @c errno will be set by realloc() on failure.
+ * Internal function - do not use.
  *
  * @param mem pointer to the pointer to allocated block
  * @param n the new size in bytes
@@ -171,20 +163,13 @@ CX_EXPORT unsigned long cx_system_page_size(void);
  * @retval non-zero failure
  * @see cx_reallocatearray()
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_reallocate_(void **mem, size_t n);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_reallocate_(void **mem, size_t n);
 
 /**
- * Reallocate a previously allocated block and changes the pointer in-place,
- * if necessary.
- *
- * The size is calculated by multiplying @p nemb and @p size.
+ * Reallocate a previously allocated block.
  *
- * @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
@@ -193,8 +178,8 @@ CX_EXPORT int cx_reallocate_(void **mem, size_t n);
  * @retval non-zero failure
  * @see cx_reallocate()
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_reallocatearray_(void **mem, size_t nmemb, size_t size);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_reallocatearray_(void **mem, size_t nmemb, size_t size);
 
 /**
  * Reallocate a previously allocated block and changes the pointer in-place,
@@ -250,8 +235,8 @@ CX_EXPORT int cx_reallocatearray_(void **mem, size_t nmemb, size_t size);
  * @param allocator the allocator
  * @param mem a pointer to the block to free
  */
-cx_attr_nonnull_arg(1)
-CX_EXPORT void cxFree(const CxAllocator *allocator, void *mem);
+CX_EXTERN CX_NONNULL_ARG(1)
+void cxFree(const CxAllocator *allocator, void *mem);
 
 /**
  * Allocate @p n bytes of memory.
@@ -260,9 +245,9 @@ CX_EXPORT void cxFree(const CxAllocator *allocator, void *mem);
  * @param n the number of bytes
  * @return a pointer to the allocated memory
  */
-cx_attr_nodiscard cx_attr_nonnull
-cx_attr_malloc cx_attr_dealloc_ucx cx_attr_allocsize(2)
-CX_EXPORT void *cxMalloc(const CxAllocator *allocator, size_t n);
+CX_EXTERN CX_NODISCARD CX_NONNULL
+CX_MALLOC CX_DEALLOC_UCX CX_ALLOCSIZE(2)
+void *cxMalloc(const CxAllocator *allocator, size_t n);
 
 /**
  * Reallocate the previously allocated block in @p mem, making the new block
@@ -272,18 +257,20 @@ CX_EXPORT void *cxMalloc(const CxAllocator *allocator, size_t n);
  *
  * @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
  * @return a pointer to the reallocated memory
  */
-cx_attr_nodiscard cx_attr_nonnull_arg(1)
-cx_attr_dealloc_ucx cx_attr_allocsize(3)
-CX_EXPORT void *cxRealloc(const CxAllocator *allocator, void *mem, size_t n);
+CX_EXTERN CX_NODISCARD CX_NONNULL_ARG(1)
+CX_DEALLOC_UCX CX_ALLOCSIZE(3)
+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.
  *
@@ -293,26 +280,23 @@ CX_EXPORT void *cxRealloc(const CxAllocator *allocator, void *mem, size_t n);
  *
  * @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
  * @param size the size of each element
  * @return a pointer to the reallocated memory
  */
-cx_attr_nodiscard cx_attr_nonnull_arg(1)
-cx_attr_dealloc_ucx cx_attr_allocsize(3, 4)
-CX_EXPORT void *cxReallocArray(const CxAllocator *allocator,
+CX_EXTERN CX_NODISCARD CX_NONNULL_ARG(1)
+CX_DEALLOC_UCX CX_ALLOCSIZE(3, 4)
+void *cxReallocArray(const CxAllocator *allocator,
         void *mem, size_t nmemb, size_t size);
 
 /**
- * Reallocate a previously allocated block and changes the pointer in-place,
- * if necessary.
- * This function acts like cxRealloc() using the pointer pointed to by @p mem.
+ * 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
@@ -320,8 +304,8 @@ CX_EXPORT void *cxReallocArray(const CxAllocator *allocator,
  * @retval zero success
  * @retval non-zero failure
  */
-cx_attr_nodiscard cx_attr_nonnull
-CX_EXPORT int cxReallocate_(const CxAllocator *allocator, void **mem, size_t n);
+CX_EXTERN CX_NODISCARD CX_NONNULL
+int cxReallocate_(const CxAllocator *allocator, void **mem, size_t n);
 
 /**
  * Reallocate a previously allocated block and changes the pointer in-place,
@@ -343,16 +327,9 @@ CX_EXPORT int cxReallocate_(const CxAllocator *allocator, void **mem, size_t n);
     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.
+ * 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 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
@@ -361,8 +338,8 @@ CX_EXPORT int cxReallocate_(const CxAllocator *allocator, void **mem, size_t n);
  * @retval zero success
  * @retval non-zero on failure
  */
-cx_attr_nodiscard cx_attr_nonnull
-CX_EXPORT int cxReallocateArray_(const CxAllocator *allocator,
+CX_EXTERN CX_NODISCARD CX_NONNULL
+int cxReallocateArray_(const CxAllocator *allocator,
         void **mem, size_t nmemb, size_t size);
 
 /**
@@ -388,16 +365,16 @@ CX_EXPORT int cxReallocateArray_(const CxAllocator *allocator,
         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
  * @param size the size of each element in bytes
  * @return a pointer to the allocated memory
  */
-cx_attr_nonnull_arg(1) cx_attr_nodiscard
-cx_attr_malloc cx_attr_dealloc_ucx cx_attr_allocsize(2, 3)
-CX_EXPORT void *cxCalloc(const CxAllocator *allocator, size_t nmemb, size_t size);
+CX_EXTERN CX_NONNULL_ARG(1) CX_NODISCARD
+CX_MALLOC CX_DEALLOC_UCX CX_ALLOCSIZE(2, 3)
+void *cxCalloc(const CxAllocator *allocator, size_t nmemb, size_t size);
 
 /**
  * Allocate @p n bytes of memory and sets every byte to zero.
@@ -406,45 +383,130 @@ CX_EXPORT void *cxCalloc(const CxAllocator *allocator, size_t nmemb, size_t size
  * @param n the number of bytes
  * @return a pointer to the allocated memory
  */
-cx_attr_nodiscard cx_attr_nonnull
-cx_attr_malloc cx_attr_dealloc_ucx cx_attr_allocsize(2)
-CX_EXPORT void *cxZalloc(const CxAllocator *allocator, size_t n);
+CX_EXTERN CX_NODISCARD CX_NONNULL
+CX_MALLOC CX_DEALLOC_UCX CX_ALLOCSIZE(2)
+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)
+
 /**
+ * Free a block of memory.
+ *
  * Convenience function that invokes cxFree() with the cxDefaultAllocator.
+ *
+ * @param mem the memory to deallocate
  */
-CX_EXPORT void cxFreeDefault(void *mem);
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
+CX_EXTERN
+void cxFreeDefault(void *mem);
 
 #endif // UCX_ALLOCATOR_H
index da276154d12dde5375b9db63a0898bee0c3b92e0..e415f45dd9f493f25fc5ebbdd8dd15bd0ea74ace 100644 (file)
 
 #include "list.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  * The maximum item size in an array list that fits into
  * a stack buffer when swapped.
@@ -50,624 +46,894 @@ 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_EXTERN CX_NONNULL
+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_EXTERN CX_NONNULL
+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_EXTERN CX_NONNULL
+int cx_array_reserve_(const CxAllocator *allocator, CxArray *array, size_t elem_size, size_t capacity);
+
+/**
+ * Changes the capacity of an array.
+ *
+ * If required, the size is reduced to fit into the new capacity.
+ *
+ * @param allocator (@c CxAllocator*) the allocator for the array
+ * @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
+ */
+#define cx_array_reserve_a(allocator, array, capacity) \
+        cx_array_reserve_(allocator, (CxArray*)&(array), sizeof((array).data[0]), 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 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(array, capacity) \
+        cx_array_reserve_a(cxDefaultAllocator, array, capacity)
 
 /**
- * Defines a reallocation mechanism for arrays.
- * You can create your own, use cx_array_reallocator(), or
- * use the #cx_array_default_reallocator.
+ * 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
  */
-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);
+CX_EXTERN CX_NONNULL
+int cx_array_copy_to_new_(const CxAllocator *allocator, CxArray *array, size_t elem_size, size_t 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.
+ *
+ * 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_EXTERN CX_NONNULL_ARG(1, 2)
+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.
  *
- * 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.
+ * When the capacity is not enough to hold the new element, a re-allocation is attempted.
  *
- * 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().
+ * @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.
  *
- * 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 element, a re-allocation is attempted.
  *
- * @note This function will reserve the minimum required capacity to hold
- * the additional elements and does not perform an overallocation.
+ * @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.
  *
- * @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)
+ * When the capacity is not enough to hold the new elements, a re-allocation is attempted.
+ *
+ * @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.
+ *
+ * @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.
  *
- * 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.
+ * When the capacity is not enough to hold the new elements, 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 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.
  *
- * @note When this function does reallocate the array, it may allocate more
- * space than required to avoid further allocations in the near future.
+ * When the capacity is not enough to hold the new elements, a re-allocation is attempted.
  *
- * @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)
+ * @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.
+ *
+ * Internal function - do not use.
  *
- * @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
+ * @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_EXTERN CX_NONNULL
+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.
+ *
+ * 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 (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
+ * @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.
+ *
+ * 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 count (@c size_t) the number of expected additional 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_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.
  *
- * @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 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_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.
  *
- * 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 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.
+ *
+ * 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 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_EXTERN CX_NONNULL_ARG(1, 2, 4, 6)
+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.
+ *
+ * @attention if the array is not sorted according to the specified @p cmp_func, the behavior is undefined.
  *
- * If the capacity is not enough to hold the new data, a reallocation
- * attempt is made.
+ * @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, context) \
+        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.
  *
- * 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.
+ * 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 cmp_func (@c cx_cmp_func) the compare function for the elements
- * @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 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.
  *
- * @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
+ * @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 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.
+ *
+ * When the capacity is not enough to hold the new elements, a re-allocation is attempted.
  *
- * @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
+ * @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.
+ *
+ * @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 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
+ * @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.
+ *
+ * @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 (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
+ * @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_EXTERN CX_NONNULL
+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_EXTERN CX_NONNULL
+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_EXTERN CX_NONNULL
+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
  */
-#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
+ */
+CX_EXTERN CX_NODISCARD CX_NONNULL
+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_simple_insert_unique(array, src, n, cmp_func) \
-    cx_array_simple_insert_unique_a(NULL, array, src, n, cmp_func)
+#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_EXTERN CX_NODISCARD CX_NONNULL
+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_EXTERN CX_NONNULL
+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_EXTERN CX_NONNULL
+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.
@@ -694,8 +960,8 @@ CX_EXPORT int cx_array_insert_unique(void **target, size_t *size, size_t *capaci
  * @see cx_array_binary_search_sup()
  * @see cx_array_binary_search()
  */
-cx_attr_nonnull
-CX_EXPORT size_t cx_array_binary_search_inf(const void *arr, size_t size,
+CX_EXTERN CX_NONNULL
+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);
 
 /**
@@ -717,8 +983,8 @@ CX_EXPORT size_t cx_array_binary_search_inf(const void *arr, size_t size,
  * @see cx_array_binary_search_inf()
  * @see cx_array_binary_search_sup()
  */
-cx_attr_nonnull
-CX_EXPORT size_t cx_array_binary_search(const void *arr, size_t size,
+CX_EXTERN CX_NONNULL
+size_t cx_array_binary_search(const void *arr, size_t size,
         size_t elem_size, const void *elem, cx_compare_func cmp_func);
 
 /**
@@ -746,10 +1012,95 @@ CX_EXPORT size_t cx_array_binary_search(const void *arr, size_t size,
  * @see cx_array_binary_search_inf()
  * @see cx_array_binary_search()
  */
-cx_attr_nonnull
-CX_EXPORT size_t cx_array_binary_search_sup(const void *arr, size_t size,
+CX_EXTERN CX_NONNULL
+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_EXTERN CX_NONNULL
+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_EXTERN CX_NONNULL
+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_EXTERN CX_NONNULL
+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.
  *
@@ -758,51 +1109,24 @@ CX_EXPORT size_t cx_array_binary_search_sup(const void *arr, size_t size,
  * @param idx1 index of the first element
  * @param idx2 index of the second element
  */
-cx_attr_nonnull
-CX_EXPORT void cx_array_swap(void *arr, size_t elem_size, size_t idx1, size_t idx2);
+CX_EXTERN CX_NONNULL
+void cx_array_swap(void *arr, size_t elem_size, size_t idx1, size_t idx2);
 
 /**
  * Allocates an array list 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().
  *
  * @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_nodiscard
-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)
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
+CX_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxListFree, 1)
+CxList *cxArrayListCreate(const CxAllocator *allocator,
+        size_t elem_size, size_t initial_capacity);
 
 #endif // UCX_ARRAY_LIST_H
index 4efa52cb2e911d6337a13dfb6964575e3a50e104..4c2bdbd9280955b7168325fc5bcfbe2989b7c60c 100644 (file)
 #include "allocator.h"
 #include "string.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  * No buffer features enabled (all flags cleared).
  */
@@ -168,18 +164,18 @@ typedef struct cx_buffer_s CxBuffer;
  * 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);
+CX_EXTERN CX_NONNULL_ARG(1)
+int cxBufferInit(CxBuffer *buffer, const CxAllocator *allocator,
+        void *space, size_t capacity, int flags);
 
 /**
  * Destroys the buffer contents.
@@ -190,8 +186,8 @@ CX_EXPORT int cxBufferInit(CxBuffer *buffer, void *space, size_t capacity,
  * @param buffer the buffer which contents shall be destroyed
  * @see cxBufferInit()
  */
-cx_attr_nonnull
-CX_EXPORT void cxBufferDestroy(CxBuffer *buffer);
+CX_EXTERN CX_NONNULL
+void cxBufferDestroy(CxBuffer *buffer);
 
 /**
  * Deallocates the buffer.
@@ -202,7 +198,8 @@ CX_EXPORT void cxBufferDestroy(CxBuffer *buffer);
  * @param buffer the buffer to deallocate
  * @see cxBufferCreate()
  */
-CX_EXPORT void cxBufferFree(CxBuffer *buffer);
+CX_EXTERN
+void cxBufferFree(CxBuffer *buffer);
 
 /**
  * Allocates and initializes a fresh buffer.
@@ -219,18 +216,18 @@ CX_EXPORT void cxBufferFree(CxBuffer *buffer);
  * 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_EXTERN CX_MALLOC CX_DEALLOC(cxBufferFree, 1) CX_NODISCARD
+CxBuffer *cxBufferCreate(const CxAllocator *allocator, void *space,
+                                   size_t capacity, int flags);
 
 /**
  * Shifts the contents of the buffer by the given offset.
@@ -268,8 +265,8 @@ CX_EXPORT CxBuffer *cxBufferCreate(void *space, size_t capacity,
  * @see cxBufferShiftLeft()
  * @see cxBufferShiftRight()
  */
-cx_attr_nonnull
-CX_EXPORT int cxBufferShift(CxBuffer *buffer, off_t shift);
+CX_EXTERN CX_NONNULL
+int cxBufferShift(CxBuffer *buffer, off_t shift);
 
 /**
  * Shifts the buffer to the right.
@@ -281,8 +278,8 @@ CX_EXPORT int cxBufferShift(CxBuffer *buffer, off_t shift);
  * @retval non-zero if a required auto-extension or copy-on-write fails
  * @see cxBufferShift()
  */
-cx_attr_nonnull
-CX_EXPORT int cxBufferShiftRight(CxBuffer *buffer, size_t shift);
+CX_EXTERN CX_NONNULL
+int cxBufferShiftRight(CxBuffer *buffer, size_t shift);
 
 /**
  * Shifts the buffer to the left.
@@ -294,8 +291,8 @@ CX_EXPORT int cxBufferShiftRight(CxBuffer *buffer, size_t shift);
  * @retval non-zero if the buffer uses copy-on-write and the allocation fails
  * @see cxBufferShift()
  */
-cx_attr_nonnull
-CX_EXPORT int cxBufferShiftLeft(CxBuffer *buffer, size_t shift);
+CX_EXTERN CX_NONNULL
+int cxBufferShiftLeft(CxBuffer *buffer, size_t shift);
 
 
 /**
@@ -318,8 +315,8 @@ CX_EXPORT int cxBufferShiftLeft(CxBuffer *buffer, size_t shift);
  * @retval non-zero if the position is invalid
  *
  */
-cx_attr_nonnull
-CX_EXPORT int cxBufferSeek(CxBuffer *buffer, off_t offset, int whence);
+CX_EXTERN CX_NONNULL
+int cxBufferSeek(CxBuffer *buffer, off_t offset, int whence);
 
 /**
  * Discards items from the end of the buffer.
@@ -332,8 +329,8 @@ CX_EXPORT int cxBufferSeek(CxBuffer *buffer, off_t offset, int whence);
  * @param nitems the number of items to discard
  * @return the actual number of discarded items
  */
-cx_attr_nonnull
-CX_EXPORT size_t cxBufferPop(CxBuffer *buffer, size_t size, size_t nitems);
+CX_EXTERN CX_NONNULL
+size_t cxBufferPop(CxBuffer *buffer, size_t size, size_t nitems);
 
 /**
  * Clears the buffer by resetting the position and deleting the data.
@@ -347,8 +344,8 @@ CX_EXPORT size_t cxBufferPop(CxBuffer *buffer, size_t size, size_t nitems);
  * @param buffer the buffer to be cleared
  * @see cxBufferReset()
  */
-cx_attr_nonnull
-CX_EXPORT void cxBufferClear(CxBuffer *buffer);
+CX_EXTERN CX_NONNULL
+void cxBufferClear(CxBuffer *buffer);
 
 /**
  * Resets the buffer by resetting the position and size to zero.
@@ -359,8 +356,8 @@ CX_EXPORT void cxBufferClear(CxBuffer *buffer);
  * @param buffer the buffer to be cleared
  * @see cxBufferClear()
  */
-cx_attr_nonnull
-CX_EXPORT void cxBufferReset(CxBuffer *buffer);
+CX_EXTERN CX_NONNULL
+void cxBufferReset(CxBuffer *buffer);
 
 /**
  * Tests, if the buffer position has exceeded the buffer size.
@@ -370,8 +367,8 @@ CX_EXPORT void cxBufferReset(CxBuffer *buffer);
  * byte of the buffer's contents
  * @retval false otherwise
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT bool cxBufferEof(const CxBuffer *buffer);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+bool cxBufferEof(const CxBuffer *buffer);
 
 /**
  * Ensures that the buffer has the required capacity.
@@ -391,8 +388,8 @@ CX_EXPORT bool cxBufferEof(const CxBuffer *buffer);
  * @see cxBufferShrink()
  * @see cxBufferMinimumCapacity()
  */
-cx_attr_nonnull
-CX_EXPORT int cxBufferReserve(CxBuffer *buffer, size_t capacity);
+CX_EXTERN CX_NONNULL
+int cxBufferReserve(CxBuffer *buffer, size_t capacity);
 
 /**
  * Limits the buffer's capacity.
@@ -410,8 +407,8 @@ CX_EXPORT int cxBufferReserve(CxBuffer *buffer, size_t capacity);
  * @see cxBufferReserve()
  * @see cxBufferMinimumCapacity()
  */
-cx_attr_nonnull
-CX_EXPORT int cxBufferMaximumCapacity(CxBuffer *buffer, size_t capacity);
+CX_EXTERN CX_NONNULL
+int cxBufferMaximumCapacity(CxBuffer *buffer, size_t capacity);
 
 /**
  * Ensures that the buffer has a minimum capacity.
@@ -430,8 +427,8 @@ CX_EXPORT int cxBufferMaximumCapacity(CxBuffer *buffer, size_t capacity);
  * @see cxBufferReserve()
  * @see cxBufferShrink()
  */
-cx_attr_nonnull
-CX_EXPORT int cxBufferMinimumCapacity(CxBuffer *buffer, size_t capacity);
+CX_EXTERN CX_NONNULL
+int cxBufferMinimumCapacity(CxBuffer *buffer, size_t capacity);
 
 /**
  * Shrinks the capacity of the buffer to fit its current size.
@@ -450,8 +447,8 @@ CX_EXPORT int cxBufferMinimumCapacity(CxBuffer *buffer, size_t capacity);
  * @see cxBufferReserve()
  * @see cxBufferMinimumCapacity()
  */
-cx_attr_nonnull
-CX_EXPORT void cxBufferShrink(CxBuffer *buffer, size_t reserve);
+CX_EXTERN CX_NONNULL
+void cxBufferShrink(CxBuffer *buffer, size_t reserve);
 
 /**
  * Writes data to a CxBuffer.
@@ -474,8 +471,8 @@ CX_EXPORT void cxBufferShrink(CxBuffer *buffer, size_t reserve);
  * @see cxBufferAppend()
  * @see cxBufferRead()
  */
-cx_attr_nonnull
-CX_EXPORT size_t cxBufferWrite(const void *ptr, size_t size,
+CX_EXTERN CX_NONNULL
+size_t cxBufferWrite(const void *ptr, size_t size,
         size_t nitems, CxBuffer *buffer);
 
 /**
@@ -497,8 +494,8 @@ CX_EXPORT size_t cxBufferWrite(const void *ptr, size_t size,
  * @see cxBufferWrite()
  * @see cxBufferRead()
  */
-cx_attr_nonnull
-CX_EXPORT size_t cxBufferAppend(const void *ptr, size_t size,
+CX_EXTERN CX_NONNULL
+size_t cxBufferAppend(const void *ptr, size_t size,
         size_t nitems, CxBuffer *buffer);
 
 /**
@@ -516,8 +513,8 @@ CX_EXPORT size_t cxBufferAppend(const void *ptr, size_t size,
  * @see cxBufferWrite()
  * @see cxBufferAppend()
  */
-cx_attr_nonnull
-CX_EXPORT size_t cxBufferRead(void *ptr, size_t size,
+CX_EXTERN CX_NONNULL
+size_t cxBufferRead(void *ptr, size_t size,
         size_t nitems, CxBuffer *buffer);
 
 /**
@@ -540,8 +537,8 @@ CX_EXPORT size_t cxBufferRead(void *ptr, size_t size,
  * stream is reached, and automatic extension is not enabled or not possible
  * @see cxBufferTerminate()
  */
-cx_attr_nonnull
-CX_EXPORT int cxBufferPut(CxBuffer *buffer, int c);
+CX_EXTERN CX_NONNULL
+int cxBufferPut(CxBuffer *buffer, int c);
 
 /**
  * Writes a terminating zero to a buffer at the current position.
@@ -555,8 +552,8 @@ CX_EXPORT int cxBufferPut(CxBuffer *buffer, int c);
  * @return zero, if the terminator could be written, non-zero otherwise
  * @see cxBufferShrink()
  */
-cx_attr_nonnull
-CX_EXPORT int cxBufferTerminate(CxBuffer *buffer);
+CX_EXTERN CX_NONNULL
+int cxBufferTerminate(CxBuffer *buffer);
 
 /**
  * Internal function - do not use.
@@ -566,8 +563,8 @@ CX_EXPORT int cxBufferTerminate(CxBuffer *buffer);
  * @return the number of bytes written
  * @see cxBufferPutString()
  */
-cx_attr_nonnull
-CX_EXPORT size_t cx_buffer_put_string(CxBuffer *buffer, cxstring str);
+CX_EXTERN CX_NONNULL
+size_t cx_buffer_put_string(CxBuffer *buffer, cxstring str);
 
 /**
  * Writes a string to a buffer with cxBufferWrite().
@@ -588,8 +585,8 @@ CX_EXPORT size_t cx_buffer_put_string(CxBuffer *buffer, cxstring str);
  * @return the number of bytes written
  * @see cxBufferPutString()
  */
-cx_attr_nonnull
-CX_EXPORT size_t cx_buffer_append_string(CxBuffer *buffer, cxstring str);
+CX_EXTERN CX_NONNULL
+size_t cx_buffer_append_string(CxBuffer *buffer, cxstring str);
 
 /**
  * Appends a string to a buffer with cxBufferAppend().
@@ -610,11 +607,29 @@ CX_EXPORT size_t cx_buffer_append_string(CxBuffer *buffer, cxstring str);
  * @param buffer the buffer to read from
  * @return the character or @c EOF, if the end of the buffer is reached
  */
-cx_attr_nonnull
-CX_EXPORT int cxBufferGet(CxBuffer *buffer);
+CX_EXTERN CX_NONNULL
+int cxBufferGet(CxBuffer *buffer);
 
-#ifdef __cplusplus
+/**
+ * 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_NONNULL 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_NONNULL CX_INLINE
+cxmutstr cx_bstr_m(CxBuffer *buffer) {
+    return cx_mutstrn(buffer->space, buffer->size);
 }
-#endif
 
 #endif // UCX_BUFFER_H
index c7c7c92eb2ee3b5c0d1310a6191e781ee7d4943d..607d6e64fabaff66272cda9252601291b7d5d806 100644 (file)
 #include "iterator.h"
 #include "compare.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  * Special constant used for creating collections that are storing pointers.
  */
@@ -57,10 +53,6 @@ struct cx_collection_s {
      * The allocator to use.
      */
     const CxAllocator *allocator;
-    /**
-     * The comparator function for the elements.
-     */
-    cx_compare_func cmpfunc;
     /**
      * The size of each element.
      */
@@ -69,6 +61,19 @@ struct cx_collection_s {
      * 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.
      *
@@ -139,6 +144,25 @@ struct cx_collection_s {
  */
 #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.
  *
@@ -154,29 +178,89 @@ struct cx_collection_s {
 #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
+ */
+#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 func (@c cx_compare_func) the compare function that shall be used by @c c
+ * @param left (@c void*) pointer to data
+ * @param right (@c void*) pointer to data
  */
-#define cxCollectionCompareFunc(c, func) (c)->collection.cmpfunc = (func)
+#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
 
@@ -228,8 +312,22 @@ struct cx_collection_s {
     if ((c)->collection.simple_destructor) cx_invoke_simple_destructor(c,e); \
     if ((c)->collection.advanced_destructor) cx_invoke_advanced_destructor(c,e)
 
-#ifdef __cplusplus
-} // extern "C"
-#endif
+/**
+ * Invokes all available destructor functions for a specific element.
+ *
+ * Usually only used by collection implementations. There should be no need
+ * to invoke this macro manually.
+ *
+ * In contrast to cx_invoke_destructor(), this macro does not automatically
+ * dereference pointers to the elements when cxCollectionStoresPointers()
+ * returns true.
+ *
+ * @param c a pointer to a struct that contains #CX_COLLECTION_BASE
+ * @param e pointer to the element
+ */
+#define cx_invoke_destructor_raw(c, e) \
+    if ((c)->collection.simple_destructor) (c)->collection.simple_destructor(e); \
+    if ((c)->collection.advanced_destructor) (c)->collection.advanced_destructor((c)->collection.destructor_data, e)
+
 
 #endif // UCX_COLLECTION_H
index 953edb5540ab631a8052a2aecd967abff444f590..5938ce36e85e93182b23f7a35f0513618bba6e6d 100644 (file)
 /**
  * Inform the compiler that falling through a switch case is intentional.
  */
-#define cx_attr_fallthrough __attribute__((__fallthrough__))
+#define CX_FALLTHROUGH __attribute__((__fallthrough__))
 
 /**
  * All pointer arguments must be non-NULL.
  */
-#define cx_attr_nonnull __attribute__((__nonnull__))
+#define CX_NONNULL __attribute__((__nonnull__))
 
 /**
  * The specified pointer arguments must be non-NULL.
  */
-#define cx_attr_nonnull_arg(...) __attribute__((__nonnull__(__VA_ARGS__)))
+#define CX_NONNULL_ARG(...) __attribute__((__nonnull__(__VA_ARGS__)))
 
 /**
  * The returned value is guaranteed to be non-NULL.
  */
-#define cx_attr_returns_nonnull __attribute__((__returns_nonnull__))
+#define CX_RETURNS_NONNULL __attribute__((__returns_nonnull__))
 
 /**
  * The attributed function always returns freshly allocated memory.
  */
-#define cx_attr_malloc __attribute__((__malloc__))
+#define CX_MALLOC __attribute__((__malloc__))
 
 #if !defined(__clang__) && __GNUC__ >= 11
 /**
  * @param freefunc the function that shall be used to free the memory
  * @param freefunc_arg the index of the pointer argument in @p freefunc
  */
-#define cx_attr_dealloc(freefunc, freefunc_arg) \
+#define CX_DEALLOC(freefunc, freefunc_arg) \
     __attribute__((__malloc__(freefunc, freefunc_arg)))
 #else
 /**
  * Not supported in clang.
  */
-#define cx_attr_dealloc(...)
+#define CX_DEALLOC(...)
 #endif // __clang__
 
 /**
  * Shortcut to specify #cxFree() as deallocator.
  */
-#define cx_attr_dealloc_ucx cx_attr_dealloc(cxFree, 2)
+#define CX_DEALLOC_UCX CX_DEALLOC(cxFree, 2)
 
 /**
  * Specifies the parameters from which the allocation size is calculated.
  */
-#define cx_attr_allocsize(...) __attribute__((__alloc_size__(__VA_ARGS__)))
+#define CX_ALLOCSIZE(...) __attribute__((__alloc_size__(__VA_ARGS__)))
 
 
 #ifdef __clang__
 /**
  * No support for @c null_terminated_string_arg in clang or GCC below 14.
  */
-#define cx_attr_cstr_arg(idx)
+#define CX_CSTR_ARG(idx)
 /**
  * No support for the access attribute in clang.
  */
-#define cx_attr_access(mode, ...)
+#define CX_ACCESS(mode, ...)
 #else
 #if __GNUC__ < 10
 /**
  * No support for access attribute in GCC < 10.
  */
-#define cx_attr_access(mode, ...)
+#define CX_ACCESS(mode, ...)
 #else
 /**
  * Helper macro to define access macros.
  */
-#define cx_attr_access(mode, ...) __attribute__((__access__(mode, __VA_ARGS__)))
+#define CX_ACCESS(mode, ...) __attribute__((__access__(mode, __VA_ARGS__)))
 #endif // __GNUC__ < 10
 #if __GNUC__ < 14
 /**
  * No support for @c null_terminated_string_arg in clang or GCC below 14.
  */
-#define cx_attr_cstr_arg(idx)
+#define CX_CSTR_ARG(idx)
 #else
 /**
  * The specified argument is expected to be a zero-terminated string.
  *
  * @param idx the index of the argument
  */
-#define cx_attr_cstr_arg(idx) \
+#define CX_CSTR_ARG(idx) \
     __attribute__((__null_terminated_string_arg__(idx)))
 #endif // __GNUC__ < 14
 #endif // __clang__
  * Takes one or two arguments: the index of the pointer and (optionally) the
  * index of another argument specifying the maximum number of accessed bytes.
  */
-#define cx_attr_access_r(...) cx_attr_access(__read_only__, __VA_ARGS__)
+#define CX_ACCESS_R(...) CX_ACCESS(__read_only__, __VA_ARGS__)
 
 /**
  * Specifies that the function will read and write through the given pointer.
  * Takes one or two arguments: the index of the pointer and (optionally) the
  * index of another argument specifying the maximum number of accessed bytes.
  */
-#define cx_attr_access_rw(...) cx_attr_access(__read_write__, __VA_ARGS__)
+#define CX_ACCESS_RW(...) CX_ACCESS(__read_write__, __VA_ARGS__)
 
 /**
  * Specifies that the function will only write through the given pointer.
  * Takes one or two arguments: the index of the pointer and (optionally) the
  * index of another argument specifying the maximum number of accessed bytes.
  */
-#define cx_attr_access_w(...) cx_attr_access(__write_only__, __VA_ARGS__)
+#define CX_ACCESS_W(...) CX_ACCESS(__write_only__, __VA_ARGS__)
 
 /**
  * Do not warn about unused variable.
  */
-#define cx_attr_unused __attribute__((__unused__))
+#define CX_UNUSED __attribute__((__unused__))
 
 /**
  * Warn about discarded return value.
  */
-#define cx_attr_nodiscard __attribute__((__warn_unused_result__))
+#define CX_NODISCARD __attribute__((__warn_unused_result__))
 
 
 // ---------------------------------------------------------------------------
-//       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 !defined __STDC_VERSION__ || __STDC_VERSION__ < 202300L
+/** Platform independent thread-local macro. */
+#define cx_thread_local _Thread_local
+#else // C23 or newer
+/** Platform independent thread-local macro. */
+#define cx_thread_local thread_local
+#endif // C23
 #endif // _MSC_VER
+#endif // __cplusplus
 
 // ---------------------------------------------------------------------------
 //       Exported and inlined functions
 #define CX_EXPORT
 #endif // CX_WINDLL / CX_WINDLL_EXPORT
 
+#ifdef __cplusplus
+#define CX_EXTERN extern "C" CX_EXPORT
+#define CX_FPTR extern "C" typedef
+#else
+/** Declares a function with external linkage. */
+#define CX_EXTERN CX_EXPORT
+/** Defines a function pointer. */
+#define CX_FPTR typedef
+#endif
+
 #ifdef __GNUC__
 /**
  * Declares a function to be inlined.
@@ -355,10 +376,8 @@ typedef size_t (*cx_read_func)(void*, size_t, size_t, void*);
  * @retval zero success
  * @retval non-zero the multiplication would overflow
  */
-#if __cplusplus
-extern "C"
-#endif
-CX_EXPORT int cx_szmul_impl(size_t a, size_t b, size_t *result);
+CX_EXTERN
+int cx_szmul_impl(size_t a, size_t b, size_t *result);
 #endif // cx_szmul
 
 #endif // UCX_COMMON_H
index 1b66e628f6bc5e8fdabeae53ac28ac5c7ed655b4..931bd9cf25dd63c0ce2f665f00fa76ece59628fe 100644 (file)
 
 #include "common.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  * A comparator function comparing two arbitrary values.
  *
@@ -54,7 +50,14 @@ extern "C" {
  * can be used, but they are NOT compatible with this function
  * pointer.
  */
-typedef int (*cx_compare_func)(const void *left, const void *right);
+CX_FPTR 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.
+ */
+CX_FPTR int (*cx_compare_func2)(const void *left, const void *right, void *data);
 
 /**
  * Compares two integers of type int.
@@ -68,8 +71,8 @@ typedef int (*cx_compare_func)(const void *left, const void *right);
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_int(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_int(const void *i1, const void *i2);
 
 /**
  * Compares two integers of type int.
@@ -80,8 +83,8 @@ CX_EXPORT int cx_cmp_int(const void *i1, const void *i2);
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_int(int i1, int i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_int(int i1, int i2);
 
 /**
  * Compares two integers of type long int.
@@ -95,8 +98,8 @@ CX_EXPORT int cx_vcmp_int(int i1, int i2);
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_longint(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_longint(const void *i1, const void *i2);
 
 /**
  * Compares two integers of type long int.
@@ -107,8 +110,8 @@ CX_EXPORT int cx_cmp_longint(const void *i1, const void *i2);
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_longint(long int i1, long int i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_longint(long int i1, long int i2);
 
 /**
  * Compares two integers of type long long.
@@ -122,8 +125,8 @@ CX_EXPORT int cx_vcmp_longint(long int i1, long int i2);
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_longlong(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_longlong(const void *i1, const void *i2);
 
 /**
  * Compares two integers of type long long.
@@ -134,8 +137,8 @@ CX_EXPORT int cx_cmp_longlong(const void *i1, const void *i2);
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_longlong(long long int i1, long long int i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_longlong(long long int i1, long long int i2);
 
 /**
  * Compares two integers of type int16_t.
@@ -149,8 +152,8 @@ CX_EXPORT int cx_vcmp_longlong(long long int i1, long long int i2);
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_int16(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_int16(const void *i1, const void *i2);
 
 /**
  * Compares two integers of type int16_t.
@@ -161,8 +164,8 @@ CX_EXPORT int cx_cmp_int16(const void *i1, const void *i2);
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_int16(int16_t i1, int16_t i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_int16(int16_t i1, int16_t i2);
 
 /**
  * Compares two integers of type int32_t.
@@ -176,8 +179,8 @@ CX_EXPORT int cx_vcmp_int16(int16_t i1, int16_t i2);
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_int32(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_int32(const void *i1, const void *i2);
 
 /**
  * Compares two integers of type int32_t.
@@ -188,8 +191,8 @@ CX_EXPORT int cx_cmp_int32(const void *i1, const void *i2);
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_int32(int32_t i1, int32_t i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_int32(int32_t i1, int32_t i2);
 
 /**
  * Compares two integers of type int64_t.
@@ -203,8 +206,8 @@ CX_EXPORT int cx_vcmp_int32(int32_t i1, int32_t i2);
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_int64(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_int64(const void *i1, const void *i2);
 
 /**
  * Compares two integers of type int64_t.
@@ -215,8 +218,8 @@ CX_EXPORT int cx_cmp_int64(const void *i1, const void *i2);
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_int64(int64_t i1, int64_t i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_int64(int64_t i1, int64_t i2);
 
 /**
  * Compares two integers of type unsigned int.
@@ -230,8 +233,8 @@ CX_EXPORT int cx_vcmp_int64(int64_t i1, int64_t i2);
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_uint(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_uint(const void *i1, const void *i2);
 
 /**
  * Compares two integers of type unsigned int.
@@ -242,8 +245,8 @@ CX_EXPORT int cx_cmp_uint(const void *i1, const void *i2);
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_uint(unsigned int i1, unsigned int i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_uint(unsigned int i1, unsigned int i2);
 
 /**
  * Compares two integers of type unsigned long int.
@@ -257,8 +260,8 @@ CX_EXPORT int cx_vcmp_uint(unsigned int i1, unsigned int i2);
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_ulongint(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_ulongint(const void *i1, const void *i2);
 
 /**
  * Compares two integers of type unsigned long int.
@@ -269,8 +272,8 @@ CX_EXPORT int cx_cmp_ulongint(const void *i1, const void *i2);
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_ulongint(unsigned long int i1, unsigned long int i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_ulongint(unsigned long int i1, unsigned long int i2);
 
 /**
  * Compares two integers of type unsigned long long.
@@ -284,8 +287,8 @@ CX_EXPORT int cx_vcmp_ulongint(unsigned long int i1, unsigned long int i2);
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_ulonglong(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_ulonglong(const void *i1, const void *i2);
 
 /**
  * Compares two integers of type unsigned long long.
@@ -296,8 +299,8 @@ CX_EXPORT int cx_cmp_ulonglong(const void *i1, const void *i2);
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_ulonglong(unsigned long long int i1, unsigned long long int i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_ulonglong(unsigned long long int i1, unsigned long long int i2);
 
 /**
  * Compares two integers of type uint16_t.
@@ -311,8 +314,8 @@ CX_EXPORT int cx_vcmp_ulonglong(unsigned long long int i1, unsigned long long in
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_uint16(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_uint16(const void *i1, const void *i2);
 
 /**
  * Compares two integers of type uint16_t.
@@ -323,8 +326,8 @@ CX_EXPORT int cx_cmp_uint16(const void *i1, const void *i2);
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_uint16(uint16_t i1, uint16_t i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_uint16(uint16_t i1, uint16_t i2);
 
 /**
  * Compares two integers of type uint32_t.
@@ -338,8 +341,8 @@ CX_EXPORT int cx_vcmp_uint16(uint16_t i1, uint16_t i2);
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_uint32(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_uint32(const void *i1, const void *i2);
 
 /**
  * Compares two integers of type uint32_t.
@@ -350,8 +353,8 @@ CX_EXPORT int cx_cmp_uint32(const void *i1, const void *i2);
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_uint32(uint32_t i1, uint32_t i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_uint32(uint32_t i1, uint32_t i2);
 
 /**
  * Compares two integers of type uint64_t.
@@ -365,8 +368,8 @@ CX_EXPORT int cx_vcmp_uint32(uint32_t i1, uint32_t i2);
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_uint64(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_uint64(const void *i1, const void *i2);
 
 /**
  * Compares two integers of type uint64_t.
@@ -377,8 +380,8 @@ CX_EXPORT int cx_cmp_uint64(const void *i1, const void *i2);
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_uint64(uint64_t i1, uint64_t i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_uint64(uint64_t i1, uint64_t i2);
 
 /**
  * Compares two integers of type size_t.
@@ -392,8 +395,8 @@ CX_EXPORT int cx_vcmp_uint64(uint64_t i1, uint64_t i2);
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_size(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_size(const void *i1, const void *i2);
 
 /**
  * Compares two integers of type size_t.
@@ -404,8 +407,8 @@ CX_EXPORT int cx_cmp_size(const void *i1, const void *i2);
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_size(size_t i1, size_t i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_size(size_t i1, size_t i2);
 
 /**
  * Compares two real numbers of type float with precision 1e-6f.
@@ -419,8 +422,8 @@ CX_EXPORT int cx_vcmp_size(size_t i1, size_t i2);
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_float(const void *f1, const void *f2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_float(const void *f1, const void *f2);
 
 /**
  * Compares two real numbers of type float with precision 1e-6f.
@@ -431,8 +434,8 @@ CX_EXPORT int cx_cmp_float(const void *f1, const void *f2);
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_float(float f1, float f2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_float(float f1, float f2);
 
 /**
  * Compares two real numbers of type double with precision 1e-14.
@@ -446,8 +449,8 @@ CX_EXPORT int cx_vcmp_float(float f1, float f2);
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_double(const void *d1, const void *d2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_double(const void *d1, const void *d2);
 
 /**
  * Compares two real numbers of type double with precision 1e-14.
@@ -458,8 +461,8 @@ CX_EXPORT int cx_cmp_double(const void *d1, const void *d2);
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_double(double d1, double d2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_double(double d1, double d2);
 
 /**
  * Compares the integer representation of two pointers.
@@ -473,8 +476,8 @@ CX_EXPORT int cx_vcmp_double(double d1, double d2);
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_intptr(const void *ptr1, const void *ptr2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_intptr(const void *ptr1, const void *ptr2);
 
 /**
  * Compares the integer representation of two pointers.
@@ -485,8 +488,8 @@ CX_EXPORT int cx_cmp_intptr(const void *ptr1, const void *ptr2);
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_intptr(intptr_t ptr1, intptr_t ptr2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_intptr(intptr_t ptr1, intptr_t ptr2);
 
 /**
  * Compares the unsigned integer representation of two pointers.
@@ -500,8 +503,8 @@ CX_EXPORT int cx_vcmp_intptr(intptr_t ptr1, intptr_t ptr2);
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_uintptr(const void *ptr1, const void *ptr2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_uintptr(const void *ptr1, const void *ptr2);
 
 /**
  * Compares the unsigned integer representation of two pointers.
@@ -512,8 +515,8 @@ CX_EXPORT int cx_cmp_uintptr(const void *ptr1, const void *ptr2);
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_uintptr(uintptr_t ptr1, uintptr_t ptr2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_uintptr(uintptr_t ptr1, uintptr_t ptr2);
 
 /**
  * Compares the pointers specified in the arguments without dereferencing.
@@ -524,11 +527,25 @@ CX_EXPORT int cx_vcmp_uintptr(uintptr_t ptr1, uintptr_t ptr2);
  * @retval 0 if both arguments are equal
  * @retval 1 if the left argument is greater than the right argument
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_ptr(const void *ptr1, const void *ptr2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_ptr(const void *ptr1, const void *ptr2);
 
-#ifdef __cplusplus
-} // extern "C"
-#endif
+/** 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_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_wrap(const void *ptr1, const void *ptr2, void* cmp_wrapper);
 
 #endif //UCX_COMPARE_H
index 23145bbb97356fff3b2dd1a338f0feb4ba08491a..e0fa2058ac78057a32c6c7cc4e4adf0a56e0c0d8 100644 (file)
 #include "common.h"
 #include "string.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /** Internal structure for a key within a hash map. */
 struct cx_hash_key_s {
     /**
@@ -78,8 +74,8 @@ typedef struct cx_hash_key_s CxHashKey;
  * @param key the key, the hash shall be computed for
  * @see cx_hash_key()
  */
-cx_attr_nonnull
-CX_EXPORT void cx_hash_murmur(CxHashKey *key);
+CX_EXTERN CX_NONNULL
+void cx_hash_murmur(CxHashKey *key);
 
 /**
  * Mixes up a 32-bit integer to be used as a hash.
@@ -89,7 +85,13 @@ CX_EXPORT void cx_hash_murmur(CxHashKey *key);
  * @param x the integer
  * @return the hash
  */
-CX_EXPORT uint32_t cx_hash_u32(uint32_t x);
+CX_INLINE
+uint32_t cx_hash_u32(uint32_t x) {
+    x = ((x >> 16) ^ x) * 0x45d9f3bu;
+    x = ((x >> 16) ^ x) * 0x45d9f3bu;
+    x = (x >> 16) ^ x;
+    return x;
+}
 
 /**
  * Mixes up a 64-bit integer to be used as a hash.
@@ -99,7 +101,27 @@ CX_EXPORT uint32_t cx_hash_u32(uint32_t x);
  * @param x the integer
  * @return the hash
  */
-CX_EXPORT uint64_t cx_hash_u64(uint64_t x);
+CX_INLINE
+uint64_t cx_hash_u64(uint64_t x){
+    x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
+    x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb);
+    x = x ^ (x >> 31);
+    return x;
+}
+
+/**
+ * Computes a hash key for an arbitrary object.
+ *
+ * The computation uses the in-memory representation that might not be
+ * the same on different platforms. Therefore, this hash should not be
+ * used for data exchange with different machines.
+ *
+ * @param obj a pointer to an arbitrary object
+ * @param len the length of the object in memory
+ * @return the hash key
+ */
+CX_EXTERN CX_NODISCARD CX_ACCESS_R(1, 2)
+CxHashKey cx_hash_key(const void *obj, size_t len);
 
 /**
  * Computes a hash key from a 32-bit integer.
@@ -107,8 +129,14 @@ CX_EXPORT uint64_t cx_hash_u64(uint64_t x);
  * @param x the integer
  * @return the hash key
  */
-cx_attr_nodiscard
-CX_EXPORT CxHashKey cx_hash_key_u32(uint32_t x);
+CX_NODISCARD CX_INLINE
+CxHashKey cx_hash_key_u32(uint32_t x) {
+    CxHashKey key;
+    key.data = NULL;
+    key.len = 0;
+    key.hash = cx_hash_u32(x);
+    return key;
+}
 
 /**
  * Computes a hash key from a 64-bit integer.
@@ -116,8 +144,14 @@ CX_EXPORT CxHashKey cx_hash_key_u32(uint32_t x);
  * @param x the integer
  * @return the hash key
  */
-cx_attr_nodiscard
-CX_EXPORT CxHashKey cx_hash_key_u64(uint64_t x);
+CX_NODISCARD CX_INLINE
+CxHashKey cx_hash_key_u64(uint64_t x) {
+    CxHashKey key;
+    key.data = NULL;
+    key.len = 0;
+    key.hash = cx_hash_u64(x);
+    return key;
+}
 
 /**
  * Computes a hash key from a string.
@@ -127,8 +161,10 @@ CX_EXPORT CxHashKey cx_hash_key_u64(uint64_t x);
  * @param str the string
  * @return the hash key
  */
-cx_attr_nodiscard cx_attr_cstr_arg(1)
-CX_EXPORT CxHashKey cx_hash_key_str(const char *str);
+CX_NODISCARD CX_CSTR_ARG(1) CX_INLINE
+CxHashKey cx_hash_key_str(const char *str) {
+    return cx_hash_key((const void*)str, str == NULL ? 0 : strlen(str));
+}
 
 /**
  * Computes a hash key from a string.
@@ -141,8 +177,10 @@ CX_EXPORT CxHashKey cx_hash_key_str(const char *str);
  * @param str the string
  * @return the hash key
  */
-cx_attr_nodiscard cx_attr_cstr_arg(1)
-CX_EXPORT CxHashKey cx_hash_key_ustr(const unsigned char *str);
+CX_NODISCARD CX_CSTR_ARG(1) CX_INLINE
+CxHashKey cx_hash_key_ustr(const unsigned char *str) {
+    return cx_hash_key((const void*)str, str == NULL ? 0 : strlen((const char*)str));
+}
 
 /**
  * Computes a hash key from a byte array.
@@ -151,23 +189,10 @@ CX_EXPORT CxHashKey cx_hash_key_ustr(const unsigned char *str);
  * @param len the length
  * @return the hash key
  */
-cx_attr_nodiscard cx_attr_access_r(1, 2)
-CX_EXPORT CxHashKey cx_hash_key_bytes(const unsigned char *bytes, size_t len);
-
-/**
- * Computes a hash key for an arbitrary object.
- *
- * The computation uses the in-memory representation that might not be
- * the same on different platforms. Therefore, this hash should not be
- * used for data exchange with different machines.
- *
- * @param obj a pointer to an arbitrary object
- * @param len the length of the object in memory
- * @return the hash key
- */
-cx_attr_nodiscard
-cx_attr_access_r(1, 2)
-CX_EXPORT CxHashKey cx_hash_key(const void *obj, size_t len);
+CX_NODISCARD CX_ACCESS_R(1, 2) CX_INLINE
+CxHashKey cx_hash_key_bytes(const unsigned char *bytes, size_t len) {
+    return cx_hash_key((const void*)bytes, len);
+}
 
 /**
  * Computes a hash key from a UCX string.
@@ -175,8 +200,10 @@ CX_EXPORT CxHashKey cx_hash_key(const void *obj, size_t len);
  * @param str the string
  * @return the hash key
  */
-cx_attr_nodiscard
-CX_EXPORT CxHashKey cx_hash_key_cxstr(cxstring str);
+CX_NODISCARD CX_INLINE
+CxHashKey cx_hash_key_cxstr(cxstring str) {
+    return cx_hash_key((void*)str.ptr, str.length);
+}
 
 /**
  * Computes a hash key from a UCX string.
@@ -184,26 +211,40 @@ CX_EXPORT CxHashKey cx_hash_key_cxstr(cxstring str);
  * @param str the string
  * @return the hash key
  */
-cx_attr_nodiscard
-CX_EXPORT CxHashKey cx_hash_key_mutstr(cxmutstr str);
+CX_NODISCARD CX_INLINE
+CxHashKey cx_hash_key_mutstr(cxmutstr str) {
+    return cx_hash_key((void*)str.ptr, str.length);
+}
 
 /**
  * The identity function for the CX_HASH_KEY() macro.
  * You should never need to use this manually.
  *
  * @param key the key
- * @return a copy of the key
+ * @return a copy of the key (not the data)
  */
-cx_attr_nodiscard
-CX_INLINE CxHashKey cx_hash_key_identity(CxHashKey key) {
+CX_NODISCARD CX_INLINE
+CxHashKey cx_hash_key_identity(CxHashKey key) {
     return key;
 }
 
+/**
+ * The dereference function for the CX_HASH_KEY() macro.
+ * You should never need to use this manually.
+ *
+ * @param key a pointer to a key
+ * @return a copy of the key (not the data)
+ */
+CX_NODISCARD CX_INLINE
+CxHashKey cx_hash_key_deref(const CxHashKey *key) {
+    return *key;
+}
+
 #ifndef __cplusplus
 /**
  * Creates a hash key from any of the supported types with implicit length.
  *
- * Does nothing when passing a CxHashkey.
+ * Does nothing when passing a CxHashKey and dereferences CxHashKey pointers.
  *
  * Supported types are UCX strings, zero-terminated C strings,
  * and 32-bit or 64-bit unsigned integers.
@@ -212,6 +253,8 @@ CX_INLINE CxHashKey cx_hash_key_identity(CxHashKey key) {
  * @returns the @c CxHashKey
  */
 #define CX_HASH_KEY(key) _Generic((key), \
+        CxHashKey*: cx_hash_key_deref, \
+        const CxHashKey*: cx_hash_key_deref, \
         CxHashKey: cx_hash_key_identity, \
         cxstring: cx_hash_key_cxstr, \
         cxmutstr: cx_hash_key_mutstr, \
@@ -233,12 +276,19 @@ CX_INLINE CxHashKey cx_hash_key_identity(CxHashKey key) {
  * @param right (@c CxHashKey*) the second key
  * @return zero when the keys equal, non-zero when they differ
  */
-cx_attr_nodiscard cx_attr_nonnull
-CX_EXPORT int cx_hash_key_cmp(const void *left, const void *right);
+CX_EXTERN CX_NODISCARD CX_NONNULL
+int cx_hash_key_cmp(const void *left, const void *right);
 
-#ifdef __cplusplus
-} // extern "C"
+/**
+ * Interprets the key data as a string and returns it.
+ *
+ * @param key the key
+ * @return the key data as a string
+ */
+CX_EXTERN
+cxstring cx_hash_key_as_string(const CxHashKey *key);
 
+#ifdef __cplusplus
 // ----------------------------------------------------------
 // Overloads of CX_HASH_KEY (the C++ version of a _Generic)
 // ----------------------------------------------------------
@@ -247,6 +297,10 @@ CX_CPPDECL CxHashKey CX_HASH_KEY(CxHashKey key) {
     return key;
 }
 
+CX_CPPDECL CxHashKey CX_HASH_KEY(const CxHashKey *key) {
+    return *key;
+}
+
 CX_CPPDECL CxHashKey CX_HASH_KEY(cxstring str) {
     return cx_hash_key_cxstr(str);
 }
index 3c9a303bbbd461eff43112a196afff1cbdc7e3b5..153d49c3ad7812c76d0eb5da25670ef881ddd8b1 100644 (file)
 
 #include "map.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /** Internal structure for an element of a hash map. */
 struct cx_hash_map_element_s;
 
@@ -83,26 +79,10 @@ struct cx_hash_map_s {
  * @param buckets the initial number of buckets in this hash map
  * @return a pointer to the new hash map
  */
-cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxMapFree, 1)
-CX_EXPORT CxMap *cxHashMapCreate(const CxAllocator *allocator,
+CX_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxMapFree, 1)
+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.
  *
@@ -122,12 +102,7 @@ CX_EXPORT CxMap *cxHashMapCreate(const CxAllocator *allocator,
  * @retval zero success
  * @retval non-zero if a memory allocation error occurred
  */
-cx_attr_nonnull
-CX_EXPORT int cxMapRehash(CxMap *map);
-
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
+CX_EXTERN CX_NONNULL
+int cxMapRehash(CxMap *map);
 
 #endif // UCX_HASH_MAP_H
index 4fe304a5ff0fcf8499dcbac7c9237f77fe925de0..74d7e589c77b540a1a7a1fd4fa858801dababe33 100644 (file)
 
 #include "common.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  * Common data for all iterators.
  */
@@ -182,16 +178,6 @@ typedef struct cx_iterator_s CxIterator;
  */
 #define cxIteratorFlagRemoval(iter) ((iter).base.remove = (iter).base.allow_remove)
 
-/**
- * Obtains a reference to an arbitrary iterator.
- *
- * This is useful for APIs that expect some iterator as an argument.
- *
- * @param iter the iterator
- * @return (@c struct @c cx_iterator_base_s*) a pointer to the iterator
- */
-#define cxIteratorRef(iter) &((iter).base)
-
 /**
  * Loops over an iterator.
  *
@@ -214,26 +200,15 @@ for (type elem; cxIteratorValid(iter) && (elem = (type)cxIteratorCurrent(iter))
  * 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);
+CX_EXTERN CX_NODISCARD
+CxIterator cxIterator(const void *array,
+        size_t elem_size, size_t elem_count);
 
 /**
  * Creates an iterator for the specified plain pointer array.
@@ -243,28 +218,12 @@ CX_EXPORT CxIterator cxIterator(const void *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);
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
+CX_EXTERN CX_NODISCARD
+CxIterator cxIteratorPtr(const void *array, size_t elem_count);
 
 #endif // UCX_ITERATOR_H
index c70a7f7a0141402d3f30d6cc0c2904cc72ce6974..72681644155fefe2245be5a44c99697c994ee2aa 100644 (file)
 #include "array_list.h"
 #include "map.h"
 
-#include <string.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
 /**
  * The type of the parsed token.
  */
@@ -116,6 +109,10 @@ enum cx_json_value_type {
      * Reserved.
      */
     CX_JSON_NOTHING, // this allows us to always return non-NULL values
+    /**
+     * No meaningful data.
+     */
+    CX_JSON_UNINITIALIZED,
     /**
      * A JSON object.
      */
@@ -184,10 +181,6 @@ typedef struct cx_json_token_s CxJsonToken;
  */
 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 map representing a JSON object.
  * The map contains pointers of type @c CxJsonValue.
@@ -210,16 +203,6 @@ typedef double CxJsonNumber;
  */
 typedef enum cx_json_literal CxJsonLiteral;
 
-/**
- * JSON array structure.
- */
-struct cx_json_array_s {
-    /**
-     * The array data.
-     */
-    CX_ARRAY_DECLARE(CxJsonValue*, data);
-};
-
 /**
  * Structure for a JSON value.
  */
@@ -243,7 +226,7 @@ struct cx_json_value_s {
         /**
          * 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.
          */
@@ -298,18 +281,12 @@ struct cx_json_s {
      * The allocator used for produced JSON values.
      */
     const CxAllocator *allocator;
+
     /**
      * The input buffer.
      */
     CxBuffer buffer;
 
-    /**
-     * Used internally.
-     *
-     * Remembers the prefix of the last uncompleted token.
-     */
-    CxJsonToken uncompleted;
-
     /**
      * A pointer to an intermediate state of the currently parsed value.
      *
@@ -324,15 +301,25 @@ struct cx_json_s {
      */
     cxmutstr uncompleted_member_name;
 
+    /**
+     * Internal buffer for uncompleted tokens.
+     */
+    cxmutstr uncompleted_content;
+
+    /**
+     * The expected type of the currently parsed, uncompleted token.
+     */
+    CxJsonTokenType uncompleted_tokentype;
+
     /**
      * 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.
@@ -439,8 +426,8 @@ typedef struct cx_json_writer_s CxJsonWriter;
  *
  * @return new JSON writer settings
  */
-cx_attr_nodiscard
-CX_EXPORT CxJsonWriter cxJsonWriterCompact(void);
+CX_EXTERN CX_NODISCARD
+CxJsonWriter cxJsonWriterCompact(void);
 
 /**
  * Creates a default writer configuration for pretty output.
@@ -448,8 +435,8 @@ CX_EXPORT CxJsonWriter cxJsonWriterCompact(void);
  * @param use_spaces false if you want tabs, true if you want four spaces instead
  * @return new JSON writer settings
  */
-cx_attr_nodiscard
-CX_EXPORT CxJsonWriter cxJsonWriterPretty(bool use_spaces);
+CX_EXTERN CX_NODISCARD
+CxJsonWriter cxJsonWriterPretty(bool use_spaces);
 
 /**
  * Writes a JSON value to a buffer or stream.
@@ -469,36 +456,36 @@ CX_EXPORT CxJsonWriter cxJsonWriterPretty(bool use_spaces);
  * @retval zero success
  * @retval non-zero when no or not all data could be written
  */
-cx_attr_nonnull_arg(1, 2, 3)
-CX_EXPORT int cxJsonWrite(void* target, const CxJsonValue* value,
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3)
+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 value the 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(1)
-CX_EXPORT cxmutstr cxJsonToString(CxJsonValue *value, const CxAllocator *allocator);
+CX_EXTERN CX_NONNULL_ARG(2) CX_NODISCARD
+cxmutstr cxJsonToString(const CxAllocator *allocator, CxJsonValue *value);
 
 /**
  * Produces a pretty string representation of the specified JSON value.
  *
- * @param value the 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(1)
-CX_EXPORT cxmutstr cxJsonToPrettyString(CxJsonValue *value, const CxAllocator *allocator);
+CX_EXTERN CX_NONNULL_ARG(2) CX_NODISCARD
+cxmutstr cxJsonToPrettyString(const CxAllocator *allocator, CxJsonValue *value);
 
 /**
  * Initializes the JSON interface.
@@ -507,8 +494,8 @@ CX_EXPORT cxmutstr cxJsonToPrettyString(CxJsonValue *value, const CxAllocator *a
  * @param allocator the allocator that shall be used for the produced values
  * @see cxJsonDestroy()
  */
-cx_attr_nonnull_arg(1)
-CX_EXPORT void cxJsonInit(CxJson *json, const CxAllocator *allocator);
+CX_EXTERN CX_NONNULL_ARG(1)
+void cxJsonInit(CxJson *json, const CxAllocator *allocator);
 
 /**
  * Destroys the JSON interface.
@@ -516,8 +503,8 @@ CX_EXPORT void cxJsonInit(CxJson *json, const CxAllocator *allocator);
  * @param json the JSON interface
  * @see cxJsonInit()
  */
-cx_attr_nonnull
-CX_EXPORT void cxJsonDestroy(CxJson *json);
+CX_EXTERN CX_NONNULL
+void cxJsonDestroy(CxJson *json);
 
 /**
  * Destroys and re-initializes the JSON interface.
@@ -527,8 +514,8 @@ CX_EXPORT void cxJsonDestroy(CxJson *json);
  *
  * @param json the JSON interface
  */
-cx_attr_nonnull
-CX_EXPORT void cxJsonReset(CxJson *json);
+CX_EXTERN CX_NONNULL
+void cxJsonReset(CxJson *json);
 
 /**
  * Fills the input buffer.
@@ -548,8 +535,8 @@ CX_EXPORT void cxJsonReset(CxJson *json);
  * @retval non-zero internal allocation error
  * @see cxJsonFill()
  */
-cx_attr_nonnull_arg(1) cx_attr_access_r(2, 3)
-CX_EXPORT int cxJsonFilln(CxJson *json, const char *buf, size_t len);
+CX_EXTERN CX_NONNULL_ARG(1) CX_ACCESS_R(2, 3)
+int cxJsonFilln(CxJson *json, const char *buf, size_t len);
 
 
 /**
@@ -560,8 +547,8 @@ CX_EXPORT int cxJsonFilln(CxJson *json, const char *buf, size_t len);
  * @retval zero success
  * @retval non-zero internal allocation error
  */
-cx_attr_nonnull
-CX_INLINE int cx_json_fill(CxJson *json, cxstring str) {
+CX_NONNULL CX_INLINE
+int cx_json_fill(CxJson *json, cxstring str) {
     return cxJsonFilln(json, str.ptr, str.length);
 }
 
@@ -593,8 +580,8 @@ CX_INLINE int cx_json_fill(CxJson *json, cxstring str) {
  * @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,
+CX_EXTERN CX_NONNULL_ARG(3) CX_ACCESS_W(3)
+CxJsonStatus cx_json_from_string(const CxAllocator *allocator,
             cxstring str, CxJsonValue **value);
 
 /**
@@ -614,6 +601,20 @@ CX_EXPORT CxJsonStatus cx_json_from_string(const CxAllocator *allocator,
 #define cxJsonFromString(allocator, str, value) \
         cx_json_from_string(allocator, cx_strcast(str), value)
 
+/**
+ * Recursively deallocates the memory of a JSON value.
+ *
+ * @remark The type of each deallocated value will be changed
+ * to #CX_JSON_NOTHING, and values of such a type will be skipped
+ * by the deallocation. That means this function protects
+ * you from double-frees when you are accidentally freeing
+ * a nested value and then the parent value (or vice versa).
+ *
+ * @param value the value
+ */
+CX_EXTERN
+void cxJsonValueFree(CxJsonValue *value);
+
 /**
  * Creates a new (empty) JSON object.
  *
@@ -622,19 +623,22 @@ CX_EXPORT CxJsonStatus cx_json_from_string(const CxAllocator *allocator,
  * @see cxJsonObjPutObj()
  * @see cxJsonArrAddValues()
  */
-cx_attr_nodiscard
-CX_EXPORT CxJsonValue* cxJsonCreateObj(const CxAllocator* allocator);
+CX_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxJsonValueFree, 1)
+CxJsonValue* cxJsonCreateObj(const CxAllocator* allocator);
 
 /**
  * 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_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxJsonValueFree, 1)
+CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator, size_t capacity);
 
 /**
  * Creates a new JSON number value.
@@ -645,8 +649,8 @@ CX_EXPORT CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator);
  * @see cxJsonObjPutNumber()
  * @see cxJsonArrAddNumbers()
  */
-cx_attr_nodiscard
-CX_EXPORT CxJsonValue* cxJsonCreateNumber(const CxAllocator* allocator, double num);
+CX_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxJsonValueFree, 1)
+CxJsonValue* cxJsonCreateNumber(const CxAllocator* allocator, double num);
 
 /**
  * Creates a new JSON number value based on an integer.
@@ -657,8 +661,8 @@ CX_EXPORT CxJsonValue* cxJsonCreateNumber(const CxAllocator* allocator, double n
  * @see cxJsonObjPutInteger()
  * @see cxJsonArrAddIntegers()
  */
-cx_attr_nodiscard
-CX_EXPORT CxJsonValue* cxJsonCreateInteger(const CxAllocator* allocator, int64_t num);
+CX_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxJsonValueFree, 1)
+CxJsonValue* cxJsonCreateInteger(const CxAllocator* allocator, int64_t num);
 
 /**
  * Creates a new JSON string.
@@ -671,8 +675,8 @@ CX_EXPORT CxJsonValue* cxJsonCreateInteger(const CxAllocator* allocator, int64_t
  * @see cxJsonObjPutString()
  * @see cxJsonArrAddCxStrings()
  */
-cx_attr_nodiscard
-CX_EXPORT CxJsonValue* cx_json_create_string(const CxAllocator* allocator, cxstring str);
+CX_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxJsonValueFree, 1)
+CxJsonValue* cx_json_create_string(const CxAllocator* allocator, cxstring str);
 
 /**
  * Creates a new JSON string.
@@ -694,8 +698,8 @@ CX_EXPORT CxJsonValue* cx_json_create_string(const CxAllocator* allocator, cxstr
  * @see cxJsonObjPutLiteral()
  * @see cxJsonArrAddLiterals()
  */
-cx_attr_nodiscard
-CX_EXPORT CxJsonValue* cxJsonCreateLiteral(const CxAllocator* allocator, CxJsonLiteral lit);
+CX_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxJsonValueFree, 1)
+CxJsonValue* cxJsonCreateLiteral(const CxAllocator* allocator, CxJsonLiteral lit);
 
 /**
  * Adds number values to a JSON array.
@@ -706,8 +710,8 @@ CX_EXPORT CxJsonValue* cxJsonCreateLiteral(const CxAllocator* allocator, CxJsonL
  * @retval zero success
  * @retval non-zero allocation failure
  */
-cx_attr_nonnull cx_attr_access_r(2, 3)
-CX_EXPORT int cxJsonArrAddNumbers(CxJsonValue* arr, const double* num, size_t count);
+CX_EXTERN CX_NONNULL CX_ACCESS_R(2, 3)
+int cxJsonArrAddNumbers(CxJsonValue* arr, const double* num, size_t count);
 
 /**
  * Adds number values, of which all are integers, to a JSON array.
@@ -718,8 +722,8 @@ CX_EXPORT int cxJsonArrAddNumbers(CxJsonValue* arr, const double* num, size_t co
  * @retval zero success
  * @retval non-zero allocation failure
  */
-cx_attr_nonnull cx_attr_access_r(2, 3)
-CX_EXPORT int cxJsonArrAddIntegers(CxJsonValue* arr, const int64_t* num, size_t count);
+CX_EXTERN CX_NONNULL CX_ACCESS_R(2, 3)
+int cxJsonArrAddIntegers(CxJsonValue* arr, const int64_t* num, size_t count);
 
 /**
  * Adds strings to a JSON array.
@@ -733,8 +737,8 @@ CX_EXPORT int cxJsonArrAddIntegers(CxJsonValue* arr, const int64_t* num, size_t
  * @retval non-zero allocation failure
  * @see cxJsonArrAddCxStrings()
  */
-cx_attr_nonnull cx_attr_access_r(2, 3)
-CX_EXPORT int cxJsonArrAddStrings(CxJsonValue* arr, const char* const* str, size_t count);
+CX_EXTERN CX_NONNULL CX_ACCESS_R(2, 3)
+int cxJsonArrAddStrings(CxJsonValue* arr, const char* const* str, size_t count);
 
 /**
  * Adds strings to a JSON array.
@@ -748,8 +752,8 @@ CX_EXPORT int cxJsonArrAddStrings(CxJsonValue* arr, const char* const* str, size
  * @retval non-zero allocation failure
  * @see cxJsonArrAddStrings()
  */
-cx_attr_nonnull cx_attr_access_r(2, 3)
-CX_EXPORT int cxJsonArrAddCxStrings(CxJsonValue* arr, const cxstring* str, size_t count);
+CX_EXTERN CX_NONNULL CX_ACCESS_R(2, 3)
+int cxJsonArrAddCxStrings(CxJsonValue* arr, const cxstring* str, size_t count);
 
 /**
  * Adds literals to a JSON array.
@@ -760,8 +764,8 @@ CX_EXPORT int cxJsonArrAddCxStrings(CxJsonValue* arr, const cxstring* str, size_
  * @retval zero success
  * @retval non-zero allocation failure
  */
-cx_attr_nonnull cx_attr_access_r(2, 3)
-CX_EXPORT int cxJsonArrAddLiterals(CxJsonValue* arr, const CxJsonLiteral* lit, size_t count);
+CX_EXTERN CX_NONNULL CX_ACCESS_R(2, 3)
+int cxJsonArrAddLiterals(CxJsonValue* arr, const CxJsonLiteral* lit, size_t count);
 
 /**
  * Add arbitrary values to a JSON array.
@@ -775,8 +779,8 @@ CX_EXPORT int cxJsonArrAddLiterals(CxJsonValue* arr, const CxJsonLiteral* lit, s
  * @retval zero success
  * @retval non-zero allocation failure
  */
-cx_attr_nonnull cx_attr_access_r(2, 3)
-CX_EXPORT int cxJsonArrAddValues(CxJsonValue* arr, CxJsonValue* const* val, size_t count);
+CX_EXTERN CX_NONNULL CX_ACCESS_R(2, 3)
+int cxJsonArrAddValues(CxJsonValue* arr, CxJsonValue* const* val, size_t count);
 
 /**
  * Adds or replaces a value within a JSON object.
@@ -789,8 +793,8 @@ CX_EXPORT int cxJsonArrAddValues(CxJsonValue* arr, CxJsonValue* const* val, size
  * @retval zero success
  * @retval non-zero allocation failure
  */
-cx_attr_nonnull
-CX_EXPORT int cx_json_obj_put(CxJsonValue* obj, cxstring name, CxJsonValue* child);
+CX_EXTERN CX_NONNULL
+int cx_json_obj_put(CxJsonValue* obj, cxstring name, CxJsonValue* child);
 
 /**
  * Adds or replaces a value within a JSON object.
@@ -819,8 +823,8 @@ CX_EXPORT int cx_json_obj_put(CxJsonValue* obj, cxstring name, CxJsonValue* chil
  * @see cxJsonObjPut()
  * @see cxJsonCreateObj()
  */
-cx_attr_nonnull
-CX_EXPORT CxJsonValue* cx_json_obj_put_obj(CxJsonValue* obj, cxstring name);
+CX_EXTERN CX_NONNULL CX_MALLOC CX_DEALLOC(cxJsonValueFree, 1)
+CxJsonValue* cx_json_obj_put_obj(CxJsonValue* obj, cxstring name);
 
 /**
  * Creates a new JSON object and adds it to an existing object.
@@ -840,23 +844,25 @@ CX_EXPORT CxJsonValue* cx_json_obj_put_obj(CxJsonValue* obj, cxstring name);
  *
  * @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_EXTERN CX_NONNULL CX_MALLOC CX_DEALLOC(cxJsonValueFree, 1)
+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.
@@ -870,8 +876,8 @@ CX_EXPORT CxJsonValue* cx_json_obj_put_arr(CxJsonValue* obj, cxstring name);
  * @see cxJsonObjPut()
  * @see cxJsonCreateNumber()
  */
-cx_attr_nonnull
-CX_EXPORT CxJsonValue* cx_json_obj_put_number(CxJsonValue* obj, cxstring name, double num);
+CX_EXTERN CX_NONNULL CX_MALLOC CX_DEALLOC(cxJsonValueFree, 1)
+CxJsonValue* cx_json_obj_put_number(CxJsonValue* obj, cxstring name, double num);
 
 /**
  * Creates a new JSON number and adds it to an object.
@@ -897,8 +903,8 @@ CX_EXPORT CxJsonValue* cx_json_obj_put_number(CxJsonValue* obj, cxstring name, d
  * @see cxJsonObjPut()
  * @see cxJsonCreateInteger()
  */
-cx_attr_nonnull
-CX_EXPORT CxJsonValue* cx_json_obj_put_integer(CxJsonValue* obj, cxstring name, int64_t num);
+CX_EXTERN CX_NONNULL CX_MALLOC CX_DEALLOC(cxJsonValueFree, 1)
+CxJsonValue* cx_json_obj_put_integer(CxJsonValue* obj, cxstring name, int64_t num);
 
 /**
  * Creates a new JSON number, based on an integer, and adds it to an object.
@@ -924,8 +930,8 @@ CX_EXPORT CxJsonValue* cx_json_obj_put_integer(CxJsonValue* obj, cxstring name,
  * @see cxJsonObjPut()
  * @see cxJsonCreateString()
  */
-cx_attr_nonnull
-CX_EXPORT CxJsonValue* cx_json_obj_put_string(CxJsonValue* obj, cxstring name, cxstring str);
+CX_EXTERN CX_NONNULL CX_MALLOC CX_DEALLOC(cxJsonValueFree, 1)
+CxJsonValue* cx_json_obj_put_string(CxJsonValue* obj, cxstring name, cxstring str);
 
 /**
  * Creates a new JSON string and adds it to an object.
@@ -953,8 +959,8 @@ CX_EXPORT CxJsonValue* cx_json_obj_put_string(CxJsonValue* obj, cxstring name, c
  * @see cxJsonObjPut()
  * @see cxJsonCreateLiteral()
  */
-cx_attr_nonnull
-CX_EXPORT CxJsonValue* cx_json_obj_put_literal(CxJsonValue* obj, cxstring name, CxJsonLiteral lit);
+CX_EXTERN CX_NONNULL CX_MALLOC CX_DEALLOC(cxJsonValueFree, 1)
+CxJsonValue* cx_json_obj_put_literal(CxJsonValue* obj, cxstring name, CxJsonLiteral lit);
 
 /**
  * Creates a new JSON literal and adds it to an object.
@@ -968,19 +974,6 @@ CX_EXPORT CxJsonValue* cx_json_obj_put_literal(CxJsonValue* obj, cxstring name,
  */
 #define cxJsonObjPutLiteral(obj, name, lit) cx_json_obj_put_literal(obj, cx_strcast(name), lit)
 
-/**
- * Recursively deallocates the memory of a JSON value.
- *
- * @remark The type of each deallocated value will be changed
- * to #CX_JSON_NOTHING, and values of such a type will be skipped
- * by the deallocation. That means this function protects
- * you from double-frees when you are accidentally freeing
- * a nested value and then the parent value (or vice versa).
- *
- * @param value the value
- */
-CX_EXPORT void cxJsonValueFree(CxJsonValue *value);
-
 /**
  * Tries to obtain the next JSON value.
  *
@@ -1003,8 +996,8 @@ CX_EXPORT void cxJsonValueFree(CxJsonValue *value);
  * @retval CX_JSON_FORMAT_ERROR_NUMBER the JSON text contains an illegally formatted number
  * @retval CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN JSON syntax error
  */
-cx_attr_nonnull cx_attr_access_w(2)
-CX_EXPORT CxJsonStatus cxJsonNext(CxJson *json, CxJsonValue **value);
+CX_EXTERN CX_NONNULL CX_ACCESS_W(2)
+CxJsonStatus cxJsonNext(CxJson *json, CxJsonValue **value);
 
 /**
  * Checks if the specified value is a JSON object.
@@ -1013,8 +1006,8 @@ CX_EXPORT CxJsonStatus cxJsonNext(CxJson *json, CxJsonValue **value);
  * @retval true the value is a JSON object
  * @retval false otherwise
  */
-cx_attr_nonnull
-CX_INLINE bool cxJsonIsObject(const CxJsonValue *value) {
+CX_NONNULL CX_NODISCARD CX_INLINE
+bool cxJsonIsObject(const CxJsonValue *value) {
     return value->type == CX_JSON_OBJECT;
 }
 
@@ -1025,8 +1018,8 @@ CX_INLINE bool cxJsonIsObject(const CxJsonValue *value) {
  * @retval true the value is a JSON array
  * @retval false otherwise
  */
-cx_attr_nonnull
-CX_INLINE bool cxJsonIsArray(const CxJsonValue *value) {
+CX_NONNULL CX_NODISCARD CX_INLINE
+bool cxJsonIsArray(const CxJsonValue *value) {
     return value->type == CX_JSON_ARRAY;
 }
 
@@ -1037,8 +1030,8 @@ CX_INLINE bool cxJsonIsArray(const CxJsonValue *value) {
  * @retval true the value is a string
  * @retval false otherwise
  */
-cx_attr_nonnull
-CX_INLINE bool cxJsonIsString(const CxJsonValue *value) {
+CX_NONNULL CX_NODISCARD CX_INLINE
+bool cxJsonIsString(const CxJsonValue *value) {
     return value->type == CX_JSON_STRING;
 }
 
@@ -1053,8 +1046,8 @@ CX_INLINE bool cxJsonIsString(const CxJsonValue *value) {
  * @retval false otherwise
  * @see cxJsonIsInteger()
  */
-cx_attr_nonnull
-CX_INLINE bool cxJsonIsNumber(const CxJsonValue *value) {
+CX_NONNULL CX_NODISCARD CX_INLINE
+bool cxJsonIsNumber(const CxJsonValue *value) {
     return value->type == CX_JSON_NUMBER || value->type == CX_JSON_INTEGER;
 }
 
@@ -1066,8 +1059,8 @@ CX_INLINE bool cxJsonIsNumber(const CxJsonValue *value) {
  * @retval false otherwise
  * @see cxJsonIsNumber()
  */
-cx_attr_nonnull
-CX_INLINE bool cxJsonIsInteger(const CxJsonValue *value) {
+CX_NONNULL CX_NODISCARD CX_INLINE
+bool cxJsonIsInteger(const CxJsonValue *value) {
     return value->type == CX_JSON_INTEGER;
 }
 
@@ -1083,8 +1076,8 @@ CX_INLINE bool cxJsonIsInteger(const CxJsonValue *value) {
  * @see cxJsonIsFalse()
  * @see cxJsonIsNull()
  */
-cx_attr_nonnull
-CX_INLINE bool cxJsonIsLiteral(const CxJsonValue *value) {
+CX_NONNULL CX_NODISCARD CX_INLINE
+bool cxJsonIsLiteral(const CxJsonValue *value) {
     return value->type == CX_JSON_LITERAL;
 }
 
@@ -1097,8 +1090,8 @@ CX_INLINE bool cxJsonIsLiteral(const CxJsonValue *value) {
  * @see cxJsonIsTrue()
  * @see cxJsonIsFalse()
  */
-cx_attr_nonnull
-CX_INLINE bool cxJsonIsBool(const CxJsonValue *value) {
+CX_NONNULL CX_NODISCARD CX_INLINE
+bool cxJsonIsBool(const CxJsonValue *value) {
     return cxJsonIsLiteral(value) && value->literal != CX_JSON_NULL;
 }
 
@@ -1114,8 +1107,8 @@ CX_INLINE bool cxJsonIsBool(const CxJsonValue *value) {
  * @see cxJsonIsBool()
  * @see cxJsonIsFalse()
  */
-cx_attr_nonnull
-CX_INLINE bool cxJsonIsTrue(const CxJsonValue *value) {
+CX_NONNULL CX_NODISCARD CX_INLINE
+bool cxJsonIsTrue(const CxJsonValue *value) {
     return cxJsonIsLiteral(value) && value->literal == CX_JSON_TRUE;
 }
 
@@ -1131,8 +1124,8 @@ CX_INLINE bool cxJsonIsTrue(const CxJsonValue *value) {
  * @see cxJsonIsBool()
  * @see cxJsonIsTrue()
  */
-cx_attr_nonnull
-CX_INLINE bool cxJsonIsFalse(const CxJsonValue *value) {
+CX_NONNULL CX_NODISCARD CX_INLINE
+bool cxJsonIsFalse(const CxJsonValue *value) {
     return cxJsonIsLiteral(value) && value->literal == CX_JSON_FALSE;
 }
 
@@ -1144,8 +1137,8 @@ CX_INLINE bool cxJsonIsFalse(const CxJsonValue *value) {
  * @retval false otherwise
  * @see cxJsonIsLiteral()
  */
-cx_attr_nonnull
-CX_INLINE bool cxJsonIsNull(const CxJsonValue *value) {
+CX_NONNULL CX_NODISCARD CX_INLINE
+bool cxJsonIsNull(const CxJsonValue *value) {
     return cxJsonIsLiteral(value) && value->literal == CX_JSON_NULL;
 }
 
@@ -1158,8 +1151,8 @@ CX_INLINE bool cxJsonIsNull(const CxJsonValue *value) {
  * @return the value represented as C string
  * @see cxJsonIsString()
  */
-cx_attr_nonnull  cx_attr_returns_nonnull
-CX_EXPORT char *cxJsonAsString(const CxJsonValue *value);
+CX_EXTERN CX_NONNULL CX_RETURNS_NONNULL CX_NODISCARD
+char *cxJsonAsString(const CxJsonValue *value);
 
 /**
  * Obtains a UCX string from the given JSON value.
@@ -1170,8 +1163,8 @@ CX_EXPORT char *cxJsonAsString(const CxJsonValue *value);
  * @return the value represented as UCX string
  * @see cxJsonIsString()
  */
-cx_attr_nonnull
-CX_EXPORT cxstring cxJsonAsCxString(const CxJsonValue *value);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+cxstring cxJsonAsCxString(const CxJsonValue *value);
 
 /**
  * Obtains a mutable UCX string from the given JSON value.
@@ -1182,8 +1175,8 @@ CX_EXPORT cxstring cxJsonAsCxString(const CxJsonValue *value);
  * @return the value represented as mutable UCX string
  * @see cxJsonIsString()
  */
-cx_attr_nonnull
-CX_EXPORT cxmutstr cxJsonAsCxMutStr(const CxJsonValue *value);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+cxmutstr cxJsonAsCxMutStr(const CxJsonValue *value);
 
 /**
  * Obtains a double-precision floating-point value from the given JSON value.
@@ -1194,8 +1187,8 @@ CX_EXPORT cxmutstr cxJsonAsCxMutStr(const CxJsonValue *value);
  * @return the value represented as double
  * @see cxJsonIsNumber()
  */
-cx_attr_nonnull
-CX_EXPORT double cxJsonAsDouble(const CxJsonValue *value);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+double cxJsonAsDouble(const CxJsonValue *value);
 
 /**
  * Obtains a 64-bit signed integer from the given JSON value.
@@ -1209,8 +1202,8 @@ CX_EXPORT double cxJsonAsDouble(const CxJsonValue *value);
  * @see cxJsonIsNumber()
  * @see cxJsonIsInteger()
  */
-cx_attr_nonnull
-CX_EXPORT int64_t cxJsonAsInteger(const CxJsonValue *value);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int64_t cxJsonAsInteger(const CxJsonValue *value);
 
 /**
  * Obtains a Boolean value from the given JSON value.
@@ -1222,8 +1215,8 @@ CX_EXPORT int64_t cxJsonAsInteger(const CxJsonValue *value);
  * @return the value represented as double
  * @see cxJsonIsLiteral()
  */
-cx_attr_nonnull
-CX_INLINE bool cxJsonAsBool(const CxJsonValue *value) {
+CX_NONNULL CX_NODISCARD CX_INLINE
+bool cxJsonAsBool(const CxJsonValue *value) {
     return value->literal == CX_JSON_TRUE;
 }
 
@@ -1236,9 +1229,9 @@ CX_INLINE bool cxJsonAsBool(const CxJsonValue *value) {
  * @return the size of the array
  * @see cxJsonIsArray()
  */
-cx_attr_nonnull
-CX_INLINE size_t cxJsonArrSize(const CxJsonValue *value) {
-    return value->array.data_size;
+CX_NONNULL CX_NODISCARD CX_INLINE
+size_t cxJsonArrSize(const CxJsonValue *value) {
+    return value->array.size;
 }
 
 /**
@@ -1255,8 +1248,8 @@ CX_INLINE size_t cxJsonArrSize(const CxJsonValue *value) {
  * @return the value at the specified index
  * @see cxJsonIsArray()
  */
-cx_attr_nonnull cx_attr_returns_nonnull
-CX_EXPORT CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index);
+CX_EXTERN CX_NONNULL CX_RETURNS_NONNULL CX_NODISCARD
+CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index);
 
 /**
  * Removes an element from a JSON array.
@@ -1271,8 +1264,8 @@ CX_EXPORT CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index);
  * @return the removed value from the specified index or @c NULL when the index was out of bounds
  * @see cxJsonIsArray()
  */
-cx_attr_nonnull
-CX_EXPORT CxJsonValue *cxJsonArrRemove(CxJsonValue *value, size_t index);
+CX_EXTERN CX_NONNULL
+CxJsonValue *cxJsonArrRemove(CxJsonValue *value, size_t index);
 
 /**
  * Returns an iterator over the JSON array elements.
@@ -1285,8 +1278,8 @@ CX_EXPORT CxJsonValue *cxJsonArrRemove(CxJsonValue *value, size_t index);
  * @return an iterator over the array elements
  * @see cxJsonIsArray()
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT CxIterator cxJsonArrIter(const CxJsonValue *value);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+CxIterator cxJsonArrIter(const CxJsonValue *value);
 
 /**
  * Returns the size of a JSON object.
@@ -1297,8 +1290,8 @@ CX_EXPORT CxIterator cxJsonArrIter(const CxJsonValue *value);
  * @return the size of the object, i.e., the number of key/value pairs
  * @see cxJsonIsObject()
  */
-cx_attr_nonnull
-CX_INLINE size_t cxJsonObjSize(const CxJsonValue *value) {
+CX_NONNULL CX_INLINE
+size_t cxJsonObjSize(const CxJsonValue *value) {
     return cxCollectionSize(value->object);
 }
 
@@ -1314,8 +1307,8 @@ CX_INLINE size_t cxJsonObjSize(const CxJsonValue *value) {
  * @return an iterator over the object members
  * @see cxJsonIsObject()
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT CxMapIterator cxJsonObjIter(const CxJsonValue *value);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+CxMapIterator cxJsonObjIter(const CxJsonValue *value);
 
 /**
  * Internal function, do not use.
@@ -1323,8 +1316,8 @@ CX_EXPORT CxMapIterator cxJsonObjIter(const CxJsonValue *value);
  * @param name the key to look up
  * @return the value corresponding to the key
  */
-cx_attr_nonnull cx_attr_returns_nonnull
-CX_EXPORT CxJsonValue *cx_json_obj_get(const CxJsonValue *value, cxstring name);
+CX_EXTERN CX_NONNULL CX_RETURNS_NONNULL CX_NODISCARD
+CxJsonValue *cx_json_obj_get(const CxJsonValue *value, cxstring name);
 
 /**
  * Returns a value corresponding to a key in a JSON object.
@@ -1348,8 +1341,8 @@ CX_EXPORT CxJsonValue *cx_json_obj_get(const CxJsonValue *value, cxstring name);
  * @param name the key to look up
  * @return the value corresponding to the key or @c NULL when the key is not part of the object
  */
-cx_attr_nonnull
-CX_EXPORT CxJsonValue *cx_json_obj_remove(CxJsonValue *value, cxstring name);
+CX_EXTERN CX_NONNULL
+CxJsonValue *cx_json_obj_remove(CxJsonValue *value, cxstring name);
 
 /**
  * Removes and returns a value corresponding to a key in a JSON object.
@@ -1366,9 +1359,65 @@ CX_EXPORT CxJsonValue *cx_json_obj_remove(CxJsonValue *value, cxstring name);
  */
 #define cxJsonObjRemove(value, name) cx_json_obj_remove(value, cx_strcast(name))
 
-#ifdef __cplusplus
-}
-#endif
+/**
+ * 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_EXTERN CX_NODISCARD
+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_EXTERN CX_NODISCARD
+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_EXTERN CX_NODISCARD
+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)
 
 #endif /* UCX_JSON_H */
 
index c66e46cee2a59baca2dec4675c61abd95d628612..5e8962cf3f9ed6ad033af0b811cefdfdef9ee66b 100644 (file)
 #include "list.h"
 #include "map.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  * 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().
  *
  * After creating the list, it can also be used as a map after converting the pointer
  * to a CxMap pointer with cxKvListAsMap().
@@ -58,24 +54,21 @@ extern "C" {
  *
  * @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()
  * @see cxKvListAsList()
  */
-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);
+CX_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxListFree, 1)
+CxList *cxKvListCreate(const CxAllocator *allocator,
+        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
@@ -83,62 +76,14 @@ CX_EXPORT CxList *cxKvListCreate(const CxAllocator *allocator,
  *
  * @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()
  * @see cxKvListAsList()
  */
-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)
+CX_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxMapFree, 1)
+CxMap *cxKvListCreateAsMap(const CxAllocator *allocator,
+        size_t elem_size);
 
 /**
  * Converts a map pointer belonging to a key-value-List back to the original list pointer.
@@ -146,17 +91,17 @@ CX_EXPORT CxMap *cxKvListCreateAsMap(const CxAllocator *allocator,
  * @param map a map pointer that was returned by a call to cxKvListAsMap()
  * @return the original list pointer
  */
-cx_attr_nodiscard cx_attr_nonnull cx_attr_returns_nonnull
-CX_EXPORT CxList *cxKvListAsList(CxMap *map);
+CX_EXTERN CX_NODISCARD CX_NONNULL CX_RETURNS_NONNULL
+CxList *cxKvListAsList(CxMap *map);
 
 /**
  * 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
-CX_EXPORT CxMap *cxKvListAsMap(CxList *list);
+CX_EXTERN CX_NODISCARD CX_NONNULL CX_RETURNS_NONNULL
+CxMap *cxKvListAsMap(CxList *list);
 
 /**
  * Sets or updates the key of a list item.
@@ -171,8 +116,8 @@ CX_EXPORT CxMap *cxKvListAsMap(CxList *list);
  * @retval non-zero memory allocation failure or the index is out of bounds
  * @see cxKvListSetKey()
  */
-cx_attr_nonnull
-CX_EXPORT int cx_kv_list_set_key(CxList *list, size_t index, CxHashKey key);
+CX_EXTERN CX_NONNULL
+int cx_kv_list_set_key(CxList *list, size_t index, CxHashKey key);
 
 /**
  * Inserts an item into the list at the specified index and associates it with the specified key.
@@ -185,8 +130,8 @@ CX_EXPORT int cx_kv_list_set_key(CxList *list, size_t index, CxHashKey key);
  * @retval non-zero memory allocation failure or the index is out of bounds
  * @see cxKvListInsert()
  */
-cx_attr_nonnull
-CX_EXPORT int cx_kv_list_insert(CxList *list, size_t index, CxHashKey key, void *value);
+CX_EXTERN CX_NONNULL
+int cx_kv_list_insert(CxList *list, size_t index, CxHashKey key, void *value);
 
 /**
  * Sets or updates the key of a list item.
@@ -216,7 +161,6 @@ CX_EXPORT int cx_kv_list_insert(CxList *list, size_t index, CxHashKey key, void
  */
 #define cxKvListInsert(list, index, key, value) cx_kv_list_insert(list, index, CX_HASH_KEY(key), value)
 
-
 /**
  * Removes the key of a list item.
  *
@@ -229,8 +173,8 @@ CX_EXPORT int cx_kv_list_insert(CxList *list, size_t index, CxHashKey key, void
  * @retval zero success
  * @retval non-zero the index is out of bounds
  */
-cx_attr_nonnull
-CX_EXPORT int cxKvListRemoveKey(CxList *list, size_t index);
+CX_EXTERN CX_NONNULL
+int cxKvListRemoveKey(CxList *list, size_t index);
 
 /**
  * Returns the key of a list item.
@@ -239,8 +183,8 @@ CX_EXPORT int cxKvListRemoveKey(CxList *list, size_t index);
  * @param index the index of the element in the list
  * @return a pointer to the key or @c NULL when the index is out of bounds or the item does not have a key
  */
-cx_attr_nonnull
-CX_EXPORT const CxHashKey *cxKvListGetKey(CxList *list, size_t index);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+const CxHashKey *cxKvListGetKey(CxList *list, size_t index);
 
 /**
  * Adds an item into the list and associates it with the specified key.
@@ -253,8 +197,4 @@ CX_EXPORT const CxHashKey *cxKvListGetKey(CxList *list, size_t index);
  */
 #define cxKvListAdd(list, key, value) cxKvListInsert(list, (list)->collection.size, key, value)
 
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
 #endif // UCX_KV_LIST_H
index 0d290043115d311da1a2e963b02914e237ad6058..b8287253a7e7726764764758b90ea91e97a0513d 100644 (file)
 #include "common.h"
 #include "list.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  * Metadata for a linked list.
  */
@@ -87,36 +83,16 @@ typedef struct cx_linked_list_s {
  *
  * 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)
+CX_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxListFree, 1)
+CxList *cxLinkedListCreate(const CxAllocator *allocator,
+        size_t elem_size);
 
 /**
  * Instructs the linked list to reserve extra data in each node.
@@ -132,8 +108,8 @@ CX_EXPORT CxList *cxLinkedListCreate(const CxAllocator *allocator,
  * @param list the list (must be a linked list)
  * @param len the length of the extra data
  */
-cx_attr_nonnull
-CX_EXPORT void cx_linked_list_extra_data(cx_linked_list *list, size_t len);
+CX_EXTERN CX_NONNULL
+void cx_linked_list_extra_data(cx_linked_list *list, size_t len);
 
 /**
  * Finds the node at a certain index.
@@ -152,8 +128,8 @@ CX_EXPORT void cx_linked_list_extra_data(cx_linked_list *list, size_t len);
  * @param index the search index
  * @return the node found at the specified index
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT void *cx_linked_list_at(const void *start,size_t start_index,
+CX_EXTERN CX_NONNULL CX_NODISCARD
+void *cx_linked_list_at(const void *start,size_t start_index,
         ptrdiff_t loc_advance, size_t index);
 
 /**
@@ -162,16 +138,34 @@ CX_EXPORT void *cx_linked_list_at(const void *start,size_t start_index,
  * @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
+ * @return a pointer to the found node or @c NULL if no matching node was found
+ */
+CX_EXTERN CX_NONNULL_ARG(1, 4, 6)
+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);
+
+/**
+ * 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, 5)
-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);
+CX_EXTERN CX_NONNULL_ARG(1, 4, 6)
+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.
@@ -184,8 +178,8 @@ CX_EXPORT void *cx_linked_list_find(const void *start, ptrdiff_t loc_advance,
  * @param loc_prev the location of the @c prev pointer
  * @return a pointer to the first node
  */
-cx_attr_nonnull cx_attr_returns_nonnull
-CX_EXPORT void *cx_linked_list_first(const void *node, ptrdiff_t loc_prev);
+CX_EXTERN CX_NONNULL CX_RETURNS_NONNULL
+void *cx_linked_list_first(const void *node, ptrdiff_t loc_prev);
 
 /**
  * Finds the last node in a linked list.
@@ -198,8 +192,8 @@ CX_EXPORT void *cx_linked_list_first(const void *node, ptrdiff_t loc_prev);
  * @param loc_next the location of the @c next pointer
  * @return a pointer to the last node
  */
-cx_attr_nonnull cx_attr_returns_nonnull
-CX_EXPORT void *cx_linked_list_last(const void *node, ptrdiff_t loc_next);
+CX_EXTERN CX_NONNULL CX_RETURNS_NONNULL
+void *cx_linked_list_last(const void *node, ptrdiff_t loc_next);
 
 /**
  * Finds the predecessor of a node in case it is not linked.
@@ -211,8 +205,8 @@ CX_EXPORT void *cx_linked_list_last(const void *node, ptrdiff_t loc_next);
  * @param node the successor of the node to find
  * @return the node or @c NULL if @p node has no predecessor
  */
-cx_attr_nonnull
-CX_EXPORT void *cx_linked_list_prev(const void *begin, ptrdiff_t loc_next, const void *node);
+CX_EXTERN CX_NONNULL
+void *cx_linked_list_prev(const void *begin, ptrdiff_t loc_next, const void *node);
 
 /**
  * Adds a new node to a linked list.
@@ -226,8 +220,8 @@ CX_EXPORT void *cx_linked_list_prev(const void *begin, ptrdiff_t loc_next, const
  * @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 appended
  */
-cx_attr_nonnull_arg(5)
-CX_EXPORT void cx_linked_list_add(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node);
+CX_EXTERN CX_NONNULL_ARG(5)
+void cx_linked_list_add(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node);
 
 /**
  * Prepends a new node to a linked list.
@@ -241,8 +235,8 @@ CX_EXPORT void cx_linked_list_add(void **begin, void **end, ptrdiff_t loc_prev,
  * @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 prepended
  */
-cx_attr_nonnull_arg(5)
-CX_EXPORT void cx_linked_list_prepend(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node);
+CX_EXTERN CX_NONNULL_ARG(5)
+void cx_linked_list_prepend(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node);
 
 /**
  * Links two nodes.
@@ -252,8 +246,8 @@ CX_EXPORT void cx_linked_list_prepend(void **begin, void **end, ptrdiff_t loc_pr
  * @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)
  */
-cx_attr_nonnull
-CX_EXPORT void cx_linked_list_link(void *left, void *right, ptrdiff_t loc_prev, ptrdiff_t loc_next);
+CX_EXTERN CX_NONNULL
+void cx_linked_list_link(void *left, void *right, ptrdiff_t loc_prev, ptrdiff_t loc_next);
 
 /**
  * Unlinks two nodes.
@@ -265,8 +259,8 @@ CX_EXPORT void cx_linked_list_link(void *left, void *right, ptrdiff_t loc_prev,
  * @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)
  */
-cx_attr_nonnull
-CX_EXPORT void cx_linked_list_unlink(void *left, void *right, ptrdiff_t loc_prev, ptrdiff_t loc_next);
+CX_EXTERN CX_NONNULL
+void cx_linked_list_unlink(void *left, void *right, ptrdiff_t loc_prev, ptrdiff_t loc_next);
 
 /**
  * Inserts a new node after a given node of a linked list.
@@ -282,8 +276,8 @@ CX_EXPORT void cx_linked_list_unlink(void *left, void *right, ptrdiff_t loc_prev
  * @param node the node after which to insert (@c NULL if you want to prepend the node to the list)
  * @param new_node a pointer to the node that shall be inserted
  */
-cx_attr_nonnull_arg(6)
-CX_EXPORT void cx_linked_list_insert(void **begin, void **end,
+CX_EXTERN CX_NONNULL_ARG(6)
+void cx_linked_list_insert(void **begin, void **end,
         ptrdiff_t loc_prev, ptrdiff_t loc_next, void *node, void *new_node);
 
 /**
@@ -306,8 +300,8 @@ CX_EXPORT void cx_linked_list_insert(void **begin, void **end,
  * @param insert_begin a pointer to the first node of the chain that shall be inserted
  * @param insert_end a pointer to the last node of the chain (or NULL if the last node shall be determined)
  */
-cx_attr_nonnull_arg(6)
-CX_EXPORT void cx_linked_list_insert_chain(void **begin, void **end,
+CX_EXTERN CX_NONNULL_ARG(6)
+void cx_linked_list_insert_chain(void **begin, void **end,
         ptrdiff_t loc_prev, ptrdiff_t loc_next, void *node, void *insert_begin, void *insert_end);
 
 /**
@@ -324,8 +318,8 @@ CX_EXPORT void cx_linked_list_insert_chain(void **begin, void **end,
  * @param new_node a pointer to the node that shall be inserted
  * @param cmp_func a compare function that will receive the node pointers
  */
-cx_attr_nonnull_arg(1, 5, 6)
-CX_EXPORT void cx_linked_list_insert_sorted(void **begin, void **end,
+CX_EXTERN CX_NONNULL_ARG(1, 5, 6)
+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);
 
 /**
@@ -347,8 +341,8 @@ CX_EXPORT void cx_linked_list_insert_sorted(void **begin, void **end,
  * @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
  */
-cx_attr_nonnull_arg(1, 5, 6)
-CX_EXPORT void cx_linked_list_insert_sorted_chain(void **begin, void **end,
+CX_EXTERN CX_NONNULL_ARG(1, 5, 6)
+void cx_linked_list_insert_sorted_chain(void **begin, void **end,
         ptrdiff_t loc_prev, ptrdiff_t loc_next, void *insert_begin, cx_compare_func cmp_func);
 
 /**
@@ -367,8 +361,8 @@ CX_EXPORT void cx_linked_list_insert_sorted_chain(void **begin, void **end,
  * @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(void **begin, void **end,
+CX_EXTERN CX_NONNULL_ARG(1, 5, 6)
+int cx_linked_list_insert_unique(void **begin, void **end,
         ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node, cx_compare_func cmp_func);
 
 /**
@@ -389,10 +383,97 @@ CX_EXPORT int cx_linked_list_insert_unique(void **begin, void **end,
  * @param cmp_func a compare function that will receive the node pointers
  * @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(void **begin, void **end,
+CX_EXTERN CX_NONNULL_ARG(1, 5, 6) CX_NODISCARD
+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_EXTERN CX_NONNULL_ARG(1, 5, 6)
+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_EXTERN CX_NONNULL_ARG(1, 5, 6)
+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
+ * @param context the context for the compare function
+ * @retval zero when the node was inserted
+ * @retval non-zero when a node with the same value already exists
+ */
+CX_EXTERN CX_NONNULL_ARG(1, 5, 6)
+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_EXTERN CX_NONNULL_ARG(1, 5, 6) CX_NODISCARD
+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.
  *
@@ -414,8 +495,8 @@ CX_EXPORT void *cx_linked_list_insert_unique_chain(void **begin, void **end,
  * @param num the number of nodes to remove
  * @return the actual number of nodes that were removed (can be less when the list did not have enough nodes)
  */
-cx_attr_nonnull_arg(5)
-CX_EXPORT size_t cx_linked_list_remove_chain(void **begin, void **end,
+CX_EXTERN CX_NONNULL_ARG(5)
+size_t cx_linked_list_remove_chain(void **begin, void **end,
         ptrdiff_t loc_prev, ptrdiff_t loc_next, void *node, size_t num);
 
 /**
@@ -437,8 +518,8 @@ CX_EXPORT size_t cx_linked_list_remove_chain(void **begin, void **end,
  * @param loc_next the location of a @c next pointer within your node struct (required)
  * @param node the node to remove
  */
-cx_attr_nonnull_arg(5)
-CX_EXPORT void cx_linked_list_remove(void **begin, void **end,
+CX_EXTERN CX_NONNULL_ARG(5)
+void cx_linked_list_remove(void **begin, void **end,
         ptrdiff_t loc_prev, ptrdiff_t loc_next, void *node);
 
 /**
@@ -448,22 +529,12 @@ CX_EXPORT void cx_linked_list_remove(void **begin, void **end,
  * @param loc_next the location of the @c next pointer within the node struct
  * @return the size of the list or zero if @p node is @c NULL
  */
-cx_attr_nodiscard
-CX_EXPORT size_t cx_linked_list_size(const void *node, ptrdiff_t loc_next);
+CX_EXTERN CX_NODISCARD
+size_t cx_linked_list_size(const void *node, ptrdiff_t loc_next);
 
 /**
  * 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)
@@ -473,10 +544,26 @@ CX_EXPORT size_t cx_linked_list_size(const void *node, ptrdiff_t loc_next);
  * @param loc_data the location of the @c data pointer within your node struct
  * @param cmp_func the compare function defining the sort order
  */
-cx_attr_nonnull_arg(1, 6)
-CX_EXPORT void cx_linked_list_sort(void **begin, void **end,
+CX_EXTERN CX_NONNULL_ARG(1, 6)
+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_EXTERN CX_NONNULL_ARG(1, 6)
+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.
@@ -491,10 +578,28 @@ CX_EXPORT void cx_linked_list_sort(void **begin, void **end,
  * @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(const void *begin_left, const void *begin_right,
+CX_EXTERN CX_NONNULL_ARG(5)
+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
+ * @param context the context for the compare function
+ * @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_EXTERN CX_NONNULL_ARG(5)
+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.
  *
@@ -503,11 +608,7 @@ CX_EXPORT int cx_linked_list_compare(const void *begin_left, const void *begin_r
  * @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)
  */
-cx_attr_nonnull_arg(1)
-CX_EXPORT void cx_linked_list_reverse(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next);
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
+CX_EXTERN CX_NONNULL_ARG(1)
+void cx_linked_list_reverse(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next);
 
 #endif // UCX_LINKED_LIST_H
index 22681d62acf934e9d6443164adf56afd0780a905..7ea098376f90c67bc95cb6bb1aa07d6e1ea252de 100644 (file)
 #include "common.h"
 #include "collection.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  * List class type.
  */
@@ -60,10 +56,6 @@ struct cx_list_s {
      * The list class definition.
      */
     const cx_list_class *cl;
-    /**
-     * The actual implementation in case the list class is delegating.
-     */
-    const cx_list_class *climpl;
 };
 
 /**
@@ -210,8 +202,8 @@ CX_EXPORT extern CxList *const cxEmptyList;
  * @param n the number of elements to insert
  * @return the number of elements actually inserted
  */
-cx_attr_nonnull
-CX_EXPORT size_t cx_list_default_insert_array(struct cx_list_s *list,
+CX_EXTERN CX_NONNULL_ARG(1)
+size_t cx_list_default_insert_array(struct cx_list_s *list,
         size_t index, const void *data, size_t n);
 
 /**
@@ -230,8 +222,8 @@ CX_EXPORT size_t cx_list_default_insert_array(struct cx_list_s *list,
  * @param n the number of elements to insert
  * @return the number of elements actually inserted
  */
-cx_attr_nonnull
-CX_EXPORT size_t cx_list_default_insert_sorted(struct cx_list_s *list,
+CX_EXTERN CX_NONNULL
+size_t cx_list_default_insert_sorted(struct cx_list_s *list,
         const void *sorted_data, size_t n);
 
 /**
@@ -250,8 +242,8 @@ CX_EXPORT size_t cx_list_default_insert_sorted(struct cx_list_s *list,
  * @param n the number of elements to insert
  * @return the number of elements from the @p sorted_data that are definitely present in the list after this call
  */
-cx_attr_nonnull
-CX_EXPORT size_t cx_list_default_insert_unique(struct cx_list_s *list,
+CX_EXTERN CX_NONNULL
+size_t cx_list_default_insert_unique(struct cx_list_s *list,
         const void *sorted_data, size_t n);
 
 /**
@@ -265,8 +257,8 @@ CX_EXPORT size_t cx_list_default_insert_unique(struct cx_list_s *list,
  *
  * @param list the list that shall be sorted
  */
-cx_attr_nonnull
-CX_EXPORT void cx_list_default_sort(struct cx_list_s *list);
+CX_EXTERN CX_NONNULL
+void cx_list_default_sort(struct cx_list_s *list);
 
 /**
  * Default unoptimized swap implementation.
@@ -281,8 +273,8 @@ CX_EXPORT void cx_list_default_sort(struct cx_list_s *list);
  * @retval non-zero when indices are out of bounds or memory
  * allocation for the temporary buffer fails
  */
-cx_attr_nonnull
-CX_EXPORT int cx_list_default_swap(struct cx_list_s *list, size_t i, size_t j);
+CX_EXTERN CX_NONNULL
+int cx_list_default_swap(struct cx_list_s *list, size_t i, size_t j);
 
 /**
  * Initializes a list struct.
@@ -291,30 +283,26 @@ CX_EXPORT int cx_list_default_swap(struct cx_list_s *list, size_t i, size_t j);
  * 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 ...
  *
@@ -325,13 +313,24 @@ CX_EXPORT int cx_list_default_swap(struct cx_list_s *list, size_t i, size_t j);
  * @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,
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3)
+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_EXTERN CX_NONNULL
+int cx_list_compare_wrapper(
+    const void *left, const void *right, void *list);
 
 /**
  * Returns the number of elements currently stored in the list.
@@ -339,8 +338,8 @@ CX_EXPORT void cx_list_init(struct cx_list_s *list,
  * @param list the list
  * @return the number of currently stored elements
  */
-cx_attr_nonnull
-CX_EXPORT size_t cxListSize(const CxList *list);
+CX_EXTERN CX_NONNULL
+size_t cxListSize(const CxList *list);
 
 /**
  * Adds an item to the end of the list.
@@ -352,8 +351,8 @@ CX_EXPORT size_t cxListSize(const CxList *list);
  * @see cxListAddArray()
  * @see cxListEmplace()
  */
-cx_attr_nonnull
-CX_EXPORT int cxListAdd(CxList *list, const void *elem);
+CX_EXTERN CX_NONNULL
+int cxListAdd(CxList *list, const void *elem);
 
 /**
  * Adds multiple items to the end of the list.
@@ -372,8 +371,8 @@ CX_EXPORT int cxListAdd(CxList *list, const void *elem);
  * @return the number of added elements
  * @see cxListEmplaceArray()
  */
-cx_attr_nonnull
-CX_EXPORT size_t cxListAddArray(CxList *list, const void *array, size_t n);
+CX_EXTERN CX_NONNULL
+size_t cxListAddArray(CxList *list, const void *array, size_t n);
 
 /**
  * Inserts an item at the specified index.
@@ -389,8 +388,8 @@ CX_EXPORT size_t cxListAddArray(CxList *list, const void *array, size_t n);
  * @see cxListInsertBefore()
  * @see cxListEmplaceAt()
  */
-cx_attr_nonnull
-CX_EXPORT int cxListInsert(CxList *list, size_t index, const void *elem);
+CX_EXTERN CX_NONNULL
+int cxListInsert(CxList *list, size_t index, const void *elem);
 
 /**
  * Allocates memory for an element at the specified index and returns a pointer to that memory.
@@ -404,8 +403,8 @@ CX_EXPORT int cxListInsert(CxList *list, size_t index, const void *elem);
  * @see cxListEmplaceArrayAt()
  * @see cxListInsert()
  */
-cx_attr_nonnull
-CX_EXPORT void *cxListEmplaceAt(CxList *list, size_t index);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+void *cxListEmplaceAt(CxList *list, size_t index);
 
 /**
  * Allocates memory for an element at the end of the list and returns a pointer to that memory.
@@ -417,8 +416,8 @@ CX_EXPORT void *cxListEmplaceAt(CxList *list, size_t index);
  * @see cxListEmplaceAt()
  * @see cxListAdd()
  */
-cx_attr_nonnull
-CX_EXPORT void *cxListEmplace(CxList *list);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+void *cxListEmplace(CxList *list);
 
 /**
  * Allocates memory for multiple elements and returns an iterator.
@@ -437,8 +436,8 @@ CX_EXPORT void *cxListEmplace(CxList *list);
  * @see cxListEmplaceAt()
  * @see cxListInsertArray()
  */
-cx_attr_nonnull
-CX_EXPORT CxIterator cxListEmplaceArrayAt(CxList *list, size_t index, size_t n);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+CxIterator cxListEmplaceArrayAt(CxList *list, size_t index, size_t n);
 
 /**
  * Allocates memory for multiple elements and returns an iterator.
@@ -456,8 +455,8 @@ CX_EXPORT CxIterator cxListEmplaceArrayAt(CxList *list, size_t index, size_t n);
  * @see cxListEmplace()
  * @see cxListAddArray()
  */
-cx_attr_nonnull
-CX_EXPORT CxIterator cxListEmplaceArray(CxList *list, size_t n);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+CxIterator cxListEmplaceArray(CxList *list, size_t n);
 
 /**
  * Inserts an item into a sorted list.
@@ -469,8 +468,8 @@ CX_EXPORT CxIterator cxListEmplaceArray(CxList *list, size_t n);
  * @retval zero success
  * @retval non-zero memory allocation failure
  */
-cx_attr_nonnull
-CX_EXPORT int cxListInsertSorted(CxList *list, const void *elem);
+CX_EXTERN CX_NONNULL
+int cxListInsertSorted(CxList *list, const void *elem);
 
 /**
  * Inserts an item into a list if it does not exist.
@@ -485,8 +484,8 @@ CX_EXPORT int cxListInsertSorted(CxList *list, const void *elem);
  * @retval zero success (also when the element was already in the list)
  * @retval non-zero memory allocation failure
  */
-cx_attr_nonnull
-CX_EXPORT int cxListInsertUnique(CxList *list, const void *elem);
+CX_EXTERN CX_NONNULL
+int cxListInsertUnique(CxList *list, const void *elem);
 
 /**
  * Inserts multiple items to the list at the specified index.
@@ -508,8 +507,8 @@ CX_EXPORT int cxListInsertUnique(CxList *list, const void *elem);
  * @return the number of added elements
  * @see cxListEmplaceArrayAt()
  */
-cx_attr_nonnull
-CX_EXPORT size_t cxListInsertArray(CxList *list, size_t index, const void *array, size_t n);
+CX_EXTERN CX_NONNULL
+size_t cxListInsertArray(CxList *list, size_t index, const void *array, size_t n);
 
 /**
  * Inserts a sorted array into a sorted list.
@@ -530,8 +529,8 @@ CX_EXPORT size_t cxListInsertArray(CxList *list, size_t index, const void *array
  * @param n the number of elements to add
  * @return the number of added elements
  */
-cx_attr_nonnull
-CX_EXPORT size_t cxListInsertSortedArray(CxList *list, const void *array, size_t n);
+CX_EXTERN CX_NONNULL
+size_t cxListInsertSortedArray(CxList *list, const void *array, size_t n);
 
 /**
  * Inserts an array into a list, skipping duplicates.
@@ -565,8 +564,8 @@ CX_EXPORT size_t cxListInsertSortedArray(CxList *list, const void *array, size_t
  *
  * @return the number of elements from the @p sorted_data that are definitely present in the list after this call
  */
-cx_attr_nonnull
-CX_EXPORT size_t cxListInsertUniqueArray(CxList *list, const void *array, size_t n);
+CX_EXTERN CX_NONNULL
+size_t cxListInsertUniqueArray(CxList *list, const void *array, size_t n);
 
 /**
  * Inserts an element after the current location of the specified iterator.
@@ -584,8 +583,8 @@ CX_EXPORT size_t cxListInsertUniqueArray(CxList *list, const void *array, size_t
  * @see cxListInsert()
  * @see cxListInsertBefore()
  */
-cx_attr_nonnull
-CX_EXPORT int cxListInsertAfter(CxIterator *iter, const void *elem);
+CX_EXTERN CX_NONNULL
+int cxListInsertAfter(CxIterator *iter, const void *elem);
 
 /**
  * Inserts an element before the current location of the specified iterator.
@@ -603,8 +602,8 @@ CX_EXPORT int cxListInsertAfter(CxIterator *iter, const void *elem);
  * @see cxListInsert()
  * @see cxListInsertAfter()
  */
-cx_attr_nonnull
-CX_EXPORT int cxListInsertBefore(CxIterator *iter, const void *elem);
+CX_EXTERN CX_NONNULL
+int cxListInsertBefore(CxIterator *iter, const void *elem);
 
 /**
  * Removes the element at the specified index.
@@ -617,8 +616,8 @@ CX_EXPORT int cxListInsertBefore(CxIterator *iter, const void *elem);
  * @retval zero success
  * @retval non-zero index out of bounds
  */
-cx_attr_nonnull
-CX_EXPORT int cxListRemove(CxList *list, size_t index);
+CX_EXTERN CX_NONNULL
+int cxListRemove(CxList *list, size_t index);
 
 /**
  * Removes and returns the element at the specified index.
@@ -633,8 +632,8 @@ CX_EXPORT int cxListRemove(CxList *list, size_t index);
  * @retval zero success
  * @retval non-zero index out of bounds
  */
-cx_attr_nonnull cx_attr_access_w(3)
-CX_EXPORT int cxListRemoveAndGet(CxList *list, size_t index, void *targetbuf);
+CX_EXTERN CX_NONNULL CX_ACCESS_W(3)
+int cxListRemoveAndGet(CxList *list, size_t index, void *targetbuf);
 
 /**
  * Removes and returns the first element of the list.
@@ -650,8 +649,8 @@ CX_EXPORT int cxListRemoveAndGet(CxList *list, size_t index, void *targetbuf);
  * @see cxListPopFront()
  * @see cxListRemoveAndGetLast()
  */
-cx_attr_nonnull cx_attr_access_w(2)
-CX_EXPORT int cxListRemoveAndGetFirst(CxList *list, void *targetbuf);
+CX_EXTERN CX_NONNULL CX_ACCESS_W(2)
+int cxListRemoveAndGetFirst(CxList *list, void *targetbuf);
 
 /**
  * Removes and returns the first element of the list.
@@ -684,8 +683,8 @@ CX_EXPORT int cxListRemoveAndGetFirst(CxList *list, void *targetbuf);
  * @retval zero success
  * @retval non-zero the list is empty
  */
-cx_attr_nonnull cx_attr_access_w(2)
-CX_EXPORT int cxListRemoveAndGetLast(CxList *list, void *targetbuf);
+CX_EXTERN CX_NONNULL CX_ACCESS_W(2)
+int cxListRemoveAndGetLast(CxList *list, void *targetbuf);
 
 /**
  * Removes and returns the last element of the list.
@@ -720,8 +719,8 @@ CX_EXPORT int cxListRemoveAndGetLast(CxList *list, void *targetbuf);
  * @param num the number of elements to remove
  * @return the actual number of removed elements
  */
-cx_attr_nonnull
-CX_EXPORT size_t cxListRemoveArray(CxList *list, size_t index, size_t num);
+CX_EXTERN CX_NONNULL
+size_t cxListRemoveArray(CxList *list, size_t index, size_t num);
 
 /**
  * Removes and returns multiple elements starting at the specified index.
@@ -736,8 +735,8 @@ CX_EXPORT size_t cxListRemoveArray(CxList *list, size_t index, size_t num);
  * @param targetbuf a buffer where to copy the elements
  * @return the actual number of removed elements
  */
-cx_attr_nonnull cx_attr_access_w(4)
-CX_EXPORT size_t cxListRemoveArrayAndGet(CxList *list, size_t index, size_t num, void *targetbuf);
+CX_EXTERN CX_NONNULL CX_ACCESS_W(4)
+size_t cxListRemoveArrayAndGet(CxList *list, size_t index, size_t num, void *targetbuf);
 
 /**
  * Removes all elements from this list.
@@ -747,8 +746,8 @@ CX_EXPORT size_t cxListRemoveArrayAndGet(CxList *list, size_t index, size_t num,
  *
  * @param list the list
  */
-cx_attr_nonnull
-CX_EXPORT void cxListClear(CxList *list);
+CX_EXTERN CX_NONNULL
+void cxListClear(CxList *list);
 
 /**
  * Swaps two items in the list.
@@ -763,8 +762,8 @@ CX_EXPORT void cxListClear(CxList *list);
  * @retval non-zero one of the indices is out of bounds,
  * or the swap needed extra memory, but allocation failed
  */
-cx_attr_nonnull
-CX_EXPORT int cxListSwap(CxList *list, size_t i, size_t j);
+CX_EXTERN CX_NONNULL
+int cxListSwap(CxList *list, size_t i, size_t j);
 
 /**
  * Returns a pointer to the element at the specified index.
@@ -775,8 +774,8 @@ CX_EXPORT int cxListSwap(CxList *list, size_t i, size_t j);
  * @param index the index of the element
  * @return a pointer to the element or @c NULL if the index is out of bounds
  */
-cx_attr_nonnull
-CX_EXPORT void *cxListAt(const CxList *list, size_t index);
+CX_EXTERN CX_NONNULL
+void *cxListAt(const CxList *list, size_t index);
 
 /**
  * Returns a pointer to the first element.
@@ -786,8 +785,8 @@ CX_EXPORT void *cxListAt(const CxList *list, size_t index);
  * @param list the list
  * @return a pointer to the first element or @c NULL if the list is empty
  */
-cx_attr_nonnull
-CX_EXPORT void *cxListFirst(const CxList *list);
+CX_EXTERN CX_NONNULL
+void *cxListFirst(const CxList *list);
 
 /**
  * Returns a pointer to the last element.
@@ -797,8 +796,8 @@ CX_EXPORT void *cxListFirst(const CxList *list);
  * @param list the list
  * @return a pointer to the last element or @c NULL if the list is empty
  */
-cx_attr_nonnull
-CX_EXPORT void *cxListLast(const CxList *list);
+CX_EXTERN CX_NONNULL
+void *cxListLast(const CxList *list);
 
 /**
  * Sets the element at the specified index in the list.
@@ -812,8 +811,8 @@ CX_EXPORT void *cxListLast(const CxList *list);
  * @retval zero on success
  * @retval non-zero when index is out of bounds
  */
-cx_attr_nonnull
-CX_EXPORT int cxListSet(CxList *list, size_t index, const void *elem);
+CX_EXTERN CX_NONNULL
+int cxListSet(CxList *list, size_t index, const void *elem);
 
 /**
  * Returns an iterator pointing to the item at the specified index.
@@ -826,8 +825,8 @@ CX_EXPORT int cxListSet(CxList *list, size_t index, const void *elem);
  * @param index the index where the iterator shall point at
  * @return a new iterator
  */
-cx_attr_nodiscard
-CX_EXPORT CxIterator cxListIteratorAt(const CxList *list, size_t index);
+CX_EXTERN CX_NODISCARD
+CxIterator cxListIteratorAt(const CxList *list, size_t index);
 
 /**
  * Returns a backwards iterator pointing to the item at the specified index.
@@ -840,8 +839,8 @@ CX_EXPORT CxIterator cxListIteratorAt(const CxList *list, size_t index);
  * @param index the index where the iterator shall point at
  * @return a new iterator
  */
-cx_attr_nodiscard
-CX_EXPORT CxIterator cxListBackwardsIteratorAt(const CxList *list, size_t index);
+CX_EXTERN CX_NODISCARD
+CxIterator cxListBackwardsIteratorAt(const CxList *list, size_t index);
 
 /**
  * Returns an iterator pointing to the first item of the list.
@@ -853,8 +852,8 @@ CX_EXPORT CxIterator cxListBackwardsIteratorAt(const CxList *list, size_t index)
  * @param list the list
  * @return a new iterator
  */
-cx_attr_nodiscard
-CX_EXPORT CxIterator cxListIterator(const CxList *list);
+CX_EXTERN CX_NODISCARD
+CxIterator cxListIterator(const CxList *list);
 
 /**
  * Returns a backwards iterator pointing to the last item of the list.
@@ -866,8 +865,8 @@ CX_EXPORT CxIterator cxListIterator(const CxList *list);
  * @param list the list
  * @return a new iterator
  */
-cx_attr_nodiscard
-CX_EXPORT CxIterator cxListBackwardsIterator(const CxList *list);
+CX_EXTERN CX_NODISCARD
+CxIterator cxListBackwardsIterator(const CxList *list);
 
 /**
  * Returns the index of the first element that equals @p elem.
@@ -880,8 +879,8 @@ CX_EXPORT CxIterator cxListBackwardsIterator(const CxList *list);
  * @see cxListIndexValid()
  * @see cxListContains()
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT size_t cxListFind(const CxList *list, const void *elem);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+size_t cxListFind(const CxList *list, const void *elem);
 
 /**
  * Checks if the list contains the specified element.
@@ -894,8 +893,8 @@ CX_EXPORT size_t cxListFind(const CxList *list, const void *elem);
  * @retval false if the element is not contained
  * @see cxListFind()
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT bool cxListContains(const CxList* list, const void* elem);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+bool cxListContains(const CxList* list, const void* elem);
 
 /**
  * Checks if the specified index is within bounds.
@@ -905,8 +904,8 @@ CX_EXPORT bool cxListContains(const CxList* list, const void* elem);
  * @retval true if the index is within bounds
  * @retval false if the index is out of bounds
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT bool cxListIndexValid(const CxList *list, size_t index);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+bool cxListIndexValid(const CxList *list, size_t index);
 
 /**
  * Removes and returns the index of the first element that equals @p elem.
@@ -919,8 +918,8 @@ CX_EXPORT bool cxListIndexValid(const CxList *list, size_t index);
  * when the element is not found or could not be removed
  * @see cxListIndexValid()
  */
-cx_attr_nonnull
-CX_EXPORT size_t cxListFindRemove(CxList *list, const void *elem);
+CX_EXTERN CX_NONNULL
+size_t cxListFindRemove(CxList *list, const void *elem);
 
 /**
  * Sorts the list.
@@ -929,16 +928,16 @@ CX_EXPORT size_t cxListFindRemove(CxList *list, const void *elem);
  *
  * @param list the list
  */
-cx_attr_nonnull
-CX_EXPORT void cxListSort(CxList *list);
+CX_EXTERN CX_NONNULL
+void cxListSort(CxList *list);
 
 /**
  * Reverses the order of the items.
  *
  * @param list the list
  */
-cx_attr_nonnull
-CX_EXPORT void cxListReverse(CxList *list);
+CX_EXTERN CX_NONNULL
+void cxListReverse(CxList *list);
 
 /**
  * Compares a list to another list of the same type.
@@ -954,8 +953,8 @@ CX_EXPORT void cxListReverse(CxList *list);
  * @retval positive the first list is larger
  * or the first non-equal element in the first list is larger
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cxListCompare(const CxList *list, const CxList *other);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cxListCompare(const CxList *list, const CxList *other);
 
 /**
  * Deallocates the memory of the specified list structure.
@@ -964,7 +963,8 @@ CX_EXPORT int cxListCompare(const CxList *list, const CxList *other);
  *
  * @param list the list that shall be freed
  */
-CX_EXPORT void cxListFree(CxList *list);
+CX_EXTERN
+void cxListFree(CxList *list);
 
 
 /**
@@ -984,10 +984,10 @@ CX_EXPORT void cxListFree(CxList *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,
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3)
+int cxListClone(CxList *dst, const CxList *src,
         cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
 
 /**
@@ -1007,10 +1007,10 @@ 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,
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3, 4)
+int cxListDifference(CxList *dst,
         const CxList *minuend, const CxList *subtrahend,
         cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
 
@@ -1031,10 +1031,10 @@ 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,
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3, 4)
+int cxListIntersection(CxList *dst, const CxList *src, const CxList *other,
         cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
 
 /**
@@ -1056,10 +1056,10 @@ CX_EXPORT int cxListIntersection(CxList *dst, const CxList *src, const CxList *o
  * @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,
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3, 4)
+int cxListUnion(CxList *dst, const CxList *src, const CxList *other,
         cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
 
 /**
@@ -1081,8 +1081,8 @@ CX_EXPORT int cxListUnion(CxList *dst, const CxList *src, const CxList *other,
  * @retval non-zero when an allocation error occurred
  * @see cxListClone()
  */
-cx_attr_nonnull
-CX_EXPORT int cxListCloneSimple(CxList *dst, const CxList *src);
+CX_EXTERN CX_NONNULL
+int cxListCloneShallow(CxList *dst, const CxList *src);
 
 /**
  * Clones elements from a list only if they are not present in another list.
@@ -1103,8 +1103,8 @@ CX_EXPORT int cxListCloneSimple(CxList *dst, const CxList *src);
  * @retval non-zero when an allocation error occurred
  * @see cxListDifference()
  */
-cx_attr_nonnull
-CX_EXPORT int cxListDifferenceSimple(CxList *dst,
+CX_EXTERN CX_NONNULL
+int cxListDifferenceShallow(CxList *dst,
         const CxList *minuend, const CxList *subtrahend);
 
 /**
@@ -1126,8 +1126,8 @@ CX_EXPORT int cxListDifferenceSimple(CxList *dst,
  * @retval non-zero when an allocation error occurred
  * @see cxListIntersection()
  */
-cx_attr_nonnull
-CX_EXPORT int cxListIntersectionSimple(CxList *dst, const CxList *src, const CxList *other);
+CX_EXTERN CX_NONNULL
+int cxListIntersectionShallow(CxList *dst, const CxList *src, const CxList *other);
 
 /**
  * Performs a deep clone of one list into another, skipping duplicates.
@@ -1150,8 +1150,8 @@ CX_EXPORT int cxListIntersectionSimple(CxList *dst, const CxList *src, const CxL
  * @retval non-zero when an allocation error occurred
  * @see cxListUnion()
  */
-cx_attr_nonnull
-CX_EXPORT int cxListUnionSimple(CxList *dst, const CxList *src, const CxList *other);
+CX_EXTERN CX_NONNULL
+int cxListUnionShallow(CxList *dst, const CxList *src, const CxList *other);
 
 /**
  * Asks the list to reserve enough memory for a given total number of elements.
@@ -1170,8 +1170,8 @@ CX_EXPORT int cxListUnionSimple(CxList *dst, const CxList *src, const CxList *ot
  * @retval non-zero when an allocation error occurred
  * @see cxListShrink()
  */
-cx_attr_nonnull
-CX_EXPORT int cxListReserve(CxList *list, size_t capacity);
+CX_EXTERN CX_NONNULL
+int cxListReserve(CxList *list, size_t capacity);
 
 /**
  * Advises the list to free any overallocated memory.
@@ -1184,11 +1184,7 @@ CX_EXPORT int cxListReserve(CxList *list, size_t capacity);
  * @param list the list
  * @return usually zero
  */
-cx_attr_nonnull
-CX_EXPORT int cxListShrink(CxList *list);
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
+CX_EXTERN CX_NONNULL
+int cxListShrink(CxList *list);
 
 #endif // UCX_LIST_H
index 871bc41716b51e4557958ea92546df3fc97c2548..7492b4ea485d5a291a9761e82d59b7d0560b5702 100644 (file)
 typedef struct cx_list_s CxList;
 #endif
 
-#ifdef    __cplusplus
-extern "C" {
-#endif
-
 /** Type for the UCX map. */
 typedef struct cx_map_s CxMap;
 
@@ -183,9 +179,9 @@ struct cx_map_class_s {
      * 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.
@@ -227,8 +223,8 @@ CX_EXPORT extern CxMap *const cxEmptyMap;
  *
  * @param map the map to be freed
  */
-CX_EXPORT void cxMapFree(CxMap *map);
-
+CX_EXTERN
+void cxMapFree(CxMap *map);
 
 /**
  * Clears a map by removing all elements.
@@ -237,8 +233,8 @@ CX_EXPORT void cxMapFree(CxMap *map);
  *
  * @param map the map to be cleared
  */
-cx_attr_nonnull
-CX_EXPORT void cxMapClear(CxMap *map);
+CX_EXTERN CX_NONNULL
+void cxMapClear(CxMap *map);
 
 /**
  * Returns the number of elements in this map.
@@ -246,8 +242,8 @@ CX_EXPORT void cxMapClear(CxMap *map);
  * @param map the map
  * @return the number of stored elements
  */
-cx_attr_nonnull
-CX_EXPORT size_t cxMapSize(const CxMap *map);
+CX_EXTERN CX_NONNULL
+size_t cxMapSize(const CxMap *map);
 
 /**
  * Creates a value iterator for a map.
@@ -262,8 +258,8 @@ CX_EXPORT size_t cxMapSize(const CxMap *map);
  * @param map the map to create the iterator for (can be @c NULL)
  * @return an iterator for the currently stored values
  */
-cx_attr_nodiscard
-CX_EXPORT CxMapIterator cxMapIteratorValues(const CxMap *map);
+CX_EXTERN CX_NODISCARD
+CxMapIterator cxMapIteratorValues(const CxMap *map);
 
 /**
  * Creates a key iterator for a map.
@@ -277,8 +273,8 @@ CX_EXPORT CxMapIterator cxMapIteratorValues(const CxMap *map);
  * @param map the map to create the iterator for (can be @c NULL)
  * @return an iterator for the currently stored keys
  */
-cx_attr_nodiscard
-CX_EXPORT CxMapIterator cxMapIteratorKeys(const CxMap *map);
+CX_EXTERN CX_NODISCARD
+CxMapIterator cxMapIteratorKeys(const CxMap *map);
 
 /**
  * Creates an iterator for a map.
@@ -294,8 +290,8 @@ CX_EXPORT CxMapIterator cxMapIteratorKeys(const CxMap *map);
  * @see cxMapIteratorKeys()
  * @see cxMapIteratorValues()
  */
-cx_attr_nodiscard
-CX_EXPORT CxMapIterator cxMapIterator(const CxMap *map);
+CX_EXTERN CX_NODISCARD
+CxMapIterator cxMapIterator(const CxMap *map);
 
 /**
  * Puts a key/value-pair into the map.
@@ -317,8 +313,8 @@ CX_EXPORT CxMapIterator cxMapIterator(const CxMap *map);
  * @retval non-zero value on memory allocation failure
  * @see cxMapPut()
  */
-cx_attr_nonnull
-CX_EXPORT int cx_map_put(CxMap *map, CxHashKey key, void *value);
+CX_EXTERN CX_NONNULL
+int cx_map_put(CxMap *map, CxHashKey key, void *value);
 
 /**
  * Puts a key/value-pair into the map.
@@ -357,12 +353,10 @@ CX_EXPORT int cx_map_put(CxMap *map, CxHashKey key, void *value);
  * @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
-CX_EXPORT void *cx_map_emplace(CxMap *map, CxHashKey key);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+void *cx_map_emplace(CxMap *map, CxHashKey key);
 
 /**
  * Allocates memory for a value in the map associated with the specified key.
@@ -379,8 +373,6 @@ CX_EXPORT void *cx_map_emplace(CxMap *map, CxHashKey key);
  * @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))
@@ -397,8 +389,8 @@ CX_EXPORT void *cx_map_emplace(CxMap *map, CxHashKey key);
  * @return the value
  * @see cxMapGet()
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT void *cx_map_get(const CxMap *map, CxHashKey key);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+void *cx_map_get(const CxMap *map, CxHashKey key);
 
 /**
  * Retrieves a value by using a key.
@@ -440,8 +432,8 @@ CX_EXPORT void *cx_map_get(const CxMap *map, CxHashKey key);
  * @see cxMapRemove()
  * @see cxMapRemoveAndGet()
  */
-cx_attr_nonnull_arg(1)
-CX_EXPORT int cx_map_remove(CxMap *map, CxHashKey key, void *targetbuf);
+CX_EXTERN CX_NONNULL_ARG(1)
+int cx_map_remove(CxMap *map, CxHashKey key, void *targetbuf);
 
 /**
  * Removes a key/value-pair from the map by using the key.
@@ -480,7 +472,6 @@ CX_EXPORT int cx_map_remove(CxMap *map, CxHashKey key, void *targetbuf);
  */
 #define cxMapRemoveAndGet(map, key, targetbuf) cx_map_remove(map, CX_HASH_KEY(key), targetbuf)
 
-
 /**
  * Performs a deep clone of one map into another.
  *
@@ -503,11 +494,10 @@ CX_EXPORT int cx_map_remove(CxMap *map, CxHashKey key, void *targetbuf);
  * @retval zero when all elements were successfully cloned
  * @retval non-zero when an allocation error occurred
  */
-cx_attr_nonnull_arg(1, 2, 3)
-CX_EXPORT int cxMapClone(CxMap *dst, const CxMap *src,
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3)
+int cxMapClone(CxMap *dst, const CxMap *src,
         cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
 
-
 /**
  * Clones entries of a map if their key is not present in another map.
  *
@@ -520,8 +510,8 @@ CX_EXPORT int cxMapClone(CxMap *dst, const CxMap *src,
  * @retval zero when the elements were successfully cloned
  * @retval non-zero when an allocation error occurred
  */
-cx_attr_nonnull_arg(1, 2, 3, 4)
-CX_EXPORT int cxMapDifference(CxMap *dst, const CxMap *minuend, const CxMap *subtrahend,
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3, 4)
+int cxMapDifference(CxMap *dst, const CxMap *minuend, const CxMap *subtrahend,
         cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
 
 /**
@@ -541,8 +531,8 @@ CX_EXPORT int cxMapDifference(CxMap *dst, const CxMap *minuend, const CxMap *sub
  * @retval zero when the elements were successfully cloned
  * @retval non-zero when an allocation error occurred
  */
-cx_attr_nonnull_arg(1, 2, 3, 4)
-CX_EXPORT int cxMapListDifference(CxMap *dst, const CxMap *src, const CxList *keys,
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3, 4)
+int cxMapListDifference(CxMap *dst, const CxMap *src, const CxList *keys,
         cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
 
 
@@ -558,8 +548,8 @@ CX_EXPORT int cxMapListDifference(CxMap *dst, const CxMap *src, const CxList *ke
  * @retval zero when the elements were successfully cloned
  * @retval non-zero when an allocation error occurred
  */
-cx_attr_nonnull_arg(1, 2, 3, 4)
-CX_EXPORT int cxMapIntersection(CxMap *dst, const CxMap *src, const CxMap *other,
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3, 4)
+int cxMapIntersection(CxMap *dst, const CxMap *src, const CxMap *other,
         cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
 
 /**
@@ -579,8 +569,8 @@ CX_EXPORT int cxMapIntersection(CxMap *dst, const CxMap *src, const CxMap *other
  * @retval zero when the elements were successfully cloned
  * @retval non-zero when an allocation error occurred
  */
-cx_attr_nonnull_arg(1, 2, 3, 4)
-CX_EXPORT int cxMapListIntersection(CxMap *dst, const CxMap *src, const CxList *keys,
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3, 4)
+int cxMapListIntersection(CxMap *dst, const CxMap *src, const CxList *keys,
         cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
 
 /**
@@ -599,8 +589,8 @@ CX_EXPORT int cxMapListIntersection(CxMap *dst, const CxMap *src, const CxList *
  * @retval zero when the elements were successfully cloned
  * @retval non-zero when an allocation error occurred
  */
-cx_attr_nonnull_arg(1, 2, 3)
-CX_EXPORT int cxMapUnion(CxMap *dst, const CxMap *src,
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3)
+int cxMapUnion(CxMap *dst, const CxMap *src,
         cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
 
 /**
@@ -626,8 +616,8 @@ CX_EXPORT int cxMapUnion(CxMap *dst, const CxMap *src,
  * @retval non-zero when an allocation error occurred
  * @see cxMapClone()
  */
-cx_attr_nonnull
-CX_EXPORT int cxMapCloneSimple(CxMap *dst, const CxMap *src);
+CX_EXTERN CX_NONNULL
+int cxMapCloneShallow(CxMap *dst, const CxMap *src);
 
 /**
  * Clones entries of a map if their key is not present in another map.
@@ -641,8 +631,8 @@ CX_EXPORT int cxMapCloneSimple(CxMap *dst, const CxMap *src);
  * @retval zero when the elements were successfully cloned
  * @retval non-zero when an allocation error occurred
  */
-cx_attr_nonnull
-CX_EXPORT int cxMapDifferenceSimple(CxMap *dst, const CxMap *minuend, const CxMap *subtrahend);
+CX_EXTERN CX_NONNULL
+int cxMapDifferenceShallow(CxMap *dst, const CxMap *minuend, const CxMap *subtrahend);
 
 /**
  * Clones entries of a map if their key is not present in a list.
@@ -662,9 +652,8 @@ CX_EXPORT int cxMapDifferenceSimple(CxMap *dst, const CxMap *minuend, const CxMa
  * @retval non-zero when an allocation error occurred
  * @see cxMapListDifference()
  */
-cx_attr_nonnull
-CX_EXPORT int cxMapListDifferenceSimple(CxMap *dst, const CxMap *src, const CxList *keys);
-
+CX_EXTERN CX_NONNULL
+int cxMapListDifferenceShallow(CxMap *dst, const CxMap *src, const CxList *keys);
 
 /**
  * Clones entries of a map only if their key is present in another map.
@@ -678,8 +667,8 @@ CX_EXPORT int cxMapListDifferenceSimple(CxMap *dst, const CxMap *src, const CxLi
  * @retval zero when the elements were successfully cloned
  * @retval non-zero when an allocation error occurred
  */
-cx_attr_nonnull
-CX_EXPORT int cxMapIntersectionSimple(CxMap *dst, const CxMap *src, const CxMap *other);
+CX_EXTERN CX_NONNULL
+int cxMapIntersectionShallow(CxMap *dst, const CxMap *src, const CxMap *other);
 
 /**
  * Clones entries of a map only if their key is present in a list.
@@ -698,8 +687,8 @@ CX_EXPORT int cxMapIntersectionSimple(CxMap *dst, const CxMap *src, const CxMap
  * @retval zero when the elements were successfully cloned
  * @retval non-zero when an allocation error occurred
  */
-cx_attr_nonnull
-CX_EXPORT int cxMapListIntersectionSimple(CxMap *dst, const CxMap *src, const CxList *keys);
+CX_EXTERN CX_NONNULL
+int cxMapListIntersectionShallow(CxMap *dst, const CxMap *src, const CxList *keys);
 
 /**
  * Clones entries into a map if their key does not exist yet.
@@ -717,11 +706,22 @@ CX_EXPORT int cxMapListIntersectionSimple(CxMap *dst, const CxMap *src, const Cx
  * @retval zero when the elements were successfully cloned
  * @retval non-zero when an allocation error occurred
  */
-cx_attr_nonnull
-CX_EXPORT int cxMapUnionSimple(CxMap *dst, const CxMap *src);
+CX_EXTERN CX_NONNULL
+int cxMapUnionShallow(CxMap *dst, const CxMap *src);
 
-#ifdef    __cplusplus
-} // extern "C"
-#endif
+/**
+ * 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_EXTERN CX_NONNULL
+int cxMapCompare(const CxMap *map, const CxMap *other);
 
 #endif // UCX_MAP_H
index 6530ea728a9e1f910f8cee39bbff740714811761..ea90ff7c4dd54e9b21cec278eaba7fc85437a486 100644 (file)
 #include "common.h"
 #include "allocator.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /** A memory block in a simple memory pool. */
 struct cx_mempool_memory_s {
     /** The destructor. */
@@ -156,7 +152,8 @@ typedef struct cx_mempool_s CxMempool;
  *
  * @param pool the memory pool to free
  */
-CX_EXPORT void cxMempoolFree(CxMempool *pool);
+CX_EXTERN
+void cxMempoolFree(CxMempool *pool);
 
 /**
  * Creates an array-based memory pool.
@@ -168,8 +165,8 @@ CX_EXPORT void cxMempoolFree(CxMempool *pool);
  * @param type the type of memory pool
  * @return the created memory pool or @c NULL if allocation failed
  */
-cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxMempoolFree, 1)
-CX_EXPORT CxMempool *cxMempoolCreate(size_t capacity, enum cx_mempool_type type);
+CX_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxMempoolFree, 1)
+CxMempool *cxMempoolCreate(size_t capacity, enum cx_mempool_type type);
 
 /**
  * Creates a basic array-based memory pool.
@@ -207,8 +204,8 @@ CX_EXPORT CxMempool *cxMempoolCreate(size_t capacity, enum cx_mempool_type type)
  * @param pool the memory pool
  * @param fnc the destructor that shall be applied to all memory blocks
  */
-cx_attr_nonnull_arg(1)
-CX_EXPORT void cxMempoolGlobalDestructor(CxMempool *pool, cx_destructor_func fnc);
+CX_EXTERN CX_NONNULL_ARG(1)
+void cxMempoolGlobalDestructor(CxMempool *pool, cx_destructor_func fnc);
 
 /**
  * Sets the global destructor for all memory blocks within the specified pool.
@@ -217,8 +214,8 @@ CX_EXPORT void cxMempoolGlobalDestructor(CxMempool *pool, cx_destructor_func fnc
  * @param fnc the destructor that shall be applied to all memory blocks
  * @param data additional data for the destructor function
  */
-cx_attr_nonnull_arg(1)
-CX_EXPORT void cxMempoolGlobalDestructor2(CxMempool *pool, cx_destructor_func2 fnc, void *data);
+CX_EXTERN CX_NONNULL_ARG(1)
+void cxMempoolGlobalDestructor2(CxMempool *pool, cx_destructor_func2 fnc, void *data);
 
 /**
  * Sets the destructor function for a specific allocated memory object.
@@ -230,8 +227,8 @@ CX_EXPORT void cxMempoolGlobalDestructor2(CxMempool *pool, cx_destructor_func2 f
  * @param memory the object allocated in the pool
  * @param fnc the destructor function
  */
-cx_attr_nonnull
-CX_EXPORT void cxMempoolSetDestructor(void *memory, cx_destructor_func fnc);
+CX_EXTERN CX_NONNULL
+void cxMempoolSetDestructor(void *memory, cx_destructor_func fnc);
 
 /**
  * Sets the destructor function for a specific allocated memory object.
@@ -244,8 +241,8 @@ CX_EXPORT void cxMempoolSetDestructor(void *memory, cx_destructor_func fnc);
  * @param fnc the destructor function
  * @param data additional data for the destructor function
  */
-cx_attr_nonnull
-CX_EXPORT void cxMempoolSetDestructor2(void *memory, cx_destructor_func2 fnc, void *data);
+CX_EXTERN CX_NONNULL
+void cxMempoolSetDestructor2(void *memory, cx_destructor_func2 fnc, void *data);
 
 /**
  * Removes the destructor function for a specific allocated memory object.
@@ -255,8 +252,8 @@ CX_EXPORT void cxMempoolSetDestructor2(void *memory, cx_destructor_func2 fnc, vo
  *
  * @param memory the object allocated in the pool
  */
-cx_attr_nonnull
-CX_EXPORT void cxMempoolRemoveDestructor(void *memory);
+CX_EXTERN CX_NONNULL
+void cxMempoolRemoveDestructor(void *memory);
 
 /**
  * Removes the destructor function for a specific allocated memory object.
@@ -266,8 +263,8 @@ CX_EXPORT void cxMempoolRemoveDestructor(void *memory);
  *
  * @param memory the object allocated in the pool
  */
-cx_attr_nonnull
-CX_EXPORT void cxMempoolRemoveDestructor2(void *memory);
+CX_EXTERN CX_NONNULL
+void cxMempoolRemoveDestructor2(void *memory);
 
 /**
  * Registers foreign memory with this pool.
@@ -284,9 +281,8 @@ CX_EXPORT void cxMempoolRemoveDestructor2(void *memory);
  * @retval zero success
  * @retval non-zero failure
  */
-cx_attr_nonnull
-CX_EXPORT int cxMempoolRegister(CxMempool *pool, void *memory, cx_destructor_func destr);
-
+CX_EXTERN CX_NONNULL
+int cxMempoolRegister(CxMempool *pool, void *memory, cx_destructor_func destr);
 
 /**
  * Registers foreign memory with this pool.
@@ -307,8 +303,8 @@ CX_EXPORT int cxMempoolRegister(CxMempool *pool, void *memory, cx_destructor_fun
  * @retval zero success
  * @retval non-zero failure
  */
-cx_attr_nonnull
-CX_EXPORT int cxMempoolRegister2(CxMempool *pool, void *memory, cx_destructor_func2 destr, void *data);
+CX_EXTERN CX_NONNULL
+int cxMempoolRegister2(CxMempool *pool, void *memory, cx_destructor_func2 destr, void *data);
 
 /**
  * Transfers all the memory managed by one pool to another.
@@ -325,8 +321,8 @@ CX_EXPORT int cxMempoolRegister2(CxMempool *pool, void *memory, cx_destructor_fu
  * @retval zero success
  * @retval non-zero allocation failure or incompatible pools
  */
-cx_attr_nonnull
-CX_EXPORT int cxMempoolTransfer(CxMempool *source, CxMempool *dest);
+CX_EXTERN CX_NONNULL
+int cxMempoolTransfer(CxMempool *source, CxMempool *dest);
 
 /**
  * Transfers an object from one pool to another.
@@ -342,11 +338,7 @@ CX_EXPORT int cxMempoolTransfer(CxMempool *source, CxMempool *dest);
  * @retval zero success
  * @retval non-zero failure, or the object was not found in the source pool, or the pools are incompatible
  */
-cx_attr_nonnull
-CX_EXPORT int cxMempoolTransferObject(CxMempool *source, CxMempool *dest, const void *obj);
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
+CX_EXTERN CX_NONNULL
+int cxMempoolTransferObject(CxMempool *source, CxMempool *dest, const void *obj);
 
 #endif // UCX_MEMPOOL_H
index 7311ab4d4998f7bd29118e18ae206eb0a825194b..9d4ea7f53505f1ec4b98753215dec99d3abfa8ab 100644 (file)
  * @param fmt_idx index of the format string parameter
  * @param arg_idx index of the first formatting argument
  */
-#define cx_attr_printf(fmt_idx, arg_idx) \
+#define CX_PRINTF_ARGS(fmt_idx, arg_idx) \
     __attribute__((__format__(printf, fmt_idx, arg_idx)))
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
 /**
  * The maximum string length that fits into stack memory.
  */
@@ -68,8 +63,8 @@ CX_EXPORT extern const unsigned cx_printf_sbo_size;
  * @param ... additional arguments
  * @return the total number of bytes written or an error code from stdlib printf implementation
  */
-cx_attr_nonnull_arg(1, 2, 3) cx_attr_printf(3, 4) cx_attr_cstr_arg(3)
-CX_EXPORT int cx_fprintf(void *stream, cx_write_func wfc, const char *fmt, ...);
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3) CX_PRINTF_ARGS(3, 4) CX_CSTR_ARG(3)
+int cx_fprintf(void *stream, cx_write_func wfc, const char *fmt, ...);
 
 /**
  * A @c vfprintf like function which writes the output to a stream by
@@ -82,8 +77,8 @@ CX_EXPORT int cx_fprintf(void *stream, cx_write_func wfc, const char *fmt, ...);
  * @return the total number of bytes written or an error code from stdlib printf implementation
  * @see cx_fprintf()
  */
-cx_attr_nonnull cx_attr_cstr_arg(3)
-CX_EXPORT int cx_vfprintf(void *stream, cx_write_func wfc, const char *fmt, va_list ap);
+CX_EXTERN CX_NONNULL CX_CSTR_ARG(3)
+int cx_vfprintf(void *stream, cx_write_func wfc, const char *fmt, va_list ap);
 
 /**
  * An @c asprintf like function which allocates space for a string
@@ -99,8 +94,8 @@ CX_EXPORT int cx_vfprintf(void *stream, cx_write_func wfc, const char *fmt, va_l
  * @return the formatted string
  * @see cx_strfree_a()
  */
-cx_attr_nonnull_arg(1, 2) cx_attr_printf(2, 3) cx_attr_cstr_arg(2)
-CX_EXPORT cxmutstr cx_asprintf_a(const CxAllocator *allocator, const char *fmt, ...);
+CX_EXTERN CX_NONNULL_ARG(1, 2) CX_PRINTF_ARGS(2, 3) CX_CSTR_ARG(2)
+cxmutstr cx_asprintf_a(const CxAllocator *allocator, const char *fmt, ...);
 
 /**
  * An @c asprintf like function which allocates space for a string
@@ -131,8 +126,8 @@ CX_EXPORT cxmutstr cx_asprintf_a(const CxAllocator *allocator, const char *fmt,
  * @return the formatted string
  * @see cx_asprintf_a()
  */
-cx_attr_nonnull cx_attr_cstr_arg(2)
-CX_EXPORT cxmutstr cx_vasprintf_a(const CxAllocator *allocator, const char *fmt, va_list ap);
+CX_EXTERN CX_NONNULL CX_CSTR_ARG(2)
+cxmutstr cx_vasprintf_a(const CxAllocator *allocator, const char *fmt, va_list ap);
 
 /**
 * A @c vasprintf like function which allocates space for a string
@@ -193,8 +188,8 @@ CX_EXPORT cxmutstr cx_vasprintf_a(const CxAllocator *allocator, const char *fmt,
  * @param ... additional arguments
  * @return the length of the produced string or an error code from stdlib printf implementation
  */
-cx_attr_nonnull_arg(1, 2, 3, 4) cx_attr_printf(4, 5) cx_attr_cstr_arg(4)
-CX_EXPORT int cx_sprintf_a(const CxAllocator *alloc, char **str, size_t *len, const char *fmt, ...);
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3, 4) CX_PRINTF_ARGS(4, 5) CX_CSTR_ARG(4)
+int cx_sprintf_a(const CxAllocator *alloc, char **str, size_t *len, const char *fmt, ...);
 
 
 /**
@@ -228,8 +223,8 @@ CX_EXPORT int cx_sprintf_a(const CxAllocator *alloc, char **str, size_t *len, co
  * @param ap argument list
  * @return the length of the produced string or an error code from stdlib printf implementation
  */
-cx_attr_nonnull  cx_attr_cstr_arg(4) cx_attr_access_rw(2) cx_attr_access_rw(3)
-CX_EXPORT int cx_vsprintf_a(const CxAllocator *alloc, char **str, size_t *len, const char *fmt, va_list ap);
+CX_EXTERN CX_NONNULL  CX_CSTR_ARG(4) CX_ACCESS_RW(2) CX_ACCESS_RW(3)
+int cx_vsprintf_a(const CxAllocator *alloc, char **str, size_t *len, const char *fmt, va_list ap);
 
 
 /**
@@ -275,9 +270,9 @@ CX_EXPORT int cx_vsprintf_a(const CxAllocator *alloc, char **str, size_t *len, c
  * @param ... additional arguments
  * @return the length of the produced string or an error code from stdlib printf implementation
  */
-cx_attr_nonnull_arg(1, 2, 4, 5) cx_attr_printf(5, 6) cx_attr_cstr_arg(5)
-cx_attr_access_rw(2) cx_attr_access_rw(3) cx_attr_access_rw(4)
-CX_EXPORT int cx_sprintf_sa(const CxAllocator *alloc, char *buf, size_t *len, char **str, const char *fmt, ...);
+CX_EXTERN CX_NONNULL_ARG(1, 2, 4, 5) CX_PRINTF_ARGS(5, 6) CX_CSTR_ARG(5)
+CX_ACCESS_RW(2) CX_ACCESS_RW(3) CX_ACCESS_RW(4)
+int cx_sprintf_sa(const CxAllocator *alloc, char *buf, size_t *len, char **str, const char *fmt, ...);
 
 /**
  * An @c sprintf like function which allocates a new string when the buffer is not large enough.
@@ -322,12 +317,7 @@ CX_EXPORT int cx_sprintf_sa(const CxAllocator *alloc, char *buf, size_t *len, ch
  * @param ap argument list
  * @return the length of the produced string or an error code from stdlib printf implementation
  */
-cx_attr_nonnull cx_attr_cstr_arg(5)
-CX_EXPORT int cx_vsprintf_sa(const CxAllocator *alloc, char *buf, size_t *len, char **str, const char *fmt, va_list ap);
-
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
+CX_EXTERN CX_NONNULL CX_CSTR_ARG(5)
+int cx_vsprintf_sa(const CxAllocator *alloc, char *buf, size_t *len, char **str, const char *fmt, va_list ap);
 
 #endif //UCX_PRINTF_H
index 8301275d406e41d84aed8c0f253c4b9a9577511c..e7bf840748891b99df0e1d97ac83762113a78172 100644 (file)
 #include "map.h"
 #include "buffer.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  * Configures the expected characters for the properties parser.
  */
@@ -73,7 +69,7 @@ struct cx_properties_config_s {
      */
     char comment3;
 
-    /*
+    /**
      * The character, when appearing at the end of a line, continues that line.
      * This is '\' by default.
      */
@@ -184,8 +180,8 @@ typedef struct cx_properties_s CxProperties;
  * @param config the properties configuration
  * @see cxPropertiesInitDefault()
  */
-cx_attr_nonnull
-CX_EXPORT void cxPropertiesInit(CxProperties *prop, CxPropertiesConfig config);
+CX_EXTERN CX_NONNULL
+void cxPropertiesInit(CxProperties *prop, CxPropertiesConfig config);
 
 /**
  * Destroys the properties interface.
@@ -198,8 +194,8 @@ CX_EXPORT void cxPropertiesInit(CxProperties *prop, CxPropertiesConfig config);
  *
  * @param prop the properties interface
  */
-cx_attr_nonnull
-CX_EXPORT void cxPropertiesDestroy(CxProperties *prop);
+CX_EXTERN CX_NONNULL
+void cxPropertiesDestroy(CxProperties *prop);
 
 /**
  * Destroys and re-initializes the properties interface.
@@ -209,8 +205,8 @@ CX_EXPORT void cxPropertiesDestroy(CxProperties *prop);
  *
  * @param prop the properties interface
  */
-cx_attr_nonnull
-CX_EXPORT void cxPropertiesReset(CxProperties *prop);
+CX_EXTERN CX_NONNULL
+void cxPropertiesReset(CxProperties *prop);
 
 /**
  * Initialize a properties parser with the default configuration.
@@ -242,8 +238,8 @@ CX_EXPORT void cxPropertiesReset(CxProperties *prop);
  * @retval non-zero a memory allocation was necessary but failed
  * @see cxPropertiesFill()
  */
-cx_attr_nonnull cx_attr_access_r(2, 3)
-CX_EXPORT int cxPropertiesFilln(CxProperties *prop, const char *buf, size_t len);
+CX_EXTERN CX_NONNULL CX_ACCESS_R(2, 3)
+int cxPropertiesFilln(CxProperties *prop, const char *buf, size_t len);
 
 /**
  * Internal function, do not use.
@@ -253,8 +249,8 @@ CX_EXPORT int cxPropertiesFilln(CxProperties *prop, const char *buf, size_t len)
  * @retval zero success
  * @retval non-zero a memory allocation was necessary but failed
  */
-cx_attr_nonnull
-CX_INLINE int cx_properties_fill(CxProperties *prop, cxstring str) {
+CX_NONNULL CX_INLINE
+int cx_properties_fill(CxProperties *prop, cxstring str) {
     return cxPropertiesFilln(prop, str.ptr, str.length);
 }
 
@@ -287,8 +283,8 @@ CX_INLINE int cx_properties_fill(CxProperties *prop, cxstring str) {
  * @param buf a pointer to stack memory
  * @param capacity the capacity of the stack memory
  */
-cx_attr_nonnull
-CX_EXPORT void cxPropertiesUseStack(CxProperties *prop, char *buf, size_t capacity);
+CX_EXTERN CX_NONNULL
+void cxPropertiesUseStack(CxProperties *prop, char *buf, size_t capacity);
 
 /**
  * Retrieves the next key/value-pair.
@@ -320,8 +316,8 @@ CX_EXPORT void cxPropertiesUseStack(CxProperties *prop, char *buf, size_t capaci
  * @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
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT CxPropertiesStatus cxPropertiesNext(CxProperties *prop, cxstring *key, cxstring *value);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+CxPropertiesStatus cxPropertiesNext(CxProperties *prop, cxstring *key, cxstring *value);
 
 /**
  * The size of the stack memory that `cxPropertiesLoad()` will reserve with `cxPropertiesUseStack()`.
@@ -336,15 +332,15 @@ CX_EXPORT extern const unsigned cx_properties_load_fill_size;
 /**
  * Internal function - use cxPropertiesLoad() instead.
  *
- * @param config the parser config
  * @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_arg(4)
-CX_EXPORT CxPropertiesStatus cx_properties_load(CxPropertiesConfig config,
-        const CxAllocator *allocator, cxstring filename, CxMap *target);
+CX_EXTERN CX_NONNULL_ARG(3)
+CxPropertiesStatus cx_properties_load(const CxAllocator *allocator,
+        cxstring filename, CxMap *target, CxPropertiesConfig config);
 
 /**
  * Loads properties from a file and inserts them into a map.
@@ -357,10 +353,10 @@ CX_EXPORT CxPropertiesStatus cx_properties_load(CxPropertiesConfig config,
  * @note When the parser finds an error, all successfully parsed keys before the error
  * are added to the map nonetheless.
  *
- * @param config the parser config
  * @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
@@ -371,8 +367,8 @@ CX_EXPORT CxPropertiesStatus cx_properties_load(CxPropertiesConfig config,
  * @retval CX_PROPERTIES_MAP_ERROR storing a key/value pair in the map failed
  * @see cxPropertiesLoadDefault()
  */
-#define cxPropertiesLoad(config, allocator, filename, target) \
-    cx_properties_load(config, allocator, cx_strcast(filename), target)
+#define cxPropertiesLoad(allocator, filename, target, config) \
+    cx_properties_load(allocator, cx_strcast(filename), target, config)
 
 /**
  * Loads properties from a file and inserts them into a map with a default config.
@@ -399,11 +395,6 @@ CX_EXPORT CxPropertiesStatus cx_properties_load(CxPropertiesConfig config,
  * @see cxPropertiesLoad()
  */
 #define cxPropertiesLoadDefault(allocator, filename, target) \
-    cx_properties_load(cx_properties_config_default, allocator, cx_strcast(filename), target)
-
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
+    cx_properties_load(allocator, cx_strcast(filename), target, cx_properties_config_default)
 
 #endif // UCX_PROPERTIES_H
index 298ca3a2e307ea0677ddb4190fb85ab4fdfdde9d..c778f7e4c8cdbc54f7b4bea780421b0bf1ca77e1 100644 (file)
 
 #include "common.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  * Reads data from a stream and writes it to another stream.
  *
@@ -61,9 +57,9 @@ extern "C" {
  * iterations.
  * @return the total number of bytes copied
  */
-cx_attr_nonnull_arg(1, 2, 3, 4)
-cx_attr_access_r(1) cx_attr_access_w(2) cx_attr_access_w(5)
-CX_EXPORT size_t cx_stream_bncopy(void *src, void *dest,
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3, 4)
+CX_ACCESS_R(1) CX_ACCESS_W(2) CX_ACCESS_W(5)
+size_t cx_stream_bncopy(void *src, void *dest,
         cx_read_func rfnc, cx_write_func wfnc,
         char *buf, size_t bufsize, size_t n);
 
@@ -95,8 +91,8 @@ CX_EXPORT size_t cx_stream_bncopy(void *src, void *dest,
  * @param n the maximum number of bytes that shall be copied.
  * @return total number of bytes copied
  */
-cx_attr_nonnull cx_attr_access_r(1) cx_attr_access_w(2)
-CX_EXPORT size_t cx_stream_ncopy(void *src, void *dest,
+CX_EXTERN CX_NONNULL CX_ACCESS_R(1) CX_ACCESS_W(2)
+size_t cx_stream_ncopy(void *src, void *dest,
         cx_read_func rfnc, cx_write_func wfnc, size_t n);
 
 /**
@@ -113,8 +109,4 @@ CX_EXPORT size_t cx_stream_ncopy(void *src, void *dest,
 #define cx_stream_copy(src, dest, rfnc, wfnc) \
     cx_stream_ncopy(src, dest, rfnc, wfnc, SIZE_MAX)
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif // UCX_STREAMS_H
index 4dd8f1765e95942b3adaa2d1c712f7c6483781cb..81ad00d971361e65e1b75233a6e7ccbfb3643afb 100644 (file)
 #include "common.h"
 #include "allocator.h"
 
+#include <string.h>
+
+/** Convenience macro for creating a null string */
+#define CX_NULLSTR cx_mutstr(NULL)
+
 /** Expands a UCX string as printf arguments. */
 #define CX_SFMT(s) (int) (s).length, (s).ptr
 
@@ -137,30 +142,6 @@ struct cx_strtok_ctx_s {
  */
 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.
  *
@@ -178,8 +159,13 @@ extern "C" {
  *
  * @see cx_mutstrn()
  */
-cx_attr_nodiscard cx_attr_cstr_arg(1)
-CX_EXPORT cxmutstr cx_mutstr(char *cstring);
+CX_NODISCARD CX_CSTR_ARG(1)
+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.
@@ -197,8 +183,13 @@ CX_EXPORT cxmutstr cx_mutstr(char *cstring);
  *
  * @see cx_mutstr()
  */
-cx_attr_nodiscard cx_attr_access_rw(1, 2)
-CX_EXPORT cxmutstr cx_mutstrn(char *cstring, size_t length);
+CX_NODISCARD CX_ACCESS_RW(1, 2)
+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.
@@ -217,8 +208,13 @@ CX_EXPORT cxmutstr cx_mutstrn(char *cstring, size_t length);
  *
  * @see cx_strn()
  */
-cx_attr_nodiscard cx_attr_cstr_arg(1)
-CX_EXPORT cxstring cx_str(const char *cstring);
+CX_NODISCARD CX_CSTR_ARG(1)
+CX_INLINE cxstring cx_str(const char *cstring) {
+    cxstring str;
+    str.ptr = cstring;
+    str.length = cstring == NULL ? 0 : strlen(cstring);
+    return str;
+}
 
 
 /**
@@ -237,47 +233,69 @@ CX_EXPORT cxstring cx_str(const char *cstring);
  *
  * @see cx_str()
  */
-cx_attr_nodiscard cx_attr_access_r(1, 2)
-CX_EXPORT cxstring cx_strn(const char *cstring, size_t length);
+CX_NODISCARD CX_ACCESS_R(1, 2)
+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);
+CX_NODISCARD
+CX_CPPDECL cxmutstr cx_strcast_m(cxmutstr str) {
+    return str;
 }
-cx_attr_nodiscard
-CX_CPPDECL cxstring cx_strcast(cxstring str) {
+CX_NODISCARD
+CX_CPPDECL cxstring cx_strcast_m(cxstring str) {
     return str;
 }
-cx_attr_nodiscard
-CX_CPPDECL cxstring cx_strcast(const char *str) {
+CX_NODISCARD
+CX_CPPDECL cxmutstr cx_strcast_m(char *str) {
+    return cx_mutstr(str);
+}
+CX_NODISCARD
+CX_CPPDECL cxmutstr cx_strcast_m(unsigned char *str) {
+    return cx_mutstr(reinterpret_cast<char*>(str));
+}
+CX_NODISCARD
+CX_CPPDECL cxstring cx_strcast_m(const char *str) {
     return cx_str(str);
 }
-cx_attr_nodiscard
-CX_CPPDECL cxstring cx_strcast(const unsigned char *str) {
+CX_NODISCARD
+CX_CPPDECL cxstring cx_strcast_m(const unsigned char *str) {
     return cx_str(reinterpret_cast<const char*>(str));
 }
-extern "C" {
+CX_NODISCARD
+CX_CPPDECL cxstring cx_strcast_(cxmutstr str) {
+    return cx_strn(str.ptr, str.length);
+}
+CX_NODISCARD
+CX_CPPDECL cxstring cx_strcast_(cxstring str) {
+    return str;
+}
+#define cx_strcast(s) cx_strcast_(cx_strcast_m(s))
 #else
 /**
  * Internal function, do not use.
  * @param str
  * @return
+ * @see cx_strcast_m()
  * @see cx_strcast()
  */
-cx_attr_nodiscard
-CX_INLINE cxstring cx_strcast_m(cxmutstr str) {
-    return (cxstring) {str.ptr, str.length};
+CX_NODISCARD
+CX_INLINE cxmutstr cx_strcast_cxms(cxmutstr str) {
+    return str;
 }
 /**
  * Internal function, do not use.
  * @param str
  * @return
+ * @see cx_strcast_m()
  * @see cx_strcast()
  */
-cx_attr_nodiscard
-CX_INLINE cxstring cx_strcast_c(cxstring str) {
+CX_NODISCARD
+CX_INLINE cxstring cx_strcast_cxs(cxstring str) {
     return str;
 }
 
@@ -285,10 +303,35 @@ CX_INLINE cxstring cx_strcast_c(cxstring str) {
  * Internal function, do not use.
  * @param str
  * @return
+ * @see cx_strcast_m()
+ * @see cx_strcast()
+ */
+CX_NODISCARD
+CX_INLINE cxmutstr cx_strcast_uc(unsigned char *str) {
+    return cx_mutstr((char*)str);
+}
+
+/**
+ * Internal function, do not use.
+ * @param str
+ * @return
+ * @see cx_strcast_m()
+ * @see cx_strcast()
+ */
+CX_NODISCARD
+CX_INLINE cxmutstr cx_strcast_c(char *str) {
+    return cx_mutstr(str);
+}
+
+/**
+ * Internal function, do not use.
+ * @param str
+ * @return
+ * @see cx_strcast_m()
  * @see cx_strcast()
  */
-cx_attr_nodiscard
-CX_INLINE cxstring cx_strcast_u(const unsigned char *str) {
+CX_NODISCARD
+CX_INLINE cxstring cx_strcast_ucc(const unsigned char *str) {
     return cx_str((const char*)str);
 }
 
@@ -296,10 +339,11 @@ CX_INLINE cxstring cx_strcast_u(const unsigned char *str) {
  * Internal function, do not use.
  * @param str
  * @return
+ * @see cx_strcast_m()
  * @see cx_strcast()
  */
-cx_attr_nodiscard
-CX_INLINE cxstring cx_strcast_z(const char *str) {
+CX_NODISCARD
+CX_INLINE cxstring cx_strcast_cc(const char *str) {
     return cx_str(str);
 }
 
@@ -307,17 +351,61 @@ CX_INLINE cxstring cx_strcast_z(const char *str) {
  * Wraps any string into an UCX string.
  *
  * @param str (any supported string type) the string to cast
- * @return (@c cxstring) the string wrapped as UCX string
- */
-#define cx_strcast(str) _Generic((str), \
-        cxmutstr: cx_strcast_m, \
-        cxstring: cx_strcast_c, \
-        const unsigned char*: cx_strcast_u, \
-        unsigned char *: cx_strcast_u, \
-        const char*: cx_strcast_z, \
-        char *: cx_strcast_z) (str)
+ * @return (@c cxstring) or (@c cxmutstr) the string wrapped as UCX string
+ */
+#define cx_strcast_m(str) _Generic((str), \
+        cxstring: cx_strcast_cxs, \
+        cxmutstr: cx_strcast_cxms, \
+        const unsigned char*: cx_strcast_ucc, \
+        unsigned char *: cx_strcast_uc, \
+        const char*: cx_strcast_cc, \
+        char *: cx_strcast_c) (str)
+
+/**
+ * Internal function, do not use.
+ * @param str
+ * @return
+ */
+CX_INLINE cxstring cx_strcast_1(cxmutstr str) {
+    return (cxstring){str.ptr, str.length};
+}
+
+/**
+ * Internal function, do not use.
+ * @param str
+ * @return
+ */
+CX_INLINE cxstring cx_strcast_2(cxstring str) {
+    return str;
+}
+
+/** internal conversion macro */
+#define cx_strcast_(str) _Generic((str), \
+        cxmutstr: cx_strcast_1, \
+        cxstring: cx_strcast_2)(str)
+
+/**
+ * Converts any string to a cxstring.
+ *
+ * @param str (any supported string type) the string to cast
+ * @return he string converted to a (@c cxstring)
+ */
+#define cx_strcast(str) cx_strcast_(cx_strcast_m(str))
 #endif
 
+/**
+ * Casts away constness and converts a cxstring to a cxmutstr.
+ * For internal use only!
+ * @param str
+ * @return
+ */
+CX_INLINE cxmutstr cx_mutstrcast(cxstring str) {
+    cxmutstr s;
+    s.ptr = (char*)str.ptr;
+    s.length = str.length;
+    return s;
+}
+
 /**
  * Passes the pointer in this string to the cxDefaultAllocator's @c free() function.
  *
@@ -330,7 +418,8 @@ CX_INLINE cxstring cx_strcast_z(const char *str) {
  *
  * @param str the string to free
  */
-CX_EXPORT void cx_strfree(cxmutstr *str);
+CX_EXTERN
+void cx_strfree(cxmutstr *str);
 
 /**
  * Passes the pointer in this string to the allocator's free function.
@@ -345,16 +434,13 @@ CX_EXPORT void cx_strfree(cxmutstr *str);
  * @param alloc the allocator
  * @param str the string to free
  */
-cx_attr_nonnull_arg(1)
-CX_EXPORT void cx_strfree_a(const CxAllocator *alloc, cxmutstr *str);
+CX_EXTERN CX_NONNULL_ARG(1)
+void cx_strfree_a(const CxAllocator *alloc, cxmutstr *str);
 
 /**
  * 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
@@ -362,10 +448,10 @@ CX_EXPORT void cx_strfree_a(const CxAllocator *alloc, cxmutstr *str);
  *
  * @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_EXTERN CX_NONNULL_ARG(1)
+int cx_strcpy_a_(const CxAllocator *alloc, cxmutstr *dest, cxstring src);
 
 /**
  * Copies a string.
@@ -375,9 +461,24 @@ CX_EXPORT int cx_strcpy_a(const CxAllocator *alloc, cxmutstr *dest, cxstring src
  *
  * 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
  */
@@ -395,8 +496,8 @@ CX_EXPORT int cx_strcpy_a(const CxAllocator *alloc, cxmutstr *dest, cxstring src
  * @param ...      all strings
  * @return the accumulated length of all strings
  */
-cx_attr_nodiscard
-CX_EXPORT size_t cx_strlen(size_t count, ...);
+CX_EXTERN CX_NODISCARD
+size_t cx_strlen(size_t count, ...);
 
 /**
  * Concatenates strings.
@@ -407,12 +508,10 @@ CX_EXPORT size_t cx_strlen(size_t count, ...);
  * If @p str already contains a string, the memory will be reallocated and
  * the other strings are appended. Otherwise, new memory is allocated.
  *
- * If memory allocation fails, the pointer in the returned string will
- * be @c NULL. Depending on the allocator, @c errno might be set.
- *
  * @note It is guaranteed that there is only one allocation for the
  * resulting string.
  * It is also guaranteed that the returned string is zero-terminated.
+ * If allocation fails, the @c ptr in the returned string will be @c NULL.
  *
  * @param alloc the allocator to use
  * @param str   the string the other strings shall be concatenated to
@@ -420,132 +519,111 @@ CX_EXPORT size_t cx_strlen(size_t count, ...);
  * @param ...   all other UCX strings
  * @return the concatenated string
  */
-cx_attr_nodiscard cx_attr_nonnull
-CX_EXPORT cxmutstr cx_strcat_ma(const CxAllocator *alloc,
+CX_EXTERN CX_NONNULL
+cxmutstr cx_strcat_a(const CxAllocator *alloc,
         cxmutstr str, size_t count, ...);
 
-/**
- * Concatenates strings and returns a new string.
- *
- * The resulting string will be allocated by the specified allocator.
- * So developers @em must pass the return value to cx_strfree_a() eventually.
- *
-* If memory allocation fails, the pointer in the returned string will
- * be @c NULL. Depending on the allocator, @c errno might be set.
- *
- * @note It is guaranteed that there is only one allocation for the
- * resulting string.
- * It is also guaranteed that the returned string is zero-terminated.
- *
- * @param alloc (@c CxAllocator*) the allocator to use
- * @param count (@c size_t) the number of the other following strings to concatenate
- * @param ...   all other UCX strings
- * @return (@c cxmutstr) the concatenated string
- */
-#define cx_strcat_a(alloc, count, ...) \
-        cx_strcat_ma(alloc, cx_mutstrn(NULL, 0), count, __VA_ARGS__)
-
 /**
  * Concatenates strings and returns a new string.
  *
  * The resulting string will be allocated by the cxDefaultAllocator.
  * So developers @em must pass the return value to cx_strfree() eventually.
  *
-* If memory allocation fails, the pointer in the returned string will
- * be @c NULL and @c errno might be set.
- *
  * @note It is guaranteed that there is only one allocation for the
  * resulting string.
  * It is also guaranteed that the returned string is zero-terminated.
+ * If allocation fails, the @c ptr in the returned string will be @c NULL.
  *
+ * @param str (@c cxmutstr*) the string the other strings shall be concatenated to
  * @param count (@c size_t) the number of the other following strings to concatenate
  * @param ... all other UCX strings
- * @return (@c cxmutstr) the concatenated string
- */
-#define cx_strcat(count, ...) \
-        cx_strcat_ma(cxDefaultAllocator, cx_mutstrn(NULL, 0), count, __VA_ARGS__)
-
-/**
- * Concatenates strings.
- *
- * The resulting string will be allocated by the cxDefaultAllocator.
- * So developers @em must pass the return value to cx_strfree() eventually.
- *
- * If @p str already contains a string, the memory will be reallocated and
- * the other strings are appended. Otherwise, new memory is allocated.
- *
-* If memory allocation fails, the pointer in the returned string will
- * be @c NULL and @c errno might be set.
- *
- * @note It is guaranteed that there is only one allocation for the
- * resulting string.
- * It is also guaranteed that the returned string is zero-terminated.
- *
- * @param str (@c cxmutstr) the string the other strings shall be concatenated to
- * @param count (@c size_t) the number of the other following strings to concatenate
- * @param ... all other strings
- * @return (@c cxmutstr) the concatenated string
+ * @return the concatenated string
  */
-#define cx_strcat_m(str, count, ...) \
-        cx_strcat_ma(cxDefaultAllocator, str, count, __VA_ARGS__)
+#define cx_strcat(str, count, ...) \
+        cx_strcat_a(cxDefaultAllocator, str, count, __VA_ARGS__)
 
 /**
- * Returns a substring starting at the specified location.
+ * Returns a substring.
  *
- * @attention the new string references the same memory area as the
- * input string and is usually @em not zero-terminated.
- * Use cx_strdup() to get a copy.
+ * Internal function - do not use.
  *
  * @param string input string
  * @param start  start location of the substring
+ * @param length the maximum length of the returned string
  * @return a substring of @p string starting at @p start
- *
  * @see cx_strsubsl()
- * @see cx_strsubs_m()
- * @see cx_strsubsl_m()
  */
-cx_attr_nodiscard
-CX_EXPORT cxstring cx_strsubs(cxstring string, size_t start);
+CX_EXTERN CX_NODISCARD
+cxstring cx_strsubsl_(cxstring string, size_t start, size_t length);
 
 /**
- * Returns a substring starting at the specified location.
- *
- * The returned string will be limited to @p length bytes or the number
- * of bytes available in @p string, whichever is smaller.
+ * Returns a substring.
  *
- * @attention the new string references the same memory area as the
- * input string and is usually @em not zero-terminated.
- * Use cx_strdup() to get a copy.
+ * Internal function - do not use.
  *
  * @param string input string
  * @param start  start location of the substring
- * @param length the maximum length of the returned string
  * @return a substring of @p string starting at @p start
- *
  * @see cx_strsubs()
- * @see cx_strsubs_m()
- * @see cx_strsubsl_m()
  */
-cx_attr_nodiscard
-CX_EXPORT cxstring cx_strsubsl(cxstring string, size_t start, size_t length);
+CX_EXTERN CX_NODISCARD
+cxstring cx_strsubs_(cxstring string, size_t start);
+
+/**
+ * Internal conversion function - do not use.
+ * @param string
+ * @param start
+ * @return
+ */
+CX_INLINE
+cxmutstr cx_strsubs_m_(cxmutstr string, size_t start) {
+    return cx_mutstrcast(cx_strsubs_(cx_strcast(string), start));
+}
+
+/**
+ * Internal conversion function - do not use.
+ * @param string
+ * @param start
+ * @param length
+ * @return
+ */
+CX_INLINE
+cxmutstr cx_strsubsl_m_(cxmutstr string, size_t start, size_t length) {
+    return cx_mutstrcast(cx_strsubsl_(cx_strcast(string), start, length));
+}
 
+#ifdef __cplusplus
+CX_CPPDECL cxstring cx_strsubs_cpp_(cxstring string, size_t start) {
+    return cx_strsubs_(string, start);
+}
+CX_CPPDECL cxstring cx_strsubsl_cpp_(cxstring string, size_t start, size_t length) {
+    return cx_strsubsl_(string, start, length);
+}
+CX_CPPDECL cxmutstr cx_strsubs_cpp_(cxmutstr string, size_t start) {
+    return cx_strsubs_m_(string, start);
+}
+CX_CPPDECL cxmutstr cx_strsubsl_cpp_(cxmutstr string, size_t start, size_t length) {
+    return cx_strsubsl_m_(string, start, length);
+}
+#define cx_strsubs(string, start) cx_strsubs_cpp_(cx_strcast_m(string), start)
+#define cx_strsubsl(string, start, length) cx_strsubsl_cpp_(cx_strcast_m(string), start, length)
+#else
 /**
  * Returns a substring starting at the specified location.
  *
  * @attention the new string references the same memory area as the
- * input string and is usually @em not zero-terminated.
+ * input string and is @em not zero-terminated.
  * Use cx_strdup() to get a copy.
  *
  * @param string input string
- * @param start  start location of the substring
- * @return a substring of @p string starting at @p start
+ * @param start (@c size_t) start location of the substring
+ * @return (@c cxstring or @c cxmutstr) a substring of @p string starting at @p start
  *
- * @see cx_strsubsl_m()
- * @see cx_strsubs()
  * @see cx_strsubsl()
  */
-cx_attr_nodiscard
-CX_EXPORT cxmutstr cx_strsubs_m(cxmutstr string, size_t start);
+#define cx_strsubs(string, start) _Generic(cx_strcast_m(string), \
+        cxstring: cx_strsubs_, \
+        cxmutstr: cx_strsubs_m_)(cx_strcast_m(string), start)
 
 /**
  * Returns a substring starting at the specified location.
@@ -562,57 +640,123 @@ CX_EXPORT cxmutstr cx_strsubs_m(cxmutstr string, size_t start);
  * @param length the maximum length of the returned string
  * @return a substring of @p string starting at @p start
  *
- * @see cx_strsubs_m()
  * @see cx_strsubs()
- * @see cx_strsubsl()
  */
-cx_attr_nodiscard
-CX_EXPORT cxmutstr cx_strsubsl_m(cxmutstr string, size_t start, size_t length);
+#define cx_strsubsl(string, start, length) _Generic(cx_strcast_m(string), \
+        cxstring: cx_strsubsl_, \
+        cxmutstr: cx_strsubsl_m_)(cx_strcast_m(string), start, length)
+#endif
 
 /**
- * Returns a substring starting at the location of the first occurrence of the
- * specified character.
+ * Returns the character at the specified index offset.
  *
- * If the string does not contain the character, an empty string is returned.
+ * Internal function - do not use.
  *
- * @param string the string where to locate the character
- * @param chr    the character to locate
- * @return       a substring starting at the first location of @p chr
- *
- * @see cx_strchr_m()
+ * @param str the string
+ * @param index the index offset
+ * @return the character at the index
+ * @see cx_strat()
  */
-cx_attr_nodiscard
-CX_EXPORT cxstring cx_strchr(cxstring string, int chr);
+CX_INLINE
+char cx_strat_(cxstring str, off_t index) {
+    size_t i;
+    if (index >= 0) {
+        i = index;
+    } else {
+        i = (size_t) (str.length + index);
+    }
+    if (i >= str.length) {
+        return '\0';
+    }
+    return str.ptr[i];
+}
 
 /**
- * Returns a substring starting at the location of the first occurrence of the
- * specified character.
+ * Returns the character at the specified index offset.
  *
- * If the string does not contain the character, an empty string is returned.
+ * When the @p index is negative, the character is counted from the end of the
+ * string where -1 denotes the last character in the string.
  *
- * @param string the string where to locate the character
- * @param chr    the character to locate
- * @return       a substring starting at the first location of @p chr
+ * When the @p index is out of bounds, the function returns zero.
  *
+ * @param str the string
+ * @param index the index offset
+ * @return the character at the index
+ * @see cx_strat()
+ */
+#define cx_strat(str, index) cx_strat_(cx_strcast(str), index)
+
+/**
+ * Searches for a character in a string.
+ * Internal function - do not use.
+ * @param string
+ * @param chr
+ * @return
  * @see cx_strchr()
  */
-cx_attr_nodiscard
-CX_EXPORT cxmutstr cx_strchr_m(cxmutstr string, int chr);
+CX_EXTERN CX_NODISCARD
+cxstring cx_strchr_(cxstring string, int chr);
 
 /**
- * Returns a substring starting at the location of the last occurrence of the
+ * Searches for a character in a string.
+ * Internal function - do not use.
+ * @param string
+ * @param chr
+ * @return
+ * @see cx_strrchr()
+ */
+CX_EXTERN CX_NODISCARD
+cxstring cx_strrchr_(cxstring string, int chr);
+
+#ifdef __cplusplus
+CX_CPPDECL cxstring cx_strchr_cpp_(cxstring string, int chr) {
+    return cx_strchr_(string, chr);
+}
+CX_CPPDECL cxmutstr cx_strchr_cpp_(cxmutstr string, int chr) {
+    return cx_mutstrcast(cx_strchr_(cx_strcast(string), chr));
+}
+#define cx_strchr(s, chr) cx_strchr_cpp_(cx_strcast_m(s), chr)
+CX_CPPDECL cxstring cx_strrchr_cpp_(cxstring string, int chr) {
+    return cx_strrchr_(string, chr);
+}
+CX_CPPDECL cxmutstr cx_strrchr_cpp_(cxmutstr string, int chr) {
+    return cx_mutstrcast(cx_strrchr_(cx_strcast(string), chr));
+}
+#define cx_strrchr(s, chr) cx_strrchr_cpp_(cx_strcast_m(s), chr)
+#else
+/**
+ * Internal conversion function - do not use.
+ * @param string
+ * @param chr
+ * @return
+ */
+CX_INLINE cxmutstr cx_strchr_m_(cxmutstr string, int chr) {
+    return cx_mutstrcast(cx_strchr_(cx_strcast(string), chr));
+}
+/**
+ * Internal conversion function - do not use.
+ * @param string
+ * @param chr
+ * @return
+ */
+CX_INLINE  cxmutstr cx_strrchr_m_(cxmutstr string, int chr) {
+    return cx_mutstrcast(cx_strrchr_(cx_strcast(string), chr));
+}
+
+/**
+ * Returns a substring starting at the location of the first occurrence of the
  * specified character.
  *
  * If the string does not contain the character, an empty string is returned.
  *
  * @param string the string where to locate the character
- * @param chr    the character to locate
- * @return       a substring starting at the last location of @p chr
- *
- * @see cx_strrchr_m()
+ * @param chr (@c int) the character to locate
+ * @return (@c cxstring or @c cxmutstr) a substring starting at the first
+ * location of @p chr
  */
-cx_attr_nodiscard
-CX_EXPORT cxstring cx_strrchr(cxstring string, int chr);
+#define cx_strchr(string, chr) _Generic(cx_strcast_m(string), \
+        cxstring: cx_strchr_, \
+        cxmutstr: cx_strchr_m_)(cx_strcast_m(string), chr)
 
 /**
  * Returns a substring starting at the location of the last occurrence of the
@@ -621,32 +765,47 @@ CX_EXPORT cxstring cx_strrchr(cxstring string, int chr);
  * If the string does not contain the character, an empty string is returned.
  *
  * @param string the string where to locate the character
- * @param chr    the character to locate
- * @return       a substring starting at the last location of @p chr
- *
- * @see cx_strrchr()
+ * @param chr (@c int) the character to locate
+ * @return (@c cxstring or @c cxmutstr) a substring starting at the last
+ * location of @p chr
  */
-cx_attr_nodiscard
-CX_EXPORT cxmutstr cx_strrchr_m(cxmutstr string, int chr);
+#define cx_strrchr(string, chr) _Generic(cx_strcast_m(string), \
+        cxstring: cx_strrchr_, \
+        cxmutstr: cx_strrchr_m_)(cx_strcast_m(string), chr)
+#endif
 
 /**
- * Returns a substring starting at the location of the first occurrence of the
- * specified string.
+ * Searches for a specific substring.
  *
- * If @p haystack does not contain @p needle, an empty string is returned.
- *
- * If @p needle is an empty string, the complete @p haystack is
- * returned.
+ * 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_m()
+ * @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);
+CX_EXTERN CX_NODISCARD
+cxstring cx_strstr_(cxstring haystack, cxstring needle);
+
+#ifdef __cplusplus
+CX_CPPDECL cxstring cx_strstr_cpp_(cxstring haystack, cxstring needle) {
+    return cx_strstr_(haystack, needle);
+}
+CX_CPPDECL cxmutstr cx_strstr_cpp_(cxmutstr haystack, cxstring needle) {
+    return cx_mutstrcast(cx_strstr_(cx_strcast(haystack), needle));
+}
+#define cx_strstr(h,n) cx_strstr_cpp_(cx_strcast_m(h), cx_strcast(n))
+#else
+/**
+ * Internal conversion - do not use.
+ * @param haystack
+ * @param needle
+ * @return
+ */
+CX_INLINE  cxmutstr cx_strstr_m_(cxmutstr haystack, cxstring needle) {
+    return cx_mutstrcast(cx_strstr_(cx_strcast(haystack), needle));
+}
 
 /**
  * Returns a substring starting at the location of the first occurrence of the
@@ -658,52 +817,46 @@ CX_EXPORT cxstring cx_strstr(cxstring haystack, cxstring needle);
  * 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
- * @see cx_strstr()
+ * @param needle string containing the sequence of characters to match
+ * @return (@c cxstring or @c cxmutstr) a substring starting at the first
+ * occurrence of @p needle, or an empty string, if the sequence is not contained
  */
-cx_attr_nodiscard
-CX_EXPORT cxmutstr cx_strstr_m(cxmutstr haystack, cxstring needle);
+#define cx_strstr(haystack, needle) _Generic(cx_strcast_m(haystack), \
+        cxstring: cx_strstr_,\
+        cxmutstr: cx_strstr_m_)(cx_strcast_m(haystack), cx_strcast(needle))
+#endif
 
 /**
  * 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_EXTERN CX_NODISCARD CX_NONNULL CX_ACCESS_W(4, 3)
+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_EXTERN CX_NODISCARD CX_NONNULL CX_ACCESS_W(5)
+size_t cx_strsplit_a_(const CxAllocator *allocator,
         cxstring string, cxstring delim,
         size_t limit, cxstring **output);
 
@@ -711,19 +864,78 @@ CX_EXPORT size_t cx_strsplit_a(const CxAllocator *allocator,
 /**
  * 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_EXTERN CX_NODISCARD CX_NONNULL CX_ACCESS_W(4, 3)
+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_EXTERN CX_NODISCARD CX_NONNULL CX_ACCESS_W(5)
+size_t cx_strsplit_ma_(const CxAllocator *allocator,
+        cxmutstr string, cxstring delim, size_t limit,
+        cxmutstr **output);
+
+#ifdef __cplusplus
+CX_CPPDECL size_t cx_strsplit_cpp_(cxstring string, cxstring delim,
+        size_t limit, cxstring *output) {
+    return cx_strsplit_(string, delim, limit, output);
+}
+CX_CPPDECL size_t cx_strsplit_cpp_(cxmutstr string, cxstring delim,
+        size_t limit, cxmutstr *output) {
+    return cx_strsplit_m_(string, delim, limit, output);
+}
+CX_CPPDECL size_t cx_strsplit_a_cpp_(const CxAllocator *allocator,
+        cxstring string, cxstring delim, size_t limit, cxstring **output) {
+    return cx_strsplit_a_(allocator, string, delim, limit, output);
+}
+CX_CPPDECL size_t cx_strsplit_a_cpp_(const CxAllocator *allocator,
+        cxmutstr string, cxstring delim, size_t limit, cxmutstr **output) {
+    return cx_strsplit_ma_(allocator, string, delim, limit, output);
+}
+#define cx_strsplit(string, delim, limit, output) \
+        cx_strsplit_cpp_(cx_strcast_m(string), cx_strcast(delim), limit, output)
+#define cx_strsplit_a(allocator, string, delim, limit, output) \
+        cx_strsplit_a_cpp_(allocator, cx_strcast_m(string), cx_strcast(delim), limit, output)
+#else
+/**
+ * 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 the string to split
+ * @param delim the delimiter
+ * @param limit (@c size_t) the maximum number of split items
+ * @param output (@c cxstring* or @c cxmutstr*) a preallocated array of at
+ * least @p limit length
+ * @return the actual number of split items
+ */
+#define cx_strsplit(string, delim, limit, output) \
+        _Generic(cx_strcast_m(string), \
+        cxstring: cx_strsplit_, \
+        cxmutstr: cx_strsplit_m_)\
+        (cx_strcast_m(string), cx_strcast(delim), limit, output)
+
 /**
  * Splits a given string using a delimiter string.
  *
@@ -735,18 +947,20 @@ CX_EXPORT size_t cx_strsplit_m(cxmutstr string, cxstring delim,
  * @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 allocator (@c CxAllocator*) 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 limit (@c size_t) the maximum number of split items
+ * @param output (@c cxstring** or @c cxmutstr**) 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) \
+        _Generic(cx_strcast_m(string), \
+        cxstring: cx_strsplit_a_, \
+        cxmutstr: cx_strsplit_ma_)\
+        (allocator, cx_strcast_m(string), cx_strcast(delim), limit, output)
+#endif
 
 /**
  * Compares two strings.
@@ -756,8 +970,8 @@ CX_EXPORT size_t cx_strsplit_ma(const CxAllocator *allocator,
  * @return negative if @p s1 is smaller than @p s2, positive if @p s1 is larger
  * than @p s2, zero if both strings equal
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_strcmp_(cxstring s1, cxstring s2);
+CX_EXTERN CX_NODISCARD
+int cx_strcmp_(cxstring s1, cxstring s2);
 
 /**
  * Compares two strings.
@@ -777,8 +991,8 @@ CX_EXPORT int cx_strcmp_(cxstring s1, cxstring s2);
  * @return negative if @p s1 is smaller than @p s2, positive if @p s1 is larger
  * than @p s2, zero if both strings equal ignoring case
  */
-cx_attr_nodiscard
-CX_EXPORT int cx_strcasecmp_(cxstring s1, cxstring s2);
+CX_EXTERN CX_NODISCARD
+int cx_strcasecmp_(cxstring s1, cxstring s2);
 
 /**
  * Compares two strings ignoring case.
@@ -803,8 +1017,8 @@ CX_EXPORT int cx_strcasecmp_(cxstring s1, cxstring s2);
  * @return negative if @p s1 is smaller than @p s2, positive if @p s1 is larger
  * than @p s2, zero if both strings equal
  */
-cx_attr_nodiscard  cx_attr_nonnull
-CX_EXPORT int cx_strcmp_p(const void *s1, const void *s2);
+CX_EXTERN CX_NODISCARD CX_NONNULL
+int cx_strcmp_p(const void *s1, const void *s2);
 
 /**
  * Compares two strings ignoring case.
@@ -816,9 +1030,8 @@ CX_EXPORT int cx_strcmp_p(const void *s1, const void *s2);
  * @return negative if @p s1 is smaller than @p s2, positive if @p s1 is larger
  * than @p s2, zero if both strings equal ignoring case
  */
-cx_attr_nodiscard cx_attr_nonnull
-CX_EXPORT int cx_strcasecmp_p(const void *s1, const void *s2);
-
+CX_EXTERN CX_NODISCARD CX_NONNULL
+int cx_strcasecmp_p(const void *s1, const void *s2);
 
 /**
  * Creates a duplicate of the specified string.
@@ -832,8 +1045,8 @@ CX_EXPORT int cx_strcasecmp_p(const void *s1, const void *s2);
  * @return a duplicate of the string
  * @see cx_strdup()
  */
-cx_attr_nodiscard cx_attr_nonnull
-CX_EXPORT cxmutstr cx_strdup_a_(const CxAllocator *allocator, cxstring string);
+CX_EXTERN CX_NODISCARD CX_NONNULL
+cxmutstr cx_strdup_a_(const CxAllocator *allocator, cxstring string);
 
 /**
  * Creates a duplicate of the specified string.
@@ -866,16 +1079,31 @@ CX_EXPORT cxmutstr cx_strdup_a_(const CxAllocator *allocator, cxstring string);
 #define cx_strdup(string) cx_strdup_a(cxDefaultAllocator, string)
 
 /**
- * Omits leading and trailing spaces.
- *
- * @note the returned string references the same memory, thus you
- * must @em not free the returned memory.
- *
- * @param string the string that shall be trimmed
- * @return the trimmed string
+ * Trims a string.
+ * Internal function - do not use.
+ * @param string
+ * @return
  */
-cx_attr_nodiscard
-CX_EXPORT cxstring cx_strtrim(cxstring string);
+CX_EXTERN CX_NODISCARD
+cxstring cx_strtrim_(cxstring string);
+
+#ifdef __cplusplus
+CX_CPPDECL cxstring cx_strtrim_cpp_(cxstring string) {
+    return cx_strtrim_(string);
+}
+CX_CPPDECL cxmutstr cx_strtrim_cpp_(cxmutstr string) {
+    return cx_mutstrcast(cx_strtrim_(cx_strcast(string)));
+}
+#define cx_strtrim(string) cx_strtrim_cpp_(cx_strcast_m(string))
+#else
+/**
+ * Internal conversion function.
+ * @param string
+ * @return
+ */
+CX_INLINE cxmutstr cx_strtrim_m_(cxmutstr string) {
+    return cx_mutstrcast(cx_strtrim_(cx_strcast(string)));
+}
 
 /**
  * Omits leading and trailing spaces.
@@ -884,10 +1112,12 @@ CX_EXPORT cxstring cx_strtrim(cxstring string);
  * must @em not free the returned memory.
  *
  * @param string the string that shall be trimmed
- * @return the trimmed string
+ * @return (@c cxstring or @c cxmutstr) the trimmed string
  */
-cx_attr_nodiscard
-CX_EXPORT cxmutstr cx_strtrim_m(cxmutstr string);
+#define cx_strtrim(string) _Generic(cx_strcast_m(string), \
+        cxstring: cx_strtrim_, \
+        cxmutstr: cx_strtrim_m_)(cx_strcast_m(string))
+#endif
 
 /**
  * Checks if a string has a specific prefix.
@@ -897,8 +1127,8 @@ CX_EXPORT cxmutstr cx_strtrim_m(cxmutstr string);
  * @return @c true, if and only if the string has the specified prefix,
  * @c false otherwise
  */
-cx_attr_nodiscard
-CX_EXPORT bool cx_strprefix_(cxstring string, cxstring prefix);
+CX_EXTERN CX_NODISCARD
+bool cx_strprefix_(cxstring string, cxstring prefix);
 
 /**
  * Checks if a string has a specific prefix.
@@ -918,8 +1148,8 @@ CX_EXPORT bool cx_strprefix_(cxstring string, cxstring prefix);
  * @return @c true, if and only if the string has the specified suffix,
  * @c false otherwise
  */
-cx_attr_nodiscard
-CX_EXPORT bool cx_strsuffix_(cxstring string, cxstring suffix);
+CX_EXTERN CX_NODISCARD
+bool cx_strsuffix_(cxstring string, cxstring suffix);
 
 /**
  * Checks if a string has a specific suffix.
@@ -939,8 +1169,8 @@ CX_EXPORT bool cx_strsuffix_(cxstring string, cxstring suffix);
  * @return @c true, if and only if the string has the specified prefix,
  * @c false otherwise
  */
-cx_attr_nodiscard
-CX_EXPORT bool cx_strcaseprefix_(cxstring string, cxstring prefix);
+CX_EXTERN CX_NODISCARD
+bool cx_strcaseprefix_(cxstring string, cxstring prefix);
 
 /**
  * Checks if a string has a specific prefix, ignoring the case.
@@ -960,8 +1190,8 @@ CX_EXPORT bool cx_strcaseprefix_(cxstring string, cxstring prefix);
  * @return @c true, if and only if the string has the specified suffix,
  * @c false otherwise
  */
-cx_attr_nodiscard
-CX_EXPORT bool cx_strcasesuffix_(cxstring string, cxstring suffix);
+CX_EXTERN CX_NODISCARD
+bool cx_strcasesuffix_(cxstring string, cxstring suffix);
 
 /**
  * Checks, if a string has a specific suffix, ignoring the case.
@@ -973,6 +1203,26 @@ CX_EXPORT bool cx_strcasesuffix_(cxstring string, cxstring suffix);
  */
 #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_EXTERN CX_NODISCARD CX_NONNULL
+cxmutstr cx_strreplace_(const CxAllocator *allocator,
+        cxstring str, cxstring search, cxstring replacement, size_t replmax);
+
 /**
  * Replaces a string with another string.
  *
@@ -984,16 +1234,15 @@ CX_EXPORT bool cx_strcasesuffix_(cxstring string, cxstring suffix);
  * 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.
@@ -1006,9 +1255,9 @@ CX_EXPORT cxmutstr cx_strreplacen_a(const CxAllocator *allocator,
  * 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
  */
@@ -1025,9 +1274,9 @@ CX_EXPORT cxmutstr cx_strreplacen_a(const CxAllocator *allocator,
  * 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) \
@@ -1042,9 +1291,9 @@ CX_EXPORT cxmutstr cx_strreplacen_a(const CxAllocator *allocator,
  * 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) \
@@ -1058,8 +1307,8 @@ CX_EXPORT cxmutstr cx_strreplacen_a(const CxAllocator *allocator,
  * @param limit the maximum number of tokens that shall be returned
  * @return a new string tokenization context
  */
-cx_attr_nodiscard
-CX_EXPORT CxStrtokCtx cx_strtok_(cxstring str, cxstring delim, size_t limit);
+CX_EXTERN CX_NODISCARD
+CxStrtokCtx cx_strtok_(cxstring str, cxstring delim, size_t limit);
 
 /**
  * Creates a string tokenization context.
@@ -1082,25 +1331,34 @@ CX_EXPORT CxStrtokCtx cx_strtok_(cxstring str, cxstring delim, size_t limit);
  * @return true if successful, false if the limit or the end of the string
  * has been reached
  */
-cx_attr_nonnull  cx_attr_nodiscard  cx_attr_access_w(2)
-CX_EXPORT bool cx_strtok_next(CxStrtokCtx *ctx, cxstring *token);
+CX_EXTERN CX_NONNULL CX_NODISCARD CX_ACCESS_W(2)
+bool cx_strtok_next_(CxStrtokCtx *ctx, cxstring *token);
 
+#ifdef __cplusplus
+CX_CPPDECL cx_strtok_next(CxStrtokCtx *ctx, cxstring *token) {
+    return cx_strtok_next_(ctx, token);
+}
+CX_CPPDECL cx_strtok_next(CxStrtokCtx *ctx, cxmutstr *token) {
+    // Note: this is actually UB - fixed with start_lifetime_as() in C++23
+    //       but it works on all supported platforms
+    return cx_strtok_next_(ctx, reinterpret_cast<cxstring*>(token));
+}
+#else // ! __cplusplus
 /**
- * Returns the next token of a mutable string.
+ * Returns the next token.
  *
  * The token will point to the source string.
  *
- * @attention
- * If the context was not initialized over a mutable string, modifying
- * the data of the returned token is undefined behavior.
- *
- * @param ctx the tokenization context
- * @param token a pointer to memory where the next token shall be stored
+ * @param ctx (@c CxStrtokCtx*) the tokenization context
+ * @param token a pointer to either a @c cxstring or @c cxmutstr
+ * where the next token shall be stored
  * @return true if successful, false if the limit or the end of the string
  * has been reached
  */
-cx_attr_nonnull  cx_attr_nodiscard  cx_attr_access_w(2)
-CX_EXPORT bool cx_strtok_next_m(CxStrtokCtx *ctx, cxmutstr *token);
+#define cx_strtok_next(ctx, token) _Generic((token), \
+        cxstring*: cx_strtok_next_, \
+        cxmutstr*: cx_strtok_next_)(ctx, (cxstring*)token)
+#endif
 
 /**
  * Defines an array of more delimiters for the specified tokenization context.
@@ -1109,8 +1367,8 @@ CX_EXPORT bool cx_strtok_next_m(CxStrtokCtx *ctx, cxmutstr *token);
  * @param delim array of more delimiters
  * @param count number of elements in the array
  */
-cx_attr_nonnull cx_attr_access_r(2, 3)
-CX_EXPORT void cx_strtok_delim(CxStrtokCtx *ctx, const cxstring *delim, size_t count);
+CX_EXTERN CX_NONNULL CX_ACCESS_R(2, 3)
+void cx_strtok_delim(CxStrtokCtx *ctx, const cxstring *delim, size_t count);
 
 /* ------------------------------------------------------------------------- *
  *                string to number conversion functions                      *
@@ -1130,8 +1388,8 @@ CX_EXPORT void cx_strtok_delim(CxStrtokCtx *ctx, const cxstring *delim, size_t c
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtos_lc_(cxstring str, short *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtos_lc_(cxstring str, short *output, int base, const char *groupsep);
 
 /**
  * Converts a string to a number.
@@ -1147,8 +1405,8 @@ CX_EXPORT int cx_strtos_lc_(cxstring str, short *output, int base, const char *g
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtoi_lc_(cxstring str, int *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtoi_lc_(cxstring str, int *output, int base, const char *groupsep);
 
 /**
  * Converts a string to a number.
@@ -1164,8 +1422,8 @@ CX_EXPORT int cx_strtoi_lc_(cxstring str, int *output, int base, const char *gro
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtol_lc_(cxstring str, long *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtol_lc_(cxstring str, long *output, int base, const char *groupsep);
 
 /**
  * Converts a string to a number.
@@ -1181,8 +1439,8 @@ CX_EXPORT int cx_strtol_lc_(cxstring str, long *output, int base, const char *gr
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtoll_lc_(cxstring str, long long *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtoll_lc_(cxstring str, long long *output, int base, const char *groupsep);
 
 /**
  * Converts a string to a number.
@@ -1198,8 +1456,8 @@ CX_EXPORT int cx_strtoll_lc_(cxstring str, long long *output, int base, const ch
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtoi8_lc_(cxstring str, int8_t *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtoi8_lc_(cxstring str, int8_t *output, int base, const char *groupsep);
 
 /**
  * Converts a string to a number.
@@ -1215,8 +1473,8 @@ CX_EXPORT int cx_strtoi8_lc_(cxstring str, int8_t *output, int base, const char
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtoi16_lc_(cxstring str, int16_t *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtoi16_lc_(cxstring str, int16_t *output, int base, const char *groupsep);
 
 /**
  * Converts a string to a number.
@@ -1232,8 +1490,8 @@ CX_EXPORT int cx_strtoi16_lc_(cxstring str, int16_t *output, int base, const cha
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtoi32_lc_(cxstring str, int32_t *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtoi32_lc_(cxstring str, int32_t *output, int base, const char *groupsep);
 
 /**
  * Converts a string to a number.
@@ -1249,8 +1507,8 @@ CX_EXPORT int cx_strtoi32_lc_(cxstring str, int32_t *output, int base, const cha
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtoi64_lc_(cxstring str, int64_t *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtoi64_lc_(cxstring str, int64_t *output, int base, const char *groupsep);
 
 /**
  * Converts a string to a number.
@@ -1266,8 +1524,8 @@ CX_EXPORT int cx_strtoi64_lc_(cxstring str, int64_t *output, int base, const cha
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtous_lc_(cxstring str, unsigned short *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtous_lc_(cxstring str, unsigned short *output, int base, const char *groupsep);
 
 /**
  * Converts a string to a number.
@@ -1283,8 +1541,8 @@ CX_EXPORT int cx_strtous_lc_(cxstring str, unsigned short *output, int base, con
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtou_lc_(cxstring str, unsigned int *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtou_lc_(cxstring str, unsigned int *output, int base, const char *groupsep);
 
 /**
  * Converts a string to a number.
@@ -1300,8 +1558,8 @@ CX_EXPORT int cx_strtou_lc_(cxstring str, unsigned int *output, int base, const
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtoul_lc_(cxstring str, unsigned long *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtoul_lc_(cxstring str, unsigned long *output, int base, const char *groupsep);
 
 /**
  * Converts a string to a number.
@@ -1317,8 +1575,8 @@ CX_EXPORT int cx_strtoul_lc_(cxstring str, unsigned long *output, int base, cons
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtoull_lc_(cxstring str, unsigned long long *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtoull_lc_(cxstring str, unsigned long long *output, int base, const char *groupsep);
 
 /**
  * Converts a string to a number.
@@ -1334,8 +1592,8 @@ CX_EXPORT int cx_strtoull_lc_(cxstring str, unsigned long long *output, int base
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtou8_lc_(cxstring str, uint8_t *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtou8_lc_(cxstring str, uint8_t *output, int base, const char *groupsep);
 
 /**
  * Converts a string to a number.
@@ -1351,8 +1609,8 @@ CX_EXPORT int cx_strtou8_lc_(cxstring str, uint8_t *output, int base, const char
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtou16_lc_(cxstring str, uint16_t *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtou16_lc_(cxstring str, uint16_t *output, int base, const char *groupsep);
 
 /**
  * Converts a string to a number.
@@ -1368,8 +1626,8 @@ CX_EXPORT int cx_strtou16_lc_(cxstring str, uint16_t *output, int base, const ch
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtou32_lc_(cxstring str, uint32_t *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtou32_lc_(cxstring str, uint32_t *output, int base, const char *groupsep);
 
 /**
  * Converts a string to a number.
@@ -1385,8 +1643,8 @@ CX_EXPORT int cx_strtou32_lc_(cxstring str, uint32_t *output, int base, const ch
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtou64_lc_(cxstring str, uint64_t *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtou64_lc_(cxstring str, uint64_t *output, int base, const char *groupsep);
 
 /**
  * Converts a string to a number.
@@ -1402,8 +1660,8 @@ CX_EXPORT int cx_strtou64_lc_(cxstring str, uint64_t *output, int base, const ch
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtoz_lc_(cxstring str, size_t *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtoz_lc_(cxstring str, size_t *output, int base, const char *groupsep);
 
 /**
  * Converts a string to a single precision floating-point number.
@@ -1419,8 +1677,8 @@ CX_EXPORT int cx_strtoz_lc_(cxstring str, size_t *output, int base, const char *
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtof_lc_(cxstring str, float *output, char decsep, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtof_lc_(cxstring str, float *output, char decsep, const char *groupsep);
 
 /**
  * Converts a string to a double precision floating-point number.
@@ -1436,8 +1694,8 @@ CX_EXPORT int cx_strtof_lc_(cxstring str, float *output, char decsep, const char
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupsep);
 
 /**
  * Converts a string to a number.
@@ -2083,8 +2341,4 @@ CX_EXPORT int cx_strtod_lc_(cxstring str, double *output, char decsep, const cha
  */
 #define cx_strtod(str, output) cx_strtod_lc_(cx_strcast(str), output, '.', ",")
 
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
 #endif //UCX_STRING_H
index ff86b33c6a14905021dc402a509f46bb08828f94..5980f49509549a73df5acca0be85aa4bd71ce76c 100644 (file)
 #include <string.h>
 #include <setjmp.h>
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 #ifndef __FUNCTION__
 /**
  * Alias for the <code>__func__</code> preprocessor macro.
@@ -136,7 +132,7 @@ struct CxTestSuite {
  * @param name optional name of the suite
  * @return a new test suite
  */
-cx_attr_nonnull cx_attr_nodiscard  cx_attr_cstr_arg(1) cx_attr_malloc
+CX_NONNULL CX_NODISCARD  CX_CSTR_ARG(1) CX_MALLOC
 static inline CxTestSuite* cx_test_suite_new(const char *name) {
     CxTestSuite* suite = (CxTestSuite*) malloc(sizeof(CxTestSuite));
     if (suite != NULL) {
@@ -173,7 +169,7 @@ CX_INLINE void cx_test_suite_free(CxTestSuite* suite) {
  * @retval zero success
  * @retval non-zero failure
  */
-cx_attr_nonnull
+CX_NONNULL
 CX_INLINE int cx_test_register(CxTestSuite* suite, CxTest test) {
     CxTestSet *t = (CxTestSet*) malloc(sizeof(CxTestSet));
     if (t) {
@@ -200,7 +196,7 @@ CX_INLINE int cx_test_register(CxTestSuite* suite, CxTest test) {
  * @param out_target the target buffer or file to write the output to
  * @param out_writer the write function writing to @p out_target
  */
-cx_attr_nonnull
+CX_NONNULL
 CX_INLINE void cx_test_run(CxTestSuite *suite, void *out_target, cx_write_func out_writer) {
     if (suite->name == NULL) {
         out_writer("*** Test Suite ***\n", 1, 19, out_target);
@@ -218,7 +214,7 @@ CX_INLINE void cx_test_run(CxTestSuite *suite, void *out_target, cx_write_func o
     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);
@@ -326,9 +322,5 @@ CX_INLINE void cx_test_run(CxTestSuite *suite, void *out_target, cx_write_func o
 #define CX_TEST_CALL_SUBROUTINE(name,...) \
         name(_suite_,_output_,_writefnc_,_env_,__VA_ARGS__)
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif /* UCX_TEST_H */
 
index 23ab58e7f717a3237e4183a4795f03047f5f09ef..d9e6fe189312c5268c601e2d6305b3e226911ebd 100644 (file)
 
 #include "collection.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * A depth-first tree iterator.
- *
- * This iterator is not position-aware in a strict sense, as it does not assume
- * a particular order of elements in the tree. However, the iterator keeps track
- * of the number of nodes it has passed in a counter-variable.
- * Each node, regardless of the number of passes, is counted only once.
- *
- * @note Objects that are pointed to by an iterator are mutable through that
- * iterator. However, if the
- * underlying data structure is mutated by other means than this iterator (e.g.,
- * elements added or removed), the iterator becomes invalid (regardless of what
- * cxIteratorValid() returns).
- *
- * @see CxIterator
- */
-typedef struct cx_tree_iterator_s {
-    /**
-     * Base members.
-     */
-    CX_ITERATOR_BASE;
-    /**
-     * Indicates whether the subtree below the current node shall be skipped.
-     */
-    bool skip;
-    /**
-     * Set to true, when the iterator shall visit a node again
-     * when all its children have been processed.
-     */
-    bool visit_on_exit;
-    /**
-     * True, if this iterator is currently leaving the node.
-     */
-    bool exiting;
-    /**
-     * Offset in the node struct for the children linked list.
-     */
-    ptrdiff_t loc_children;
-    /**
-     * Offset in the node struct for the next pointer.
-     */
-    ptrdiff_t loc_next;
-    /**
-     * The total number of distinct nodes that have been passed so far.
-     * This includes the current node.
-     */
-    size_t counter;
-    /**
-     * The currently observed node.
-     *
-     * This is the same what cxIteratorCurrent() would return.
-     */
-    void *node;
-    /**
-     * Stores a copy of the pointer to the successor of the visited node.
-     * Allows freeing a node on exit without corrupting the iteration.
-     */
-    void *node_next;
-    /**
-     * Internal stack.
-     * Will be automatically freed once the iterator becomes invalid.
-     *
-     * If you want to discard the iterator before, you need to manually
-     * call cxTreeIteratorDispose().
-     */
-    void **stack;
-    /**
-     * Internal capacity of the stack.
-     */
-    size_t stack_capacity;
-    union {
-        /**
-         * Internal stack size.
-         */
-        size_t stack_size;
-        /**
-         * The current depth in the tree.
-         * The node with which the iteration starts has depth 1.
-         */
-        size_t depth;
-    };
-} CxTreeIterator;
-
 /**
  * An element in a visitor queue.
  */
@@ -147,35 +60,13 @@ struct cx_tree_visitor_queue_s {
 };
 
 /**
- * A breadth-first tree iterator.
- *
- * This iterator needs to maintain a visitor queue that will be automatically
- * freed once the iterator becomes invalid.
- * If you want to discard the iterator before, you MUST manually call
- * cxTreeVisitorDispose().
- *
- * This iterator is not position-aware in a strict sense, as it does not assume
- * a particular order of elements in the tree. However, the iterator keeps track
- * of the number of nodes it has passed in a counter-variable.
- * Each node, regardless of the number of passes, is counted only once.
- *
- * @note Objects that are pointed to by an iterator are mutable through that
- * iterator. However, if the
- * underlying data structure is mutated by other means than this iterator (e.g.,
- * elements added or removed), the iterator becomes invalid (regardless of what
- * cxIteratorValid() returns).
- *
- * @see CxIterator
+ * An iterator (DFS) or visitor (BFS) for a tree.
  */
-typedef struct cx_tree_visitor_s {
+typedef struct cx_tree_combined_iterator_s {
     /**
      * Base members.
      */
     CX_ITERATOR_BASE;
-    /**
-     * Indicates whether the subtree below the current node shall be skipped.
-     */
-    bool skip;
     /**
      * Offset in the node struct for the children linked list.
      */
@@ -189,6 +80,10 @@ typedef struct cx_tree_visitor_s {
      * This includes the currently visited node.
      */
     size_t counter;
+    /**
+     * The current depth in the tree.
+     */
+    size_t depth;
     /**
      * The currently observed node.
      *
@@ -196,32 +91,64 @@ typedef struct cx_tree_visitor_s {
      */
     void *node;
     /**
-     * The current depth in the tree.
+     * Memory for BFS or DFS-specific data.
      */
-    size_t depth;
+    union {
+        struct {
+            /**
+             * Stores a copy of the pointer to the successor of the visited node.
+             * Allows freeing a node on exit without corrupting the iteration.
+             */
+            void *node_next;
+            /**
+             * Internal stack.
+             * Will be automatically freed once the iterator becomes invalid.
+             *
+             * If you want to discard the iterator before, you need to manually
+             * call cxTreeIteratorDispose().
+             */
+            void **stack;
+            /**
+             * Internal capacity of the stack.
+             */
+            size_t stack_capacity;
+        };
+        struct {
+            /**
+             * The next element in the visitor queue.
+             */
+            struct cx_tree_visitor_queue_s *queue_next;
+            /**
+             * The last element in the visitor queue.
+             */
+            struct cx_tree_visitor_queue_s *queue_last;
+        };
+    };
     /**
-     * The next element in the visitor queue.
+     * Indicates whether the subtree below the current node shall be skipped.
      */
-    struct cx_tree_visitor_queue_s *queue_next;
+    bool skip;
     /**
-     * The last element in the visitor queue.
+     * Set to true, when the iterator shall visit a node again
+     * when all its children have been processed.
      */
-    struct cx_tree_visitor_queue_s *queue_last;
-} CxTreeVisitor;
+    bool visit_on_exit;
+    /**
+     * True, if this iterator is currently leaving the node.
+     */
+    bool exiting;
+    /**
+     * Indicates whether the @c iterator (true) or the @c visitor (false) aspect is active.
+     */
+    bool use_dfs;
+} CxTreeIterator;
 
 /**
  * Releases internal memory of the given tree iterator.
  * @param iter the iterator
  */
-cx_attr_nonnull
-CX_EXPORT void cxTreeIteratorDispose(CxTreeIterator *iter);
-
-/**
- * Releases internal memory of the given tree visitor.
- * @param visitor the visitor
- */
-cx_attr_nonnull
-CX_EXPORT void cxTreeVisitorDispose(CxTreeVisitor *visitor);
+CX_EXTERN CX_NONNULL
+void cxTreeIteratorDispose(CxTreeIterator *iter);
 
 /**
  * Advises the iterator to skip the subtree below the current node and
@@ -231,14 +158,6 @@ CX_EXPORT void cxTreeVisitorDispose(CxTreeVisitor *visitor);
  */
 #define cxTreeIteratorContinue(iterator) (iterator).skip = true; continue
 
-/**
- * Advises the visitor to skip the subtree below the current node and
- * also continues the current loop.
- *
- * @param visitor (@c CxTreeVisitor) the visitor
- */
-#define cxTreeVisitorContinue(visitor) cxTreeIteratorContinue(visitor)
-
 /**
  * Links a node to a (new) parent.
  *
@@ -254,10 +173,10 @@ CX_EXPORT void cxTreeVisitorDispose(CxTreeVisitor *visitor);
  * the last child in the linked list (negative if there is no such pointer)
  * @param loc_prev optional offset in the node struct for the prev pointer
  * @param loc_next offset in the node struct for the next pointer
- * @see cx_tree_unlink()
+ * @see cx_tree_remove()
  */
-cx_attr_nonnull
-CX_EXPORT void cx_tree_link(void *parent, void *node,
+CX_EXTERN CX_NONNULL
+void cx_tree_add(void *parent, void *node,
         ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
         ptrdiff_t loc_prev, ptrdiff_t loc_next);
 
@@ -273,10 +192,10 @@ CX_EXPORT void cx_tree_link(void *parent, void *node,
  * the last child in the linked list (negative if there is no such pointer)
  * @param loc_prev optional offset in the node struct for the prev pointer
  * @param loc_next offset in the node struct for the next pointer
- * @see cx_tree_link()
+ * @see cx_tree_add()
  */
-cx_attr_nonnull
-CX_EXPORT void cx_tree_unlink(void *node,
+CX_EXTERN CX_NONNULL
+void cx_tree_remove(void *node,
         ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
         ptrdiff_t loc_prev, ptrdiff_t loc_next);
 
@@ -311,43 +230,14 @@ CX_EXPORT void cx_tree_unlink(void *node,
  * positive if one of the children might contain the data,
  * negative if neither the node nor the children contains the data
  */
-typedef int (*cx_tree_search_data_func)(const void *node, const void *data);
-
-
-/**
- * Function pointer for a search function.
- *
- * A function of this kind shall check if the specified @p node
- * contains the same @p data as @p new_node or if one of the children might
- * contain the data.
- *
- * The function should use the returned integer to indicate how close the
- * match is, where a negative number means that it does not match at all.
- * Zero means exact match and a positive number is an implementation defined
- * measure for the distance to an exact match.
- *
- * For example, consider a tree that stores file path information.
- * A node which is describing a parent directory of a searched file shall
- * return a positive number to indicate that a child node might contain the
- * searched item. On the other hand, if the node denotes a path that is not a
- * prefix of the searched filename, the function would return -1 to indicate
- * that the search does not need to be continued in that branch.
- *
- * @param node the node that is currently investigated
- * @param new_node a new node with the information which is searched
- *
- * @return 0 if @p node contains the same data as @p new_node,
- * positive if one of the children might contain the data,
- * negative if neither the node nor the children contains the data
- */
-typedef int (*cx_tree_search_func)(const void *node, const void *new_node);
+typedef int (*cx_tree_search_func)(const void *node, const void *data);
 
 /**
  * Searches for data in a tree.
  *
  * When the data cannot be found exactly, the search function might return the
  * closest result, which might be a good starting point for adding a new node
- * to the tree (see also #cx_tree_add()).
+ * to the tree.
  *
  * Depending on the tree structure, it is not necessarily guaranteed that the
  * "closest" match is uniquely defined. This function will search for a node
@@ -356,7 +246,7 @@ typedef int (*cx_tree_search_func)(const void *node, const void *new_node);
  * node matching the criteria is returned.
  *
  * @param root the root node
- * @param depth the maximum depth (zero=indefinite, one=just root)
+ * @param max_depth the maximum depth (zero=indefinite, one=just root)
  * @param data the data to search for
  * @param sfunc the search function
  * @param result where the result shall be stored
@@ -366,39 +256,10 @@ typedef int (*cx_tree_search_func)(const void *node, const void *new_node);
  * could contain the node (but doesn't right now), negative if the tree does not
  * contain any node that might be related to the searched data
  */
-cx_attr_nonnull cx_attr_access_w(5)
-CX_EXPORT int cx_tree_search_data(const void *root, size_t depth,
-        const void *data, cx_tree_search_data_func sfunc,
-        void **result, ptrdiff_t loc_children, ptrdiff_t loc_next);
-
-/**
- * Searches for a node in a tree.
- *
- * When no node with the same data can be found, the search function might
- * return the closest result, which might be a good starting point for adding the
- * new node to the tree (see also #cx_tree_add()).
- *
- * Depending on the tree structure, it is not necessarily guaranteed that the
- * "closest" match is uniquely defined. This function will search for a node
- * with the best match according to the @p sfunc (meaning: the return value of
- * @p sfunc which is closest to zero). If that is also ambiguous, an arbitrary
- * node matching the criteria is returned.
- *
- * @param root the root node
-* @param depth the maximum depth (zero=indefinite, one=just root)
- * @param node the node to search for
- * @param sfunc the search function
- * @param result where the result shall be stored
- * @param loc_children offset in the node struct for the children linked list
- * @param loc_next offset in the node struct for the next pointer
- * @return zero if the node was found exactly, positive if a node was found that
- * could contain the node (but doesn't right now), negative if the tree does not
- * contain any node that might be related to the searched data
- */
-cx_attr_nonnull cx_attr_access_w(5)
-CX_EXPORT int cx_tree_search(const void *root, size_t depth,
-        const void *node, cx_tree_search_func sfunc,
-        void **result, ptrdiff_t loc_children, ptrdiff_t loc_next);
+CX_EXTERN CX_NONNULL_ARG(4, 5) CX_ACCESS_W(5)
+int cx_tree_search(const void *root, size_t max_depth,
+        const void *data, cx_tree_search_func sfunc, void **result,
+        ptrdiff_t loc_children, ptrdiff_t loc_next);
 
 /**
  * Creates a depth-first iterator for a tree with the specified root node.
@@ -420,8 +281,8 @@ CX_EXPORT int cx_tree_search(const void *root, size_t depth,
  * @return the new tree iterator
  * @see cxTreeIteratorDispose()
  */
-cx_attr_nodiscard
-CX_EXPORT CxTreeIterator cx_tree_iterator(void *root, bool visit_on_exit,
+CX_EXTERN CX_NODISCARD
+CxTreeIterator cx_tree_iterator(void *root, bool visit_on_exit,
         ptrdiff_t loc_children, ptrdiff_t loc_next);
 
 /**
@@ -431,7 +292,7 @@ CX_EXPORT CxTreeIterator cx_tree_iterator(void *root, bool visit_on_exit,
  * is allocated using the cxDefaultAllocator.
  * When the visitor becomes invalid, this memory is automatically released.
  * However, if you wish to cancel the iteration before the visitor becomes
- * invalid by itself, you MUST call cxTreeVisitorDispose() manually to release
+ * invalid by itself, you MUST call cxTreeIteratorDispose() manually to release
  * the memory.
  *
  * @remark The returned iterator does not support cxIteratorFlagRemoval().
@@ -440,179 +301,12 @@ CX_EXPORT CxTreeIterator cx_tree_iterator(void *root, bool visit_on_exit,
  * @param loc_children offset in the node struct for the children linked list
  * @param loc_next offset in the node struct for the next pointer
  * @return the new tree visitor
- * @see cxTreeVisitorDispose()
+ * @see cxTreeIteratorDispose()
  */
-cx_attr_nodiscard
-CX_EXPORT CxTreeVisitor cx_tree_visitor(void *root,
+CX_EXTERN CX_NODISCARD
+CxTreeIterator cx_tree_visitor(void *root,
         ptrdiff_t loc_children, ptrdiff_t loc_next);
 
-/**
- * Describes a function that creates a tree node from the specified data.
- * The first argument points to the data the node shall contain, and
- * the second argument may be used for additional data (e.g., an allocator).
- * Functions of this type shall either return a new pointer to a newly
- * created node or @c NULL when allocation fails.
- *
- * @note the function may leave the node pointers in the struct uninitialized.
- * The caller is responsible to set them according to the intended use case.
- */
-typedef void *(*cx_tree_node_create_func)(const void *, void *);
-
-/**
- * The local search depth for a new subtree when adding multiple elements.
- * The default value is 3.
- * This variable is used by #cx_tree_add_array() and #cx_tree_add_iter() to
- * implement optimized insertion of multiple elements into a tree.
- */
-CX_EXPORT extern unsigned int cx_tree_add_look_around_depth;
-
-/**
- * Adds multiple elements efficiently to a tree.
- *
- * Once an element cannot be added to the tree, this function returns, leaving
- * the iterator in a valid state pointing to the element that could not be
- * added.
- * Also, the pointer of the created node will be stored to @p failed.
- * The integer returned by this function denotes the number of elements obtained
- * from the @p iter that have been successfully processed.
- * When all elements could be processed, a @c NULL pointer will be written to
- * @p failed.
- *
- * The advantage of this function compared to multiple invocations of
- * #cx_tree_add() is that the search for the insert locations is not always
- * started from the root node.
- * Instead, the function checks #cx_tree_add_look_around_depth many parent nodes
- * of the current insert location before starting from the root node again.
- * When the variable is set to zero, only the last found location is checked
- * again.
- *
- * Refer to the documentation of #cx_tree_add() for more details.
- *
- * @param iter a pointer to an arbitrary iterator
- * @param num the maximum number of elements to obtain from the iterator
- * @param sfunc a search function
- * @param cfunc a node creation function
- * @param cdata optional additional data
- * @param root the root node of the tree
- * @param failed location where the pointer to a failed node shall be stored
- * @param loc_parent offset in the node struct for the parent pointer
- * @param loc_children offset in the node struct for the children linked list
- * @param loc_last_child optional offset in the node struct for the pointer to
- * the last child in the linked list (negative if there is no such pointer)
- * @param loc_prev optional offset in the node struct for the prev pointer
- * @param loc_next offset in the node struct for the next pointer
- * @return the number of nodes created and added
- * @see cx_tree_add()
- */
-cx_attr_nonnull_arg(1, 3, 4, 6, 7) cx_attr_access_w(6)
-CX_EXPORT size_t cx_tree_add_iter(struct cx_iterator_base_s *iter, size_t num,
-        cx_tree_search_func sfunc, cx_tree_node_create_func cfunc,
-        void *cdata, void **failed, void *root,
-        ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
-        ptrdiff_t loc_prev, ptrdiff_t loc_next);
-
-/**
- * Adds multiple elements efficiently to a tree.
- *
- * Once an element cannot be added to the tree, this function returns, storing
- * the pointer of the created node to @p failed.
- * The integer returned by this function denotes the number of elements from
- * the @p src array that have been successfully processed.
- * When all elements could be processed, a @c NULL pointer will be written to
- * @p failed.
- *
- * The advantage of this function compared to multiple invocations of
- * #cx_tree_add() is that the search for the insert locations is not always
- * started from the root node.
- * Instead, the function checks #cx_tree_add_look_around_depth many parent nodes
- * of the current insert location before starting from the root node again.
- * When the variable is set to zero, only the last found location is checked
- * again.
- *
- * Refer to the documentation of #cx_tree_add() for more details.
- *
- * @param src a pointer to the source data array
- * @param num the number of elements in the @p src array
- * @param elem_size the size of each element in the @p src array
- * @param sfunc a search function
- * @param cfunc a node creation function
- * @param cdata optional additional data
- * @param failed location where the pointer to a failed node shall be stored
- * @param root the root node of the tree
- * @param loc_parent offset in the node struct for the parent pointer
- * @param loc_children offset in the node struct for the children linked list
- * @param loc_last_child optional offset in the node struct for the pointer to
- * the last child in the linked list (negative if there is no such pointer)
- * @param loc_prev optional offset in the node struct for the prev pointer
- * @param loc_next offset in the node struct for the next pointer
- * @return the number of array elements successfully processed
- * @see cx_tree_add()
- */
-cx_attr_nonnull_arg(1, 4, 5, 7, 8) cx_attr_access_w(7)
-CX_EXPORT size_t cx_tree_add_array(const void *src, size_t num, size_t elem_size,
-        cx_tree_search_func sfunc, cx_tree_node_create_func cfunc,
-        void *cdata, void **failed, void *root,
-        ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
-        ptrdiff_t loc_prev, ptrdiff_t loc_next);
-
-/**
- * Adds data to a tree.
- *
- * An adequate location where to add the new tree node is searched with the
- * specified @p sfunc.
- *
- * When a location is found, the @p cfunc will be invoked with @p cdata.
- *
- * The node returned by @p cfunc will be linked into the tree.
- * When @p sfunc returns a positive integer, the new node will be linked as a
- * child. The other children (now siblings of the new node) are then checked
- * with @p sfunc, whether they could be children of the new node and re-linked
- * accordingly.
- *
- * When @p sfunc returns zero and the found node has a parent, the new
- * node will be added as a sibling - otherwise, the new node will be added
- * as a child.
- *
- * When @p sfunc returns a negative value, the new node will not be added to
- * the tree, and this function returns a non-zero value.
- * The caller should check if @p cnode contains a node pointer and deal with the
- * node that could not be added.
- *
- * This function also returns a non-zero value when @p cfunc tries to allocate
- * a new node but fails to do so. In that case, the pointer stored to @p cnode
- * will be @c NULL.
- *
- * Multiple elements can be added more efficiently with
- * #cx_tree_add_array() or #cx_tree_add_iter().
- *
- * @param src a pointer to the data
- * @param sfunc a search function
- * @param cfunc a node creation function
- * @param cdata optional additional data
- * @param cnode the location where a pointer to the new node is stored
- * @param root the root node of the tree
- * @param loc_parent offset in the node struct for the parent pointer
- * @param loc_children offset in the node struct for the children linked list
- * @param loc_last_child optional offset in the node struct for the pointer to
- * the last child in the linked list (negative if there is no such pointer)
- * @param loc_prev optional offset in the node struct for the prev pointer
- * @param loc_next offset in the node struct for the next pointer
- * @return zero when a new node was created and added to the tree,
- * non-zero otherwise
- */
-cx_attr_nonnull_arg(1, 2, 3, 5, 6) cx_attr_access_w(5)
-CX_EXPORT int cx_tree_add(const void *src,
-        cx_tree_search_func sfunc, cx_tree_node_create_func cfunc,
-        void *cdata, void **cnode, void *root,
-        ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
-        ptrdiff_t loc_prev, ptrdiff_t loc_next);
-
-
-/**
- * Tree class type.
- */
-typedef struct cx_tree_class_s cx_tree_class;
-
 /**
  * Base structure that can be used for tree nodes in a #CxTree.
  */
@@ -642,13 +336,9 @@ struct cx_tree_node_base_s {
 /**
  * Structure for holding the base data of a tree.
  */
-struct cx_tree_s {
+typedef struct cx_tree_s {
+    /** Base attributes. */
     CX_COLLECTION_BASE;
-    /**
-     * The tree class definition.
-     */
-    const cx_tree_class *cl;
-
     /**
      * A pointer to the root node.
      *
@@ -657,24 +347,9 @@ struct cx_tree_s {
     void *root;
 
     /**
-     * A function to create new nodes.
-     *
-     * Invocations to this function will receive a pointer to this tree
-     * structure as the second argument.
-     *
-     * Nodes MAY use #cx_tree_node_base_s as the base layout, but do not need to.
-     */
-    cx_tree_node_create_func node_create;
-
-    /**
-     * A function to compare two nodes.
+     * The size of the node structure.
      */
-    cx_tree_search_func search;
-
-    /**
-     * A function to compare a node with data.
-     */
-    cx_tree_search_data_func search_data;
+    size_t node_size;
 
     /**
      * Offset in the node struct for the parent pointer.
@@ -701,7 +376,12 @@ struct cx_tree_s {
      * Offset in the node struct for the next sibling pointer.
      */
     ptrdiff_t loc_next;
-};
+
+    /**
+     * Offset in the node struct where the payload is located.
+     */
+    ptrdiff_t loc_data;
+} CxTree;
 
 /**
  * Macro to roll out the #cx_tree_node_base_s structure with a custom
@@ -711,7 +391,7 @@ struct cx_tree_s {
  *
  * @param type the data type for the nodes
  */
-#define CX_TREE_NODE_BASE(type) \
+#define CX_TREE_NODE(type) \
     type *parent; \
     type *children;\
     type *last_child;\
@@ -719,51 +399,20 @@ struct cx_tree_s {
     type *next
 
 /**
- * Macro for specifying the layout of a base node tree.
+ * Macro for specifying the layout of a tree node.
  *
- * When your tree uses #CX_TREE_NODE_BASE, you can use this
+ * When your tree uses #CX_TREE_NODE, you can use this
  * macro in all tree functions that expect the layout parameters
  * @c loc_parent, @c loc_children, @c loc_last_child, @c loc_prev,
  * and @c loc_next.
+ * @param struct_name the name of the node structure
  */
-#define cx_tree_node_base_layout \
-    offsetof(struct cx_tree_node_base_s, parent),\
-    offsetof(struct cx_tree_node_base_s, children),\
-    offsetof(struct cx_tree_node_base_s, last_child),\
-    offsetof(struct cx_tree_node_base_s, prev),  \
-    offsetof(struct cx_tree_node_base_s, next)
-
-/**
- * The class definition for arbitrary trees.
- */
-struct cx_tree_class_s {
-    /**
-     * Member function for inserting a single element.
-     *
-     * Implementations SHALL NOT simply invoke @p insert_many as this comes
-     * with too much overhead.
-     */
-    int (*insert_element)(struct cx_tree_s *tree, const void *data);
-
-    /**
-     * Member function for inserting multiple elements.
-     *
-     * Implementations SHALL avoid performing a full search in the tree for
-     * every element even though the source data MAY be unsorted.
-     */
-    size_t (*insert_many)(struct cx_tree_s *tree, struct cx_iterator_base_s *iter, size_t n);
-
-    /**
-     * Member function for finding a node.
-     */
-    void *(*find)(struct cx_tree_s *tree, const void *subtree, const void *data, size_t depth);
-};
-
-/**
- * Common type for all tree implementations.
- */
-typedef struct cx_tree_s CxTree;
-
+#define cx_tree_node_layout(struct_name) \
+    offsetof(struct_name, parent),\
+    offsetof(struct_name, children),\
+    offsetof(struct_name, last_child),\
+    offsetof(struct_name, prev),  \
+    offsetof(struct_name, next)
 
 /**
  * Destroys a node and its subtree.
@@ -783,11 +432,11 @@ typedef struct cx_tree_s CxTree;
  * and would therefore result in a double-free.
  *
  * @param tree the tree
- * @param node the node to remove
+ * @param node the node being the root of the subtree to remove
  * @see cxTreeFree()
  */
-cx_attr_nonnull
-CX_EXPORT void cxTreeDestroySubtree(CxTree *tree, void *node);
+CX_EXTERN CX_NONNULL
+void cxTreeDestroySubtree(CxTree *tree, void *node);
 
 
 /**
@@ -807,7 +456,12 @@ CX_EXPORT void cxTreeDestroySubtree(CxTree *tree, void *node);
  * @param tree the tree
  * @see cxTreeDestroySubtree()
  */
-#define cxTreeClear(tree) cxTreeDestroySubtree(tree, tree->root)
+CX_INLINE
+void cxTreeClear(CxTree *tree) {
+    if (tree->root != NULL) {
+        cxTreeDestroySubtree(tree, tree->root);
+    }
+}
 
 /**
  * Deallocates the tree structure.
@@ -825,71 +479,29 @@ CX_EXPORT void cxTreeDestroySubtree(CxTree *tree, void *node);
  *
  * @param tree the tree to free
  */
-CX_EXPORT void cxTreeFree(CxTree *tree);
+CX_EXTERN
+void cxTreeFree(CxTree *tree);
 
 /**
- * Creates a new tree structure based on the specified layout.
+ * Creates a new tree.
  *
  * The specified @p allocator will be used for creating the tree struct
- * and SHALL be used by @p create_func to allocate memory for the nodes.
+ * @em and the nodes.
  *
- * @note This function will also register an advanced destructor which
- * will free the nodes with the allocator's free() method.
+ * When you do not specify an existing @p root, the tree will be created
+ * empty. Additionally, cxFree() is registered as the advanced destructor for
+ * the tree so that all nodes that you will add to the tree are automatically
+ * freed together with the tree.
+ * When @p root is not @c NULL, no destructors are registered automatically.
  *
- * @param allocator the allocator that shall be used
- * (if @c NULL, the cxDefaultAllocator will be used)
- * @param create_func a function that creates new nodes
- * @param search_func a function that compares two nodes
- * @param search_data_func a function that compares a node with data
- * @param loc_parent offset in the node struct for the parent pointer
- * @param loc_children offset in the node struct for the children linked list
- * @param loc_last_child optional offset in the node struct for the pointer to
- * the last child in the linked list (negative if there is no such pointer)
- * @param loc_prev optional offset in the node struct for the prev pointer
- * @param loc_next offset in the node struct for the next pointer
- * @return the new tree
- * @see cxTreeCreateSimple()
- * @see cxTreeCreateWrapped()
- */
-cx_attr_nonnull_arg(2, 3, 4) cx_attr_nodiscard  cx_attr_malloc cx_attr_dealloc(cxTreeFree, 1)
-CX_EXPORT CxTree *cxTreeCreate(const CxAllocator *allocator, cx_tree_node_create_func create_func,
-        cx_tree_search_func search_func, cx_tree_search_data_func search_data_func,
-        ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
-        ptrdiff_t loc_prev, ptrdiff_t loc_next);
-
-/**
- * Creates a new tree structure based on a default layout.
- *
- * Nodes created by @p create_func MUST contain #cx_tree_node_base_s as the first
- * member (or at least respect the default offsets specified in the tree
- * struct), and they MUST be allocated with the specified allocator.
- *
- * @note This function will also register an advanced destructor which
- * will free the nodes with the allocator's free() method.
- *
- * @param allocator (@c CxAllocator*) the allocator that shall be used
- * @param create_func (@c cx_tree_node_create_func) a function that creates new nodes
- * @param search_func (@c cx_tree_search_func) a function that compares two nodes
- * @param search_data_func (@c cx_tree_search_data_func)  a function that compares a node with data
- * @return (@c CxTree*) the new tree
- * @see cxTreeCreate()
- */
-#define cxTreeCreateSimple(allocator, create_func, search_func, search_data_func) \
-        cxTreeCreate(allocator, create_func, search_func, search_data_func, cx_tree_node_base_layout)
-
-/**
- * Creates a new tree structure based on an existing tree.
- *
- * The specified @p allocator will be used for creating the tree struct.
- *
- * @attention This function will create an incompletely defined tree structure
- * where neither the create function, the search function, nor a destructor
- * will be set. If you wish to use any of this functionality for the wrapped
- * tree, you need to specify those functions afterward.
- *
- * @param allocator the allocator that was used for nodes of the wrapped tree
+ * @param allocator the allocator to use
  * (if @c NULL, the cxDefaultAllocator is assumed)
- * @param root the root node of the tree that shall be wrapped
+ * @param node_size the complete size of one node
+ * @param elem_size the size of the payload stored in the node
+ * (@c CX_STORE_POINTERS is also supported)
+ * @param root an optional already existing root node
+ * @param loc_data optional offset in the node struct for the payload
+ * (when negative, cxTreeAddData() is not supported)
  * @param loc_parent offset in the node struct for the parent pointer
  * @param loc_children offset in the node struct for the children linked list
  * @param loc_last_child optional offset in the node struct for the pointer to
@@ -899,92 +511,91 @@ CX_EXPORT CxTree *cxTreeCreate(const CxAllocator *allocator, cx_tree_node_create
  * @return the new tree
  * @see cxTreeCreate()
  */
-cx_attr_nonnull_arg(2) cx_attr_nodiscard  cx_attr_malloc  cx_attr_dealloc(cxTreeFree, 1)
-CX_EXPORT CxTree *cxTreeCreateWrapped(const CxAllocator *allocator, void *root,
+CX_EXTERN CX_NODISCARD  CX_MALLOC  CX_DEALLOC(cxTreeFree, 1)
+CxTree *cxTreeCreate(const CxAllocator *allocator,
+        size_t node_size, size_t elem_size, void *root, ptrdiff_t loc_data,
         ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
         ptrdiff_t loc_prev, ptrdiff_t loc_next);
 
 /**
- * Inserts data into the tree.
- *
- * @remark For this function to work, the tree needs specified search and
- * create functions, which might not be available for wrapped trees
- * (see #cxTreeCreateWrapped()).
- *
- * @param tree the tree
- * @param data the data to insert
- * @retval zero success
- * @retval non-zero failure
- */
-cx_attr_nonnull
-CX_EXPORT int cxTreeInsert(CxTree *tree, const void *data);
-
-/**
- * Inserts elements provided by an iterator efficiently into the tree.
+ * Searches the data in the specified subtree.
  *
- * @remark For this function to work, the tree needs specified search and
- * create functions, which might not be available for wrapped trees
- * (see #cxTreeCreateWrapped()).
+ * When @p max_depth is zero, the depth is not limited.
+ * The @p subtree_root itself is on depth 1 and its children have depth 2.
  *
- * @param tree the tree
- * @param iter the iterator providing the elements
- * @param n the maximum number of elements to insert
- * @return the number of elements that could be successfully inserted
- */
-cx_attr_nonnull
-CX_EXPORT size_t cxTreeInsertIter(CxTree *tree, CxIteratorBase *iter, size_t n);
-
-/**
- * Inserts an array of data efficiently into the tree.
+ * @note When @p subtree_root is not @c NULL and not part of the @p tree,
+ * the behavior is undefined.
  *
- * @remark For this function to work, the tree needs specified search and
- * create functions, which might not be available for wrapped trees
- * (see #cxTreeCreateWrapped()).
+ * @attention When @p loc_data is not available, this function immediately returns
+ * @c NULL.
  *
  * @param tree the tree
- * @param data the array of data to insert
- * @param elem_size the size of each element in the array
- * @param n the number of elements in the array
- * @return the number of elements that could be successfully inserted
+ * @param data the data to search for
+ * @param subtree_root the node where to start (@c NULL to start from root)
+ * @param max_depth the maximum search depth
+ * @param use_dfs @c true when depth-first search should be used;
+ * @c false when breadth-first search should be used
+ * @return the first matching node, or @c NULL when the data cannot be found
+ * @see cxTreeFind()
  */
-cx_attr_nonnull
-CX_EXPORT size_t cxTreeInsertArray(CxTree *tree, const void *data, size_t elem_size, size_t n);
+CX_EXTERN CX_NONNULL_ARG(1, 2) CX_NODISCARD
+void *cxTreeFindInSubtree(CxTree *tree, const void *data, void *subtree_root,
+        size_t max_depth, bool use_dfs);
 
 /**
  * Searches the data in the specified tree.
  *
- * @remark For this function to work, the tree needs a specified @c search_data
- * function, which might not be available wrapped trees
- * (see #cxTreeCreateWrapped()).
+ * @attention When @p loc_data is not available, this function immediately returns
+ * @c NULL.
  *
  * @param tree the tree
  * @param data the data to search for
+ * @param use_dfs @c true when depth-first search should be used;
+ * @c false when breadth-first search should be used
  * @return the first matching node, or @c NULL when the data cannot be found
+ * @see cxTreeFindInSubtree()
+ * @see cxTreeFindFast()
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT void *cxTreeFind(CxTree *tree, const void *data);
+CX_INLINE CX_NONNULL CX_NODISCARD
+void *cxTreeFind(CxTree *tree, const void *data, bool use_dfs) {
+    if (tree->root == NULL) return NULL;
+    return cxTreeFindInSubtree(tree, data, tree->root, 0, use_dfs);
+}
 
 /**
- * Searches the data in the specified subtree.
+ * Performs an efficient depth-first search in a branch of the specified tree.
  *
  * When @p max_depth is zero, the depth is not limited.
  * The @p subtree_root itself is on depth 1 and its children have depth 2.
  *
- * @note When @p subtree_root is not part of the @p tree, the behavior is
- * undefined.
- *
- * @remark For this function to work, the tree needs a specified @c search_data
- * function, which might not be the case for wrapped trees
- * (see #cxTreeCreateWrapped()).
+ * @note When @p subtree_root is not @c NULL and not part of the @p tree,
+ * the behavior is undefined.
  *
  * @param tree the tree
  * @param data the data to search for
- * @param subtree_root the node where to start
+ * @param sfunc the search function
+ * @param subtree_root the node where to start (@c NULL to start from root)
  * @param max_depth the maximum search depth
  * @return the first matching node, or @c NULL when the data cannot be found
+ * @see cxTreeFindInSubtree()
+ */
+CX_EXTERN CX_NONNULL CX_NODISCARD
+void *cxTreeFindFastInSubtree(CxTree *tree, const void *data,
+        cx_tree_search_func sfunc, void *subtree_root, size_t max_depth);
+
+/**
+ * Performs an efficient depth-first search in the tree.
+ *
+ * @param tree the tree
+ * @param data the data to search for
+ * @param sfunc the search function
+ * @return the first matching node, or @c NULL when the data cannot be found
+ * @see cxTreeFind()
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT void *cxTreeFindInSubtree(CxTree *tree, const void *data, void *subtree_root, size_t max_depth);
+CX_INLINE CX_NONNULL CX_NODISCARD
+void *cxTreeFindFast(CxTree *tree, const void *data, cx_tree_search_func sfunc) {
+    return cxTreeFindFastInSubtree(tree, data, sfunc, tree->root, 0);
+}
 
 /**
  * Determines the size of the specified subtree.
@@ -993,8 +604,8 @@ CX_EXPORT void *cxTreeFindInSubtree(CxTree *tree, const void *data, void *subtre
  * @param subtree_root the root node of the subtree
  * @return the number of nodes in the specified subtree
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT size_t cxTreeSubtreeSize(CxTree *tree, void *subtree_root);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+size_t cxTreeSubtreeSize(CxTree *tree, void *subtree_root);
 
 /**
  * Determines the depth of the specified subtree.
@@ -1003,8 +614,8 @@ CX_EXPORT size_t cxTreeSubtreeSize(CxTree *tree, void *subtree_root);
  * @param subtree_root the root node of the subtree
  * @return the tree depth including the @p subtree_root
  */
-cx_attr_nonnull  cx_attr_nodiscard
-CX_EXPORT size_t cxTreeSubtreeDepth(CxTree *tree, void *subtree_root);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+size_t cxTreeSubtreeDepth(CxTree *tree, void *subtree_root);
 
 /**
  * Determines the size of the entire tree.
@@ -1012,8 +623,8 @@ CX_EXPORT size_t cxTreeSubtreeDepth(CxTree *tree, void *subtree_root);
  * @param tree the tree
  * @return the tree size, counting the root as one
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT size_t cxTreeSize(CxTree *tree);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+size_t cxTreeSize(CxTree *tree);
 
 /**
  * Determines the depth of the entire tree.
@@ -1021,8 +632,8 @@ CX_EXPORT size_t cxTreeSize(CxTree *tree);
  * @param tree the tree
  * @return the tree depth, counting the root as one
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT size_t cxTreeDepth(CxTree *tree);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+size_t cxTreeDepth(CxTree *tree);
 
 /**
  * Creates a depth-first iterator for the specified tree starting in @p node.
@@ -1036,8 +647,8 @@ CX_EXPORT size_t cxTreeDepth(CxTree *tree);
  * @return a tree iterator (depth-first)
  * @see cxTreeVisit()
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT CxTreeIterator cxTreeIterateSubtree(CxTree *tree, void *node, bool visit_on_exit);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+CxTreeIterator cxTreeIterateSubtree(CxTree *tree, void *node, bool visit_on_exit);
 
 /**
  * Creates a breadth-first iterator for the specified tree starting in @p node.
@@ -1049,8 +660,8 @@ CX_EXPORT CxTreeIterator cxTreeIterateSubtree(CxTree *tree, void *node, bool vis
  * @return a tree visitor (a.k.a. breadth-first iterator)
  * @see cxTreeIterate()
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT CxTreeVisitor cxTreeVisitSubtree(CxTree *tree, void *node);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+CxTreeIterator cxTreeVisitSubtree(CxTree *tree, void *node);
 
 /**
  * Creates a depth-first iterator for the specified tree.
@@ -1061,8 +672,8 @@ CX_EXPORT CxTreeVisitor cxTreeVisitSubtree(CxTree *tree, void *node);
  * @return a tree iterator (depth-first)
  * @see cxTreeVisit()
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT CxTreeIterator cxTreeIterate(CxTree *tree, bool visit_on_exit);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+CxTreeIterator cxTreeIterate(CxTree *tree, bool visit_on_exit);
 
 /**
  * Creates a breadth-first iterator for the specified tree.
@@ -1071,22 +682,22 @@ CX_EXPORT CxTreeIterator cxTreeIterate(CxTree *tree, bool visit_on_exit);
  * @return a tree visitor (a.k.a. breadth-first iterator)
  * @see cxTreeIterate()
  */
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT CxTreeVisitor cxTreeVisit(CxTree *tree);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+CxTreeIterator cxTreeVisit(CxTree *tree);
 
 /**
  * Sets the (new) parent of the specified child.
  *
  * If the @p child is not already a member of the tree, this function behaves
- * as #cxTreeAddChildNode().
+ * as #cxTreeAddNode().
  *
  * @param tree the tree
  * @param parent the (new) parent of the child
  * @param child the node to add
- * @see cxTreeAddChildNode()
+ * @see cxTreeAddNode()
  */
-cx_attr_nonnull
-CX_EXPORT void cxTreeSetParent(CxTree *tree, void *parent, void *child);
+CX_EXTERN CX_NONNULL
+void cxTreeSetParent(CxTree *tree, void *parent, void *child);
 
 /**
  * Adds a new node to the tree.
@@ -1095,36 +706,81 @@ CX_EXPORT void cxTreeSetParent(CxTree *tree, void *parent, void *child);
  * Use #cxTreeSetParent() if you want to move a subtree to another location.
  *
  * @attention The node may be externally created, but MUST obey the same rules
- * as if it was created by the tree itself with #cxTreeAddChild() (e.g., use
- * the same allocator).
+ * as if it was created by the tree itself with #cxTreeAddData() (e.g., use
+ * the tree's allocator).
  *
  * @param tree the tree
  * @param parent the parent of the node to add
  * @param child the node to add
  * @see cxTreeSetParent()
+ * @see cxTreeAddData()
  */
-cx_attr_nonnull
-CX_EXPORT void cxTreeAddChildNode(CxTree *tree, void *parent, void *child);
+CX_EXTERN CX_NONNULL
+void cxTreeAddNode(CxTree *tree, void *parent, void *child);
 
 /**
  * Creates a new node and adds it to the tree.
  *
- * With this function you can decide where exactly the new node shall be added.
- * If you specified an appropriate search function, you may want to consider
- * leaving this task to the tree by using #cxTreeInsert().
- *
- * Be aware that adding nodes at arbitrary locations in the tree might cause
- * wrong or undesired results when subsequently invoking #cxTreeInsert(), and
- * the invariant imposed by the search function does not hold any longer.
- *
  * @param tree the tree
  * @param parent the parent node of the new node
  * @param data the data that will be submitted to the create function
- * @return zero when the new node was created, non-zero on allocation failure
- * @see cxTreeInsert()
+ * @return the added node or @c NULL when the allocation failed
+ * @see cxTreeAdd()
+ */
+CX_EXTERN CX_NONNULL
+void *cxTreeAddData(CxTree *tree, void *parent, const void *data);
+
+/**
+ * Creates a new node and adds it to the tree.
+ *
+ * @param tree the tree
+ * @param parent the parent node of the new node
+ * @return the added node or @c NULL when the allocation failed
  */
-cx_attr_nonnull
-CX_EXPORT int cxTreeAddChild(CxTree *tree, void *parent, const void *data);
+CX_EXTERN CX_NODISCARD CX_NONNULL
+void *cxTreeCreateNode(CxTree *tree, void *parent);
+
+/**
+ * Creates a new root node or returns the existing root node.
+ *
+ * @param tree the tree
+ * @return the new root node, the existing root node, or @c NULL when the allocation failed
+ * @see cxTreeCreateRootData()
+ */
+CX_EXTERN CX_NODISCARD CX_NONNULL
+void *cxTreeCreateRoot(CxTree *tree);
+
+/**
+ * Creates a new root node or uses the existing root node and writes the specified data to that node.
+ *
+ * @note This function immediately returns @c NULL when @c loc_data was not initialized with a positive value.
+ *
+ * @param tree the tree
+ * @param data the data for the root node
+ * @return the new root node, the existing root node,
+ * or @c NULL when the allocation failed or the data location is not known
+ * @see cxTreeCreateRoot()
+ */
+CX_EXTERN CX_NODISCARD CX_NONNULL
+void *cxTreeCreateRootData(CxTree *tree, const void *data);
+
+/**
+ * Exchanges the root of the tree.
+ *
+ * @attention The old tree nodes might need to be deallocated by the caller.
+ * On the other hand, when the tree has destructors registered, keep in mind
+ * that they will be applied to the new tree.
+ * In particular, using cxTreeCreate() with a @c NULL root and setting the
+ * root with this function is @em not equivalent to creating the tree with
+ * a reference to an existing root because trees created without a root will
+ * have destructors registered.
+ *
+ * @param tree the tree
+ * @param new_root the new root node
+ * @return the old root node (or @c NULL when the tree was empty)
+ */
+CX_EXTERN CX_NONNULL_ARG(1) CX_NODISCARD
+void *cxTreeSetRoot(CxTree *tree, void *new_root);
 
 /**
  * A function that is invoked when a node needs to be re-linked to a new parent.
@@ -1158,8 +814,8 @@ typedef void (*cx_tree_relink_func)(
  * node
  * @return zero on success, non-zero if @p node is the root node of the tree
  */
-cx_attr_nonnull_arg(1, 2)
-CX_EXPORT int cxTreeRemoveNode(CxTree *tree, void *node, cx_tree_relink_func relink_func);
+CX_EXTERN CX_NONNULL_ARG(1, 2)
+int cxTreeRemoveNode(CxTree *tree, void *node, cx_tree_relink_func relink_func);
 
 /**
  * Removes a node and its subtree from the tree.
@@ -1172,8 +828,8 @@ CX_EXPORT int cxTreeRemoveNode(CxTree *tree, void *node, cx_tree_relink_func rel
  * @param tree the tree
  * @param node the node to remove
  */
-cx_attr_nonnull
-CX_EXPORT void cxTreeRemoveSubtree(CxTree *tree, void *node);
+CX_EXTERN CX_NONNULL
+void cxTreeRemoveSubtree(CxTree *tree, void *node);
 
 /**
  * Destroys a node and re-links its children to its former parent.
@@ -1193,11 +849,7 @@ CX_EXPORT void cxTreeRemoveSubtree(CxTree *tree, void *node);
  * node
  * @return zero on success, non-zero if @p node is the root node of the tree
  */
-cx_attr_nonnull_arg(1, 2)
-CX_EXPORT int cxTreeDestroyNode(CxTree *tree, void *node, cx_tree_relink_func relink_func);
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
+CX_EXTERN CX_NONNULL_ARG(1, 2)
+int cxTreeDestroyNode(CxTree *tree, void *node, cx_tree_relink_func relink_func);
 
 #endif //UCX_TREE_H
index 3eb72995416fb763d8c99ae7f6d53f89934c7691..5884b1f0332738c2126ba13ce8604b44a5d3378b 100644 (file)
@@ -63,14 +63,14 @@ void cx_hash_murmur(CxHashKey *key) {
     switch (len) {
         case 3:
             h ^= (data[i + 2] & 0xFF) << 16;
-            cx_attr_fallthrough;
+            CX_FALLTHROUGH;
         case 2:
             h ^= (data[i + 1] & 0xFF) << 8;
-            cx_attr_fallthrough;
+            CX_FALLTHROUGH;
         case 1:
             h ^= (data[i + 0] & 0xFF);
             h *= m;
-            cx_attr_fallthrough;
+            CX_FALLTHROUGH;
         default: // do nothing
             ;
     }
@@ -82,56 +82,6 @@ void cx_hash_murmur(CxHashKey *key) {
     key->hash = h;
 }
 
-
-uint32_t cx_hash_u32(uint32_t x) {
-    x = ((x >> 16) ^ x) * 0x45d9f3bu;
-    x = ((x >> 16) ^ x) * 0x45d9f3bu;
-    x = (x >> 16) ^ x;
-    return x;
-}
-
-uint64_t cx_hash_u64(uint64_t x) {
-    x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
-    x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb);
-    x = x ^ (x >> 31);
-    return x;
-}
-
-CxHashKey cx_hash_key_str(const char *str) {
-    CxHashKey key;
-    key.data = str;
-    key.len = str == NULL ? 0 : strlen(str);
-    cx_hash_murmur(&key);
-    return key;
-}
-
-CxHashKey cx_hash_key_ustr(unsigned const char *str) {
-    CxHashKey key;
-    key.data = str;
-    key.len = str == NULL ? 0 : strlen((const char*)str);
-    cx_hash_murmur(&key);
-    return key;
-}
-
-CxHashKey cx_hash_key_cxstr(cxstring str) {
-    return cx_hash_key(str.ptr, str.length);
-}
-
-CxHashKey cx_hash_key_mutstr(cxmutstr str) {
-    return cx_hash_key(str.ptr, str.length);
-}
-
-CxHashKey cx_hash_key_bytes(
-        const unsigned char *bytes,
-        size_t len
-) {
-    CxHashKey key;
-    key.data = bytes;
-    key.len = len;
-    cx_hash_murmur(&key);
-    return key;
-}
-
 CxHashKey cx_hash_key(
         const void *obj,
         size_t len
@@ -143,22 +93,6 @@ CxHashKey cx_hash_key(
     return key;
 }
 
-CxHashKey cx_hash_key_u32(uint32_t x) {
-    CxHashKey key;
-    key.data = NULL;
-    key.len = 0;
-    key.hash = cx_hash_u32(x);
-    return key;
-}
-
-CxHashKey cx_hash_key_u64(uint64_t x) {
-    CxHashKey key;
-    key.data = NULL;
-    key.len = 0;
-    key.hash = cx_hash_u64(x);
-    return key;
-}
-
 int cx_hash_key_cmp(const void *l, const void *r) {
     const CxHashKey *left = l;
     const CxHashKey *right = r;
@@ -170,3 +104,7 @@ int cx_hash_key_cmp(const void *l, const void *r) {
     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);
+}
index 504784e16683f01ad5e2aa533080329cd14627bb..456494c8fc1902df4c7bf4bb3c8e4213de4fe1bb 100644 (file)
@@ -78,7 +78,7 @@ static void cx_hash_map_destructor(struct cx_map_s *map) {
     cxFree(map->collection.allocator, map);
 }
 
-static void *cx_hash_map_put(
+static CxMapEntry cx_hash_map_put(
         CxMap *map,
         CxHashKey key,
         void *value
@@ -117,7 +117,7 @@ static void *cx_hash_map_put(
                 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) {
@@ -132,7 +132,7 @@ static void *cx_hash_map_put(
         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;
@@ -152,8 +152,8 @@ static void *cx_hash_map_put(
         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(
@@ -400,6 +400,13 @@ static cx_map_class cx_hash_map_class = {
         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,
@@ -414,8 +421,7 @@ CxMap *cxHashMapCreate(
         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
@@ -433,9 +439,12 @@ CxMap *cxHashMapCreate(
 
     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;
index 292c11ea06002994f02e0a563cc0ca5e4e493e90..f7d187251ab9a760d4832f0f035da33f6fe7cc1c 100644 (file)
@@ -29,6 +29,7 @@
 #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;
@@ -45,51 +46,14 @@ static void *cx_iter_current_ptr(const void *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;
@@ -99,19 +63,18 @@ CxIterator cxIterator(
     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;
 }
index 967356b708b20976b67822913bd0d1bd315fc865..b9b3d7c8078b8b868a8b06f711081c8e06184742 100644 (file)
@@ -98,21 +98,21 @@ static CxJsonTokenType token_numbertype(const char *content, size_t length) {
 static CxJsonToken token_create(CxJson *json, bool isstring, size_t start, size_t end) {
     cxmutstr str = cx_mutstrn(json->buffer.space + start, end - start);
     bool allocated = false;
-    if (json->uncompleted.tokentype != CX_JSON_NO_TOKEN) {
+    if (json->uncompleted_tokentype != CX_JSON_NO_TOKEN) {
         allocated = true;
-        str = cx_strcat_m(json->uncompleted.content, 1, str);
-        if (str.ptr == NULL) { // LCOV_EXCL_START
-            return (CxJsonToken){CX_JSON_NO_TOKEN, false, {NULL, 0}};
-        } // LCOV_EXCL_STOP
+        str = cx_strcat(json->uncompleted_content, 1, str);
+        if (str.ptr == NULL) {
+            return (CxJsonToken){CX_JSON_NO_TOKEN, false, CX_NULLSTR}; // LCOV_EXCL_LINE
+        }
+        json->uncompleted_content = CX_NULLSTR;
+        json->uncompleted_tokentype = CX_JSON_NO_TOKEN;
     }
-    json->uncompleted = (CxJsonToken){0};
     CxJsonTokenType ttype;
     if (isstring) {
         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(str, "true") || !cx_strcmp(str, "false")
+            || !cx_strcmp(str, "null")) {
             ttype = CX_JSON_TOKEN_LITERAL;
         } else {
             ttype = token_numbertype(str.ptr, str.length);
@@ -122,7 +122,7 @@ static CxJsonToken token_create(CxJson *json, bool isstring, size_t start, size_
         if (allocated) {
             cx_strfree(&str);
         }
-        return (CxJsonToken){CX_JSON_TOKEN_ERROR, false, {NULL, 0}};
+        return (CxJsonToken){CX_JSON_TOKEN_ERROR, false, CX_NULLSTR};
     }
     return (CxJsonToken){ttype, allocated, str};
 }
@@ -162,16 +162,16 @@ static CxJsonTokenType char2ttype(char c) {
 static enum cx_json_status token_parse_next(CxJson *json, CxJsonToken *result) {
     // check if there is data in the buffer
     if (cxBufferEof(&json->buffer)) {
-        return json->uncompleted.tokentype == CX_JSON_NO_TOKEN ?
+        return json->uncompleted_tokentype == CX_JSON_NO_TOKEN ?
             CX_JSON_NO_DATA : CX_JSON_INCOMPLETE_DATA;
     }
 
     // current token type and start index
-    CxJsonTokenType ttype = json->uncompleted.tokentype;
+    CxJsonTokenType ttype = json->uncompleted_tokentype;
     size_t token_part_start = json->buffer.pos;
 
     bool escape_end_of_string = ttype == CX_JSON_TOKEN_STRING
-        && json->uncompleted.content.ptr[json->uncompleted.content.length-1] == '\\';
+        && cx_strat(json->uncompleted_content, -1) == '\\';
 
     for (size_t i = json->buffer.pos; i < json->buffer.size; i++) {
         char c = json->buffer.space[i];
@@ -189,7 +189,7 @@ static enum cx_json_status token_parse_next(CxJson *json, CxJsonToken *result) {
                 } else if (ctype != CX_JSON_NO_TOKEN) {
                     // single-char token
                     json->buffer.pos = i + 1;
-                    *result = (CxJsonToken){ctype, false, {NULL, 0}};
+                    *result = (CxJsonToken){ctype, false, CX_NULLSTR};
                     return CX_JSON_NO_ERROR;
                 } else {
                     ttype = CX_JSON_TOKEN_LITERAL; // number or literal
@@ -232,31 +232,25 @@ static enum cx_json_status token_parse_next(CxJson *json, CxJsonToken *result) {
         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) {
-            // current token is uncompleted
-            // save current token content
-            CxJsonToken uncompleted = {
-                ttype, true,
-                cx_strdup(cx_strn(json->buffer.space + token_part_start, uncompleted_len))
-            };
-            if (uncompleted.content.ptr == NULL) {
+        cxstring uncompleted = cx_strn(json->buffer.space + token_part_start, json->buffer.size - token_part_start);
+        if (json->uncompleted_tokentype == CX_JSON_NO_TOKEN) {
+            assert(json->uncompleted_content.ptr == NULL);
+            json->uncompleted_content = cx_strdup(uncompleted);
+            if (json->uncompleted_content.ptr == NULL) {
                 return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE
             }
-            json->uncompleted = uncompleted;
+            json->uncompleted_tokentype = ttype;
         } else {
-            // previously we also had an uncompleted token
+            // previously we already had an uncompleted token
             // combine the uncompleted token with the current token
-            assert(json->uncompleted.allocated);
-            cxmutstr str = cx_strcat_m(json->uncompleted.content, 1,
-                cx_strn(json->buffer.space + token_part_start, uncompleted_len));
-            if (str.ptr == NULL) {
+            cxmutstr s = cx_strcat(json->uncompleted_content, 1, uncompleted);
+            if (s.ptr == NULL) {
                 return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE
             }
-            json->uncompleted.content = str;
+            json->uncompleted_content = s;
         }
         // advance the buffer position - we saved the stuff in the uncompleted token
-        json->buffer.pos += uncompleted_len;
+        json->buffer.pos += uncompleted.length;
         return CX_JSON_INCOMPLETE_DATA;
     }
 }
@@ -410,7 +404,7 @@ static cxmutstr escape_string(cxstring str, bool escape_slash) {
             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;
         }
@@ -453,10 +447,10 @@ static cxmutstr escape_string(cxstring str, bool escape_slash) {
 }
 
 static CxJsonObject json_create_object_map(const CxAllocator *allocator) {
-    // TODO: we might want to add a comparator that is sorting the elements by their key
-    CxMap *map = cxKvListCreateAsMap(allocator, NULL, CX_STORE_POINTERS);
+    CxMap *map = cxKvListCreateAsMap(allocator, CX_STORE_POINTERS);
     if (map == NULL) return NULL; // LCOV_EXCL_LINE
-    cxDefineDestructor(map, cxJsonValueFree);
+    cxSetCompareFunc(map, cxJsonCompare);
+    cxSetDestructor(map, cxJsonValueFree);
     return map;
 }
 
@@ -472,20 +466,20 @@ 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->array.data, 16);
-        if (v->array.data == NULL) goto create_json_value_exit_error; // LCOV_EXCL_LINE
+        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->array.data, 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) {
@@ -503,10 +497,19 @@ static CxJsonValue* json_create_value(CxJson *json, CxJsonValueType type) {
 
     // 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
@@ -540,26 +543,23 @@ void cxJsonInit(CxJson *json, const CxAllocator *allocator) {
     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;
-    token_destroy(&json->uncompleted);
+    json->uncompleted_tokentype = CX_JSON_NO_TOKEN;
+    cx_strfree(&json->uncompleted_content);
     cx_strfree_a(json->allocator, &json->uncompleted_member_name);
 }
 
@@ -574,8 +574,8 @@ int cxJsonFilln(CxJson *json, const char *buf, size_t size) {
         // 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 {
@@ -584,9 +584,9 @@ int cxJsonFilln(CxJson *json, const char *buf, size_t size) {
 }
 
 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) \
@@ -607,13 +607,21 @@ static enum cx_json_status json_parse(CxJson *json) {
     }
 
     // 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
+        }
     }
 
 
@@ -645,6 +653,16 @@ static enum cx_json_status json_parse(CxJson *json) {
                 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
@@ -678,9 +696,9 @@ static enum cx_json_status json_parse(CxJson *json) {
                 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"))) {
+                if (0 == cx_strcmp(token.content, "true")) {
                     vbuf->literal = CX_JSON_TRUE;
-                } else if (0 == cx_strcmp(cx_strcast(token.content), cx_str("false"))) {
+                } else if (0 == cx_strcmp(token.content, "false")) {
                     vbuf->literal = CX_JSON_FALSE;
                 } else {
                     vbuf->literal = CX_JSON_NULL;
@@ -698,7 +716,7 @@ static enum cx_json_status json_parse(CxJson *json) {
             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);
@@ -706,7 +724,7 @@ static enum cx_json_status json_parse(CxJson *json) {
     } 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
@@ -721,7 +739,7 @@ static enum cx_json_status json_parse(CxJson *json) {
             }
             assert(json->uncompleted_member_name.ptr == NULL);
             json->uncompleted_member_name = name;
-            assert(json->vbuf_size > 0);
+            assert(json->vbuf.size > 0);
 
             // next state
             json_add_state(json, JP_STATE_OBJ_COLON);
@@ -742,7 +760,7 @@ static enum cx_json_status json_parse(CxJson *json) {
             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);
@@ -767,17 +785,17 @@ CxJsonStatus cxJsonNext(CxJson *json, CxJsonValue **value) {
     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;
         }
@@ -786,7 +804,7 @@ CxJsonStatus cxJsonNext(CxJson *json, CxJsonValue **value) {
     // 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;
     }
 
@@ -832,11 +850,10 @@ void cxJsonValueFree(CxJsonValue *value) {
             break;
         }
         case CX_JSON_ARRAY: {
-            CxJsonArray array = value->array;
-            for (size_t i = 0; i < array.data_size; i++) {
-                cxJsonValueFree(array.data[i]);
+            for (size_t i = 0; i < value->array.size; i++) {
+                cxJsonValueFree(value->array.data[i]);
             }
-            cxFree(value->allocator, array.data);
+            cx_array_free_a(value->allocator, value->array);
             break;
         }
         case CX_JSON_STRING: {
@@ -865,14 +882,23 @@ CxJsonValue* cxJsonCreateObj(const CxAllocator* allocator) {
     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->array.data, 16);
-    if (v->array.data == 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;
 }
 
@@ -990,13 +1016,8 @@ int cxJsonArrAddLiterals(CxJsonValue* arr, const CxJsonLiteral* lit, size_t coun
 }
 
 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->array.data,
-            arr->array.data_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) {
@@ -1010,8 +1031,8 @@ 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;
@@ -1046,23 +1067,18 @@ CxJsonValue* cx_json_obj_put_literal(CxJsonValue* obj, cxstring name, CxJsonLite
 }
 
 CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index) {
-    if (index >= value->array.data_size) {
+    if (index >= value->array.size) {
         return &cx_json_value_nothing;
     }
     return value->array.data[index];
 }
 
 CxJsonValue *cxJsonArrRemove(CxJsonValue *value, size_t index) {
-    if (index >= value->array.data_size) {
+    if (index >= value->array.size) {
         return NULL;
     }
     CxJsonValue *ret = value->array.data[index];
-    // TODO: replace with a low level cx_array_remove()
-    size_t count = value->array.data_size - index - 1;
-    if (count > 0) {
-        memmove(value->array.data + index, value->array.data + index + 1, count * sizeof(CxJsonValue*));
-    }
-    value->array.data_size--;
+    cx_array_remove(value->array, index);
     return ret;
 }
 
@@ -1095,11 +1111,7 @@ int64_t cxJsonAsInteger(const CxJsonValue *value) {
 }
 
 CxIterator cxJsonArrIter(const CxJsonValue *value) {
-    return cxIteratorPtr(
-        value->array.data,
-        value->array.data_size,
-        true // arrays need to keep order
-    );
+    return cx_array_iterator_ptr(value->array);
 }
 
 CxMapIterator cxJsonObjIter(const CxJsonValue *value) {
@@ -1209,7 +1221,7 @@ int cx_json_write_rec(
 
                 // the name
                 actual += wfunc("\"", 1, 1, target);
-                cxstring key = cx_strn(member->key->data, member->key->len);
+                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);
                 actual += wfunc("\"", 1, 1, target);
@@ -1413,31 +1425,138 @@ int cxJsonWrite(
 static cxmutstr cx_json_to_string(CxJsonValue *value, const CxAllocator *allocator, CxJsonWriter *writer) {
     if (allocator == NULL) allocator = cxDefaultAllocator;
     CxBuffer buffer;
-    if (cxBufferInit(&buffer, NULL, 128, allocator,
-        CX_BUFFER_AUTO_EXTEND | CX_BUFFER_DO_NOT_FREE)) {
-        return (cxmutstr){NULL, 0};
+    if (cxBufferInit(&buffer, allocator, NULL, 128,
+                     CX_BUFFER_AUTO_EXTEND | CX_BUFFER_DO_NOT_FREE)) {
+        return CX_NULLSTR; // LCOV_EXCL_LINE
     }
     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};
+        return CX_NULLSTR;
         // LCOV_EXCL_STOP
     } else {
-        cxmutstr str = cx_mutstrn(buffer.space, buffer.size);
+        cxmutstr str = cx_bstr_m(&buffer);
         cxBufferDestroy(&buffer);
         return str;
     }
 
 }
 
-cxmutstr cxJsonToString(CxJsonValue *value, const CxAllocator *allocator) {
+cxmutstr cxJsonToString(const CxAllocator *allocator, CxJsonValue *value) {
     CxJsonWriter writer = cxJsonWriterCompact();
     return cx_json_to_string(value, allocator, &writer);
 }
 
-cxmutstr cxJsonToPrettyString(CxJsonValue *value, const CxAllocator *allocator) {
+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_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; \
+            ret->type = CX_JSON_UNINITIALIZED; \
+            cxJsonValueFree(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
+}
index d6375d9c40f6942d788daff404437f89cffa3aec..d1ee51534957d1076e97ad96954c4f494fa9dc0e 100644 (file)
@@ -221,11 +221,12 @@ static size_t cx_kvl_find_remove(
 
     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;
@@ -348,7 +349,7 @@ static int cx_kvl_map_remove(CxMap *map, CxHashKey key, void *targetbuf) {
     return 0;
 }
 
-static void *cx_kvl_map_put(CxMap *map, CxHashKey key, void *value) {
+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) {
@@ -359,8 +360,8 @@ static void *cx_kvl_map_put(CxMap *map, CxHashKey key, void *value) {
     cx_kvl_map_remove(map, key, NULL);
 
     // now reserve new memory in the map
-    void **map_data = kv_list->map_methods->put(map, key, NULL);
-    if (map_data == NULL) return NULL; // LCOV_EXCL_LINE
+    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;
@@ -369,20 +370,20 @@ static void *cx_kvl_map_put(CxMap *map, CxHashKey key, void *value) {
         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;
+        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
-    *map_data = node_data;
+    *(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 = key;
+    *key_ptr = *map_entry.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;
+    // 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) {
@@ -514,7 +515,7 @@ static CxMapIterator cx_kvl_map_iterator(const CxMap *map, enum cx_map_iterator_
 }
 
 static int cx_kvl_change_capacity(struct cx_list_s *list,
-        cx_attr_unused size_t cap) {
+        CX_UNUSED size_t cap) {
     // since our backing list is a linked list, we don't need to do much here,
     // but rehashing the map is quite useful
     cx_kv_list *kv_list = (cx_kv_list*)list;
@@ -552,7 +553,6 @@ static cx_map_class cx_kv_map_class = {
 
 CxList *cxKvListCreate(
         const CxAllocator *allocator,
-        cx_compare_func comparator,
         size_t elem_size
 ) {
     if (allocator == NULL) {
@@ -560,7 +560,7 @@ CxList *cxKvListCreate(
     }
 
     // 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));
@@ -600,23 +600,17 @@ CxList *cxKvListCreate(
     // 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);
 }
 
@@ -649,14 +643,14 @@ int cx_kv_list_set_key(CxList *list, size_t index, CxHashKey key) {
         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;
 }
@@ -698,22 +692,23 @@ int cx_kv_list_insert(CxList *list, size_t index, CxHashKey key, void *value) {
     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;
 }
index aa36de0f0970e0d22e6fc5c4a8787525c5573b48..5ee5ade395b74badda5066db8967626b14229d89 100644 (file)
@@ -65,13 +65,14 @@ void *cx_linked_list_at(
     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);
@@ -82,7 +83,7 @@ void *cx_linked_list_find(
     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;
             }
@@ -94,6 +95,19 @@ void *cx_linked_list_find(
     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
@@ -240,26 +254,14 @@ void cx_linked_list_insert_chain(
     }
 }
 
-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);
@@ -276,7 +278,7 @@ static void *cx_linked_list_insert_sorted_chain_impl(
 
     // 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;
@@ -302,7 +304,7 @@ static void *cx_linked_list_insert_sorted_chain_impl(
 
     // 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);
@@ -327,7 +329,7 @@ static void *cx_linked_list_insert_sorted_chain_impl(
         } 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 {
@@ -356,7 +358,7 @@ static void *cx_linked_list_insert_sorted_chain_impl(
         } 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 {
@@ -394,6 +396,19 @@ static void *cx_linked_list_insert_sorted_chain_impl(
     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,
@@ -402,9 +417,10 @@ void cx_linked_list_insert_sorted_chain(
         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(
@@ -427,10 +443,67 @@ void *cx_linked_list_insert_unique_chain(
         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(
@@ -511,6 +584,8 @@ size_t cx_linked_list_size(
 #endif
 
 static void cx_linked_list_sort_merge(
+        void **begin,
+        void **end,
         ptrdiff_t loc_prev,
         ptrdiff_t loc_next,
         ptrdiff_t loc_data,
@@ -518,9 +593,8 @@ static void cx_linked_list_sort_merge(
         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 ?
@@ -532,7 +606,7 @@ static void cx_linked_list_sort_merge(
     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 {
@@ -566,13 +640,14 @@ static void cx_linked_list_sort_merge(
     }
 }
 
-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);
@@ -590,7 +665,7 @@ void cx_linked_list_sort( // NOLINT(misc-no-recursion) - purposely recursive fun
     // 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++;
     }
@@ -602,7 +677,7 @@ void cx_linked_list_sort( // NOLINT(misc-no-recursion) - purposely recursive fun
         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++;
         }
@@ -610,40 +685,55 @@ void cx_linked_list_sort( // NOLINT(misc-no-recursion) - purposely recursive fun
 
         // {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);
@@ -654,6 +744,18 @@ int cx_linked_list_compare(
     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,
@@ -798,13 +900,11 @@ static void *cx_ll_insert_element(
     }
 }
 
-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(
@@ -839,29 +939,19 @@ 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);
@@ -1090,12 +1180,12 @@ static size_t cx_ll_find_remove(
 
     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;
     }
@@ -1111,9 +1201,9 @@ static size_t cx_ll_find_remove(
 
 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) {
@@ -1129,9 +1219,9 @@ static int cx_ll_compare(
     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) {
@@ -1272,7 +1362,6 @@ static cx_list_class cx_linked_list_class = {
 
 CxList *cxLinkedListCreate(
         const CxAllocator *allocator,
-        cx_compare_func comparator,
         size_t elem_size
 ) {
     if (allocator == NULL) {
@@ -1287,7 +1376,7 @@ CxList *cxLinkedListCreate(
     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;
 }
@@ -1295,8 +1384,8 @@ CxList *cxLinkedListCreate(
 void cx_linked_list_extra_data(cx_linked_list *list, size_t len) {
     list->extra_data_len = len;
 
-    off_t loc_extra = list->loc_data + list->base.collection.elem_size;
+    off_t loc_extra = list->loc_data + (off_t) list->base.collection.elem_size;
     size_t alignment = alignof(void*);
-    size_t padding = alignment - (loc_extra % alignment);
-    list->loc_extra = loc_extra + padding;
+    size_t padding = alignment - ((size_t)loc_extra % alignment);
+    list->loc_extra = loc_extra + (off_t) padding;
 }
index 75268c51f18fcf794969a1f1521ca3599e8837c1..769a9cc18c369fe39bef272c65b39a4749e63f5e 100644 (file)
 #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">
 
-static void cx_emptyl_noop(cx_attr_unused CxList *list) {
+static void cx_emptyl_noop(CX_UNUSED CxList *list) {
     // this is a noop, but MUST be implemented
 }
 
 static void *cx_emptyl_at(
-        cx_attr_unused const struct cx_list_s *list,
-        cx_attr_unused size_t index
+        CX_UNUSED const struct cx_list_s *list,
+        CX_UNUSED size_t index
 ) {
     return NULL;
 }
 
 static size_t cx_emptyl_find_remove(
-        cx_attr_unused struct cx_list_s *list,
-        cx_attr_unused const void *elem,
-        cx_attr_unused bool remove
+        CX_UNUSED struct cx_list_s *list,
+        CX_UNUSED const void *elem,
+        CX_UNUSED bool remove
 ) {
     return 0;
 }
 
-static bool cx_emptyl_iter_valid(cx_attr_unused const void *iter) {
+static bool cx_emptyl_iter_valid(CX_UNUSED const void *iter) {
     return false;
 }
 
 static CxIterator cx_emptyl_iterator(
         const struct cx_list_s *list,
         size_t index,
-        cx_attr_unused bool backwards
+        CX_UNUSED bool backwards
 ) {
     CxIterator iter = {0};
     iter.src_handle = (void*) list;
@@ -282,28 +119,25 @@ static cx_list_class cx_empty_list_class = {
 
 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,
@@ -313,9 +147,7 @@ size_t cx_list_default_insert_array(
     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) {
@@ -335,7 +167,6 @@ static size_t cx_list_default_insert_sorted_impl(
     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
@@ -343,19 +174,19 @@ static size_t cx_list_default_insert_sorted_impl(
 
     // 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++;
@@ -375,7 +206,7 @@ static size_t cx_list_default_insert_sorted_impl(
         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;
@@ -389,7 +220,7 @@ static size_t cx_list_default_insert_sorted_impl(
             }
             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
@@ -398,11 +229,11 @@ static size_t cx_list_default_insert_sorted_impl(
 
         // 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
             }
@@ -420,13 +251,13 @@ static size_t cx_list_default_insert_sorted_impl(
     // 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;
@@ -466,19 +297,18 @@ void cx_list_default_sort(struct cx_list_s *list) {
     // 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;
     }
@@ -496,8 +326,8 @@ int cx_list_default_swap(struct cx_list_s *list, size_t i, size_t j) {
     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);
@@ -508,26 +338,35 @@ int cx_list_default_swap(struct cx_list_s *list, size_t i, size_t j) {
     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;
     }
 }
 
@@ -535,33 +374,28 @@ int cxListCompare(
         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;
                 }
@@ -584,7 +418,7 @@ size_t cxListSize(const CxList *list) {
 
 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) {
@@ -594,7 +428,7 @@ 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) {
@@ -621,11 +455,6 @@ CxIterator cxListEmplaceArrayAt(CxList *list, size_t index, size_t n) {
     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;
 }
 
@@ -636,15 +465,13 @@ CxIterator cxListEmplaceArray(CxList *list, size_t n) {
 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;
@@ -673,8 +500,7 @@ size_t cxListInsertUniqueArray(CxList *list, const void *array, size_t n) {
         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
@@ -687,15 +513,15 @@ size_t cxListInsertUniqueArray(CxList *list, const void *array, size_t n) {
 }
 
 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) {
@@ -734,15 +560,17 @@ int cxListSwap(CxList *list, size_t i, size_t j) {
 }
 
 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) {
@@ -751,8 +579,7 @@ 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);
@@ -762,32 +589,48 @@ int cxListSet(CxList *list, size_t index, const void *elem) {
     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) {
@@ -795,7 +638,7 @@ 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) {
@@ -829,14 +672,14 @@ static void cx_list_pop_uninitialized_elements(CxList *list, size_t n) {
     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) {
@@ -902,8 +745,7 @@ int cxListDifference(CxList *dst,
             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
@@ -971,7 +813,7 @@ int cxListIntersection(CxList *dst,
         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);
@@ -1041,7 +883,7 @@ int cxListUnion(CxList *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) {
@@ -1097,20 +939,20 @@ int cxListUnion(CxList *dst,
     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) {
index a83df9edd2518e3f1c918dce73d114b2dbe671f1..eeb98c23fbdc6d00dca4180e3cbed23b98695685 100644 (file)
--- a/ucx/map.c
+++ b/ucx/map.c
 \r
 // <editor-fold desc="empty map implementation">\r
 \r
-static void cx_empty_map_noop(cx_attr_unused CxMap *map) {\r
+static void cx_empty_map_noop(CX_UNUSED CxMap *map) {\r
     // this is a noop, but MUST be implemented\r
 }\r
 \r
 static void *cx_empty_map_get(\r
-        cx_attr_unused const CxMap *map,\r
-        cx_attr_unused CxHashKey key\r
+        CX_UNUSED const CxMap *map,\r
+        CX_UNUSED CxHashKey key\r
 ) {\r
     return NULL;\r
 }\r
 \r
-static bool cx_empty_map_iter_valid(cx_attr_unused const void *iter) {\r
+static bool cx_empty_map_iter_valid(CX_UNUSED const void *iter) {\r
     return false;\r
 }\r
 \r
 static CxMapIterator cx_empty_map_iterator(\r
         const struct cx_map_s *map,\r
-        cx_attr_unused enum cx_map_iterator_type type\r
+        CX_UNUSED enum cx_map_iterator_type type\r
 ) {\r
     CxMapIterator iter = {0};\r
     iter.map = (CxMap*) map;\r
@@ -69,13 +69,15 @@ static struct cx_map_class_s cx_empty_map_class = {
 \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
@@ -110,11 +112,13 @@ CxMapIterator cxMapIterator(const CxMap *map) {
 }\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
@@ -140,14 +144,14 @@ static void cx_map_remove_uninitialized_entry(CxMap *map, CxHashKey key) {
     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
@@ -303,26 +307,55 @@ int cxMapUnion(CxMap *dst, const CxMap *src,
     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
index c8cdc5cc14839915a6013124a1a710a052138a6b..5a32828ec11ecd153439637daf813933d6d84c06 100644 (file)
@@ -68,8 +68,8 @@ int cxPropertiesFilln(
     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;
@@ -82,7 +82,7 @@ void cxPropertiesUseStack(
         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(
@@ -112,7 +112,7 @@ CxPropertiesStatus cxPropertiesNext(
         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];
+            char previous = nl.ptr > input.ptr ? nl.ptr[-1] : cx_strat(cx_bstr(&prop->buffer), -1);
             if (previous == continuation) {
                 // this nl is a line continuation, check the next newline
                 nl = cx_strchr(cx_strsubs(nl, 1), '\n');
@@ -128,7 +128,7 @@ CxPropertiesStatus cxPropertiesNext(
 
             if (cxBufferAppend(input.ptr, 1,
                 len_until_nl, &prop->buffer) < len_until_nl) {
-                return CX_PROPERTIES_BUFFER_ALLOC_FAILED;
+                return CX_PROPERTIES_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE
             }
 
             // advance the position in the input buffer
@@ -190,14 +190,14 @@ CxPropertiesStatus cxPropertiesNext(
             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
                 cxBufferReset(&prop->buffer);
             }
             if (cxBufferAppend(buf, 1, len, &prop->buffer) < len) {
-                return CX_PROPERTIES_BUFFER_ALLOC_FAILED;
+                return CX_PROPERTIES_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE
             }
             // reset the input buffer (make way for a re-fill)
             cxBufferReset(&prop->input);
@@ -251,12 +251,12 @@ CxPropertiesStatus cxPropertiesNext(
                     if (current_buffer != &prop->buffer) {
                         // move value to the rescue buffer
                         if (prop->buffer.space == NULL) {
-                            cxBufferInit(&prop->buffer, NULL, 256, NULL, CX_BUFFER_AUTO_EXTEND);
+                            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;
+                            return CX_PROPERTIES_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE
                         }
                         val.ptr = prop->buffer.space;
                         ptr = prop->buffer.space;
@@ -312,8 +312,8 @@ const unsigned cx_properties_load_fill_size = CX_PROPERTIES_LOAD_FILL_SIZE;
 #endif
 const unsigned cx_properties_load_buf_size = CX_PROPERTIES_LOAD_BUF_SIZE;
 
-CxPropertiesStatus cx_properties_load(CxPropertiesConfig config,
-        const CxAllocator *allocator, cxstring filename, CxMap *target) {
+CxPropertiesStatus cx_properties_load(const CxAllocator *allocator,
+                                      cxstring filename, CxMap *target, CxPropertiesConfig config) {
     if (allocator == NULL) {
         allocator = cxDefaultAllocator;
     }
@@ -338,12 +338,12 @@ CxPropertiesStatus cx_properties_load(CxPropertiesConfig config,
     }
 
     // initialize the parser
-    char linebuf[cx_properties_load_buf_size];
-    char fillbuf[cx_properties_load_fill_size];
+    char linebuf[CX_PROPERTIES_LOAD_BUF_SIZE];
+    char fillbuf[CX_PROPERTIES_LOAD_FILL_SIZE];
     CxPropertiesStatus status;
     CxProperties parser;
     cxPropertiesInit(&parser, config);
-    cxPropertiesUseStack(&parser, linebuf, cx_properties_load_buf_size);
+    cxPropertiesUseStack(&parser, linebuf, CX_PROPERTIES_LOAD_BUF_SIZE);
 
     // read/fill/parse loop
     status = CX_PROPERTIES_NO_DATA;
@@ -351,15 +351,19 @@ CxPropertiesStatus cx_properties_load(CxPropertiesConfig config,
     while (true) {
         size_t r = fread(fillbuf, 1, cx_properties_load_fill_size, f);
         if (ferror(f)) {
+            // LCOV_EXCL_START
             status = CX_PROPERTIES_FILE_ERROR;
             break;
+            // LCOV_EXCL_STOP
         }
         if (r == 0) {
             break;
         }
         if (cxPropertiesFilln(&parser, fillbuf, r)) {
+            // LCOV_EXCL_START
             status = CX_PROPERTIES_BUFFER_ALLOC_FAILED;
             break;
+            // LCOV_EXCL_STOP
         }
         cxstring key, value;
         while (true) {
@@ -368,15 +372,19 @@ CxPropertiesStatus cx_properties_load(CxPropertiesConfig config,
                 break;
             } else {
                 cxmutstr v = cx_strdup_a(allocator, value);
+                // LCOV_EXCL_START
                 if (v.ptr == NULL) {
                     status = CX_PROPERTIES_MAP_ERROR;
                     break;
                 }
+                // LCOV_EXCL_STOP
                 void *mv = use_cstring ? (void*)v.ptr : &v;
                 if (cxMapPut(target, key, mv)) {
+                    // LCOV_EXCL_START
                     cx_strfree(&v);
                     status = CX_PROPERTIES_MAP_ERROR;
                     break;
+                    // LCOV_EXCL_STOP
                 }
                 keys_found++;
             }
index 4367594d85f4422c3e163d770800f46cca139484..70c0f0373c3bc5fbc2d328e2d9c89435a5c4d41a 100644 (file)
@@ -25,7 +25,8 @@
  * 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
 
 #include <ctype.h>
 
 #ifdef _WIN32
-#define cx_strcasecmp_impl _strnicmp
+static int cx_fixed_strnicmp(const char* s1, const char* s2, size_t count) {
+    // Microsoft's implementation crashes when count == 0 and either string is NULL
+    if (count == 0) return 0;
+    return _strnicmp(s1, s2, count);
+}
+#define cx_strcasecmp_impl cx_fixed_strnicmp
 #else
 #include <strings.h>
 #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);
@@ -85,7 +69,7 @@ void cx_strfree_a(
     str->length = 0;
 }
 
-int cx_strcpy_a(
+int cx_strcpy_a_(
         const CxAllocator *alloc,
         cxmutstr *dest,
         cxstring src
@@ -120,13 +104,19 @@ size_t cx_strlen(
     return size;
 }
 
-cxmutstr cx_strcat_ma(
+cxmutstr cx_strcat_a(
         const CxAllocator *alloc,
         cxmutstr str,
         size_t count,
         ...
 ) {
-    if (count == 0) return str;
+    if (count == 0) {
+        if (cxReallocate(alloc, &str.ptr, str.length + 1)) {
+            return CX_NULLSTR; // LCOV_EXCL_LINE
+        }
+        str.ptr[str.length] = '\0';
+        return str;
+    }
     va_list ap;
     va_start(ap, count);
     va_list ap2;
@@ -146,21 +136,16 @@ cxmutstr cx_strcat_ma(
     if (overflow) {
         va_end(ap2);
         errno = EOVERFLOW;
-        return (cxmutstr) { NULL, 0 };
+        return CX_NULLSTR;
     }
 
-    // reallocate or create new string
-    char *newstr;
-    if (str.ptr == NULL) {
-        newstr = cxMalloc(alloc, slen + 1);
-    } else {
-        newstr = cxRealloc(alloc, str.ptr, slen + 1);
-    }
-    if (newstr == NULL) { // LCOV_EXCL_START
+    // reallocate or create a new string
+    if (cxReallocate(alloc, &str.ptr, slen + 1)) {
+        // LCOV_EXCL_START
         va_end(ap2);
-        return (cxmutstr) {NULL, 0};
-    } // LCOV_EXCL_STOP
-    str.ptr = newstr;
+        return CX_NULLSTR;
+        // LCOV_EXCL_STOP
+    }
 
     // concatenate strings
     size_t pos = str.length;
@@ -178,21 +163,14 @@ cxmutstr cx_strcat_ma(
     return str;
 }
 
-cxstring cx_strsubs(
+cxstring cx_strsubs_(
         cxstring string,
         size_t start
 ) {
-    return cx_strsubsl(string, start, string.length - start);
-}
-
-cxmutstr cx_strsubs_m(
-        cxmutstr string,
-        size_t start
-) {
-    return cx_strsubsl_m(string, start, string.length - start);
+    return cx_strsubsl_(string, start, string.length);
 }
 
-cxstring cx_strsubsl(
+cxstring cx_strsubsl_(
         cxstring string,
         size_t start,
         size_t length
@@ -209,16 +187,7 @@ cxstring cx_strsubsl(
     return (cxstring) {string.ptr + start, length};
 }
 
-cxmutstr cx_strsubsl_m(
-        cxmutstr string,
-        size_t start,
-        size_t length
-) {
-    cxstring result = cx_strsubsl(cx_strcast(string), start, length);
-    return (cxmutstr) {(char *) result.ptr, result.length};
-}
-
-cxstring cx_strchr(
+cxstring cx_strchr_(
         cxstring string,
         int chr
 ) {
@@ -227,15 +196,7 @@ cxstring cx_strchr(
     return (cxstring) {ret, string.length - (ret - string.ptr)};
 }
 
-cxmutstr cx_strchr_m(
-        cxmutstr string,
-        int chr
-) {
-    cxstring result = cx_strchr(cx_strcast(string), chr);
-    return (cxmutstr) {(char *) result.ptr, result.length};
-}
-
-cxstring cx_strrchr(
+cxstring cx_strrchr_(
     cxstring string,
     int chr
 ) {
@@ -256,23 +217,12 @@ cxstring cx_strrchr(
 #endif
 }
 
-cxmutstr cx_strrchr_m(
-        cxmutstr string,
-        int chr
-) {
-    cxstring result = cx_strrchr(cx_strcast(string), chr);
-    return (cxmutstr) {(char *) result.ptr, result.length};
-}
-
 #ifndef CX_STRSTR_SBO_SIZE
 #define CX_STRSTR_SBO_SIZE 128
 #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;
     }
@@ -342,15 +292,7 @@ cxstring cx_strstr(
     return result;
 }
 
-cxmutstr cx_strstr_m(
-        cxmutstr haystack,
-        cxstring needle
-) {
-    cxstring result = cx_strstr(cx_strcast(haystack), needle);
-    return (cxmutstr) {(char *) result.ptr, result.length};
-}
-
-size_t cx_strsplit(
+size_t cx_strsplit_(
         cxstring string,
         cxstring delim,
         size_t limit,
@@ -408,7 +350,7 @@ size_t cx_strsplit(
     return n;
 }
 
-size_t cx_strsplit_a(
+size_t cx_strsplit_a_(
         const CxAllocator *allocator,
         cxstring string,
         cxstring delim,
@@ -437,27 +379,27 @@ size_t cx_strsplit_a(
         }
     }
     *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);
 }
 
@@ -532,23 +474,18 @@ cxmutstr cx_strdup_a_(
     return result;
 }
 
-cxstring cx_strtrim(cxstring string) {
+cxstring cx_strtrim_(cxstring string) {
     cxstring result = string;
-    while (result.length > 0 && isspace((unsigned char)(result.ptr[0]))) {
+    while (isspace((unsigned char)cx_strat(result, 0))) {
         result.ptr++;
         result.length--;
     }
-    while (result.length > 0 && isspace((unsigned char)result.ptr[result.length - 1])) {
+    while (isspace((unsigned char)cx_strat(result, -1))) {
         result.length--;
     }
     return result;
 }
 
-cxmutstr cx_strtrim_m(cxmutstr string) {
-    cxstring result = cx_strtrim(cx_strcast(string));
-    return (cxmutstr) {(char *) result.ptr, result.length};
-}
-
 bool cx_strprefix_(
         cxstring string,
         cxstring prefix
@@ -592,7 +529,7 @@ bool cx_strcasesuffix_(
 #endif
 }
 
-cxmutstr cx_strreplacen_a(
+cxmutstr cx_strreplace_(
         const CxAllocator *allocator,
         cxstring str,
         cxstring search,
@@ -673,7 +610,7 @@ CxStrtokCtx cx_strtok_(
     return ctx;
 }
 
-bool cx_strtok_next(
+bool cx_strtok_next_(
         CxStrtokCtx *ctx,
         cxstring *token
 ) {
@@ -716,13 +653,6 @@ bool cx_strtok_next(
     return true;
 }
 
-bool cx_strtok_next_m(
-        CxStrtokCtx *ctx,
-        cxmutstr *token
-) {
-    return cx_strtok_next(ctx, (cxstring *) token);
-}
-
 void cx_strtok_delim(
         CxStrtokCtx *ctx,
         const cxstring *delim,
index 3905cc0ec9acdcb1c6c5d665f2da6dceebd120ab..37dab5035646621093761f6d0eb8ee2624956891 100644 (file)
@@ -28,9 +28,8 @@
 
 #include "cx/tree.h"
 
-#include "cx/array_list.h"
-
 #include <assert.h>
+#include <string.h>
 
 #define CX_TREE_PTR(cur, off) (*(void**)(((char*)(cur))+(off)))
 #define tree_parent(node) CX_TREE_PTR(node, loc_parent)
 #define tree_prev(node) CX_TREE_PTR(node, loc_prev)
 #define tree_next(node) CX_TREE_PTR(node, loc_next)
 
-#define cx_tree_ptr_locations \
-    loc_parent, loc_children, loc_last_child, loc_prev, loc_next
-
-#define cx_tree_node_layout(tree) \
+#define tree_layout(tree) \
     (tree)->loc_parent,\
     (tree)->loc_children,\
     (tree)->loc_last_child,\
     (tree)->loc_prev,  \
     (tree)->loc_next
 
-static void cx_tree_zero_pointers(
-        void *node,
-        ptrdiff_t loc_parent,
-        ptrdiff_t loc_children,
-        ptrdiff_t loc_last_child,
-        ptrdiff_t loc_prev,
-        ptrdiff_t loc_next
-) {
-    tree_parent(node) = NULL;
-    if (loc_prev >= 0) {
-        tree_prev(node) = NULL;
-    }
-    tree_next(node) = NULL;
-    tree_children(node) = NULL;
-    if (loc_last_child >= 0) {
-        tree_last_child(node) = NULL;
-    }
-}
-
-void cx_tree_link(
+void cx_tree_add(
         void *parent,
         void *node,
         ptrdiff_t loc_parent,
@@ -84,7 +61,8 @@ void cx_tree_link(
     void *current_parent = tree_parent(node);
     if (current_parent == parent) return;
     if (current_parent != NULL) {
-        cx_tree_unlink(node, cx_tree_ptr_locations);
+        cx_tree_remove(node, loc_parent, loc_children,
+            loc_last_child, loc_prev, loc_next);
     }
 
     if (tree_children(parent) == NULL) {
@@ -130,7 +108,7 @@ static void *cx_tree_node_prev(
     }
 }
 
-void cx_tree_unlink(
+void cx_tree_remove(
         void *node,
         ptrdiff_t loc_parent,
         ptrdiff_t loc_children,
@@ -177,21 +155,20 @@ void cx_tree_unlink(
     }
 }
 
-int cx_tree_search(
-        const void *root,
-        size_t depth,
-        const void *node,
-        cx_tree_search_func sfunc,
-        void **result,
-        ptrdiff_t loc_children,
-        ptrdiff_t loc_next
-) {
+int cx_tree_search(const void *root, size_t max_depth,
+        const void *data, cx_tree_search_func sfunc, void **result,
+        ptrdiff_t loc_children, ptrdiff_t loc_next) {
     // help avoiding bugs due to uninitialized memory
     assert(result != NULL);
     *result = NULL;
 
+    // NULL root? exit!
+    if (root == NULL) {
+        return -1;
+    }
+
     // remember return value for best match
-    int ret = sfunc(root, node);
+    int ret = sfunc(root, data);
     if (ret < 0) {
         // not contained, exit
         return -1;
@@ -203,13 +180,13 @@ int cx_tree_search(
     }
 
     // when depth is one, we are already done
-    if (depth == 1) {
+    if (max_depth == 1) {
         return ret;
     }
 
     // special case: indefinite depth
-    if (depth == 0) {
-        depth = SIZE_MAX;
+    if (max_depth == 0) {
+        max_depth = SIZE_MAX;
     }
 
     // create an iterator
@@ -223,7 +200,7 @@ int cx_tree_search(
     // loop through the remaining tree
     cx_foreach(void *, elem, iter) {
         // investigate the current node
-        int ret_elem = sfunc(elem, node);
+        int ret_elem = sfunc(elem, data);
         if (ret_elem == 0) {
             // if found, exit the search
             *result = elem;
@@ -239,47 +216,30 @@ int cx_tree_search(
         }
 
         // when we reached the max depth, skip the subtree
-        if (iter.depth == depth) {
+        if (iter.depth == max_depth) {
             cxTreeIteratorContinue(iter);
         }
     }
 
-    // dispose the iterator as we might have exited the loop early
+    // dispose of the iterator as we might have exited the loop early
     cxTreeIteratorDispose(&iter);
 
     assert(ret < 0 || *result != NULL);
     return ret;
 }
 
-int cx_tree_search_data(
-        const void *root,
-        size_t depth,
-        const void *data,
-        cx_tree_search_data_func sfunc,
-        void **result,
-        ptrdiff_t loc_children,
-        ptrdiff_t loc_next
-) {
-    // it is basically the same implementation
-    return cx_tree_search(
-            root, depth, data,
-            (cx_tree_search_func) sfunc,
-            result,
-            loc_children, loc_next);
-}
-
 static bool cx_tree_iter_valid(const void *it) {
-    const struct cx_tree_iterator_s *iter = it;
+    const CxTreeIterator *iter = it;
     return iter->node != NULL;
 }
 
 static void *cx_tree_iter_current(const void *it) {
-    const struct cx_tree_iterator_s *iter = it;
+    const CxTreeIterator *iter = it;
     return iter->node;
 }
 
 static void cx_tree_iter_next(void *it) {
-    struct cx_tree_iterator_s *iter = it;
+    CxTreeIterator *iter = it;
     ptrdiff_t const loc_next = iter->loc_next;
     ptrdiff_t const loc_children = iter->loc_children;
     // protect us from misuse
@@ -352,7 +312,16 @@ static void cx_tree_iter_next(void *it) {
         }
     } else {
         // node has children, push the first child onto the stack and enter it
-        cx_array_simple_add(iter->stack, children);
+        if (iter->depth >= iter->stack_capacity) {
+            const size_t newcap = iter->stack_capacity + 8;
+            if (cxReallocateArrayDefault(&iter->stack, newcap, sizeof(void*))) {
+                // we cannot return an error in this function
+                abort(); // LCOV_EXCL_LINE
+            }
+            iter->stack_capacity = newcap;
+        }
+        iter->stack[iter->depth] = children;
+        iter->depth++;
         iter->node = children;
         iter->counter++;
     }
@@ -364,55 +333,56 @@ CxTreeIterator cx_tree_iterator(
         ptrdiff_t loc_children,
         ptrdiff_t loc_next
 ) {
-    CxTreeIterator iter;
-    iter.loc_children = loc_children;
-    iter.loc_next = loc_next;
-    iter.visit_on_exit = visit_on_exit;
+    CxTreeIterator ret;
+    ret.use_dfs = true;
+    ret.loc_children = loc_children;
+    ret.loc_next = loc_next;
+    ret.visit_on_exit = visit_on_exit;
 
     // initialize members
-    iter.node_next = NULL;
-    iter.exiting = false;
-    iter.skip = false;
+    ret.node_next = NULL;
+    ret.exiting = false;
+    ret.skip = false;
 
     // assign base iterator functions
-    iter.base.allow_remove = false;
-    iter.base.remove = false;
-    iter.base.current_impl = NULL;
-    iter.base.valid = cx_tree_iter_valid;
-    iter.base.next = cx_tree_iter_next;
-    iter.base.current = cx_tree_iter_current;
+    ret.base.allow_remove = false;
+    ret.base.remove = false;
+    ret.base.current_impl = NULL;
+    ret.base.valid = cx_tree_iter_valid;
+    ret.base.next = cx_tree_iter_next;
+    ret.base.current = cx_tree_iter_current;
 
     // visit the root node
-    iter.node = root;
+    ret.node = root;
     if (root != NULL) {
-        iter.stack_capacity = 16;
-        iter.stack = cxMallocDefault(sizeof(void *) * 16);
-        iter.stack[0] = root;
-        iter.counter = 1;
-        iter.depth = 1;
+        ret.stack_capacity = 16;
+        ret.stack = cxMallocDefault(sizeof(void *) * 16);
+        ret.stack[0] = root;
+        ret.counter = 1;
+        ret.depth = 1;
     } else {
-        iter.stack_capacity = 0;
-        iter.stack = NULL;
-        iter.counter = 0;
-        iter.depth = 0;
+        ret.stack_capacity = 0;
+        ret.stack = NULL;
+        ret.counter = 0;
+        ret.depth = 0;
     }
 
-    return iter;
+    return ret;
 }
 
 static bool cx_tree_visitor_valid(const void *it) {
-    const struct cx_tree_visitor_s *iter = it;
+    const CxTreeIterator *iter = it;
     return iter->node != NULL;
 }
 
 static void *cx_tree_visitor_current(const void *it) {
-    const struct cx_tree_visitor_s *iter = it;
+    const CxTreeIterator *iter = it;
     return iter->node;
 }
 
-cx_attr_nonnull
+CX_NONNULL
 static void cx_tree_visitor_enqueue_siblings(
-        struct cx_tree_visitor_s *iter, void *node, ptrdiff_t loc_next) {
+        CxTreeIterator *iter, void *node, ptrdiff_t loc_next) {
     node = tree_next(node);
     while (node != NULL) {
         struct cx_tree_visitor_queue_s *q;
@@ -427,7 +397,7 @@ static void cx_tree_visitor_enqueue_siblings(
 }
 
 static void cx_tree_visitor_next(void *it) {
-    struct cx_tree_visitor_s *iter = it;
+    CxTreeIterator *iter = it;
     // protect us from misuse
     if (!iter->base.valid(iter)) return;
 
@@ -481,358 +451,98 @@ static void cx_tree_visitor_next(void *it) {
     iter->counter++;
 }
 
-CxTreeVisitor cx_tree_visitor(
+CxTreeIterator cx_tree_visitor(
         void *root,
         ptrdiff_t loc_children,
         ptrdiff_t loc_next
 ) {
-    CxTreeVisitor iter;
-    iter.loc_children = loc_children;
-    iter.loc_next = loc_next;
+    CxTreeIterator ret;
+    ret.visit_on_exit = false;
+    ret.exiting = false;
+    ret.use_dfs = false;
+    ret.loc_children = loc_children;
+    ret.loc_next = loc_next;
 
     // initialize members
-    iter.skip = false;
-    iter.queue_next = NULL;
-    iter.queue_last = NULL;
+    ret.skip = false;
+    ret.queue_next = NULL;
+    ret.queue_last = NULL;
 
     // assign base iterator functions
-    iter.base.allow_remove = false;
-    iter.base.remove = false;
-    iter.base.current_impl = NULL;
-    iter.base.valid = cx_tree_visitor_valid;
-    iter.base.next = cx_tree_visitor_next;
-    iter.base.current = cx_tree_visitor_current;
+    ret.base.allow_remove = false;
+    ret.base.remove = false;
+    ret.base.current_impl = NULL;
+    ret.base.valid = cx_tree_visitor_valid;
+    ret.base.next = cx_tree_visitor_next;
+    ret.base.current = cx_tree_visitor_current;
 
     // visit the root node
-    iter.node = root;
+    ret.node = root;
     if (root != NULL) {
-        iter.counter = 1;
-        iter.depth = 1;
-    } else {
-        iter.counter = 0;
-        iter.depth = 0;
-    }
-
-    return iter;
-}
-
-static void cx_tree_add_link_duplicate(
-        void *original, void *duplicate,
-        ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
-        ptrdiff_t loc_prev, ptrdiff_t loc_next
-) {
-    void *shared_parent = tree_parent(original);
-    if (shared_parent == NULL) {
-        cx_tree_link(original, duplicate, cx_tree_ptr_locations);
-    } else {
-        cx_tree_link(shared_parent, duplicate, cx_tree_ptr_locations);
-    }
-}
-
-static void cx_tree_add_link_new(
-        void *parent, void *node, cx_tree_search_func sfunc,
-        ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
-        ptrdiff_t loc_prev, ptrdiff_t loc_next
-) {
-    // check the current children one by one,
-    // if they could be children of the new node
-    void *child = tree_children(parent);
-    while (child != NULL) {
-        void *next = tree_next(child);
-
-        if (sfunc(node, child) > 0) {
-            // the sibling could be a child -> re-link
-            cx_tree_link(node, child, cx_tree_ptr_locations);
-        }
-
-        child = next;
-    }
-
-    // add new node as new child
-    cx_tree_link(parent, node, cx_tree_ptr_locations);
-}
-
-int cx_tree_add(
-        const void *src,
-        cx_tree_search_func sfunc,
-        cx_tree_node_create_func cfunc,
-        void *cdata,
-        void **cnode,
-        void *root,
-        ptrdiff_t loc_parent,
-        ptrdiff_t loc_children,
-        ptrdiff_t loc_last_child,
-        ptrdiff_t loc_prev,
-        ptrdiff_t loc_next
-) {
-    *cnode = cfunc(src, cdata);
-    if (*cnode == NULL) return 1;  // LCOV_EXCL_LINE
-    cx_tree_zero_pointers(*cnode, cx_tree_ptr_locations);
-
-    void *match = NULL;
-    int result = cx_tree_search(
-            root,
-            0,
-            *cnode,
-            sfunc,
-            &match,
-            loc_children,
-            loc_next
-    );
-
-    if (result < 0) {
-        // node does not fit into the tree - return non-zero value
-        return 1;
-    } else if (result == 0) {
-        // data already found in the tree, link duplicate
-        cx_tree_add_link_duplicate(match, *cnode, cx_tree_ptr_locations);
+        ret.counter = 1;
+        ret.depth = 1;
     } else {
-        // closest match found, add new node
-        cx_tree_add_link_new(match, *cnode, sfunc, cx_tree_ptr_locations);
+        ret.counter = 0;
+        ret.depth = 0;
     }
 
-    return 0;
+    return ret;
 }
 
-unsigned int cx_tree_add_look_around_depth = 3;
-
-size_t cx_tree_add_iter(
-        struct cx_iterator_base_s *iter,
-        size_t num,
-        cx_tree_search_func sfunc,
-        cx_tree_node_create_func cfunc,
-        void *cdata,
-        void **failed,
-        void *root,
-        ptrdiff_t loc_parent,
-        ptrdiff_t loc_children,
-        ptrdiff_t loc_last_child,
-        ptrdiff_t loc_prev,
-        ptrdiff_t loc_next
-) {
-    // erase the failed pointer
-    *failed = NULL;
-
-    // iter not valid? cancel...
-    if (!iter->valid(iter)) return 0;
-
-    size_t processed = 0;
-    void *current_node = root;
-    const void *elem;
-
-    for (void **eptr; processed < num &&
-         iter->valid(iter) && (eptr = iter->current(iter)) != NULL;
-         iter->next(iter)) {
-        elem = *eptr;
-
-        // create the new node
-        void *new_node = cfunc(elem, cdata);
-        if (new_node == NULL) return processed;  // LCOV_EXCL_LINE
-        cx_tree_zero_pointers(new_node, cx_tree_ptr_locations);
-
-        // start searching from current node
-        void *match;
-        int result;
-        unsigned int look_around_retries = cx_tree_add_look_around_depth;
-        cx_tree_add_look_around_retry:
-        result = cx_tree_search(
-                current_node,
-                0,
-                new_node,
-                sfunc,
-                &match,
-                loc_children,
-                loc_next
-        );
-
-        if (result < 0) {
-            // traverse upwards and try to find better parents
-            void *parent = tree_parent(current_node);
-            if (parent != NULL) {
-                if (look_around_retries > 0) {
-                    look_around_retries--;
-                    current_node = parent;
-                } else {
-                    // look around retries exhausted, start from the root
-                    current_node = root;
-                }
-                goto cx_tree_add_look_around_retry;
-            } else {
-                // no parents. so we failed
-                *failed = new_node;
-                return processed;
-            }
-        } else if (result == 0) {
-            // data already found in the tree, link duplicate
-            cx_tree_add_link_duplicate(match, new_node, cx_tree_ptr_locations);
-            // but stick with the original match, in case we needed a new root
-            current_node = match;
-        } else {
-            // closest match found, add new node as child
-            cx_tree_add_link_new(match, new_node, sfunc,
-                                 cx_tree_ptr_locations);
-            current_node = match;
-        }
-
-        processed++;
+size_t cx_tree_size(void *root, ptrdiff_t loc_children, ptrdiff_t loc_next) {
+    CxTreeIterator iter = cx_tree_iterator(root, false, loc_children, loc_next);
+    while (cxIteratorValid(iter)) {
+        cxIteratorNext(iter);
     }
-    return processed;
+    return iter.counter;
 }
 
-size_t cx_tree_add_array(
-        const void *src,
-        size_t num,
-        size_t elem_size,
-        cx_tree_search_func sfunc,
-        cx_tree_node_create_func cfunc,
-        void *cdata,
-        void **failed,
-        void *root,
-        ptrdiff_t loc_parent,
-        ptrdiff_t loc_children,
-        ptrdiff_t loc_last_child,
-        ptrdiff_t loc_prev,
-        ptrdiff_t loc_next
-) {
-    // erase failed pointer
-    *failed = NULL;
-
-    // super special case: zero elements
-    if (num == 0) {
-        return 0;
-    }
-
-    // special case: one element does not need an iterator
-    if (num == 1) {
-        void *node;
-        if (0 == cx_tree_add(
-                src, sfunc, cfunc, cdata, &node, root,
-                loc_parent, loc_children, loc_last_child,
-                loc_prev, loc_next)) {
-            return 1;
-        } else {
-            *failed = node;
-            return 0;
+size_t cx_tree_depth(void *root, ptrdiff_t loc_children, ptrdiff_t loc_next) {
+    CxTreeIterator iter = cx_tree_iterator(root, false, loc_children, loc_next);
+    size_t depth = 0;
+    while (cxIteratorValid(iter)) {
+        if (iter.depth > depth) {
+            depth = iter.depth;
         }
+        cxIteratorNext(iter);
     }
-
-    // otherwise, create iterator and hand over to other function
-    CxIterator iter = cxIterator(src, elem_size, num, false);
-    return cx_tree_add_iter(cxIteratorRef(iter), num, sfunc,
-                            cfunc, cdata, failed, root,
-                            loc_parent, loc_children, loc_last_child,
-                            loc_prev, loc_next);
+    return depth;
 }
 
-static int cx_tree_default_insert_element(
-        CxTree *tree,
-        const void *data
-) {
-    void *node;
-    if (tree->root == NULL) {
-        node = tree->node_create(data, tree);
-        if (node == NULL) return 1;  // LCOV_EXCL_LINE
-        cx_tree_zero_pointers(node, cx_tree_node_layout(tree));
-        tree->root = node;
-        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->collection.size++;
-    } else {
-        cxFree(tree->collection.allocator, node);
-    }
-    return result;
-}
-
-static size_t cx_tree_default_insert_many(
-        CxTree *tree,
-        CxIteratorBase *iter,
-        size_t n
-) {
-    size_t ins = 0;
-    if (!iter->valid(iter)) return 0;
-    if (tree->root == NULL) {
-        // use the first element from the iter to create the root node
-        void **eptr = iter->current(iter);
-        void *node = tree->node_create(*eptr, tree);
-        if (node == NULL) return 0;  // LCOV_EXCL_LINE
-        cx_tree_zero_pointers(node, cx_tree_node_layout(tree));
-        tree->root = node;
-        ins = 1;
-        iter->next(iter);
-    }
-    void *failed;
-    ins += cx_tree_add_iter(iter, n, tree->search, tree->node_create,
-                                  tree, &failed, tree->root, cx_tree_node_layout(tree));
-    tree->collection.size += ins;
-    if (ins < n) {
-        cxFree(tree->collection.allocator, failed);
-    }
-    return ins;
-}
-
-static void *cx_tree_default_find(
-        CxTree *tree,
-        const void *subtree,
-        const void *data,
-        size_t depth
-) {
-    if (tree->root == NULL) return NULL;  // LCOV_EXCL_LINE
-
-    void *found;
-    if (0 == cx_tree_search_data(
-            subtree,
-            depth,
-            data,
-            tree->search_data,
-            &found,
-            tree->loc_children,
-            tree->loc_next
-    )) {
-        return found;
-    } else {
-        return NULL;
-    }
-}
-
-static cx_tree_class cx_tree_default_class = {
-        cx_tree_default_insert_element,
-        cx_tree_default_insert_many,
-        cx_tree_default_find
-};
-
 CxTree *cxTreeCreate(const CxAllocator *allocator,
-        cx_tree_node_create_func create_func,
-        cx_tree_search_func search_func,
-        cx_tree_search_data_func search_data_func,
+        size_t node_size, size_t elem_size, void *root, ptrdiff_t loc_data,
         ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
-        ptrdiff_t loc_prev, ptrdiff_t loc_next
-) {
+        ptrdiff_t loc_prev, ptrdiff_t loc_next) {
+
     if (allocator == NULL) {
         allocator = cxDefaultAllocator;
     }
-    assert(create_func != NULL);
-    assert(search_func != NULL);
-    assert(search_data_func != NULL);
 
     CxTree *tree = cxZalloc(allocator, sizeof(CxTree));
     if (tree == NULL) return NULL;  // LCOV_EXCL_LINE
-
-    tree->cl = &cx_tree_default_class;
     tree->collection.allocator = allocator;
-    tree->node_create = create_func;
-    tree->search = search_func;
-    tree->search_data = search_data_func;
-    tree->collection.advanced_destructor = (cx_destructor_func2) cxFree;
-    tree->collection.destructor_data = (void *) allocator;
+
+    if (elem_size == CX_STORE_POINTERS) {
+        tree->collection.store_pointer = true;
+        tree->collection.elem_size = sizeof(void*);
+    } else {
+        tree->collection.elem_size = elem_size;
+    }
+
+    tree->root = root;
+    tree->node_size = node_size;
     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->loc_data = loc_data;
+
+    if (root == NULL) {
+        cxSetAdvancedDestructor(tree, cxFree, (void*)allocator);
+    } else {
+        tree->collection.size = cx_tree_size(root, loc_children, loc_next);
+    }
 
     return tree;
 }
@@ -845,97 +555,120 @@ void cxTreeFree(CxTree *tree) {
     cxFree(tree->collection.allocator, tree);
 }
 
-CxTree *cxTreeCreateWrapped(const CxAllocator *allocator, void *root,
-        ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
-        ptrdiff_t loc_prev, ptrdiff_t loc_next) {
-    if (allocator == NULL) {
-        allocator = cxDefaultAllocator;
-    }
-    assert(root != NULL);
-
-    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->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->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->collection.size++;
     }
-    cx_tree_link(parent, child, cx_tree_node_layout(tree));
+    cx_tree_add(parent, child, tree_layout(tree));
 }
 
-void cxTreeAddChildNode(CxTree *tree, void *parent, void *child) {
-    cx_tree_link(parent, child, cx_tree_node_layout(tree));
+void cxTreeAddNode(CxTree *tree, void *parent, void *child) {
+    cx_tree_add(parent, child, tree_layout(tree));
     tree->collection.size++;
 }
 
-int cxTreeAddChild(CxTree *tree, void *parent, const void *data) {
-    void *node = tree->node_create(data, tree);
-    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));
+void *cxTreeCreateNode(CxTree *tree, void *parent) {
+    void *node = cxZalloc(tree->collection.allocator, tree->node_size);
+    if (node == NULL) return NULL; // LCOV_EXCL_LINE
+    cx_tree_add(parent, node, tree_layout(tree));
     tree->collection.size++;
-    return 0;
+    return node;
 }
 
-int cxTreeInsert(CxTree *tree, const void *data) {
-    return tree->cl->insert_element(tree, data);
+void *cxTreeAddData(CxTree *tree, void *parent, const void *data) {
+    if (tree->loc_data < 0) return NULL;
+
+    void *node = cxTreeCreateNode(tree, parent);
+    if (node == NULL) return NULL; // LCOV_EXCL_LINE
+
+    char *dst = node;
+    dst += tree->loc_data;
+    const void *src = cxCollectionStoresPointers(tree) ? (const void*)&data : data;
+    memcpy(dst, src, tree->collection.elem_size);
+
+    return node;
 }
 
-size_t cxTreeInsertIter(CxTree *tree, CxIteratorBase *iter, size_t n) {
-    return tree->cl->insert_many(tree, iter, n);
+void *cxTreeCreateRoot(CxTree *tree) {
+    if (tree->root != NULL) {
+        return tree->root;
+    }
+
+    void *node = cxZalloc(tree->collection.allocator, tree->node_size);
+    if (node == NULL) return NULL; // LCOV_EXCL_LINE
+    tree->root = node;
+    tree->collection.size = 1;
+    return node;
 }
 
-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);
-    return cxTreeInsertIter(tree, cxIteratorRef(iter), n);
+void *cxTreeCreateRootData(CxTree *tree, const void *data) {
+    if (tree->loc_data < 0) return NULL;
+
+    void *node = cxTreeCreateRoot(tree);
+    if (node == NULL) return NULL; // LCOV_EXCL_LINE
+
+    char *dst = node;
+    dst += tree->loc_data;
+    const void *src = cxCollectionStoresPointers(tree) ? (const void*)&data : data;
+    memcpy(dst, src, tree->collection.elem_size);
+
+    return node;
 }
 
-void *cxTreeFind( CxTree *tree, const void *data) {
-    return tree->cl->find(tree, tree->root, data, 0);
+void *cxTreeSetRoot(CxTree *tree, void *new_root) {
+    void *old_root = tree->root;
+    tree->root = new_root;
+    return old_root;
 }
 
-void *cxTreeFindInSubtree(CxTree *tree, const void *data, void *subtree_root, size_t max_depth) {
-    return tree->cl->find(tree, subtree_root, data, max_depth);
+void *cxTreeFindInSubtree(CxTree *tree, const void *data,
+        void *subtree_root, size_t max_depth, bool use_dfs) {
+    if (tree->loc_data < 0 || subtree_root == NULL) {
+        return NULL;
+    }
+
+    CxTreeIterator iter = use_dfs
+        ? cx_tree_iterator(subtree_root, false, tree->loc_children, tree->loc_next)
+        : cx_tree_visitor(subtree_root, tree->loc_children, tree->loc_next);
+
+    cx_foreach(char*, node, iter) {
+        char *node_data = node + tree->loc_data;
+        if (cxCollectionStoresPointers(tree)) {
+            node_data = *(void**)node_data;
+        }
+        if (cx_invoke_compare_func(tree, node_data, data) == 0) {
+            cxTreeIteratorDispose(&iter);
+            return node;
+        }
+        if (iter.depth == max_depth) {
+            cxTreeIteratorContinue(iter);
+        }
+    }
+    return NULL;
+}
+
+void *cxTreeFindFastInSubtree(CxTree *tree, const void *data,
+        cx_tree_search_func sfunc, void *root, size_t max_depth) {
+    void *result;
+    int ret = cx_tree_search(root, max_depth, data, sfunc, &result,
+        tree->loc_children, tree->loc_next);
+    if (ret == 0) {
+        return result;
+    } else {
+        return NULL;
+    }
 }
 
 size_t cxTreeSubtreeSize(CxTree *tree, void *subtree_root) {
-    CxTreeVisitor visitor = cx_tree_visitor(
-            subtree_root,
-            tree->loc_children,
-            tree->loc_next
-    );
-    while (cxIteratorValid(visitor)) {
-        cxIteratorNext(visitor);
+    if (subtree_root == tree->root) {
+        return tree->collection.size;
     }
-    return visitor.counter;
+    return cx_tree_size(subtree_root, tree->loc_children, tree->loc_next);
 }
 
 size_t cxTreeSubtreeDepth(CxTree *tree, void *subtree_root) {
-    CxTreeVisitor visitor = cx_tree_visitor(
-            subtree_root,
-            tree->loc_children,
-            tree->loc_next
-    );
-    while (cxIteratorValid(visitor)) {
-        cxIteratorNext(visitor);
-    }
-    return visitor.depth;
+    return cx_tree_depth(subtree_root, tree->loc_children, tree->loc_next);
 }
 
 size_t cxTreeSize(CxTree *tree) {
@@ -943,13 +676,7 @@ size_t cxTreeSize(CxTree *tree) {
 }
 
 size_t cxTreeDepth(CxTree *tree) {
-    CxTreeVisitor visitor = cx_tree_visitor(
-            tree->root, tree->loc_children, tree->loc_next
-    );
-    while (cxIteratorValid(visitor)) {
-        cxIteratorNext(visitor);
-    }
-    return visitor.depth;
+    return cx_tree_depth(tree->root, tree->loc_children, tree->loc_next);
 }
 
 int cxTreeRemoveNode(
@@ -964,7 +691,7 @@ int cxTreeRemoveNode(
     void *new_parent = tree_parent(node);
 
     // first, unlink from the parent
-    cx_tree_unlink(node, cx_tree_node_layout(tree));
+    cx_tree_remove(node, tree_layout(tree));
 
     // then relink each child
     ptrdiff_t loc_children = tree->loc_children;
@@ -981,7 +708,7 @@ int cxTreeRemoveNode(
         }
 
         // link to new parent
-        cx_tree_link(new_parent, child, cx_tree_node_layout(tree));
+        cx_tree_add(new_parent, child, tree_layout(tree));
 
         // proceed to next child
         child = tree_next(child);
@@ -1005,7 +732,7 @@ void cxTreeRemoveSubtree(CxTree *tree, void *node) {
         return;
     }
     size_t subtree_size = cxTreeSubtreeSize(tree, node);
-    cx_tree_unlink(node, cx_tree_node_layout(tree));
+    cx_tree_remove(node, tree_layout(tree));
     tree->collection.size -= subtree_size;
 }
 
@@ -1016,7 +743,7 @@ int cxTreeDestroyNode(
 ) {
     int result = cxTreeRemoveNode(tree, node, relink_func);
     if (result == 0) {
-        cx_invoke_destructor(tree, node);
+        cx_invoke_destructor_raw(tree, node);
         return 0;
     } else {
         return result;
@@ -1024,14 +751,15 @@ int cxTreeDestroyNode(
 }
 
 void cxTreeDestroySubtree(CxTree *tree, void *node) {
-    cx_tree_unlink(node, cx_tree_node_layout(tree));
+    cx_tree_remove(node, tree_layout(tree));
     CxTreeIterator iter = cx_tree_iterator(
             node, true,
             tree->loc_children, tree->loc_next
     );
     cx_foreach(void *, child, iter) {
         if (iter.exiting) {
-            cx_invoke_destructor(tree, child);
+            // always call the destructors with the node!
+            cx_invoke_destructor_raw(tree, child);
         }
     }
     tree->collection.size -= iter.counter;
@@ -1041,18 +769,18 @@ void cxTreeDestroySubtree(CxTree *tree, void *node) {
 }
 
 void cxTreeIteratorDispose(CxTreeIterator *iter) {
-    cxFreeDefault(iter->stack);
-    iter->stack = NULL;
-}
-
-void cxTreeVisitorDispose(CxTreeVisitor *visitor) {
-    struct cx_tree_visitor_queue_s *q = visitor->queue_next;
-    while (q != NULL) {
-        struct cx_tree_visitor_queue_s *next = q->next;
-        cxFreeDefault(q);
-        q = next;
+    if (iter->use_dfs) {
+        cxFreeDefault(iter->stack);
+        iter->stack = NULL;
+    } else {
+        struct cx_tree_visitor_queue_s *q = iter->queue_next;
+        while (q != NULL) {
+            struct cx_tree_visitor_queue_s *next = q->next;
+            cxFreeDefault(q);
+            q = next;
+        }
+        iter->queue_next = iter->queue_last = NULL;
     }
-    visitor->queue_next = visitor->queue_last = NULL;
 }
 
 CxTreeIterator cxTreeIterateSubtree(CxTree *tree, void *node, bool visit_on_exit) {
@@ -1062,7 +790,7 @@ CxTreeIterator cxTreeIterateSubtree(CxTree *tree, void *node, bool visit_on_exit
     );
 }
 
-CxTreeVisitor cxTreeVisitSubtree(CxTree *tree, void *node) {
+CxTreeIterator cxTreeVisitSubtree(CxTree *tree, void *node) {
     return cx_tree_visitor(
             node, tree->loc_children, tree->loc_next
     );
@@ -1072,6 +800,6 @@ CxTreeIterator cxTreeIterate(CxTree *tree, bool visit_on_exit) {
     return cxTreeIterateSubtree(tree, tree->root, visit_on_exit);
 }
 
-CxTreeVisitor cxTreeVisit(CxTree *tree) {
+CxTreeIterator cxTreeVisit(CxTree *tree) {
     return cxTreeVisitSubtree(tree, tree->root);
 }
index c27ba1dfec4e791209e9560d3adbfc6d6a1e832f..5564cfcc061f50382e1363dbfa523f54cddbaec7 100644 (file)
@@ -38,7 +38,7 @@
     self = [super init];
     _columnspacing = 0;
     _rowspacing = 0;
-    _children = cxArrayListCreateSimple(sizeof(GridElm), 32);
+    _children = cxArrayListCreate(NULL, sizeof(GridElm), 32);
     _preferredSize.width = -1;
     _preferredSize.height = -1;
     
index 439cae1a5bf975a2d7736bf8843dce0d5daa5557..63ba079e02592ff813df6ef10298e9923724ecc2 100644 (file)
 - (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
index 8894b207ba5a42e3d11ceb72680ed00744468983..781f415fdfd8feefb6a906a2448bf7d66245b5c1 100644 (file)
@@ -114,3 +114,27 @@ objectValueForTableColumn:(NSTableColumn *) tableColumn
 }
 
 @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
index faa852d9e1d72eb45ee5605fc1bf94d5aed5aaec..54295dab1501b97bad2083565a7afe47beb9c429 100644 (file)
@@ -353,19 +353,19 @@ void ui_radiobuttons_set(UiInteger *i, int64_t value) {
 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();
index 2e1a05b76cad9d2b6bcff190e8d15d712bc4951b..8b80a70f50fe9c14dba3457623b420da43b6c260 100644 (file)
 
 #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);
index 7adf301892dd2313f09eb445cdf49d11534dfdb8..a6794c08b3b321275f479b7581efec9949c7f711 100644 (file)
@@ -107,11 +107,54 @@ 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);
 }
index 98d4a6fa9d4f38cb5c9a2f2f702cef370418a4e7..5e72e9243a868c276d5d34e021eec0209f4bbf3b 100644 (file)
@@ -87,6 +87,8 @@ UIWIDGET ui_listview_create(UiObject* obj, UiListArgs *args) {
     
     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;
@@ -111,6 +113,15 @@ UIWIDGET ui_listview_create(UiObject* obj, UiListArgs *args) {
         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);
     }
 
@@ -397,7 +408,7 @@ static CxList* copy_sublists(const CxAllocator *a, UiSourceListArgs *args) {
             max = INT_MAX;
         }
         
-        CxList *sublists = cxArrayListCreate(a, NULL, sizeof(UiSubList), args->numsublists);
+        CxList *sublists = cxArrayListCreate(a, sizeof(UiSubList), args->numsublists);
         sublists->collection.advanced_destructor = (cx_destructor_func2)sublist_free;
         
         for(int i=0;i<max;i++) {
index 2ef732e53afc72cc5ec6ff0874f680b62a9be367..0ec8a4e5c9d1d19d9245ddc2e0b3acf038756d0c 100644 (file)
@@ -133,20 +133,28 @@ void ui_dialogwindow_args_set_rbutton4(UiDialogWindowArgs *args, const char *lab
     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) {
index 41d33f6e4adfca0ff3938b57bf0127a1adc17747..12c0160b0fe1a8a2d99a7a137cb783ab7a23e5f1 100644 (file)
@@ -67,10 +67,10 @@ UIEXPORT void ui_dialogwindow_args_set_lbutton1(UiDialogWindowArgs *args, const
 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);
index bd1d8558d46d13f0f85febd3b6dece6fdd8f730a..9ddfdb75b4c0974dfe6c4e20f17690b9b0b55f32 100644 (file)
@@ -59,13 +59,14 @@ UiContext* uic_context(UiObject *toplevel, CxMempool *mp) {
     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->state_widgets = cxLinkedListCreate(mp->allocator, cx_cmp_ptr, sizeof(UiStateWidget));
-    ctx->states = 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;
@@ -173,7 +174,7 @@ void uic_context_detach_document(UiContext *ctx, void *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);
@@ -590,7 +591,7 @@ void ui_widget_set_states(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable,
         enable = (ui_enablefunc)ui_set_enabled;
     }
     // get states
-    CxList *states = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), 16);
+    CxList *states = cxArrayListCreate(cxDefaultAllocator, sizeof(int), 16);
     va_list ap;
     va_start(ap, enable);
     int state;
@@ -608,7 +609,7 @@ void ui_widget_set_states2(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable
     if(enable == NULL) {
         enable = (ui_enablefunc)ui_set_enabled;
     }
-    CxList *ls = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), nstates);
+    CxList *ls = cxArrayListCreate(cxDefaultAllocator, sizeof(int), nstates);
     for(int i=0;i<nstates;i++) {
         cxListAdd(ls, states+i);
     }
@@ -694,6 +695,117 @@ 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;
+        }
+        case UI_VAR_GENERIC: break;
+        case UI_VAR_SPECIAL: break;
+    }
+}
+
+void ui_int_add_observer(UiInteger *i, ui_callback f, void *data) {
+    i->observers = ui_add_observer(i->observers, f, data);
+}
+
+void ui_double_add_observer(UiDouble *d, ui_callback f, void *data) {
+    d->observers = ui_add_observer(d->observers, f, data);
+}
+
+void ui_range_add_observer(UiRange *r, ui_callback f, void *data) {
+    r->observers = ui_add_observer(r->observers, f, data);
+}
+
+void ui_string_add_observer(UiString *s, ui_callback f, void *data) {
+    s->observers = ui_add_observer(s->observers, f, data);
+}
+
+void ui_text_add_observer(UiText *t, ui_callback f, void *data) {
+    t->observers = ui_add_observer(t->observers, f, data);
+}
+
+void ui_list_add_observer(UiList *l, ui_callback f, void *data) {
+    l->observers = ui_add_observer(l->observers, f, data);
+}
+
 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;
@@ -719,6 +831,11 @@ UiRange* ui_get_range_var(UiContext *ctx, const char *name) {
     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;
index 5efcc2ee50d8a2d0a11494dc012a46c623463d5d..407c051d963dd7d620f3cfb0120199d31a61a528 100644 (file)
@@ -83,6 +83,10 @@ struct UiContext {
 #elif UI_GTK2 || UI_GTK3
     GtkAccelGroup *accel_group;
 #endif 
+#endif
+#ifdef UI_WIN32
+    CxMap *command_map; // key: int, value: UiCommand
+    uint64_t command_id_counter;
 #endif
     
     // allow only one document to be attached
index 64c49c19ffcf702349b528b74fe7209378723471..b0a5d2e6bad0e69bdf40c2e182563cf6e54feeaa 100644 (file)
@@ -58,7 +58,7 @@ int uic_get_tmp_eventdata_type(void) {
 }
 
 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;
 }
 
@@ -270,7 +270,7 @@ void ui_contextmenu_builder(UiMenuBuilder **out_builder) {
     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;
index 30ef304b3bd0452878a623d74a367eb843198f82..a77ad99ca1114e206f4b263605b28c525e71658c 100644 (file)
@@ -46,7 +46,7 @@ UiMessageHandler* uic_simple_msg_handler(int in, int out, msg_received_callback
     handler->handler.callback = callback;
     handler->in = in;
     handler->out = out;
-    handler->outbuf = cxBufferCreate(NULL, 4096, NULL, CX_BUFFER_FREE_CONTENTS | CX_BUFFER_AUTO_EXTEND);
+    handler->outbuf = cxBufferCreate(NULL, NULL, 4096, CX_BUFFER_FREE_CONTENTS | CX_BUFFER_AUTO_EXTEND);
     handler->stop = 0;
     pthread_mutex_init(&handler->queue_lock, NULL);
     pthread_mutex_init(&handler->avlbl_lock, NULL);
index 9b0a8b7b022a1481bf05798ab9c72c2cc0b96c6b..f1582307ef28081cd2889fac28f2fa6c8964720b 100644 (file)
@@ -46,7 +46,7 @@ typedef struct objcallback {
 
 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);
@@ -54,7 +54,7 @@ void ui_register_object_creation_callback(ui_object_callback func, void *userdat
 
 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);
@@ -107,15 +107,11 @@ void uic_object_destroy(UiObject *obj) {
 }
 
 UiObject* uic_object_new_toplevel(void) {
-    fflush(stdout);
     CxMempool *mp = cxMempoolCreateSimple(256);
     UiObject *obj = cxCalloc(mp->allocator, 1, sizeof(UiObjectPrivate));
-    fflush(stdout);
     obj->ctx = uic_context(obj, mp);
     obj->ctx->parent = ui_global_context();
-    fflush(stdout);
     uic_object_created(obj);
-    fflush(stdout);
     return obj;
 }
 
index 138be626729064873236cc78ff8657684b333132..642603da398d26f74faabc48f1ac20b945036bcb 100644 (file)
@@ -101,7 +101,7 @@ char* ui_configfile(const char *name) {
     }
     
     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);
@@ -121,7 +121,7 @@ char* ui_configfile(const char *name) {
     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
     if(use_xdg_config_home) {
         // app dir is $HOME/.config/$APPNAME/
@@ -336,7 +336,7 @@ int ui_properties_store(void) {
 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] != '/') {
index eed1246e0d634c5bc7ca3dfbead09f29035940e2..f87e02a373a8fd97ca16e63ef1b9b3ddf98a47ee 100644 (file)
@@ -42,7 +42,7 @@ static UiToolbarMenuItem* ui_appmenu;
 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
index e8680b785f4de29de1cf801b663ece55ab241923..2f65c16083d8fe68c40c80b399593ee1f321ff8b 100644 (file)
@@ -100,7 +100,7 @@ void ui_notify_evt(UiObserver *observer, UiEvent *event) {
 /* --------------------------- 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;
@@ -217,7 +217,7 @@ UiModel* ui_model(UiContext *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*);
index 8d3b5ddce50f0157fa31926cb065a5f3185c4e5c..4b9c07abcf581ee09014632a9914c36bb8cd3b3a 100644 (file)
@@ -37,7 +37,7 @@
 
 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;
@@ -104,7 +104,7 @@ static cxmutstr escape_string(cxstring str, bool escape_slash) {
             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;
         }
index cf911cffbd865b4b70f5ea77dee0127e4b8b8e58..d9acdbf38284f00bb07228232705114dbee60825 100644 (file)
@@ -241,6 +241,8 @@ void ui_bind_togglebutton(
         event->observers = NULL;
         event->callback = NULL;
         event->userdata = NULL;
+        event->customint1 = 0;
+        event->customint2 = 0;
 
         g_signal_connect(
                 widget,
@@ -397,7 +399,7 @@ static void switch_changed(
         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;
@@ -405,6 +407,7 @@ static void switch_changed(
     e.window = e.obj->window;
     e.eventdata = NULL;
     e.eventdatatype = 0;
+    e.intval = active;
     e.set = ui_get_setop();
     
     if(event->callback) {
@@ -414,6 +417,13 @@ static void switch_changed(
         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) {
@@ -436,11 +446,13 @@ UIWIDGET ui_switch_create(UiObject* obj, UiToggleArgs *args) {
     }
     
     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,
@@ -453,7 +465,7 @@ UIWIDGET ui_switch_create(UiObject* obj, UiToggleArgs *args) {
             "destroy",
             G_CALLBACK(ui_destroy_vardata),
             event);
-    
+     
     UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
     UiLayout layout = UI_ARGS2LAYOUT(args);
     ct->add(ct, widget, &layout);
@@ -565,6 +577,8 @@ UIWIDGET ui_radiobutton_create(UiObject *obj, UiToggleArgs *args) {
         event->observers = NULL;
         event->callback = NULL;
         event->userdata = NULL;
+        event->customint1 = 0;
+        event->customint2 = 0;
         
         UiRadioButtonData *rbdata = malloc(sizeof(UiRadioButtonData));
         rbdata->value = rgroup;
@@ -773,24 +787,24 @@ static void linkbutton_apply_value(UiLinkButton *link, const char *jsonvalue) {
 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);
index a8df7e8c338a972ab7801b68b2d413674cc47d42..7da333da50cdf2d75f557a1c17669d0bb3ebcf51 100644 (file)
@@ -170,8 +170,6 @@ void ui_box_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *l
 #else
     gtk_box_pack_start(GTK_BOX(ct->widget), widget, expand, fill, 0);
 #endif
-    
-    ct->current = widget;
 }
 
 UiContainerX* ui_grid_container(
@@ -226,8 +224,6 @@ void ui_grid_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *
     
     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
@@ -288,7 +284,6 @@ UiContainerX* ui_frame_container(UiObject *obj, GtkWidget *frame) {
 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) {
@@ -304,14 +299,12 @@ 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) {
@@ -342,8 +335,6 @@ void ui_tabview_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayou
     }
     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
@@ -1162,7 +1153,7 @@ UiSplitPane* ui_create_splitpane_data(GtkWidget *pane, UiOrientation orientation
     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;
 }
 
@@ -1246,7 +1237,7 @@ static void remove_item(void *data, void *item) {
 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;
     
@@ -1331,7 +1322,7 @@ UIWIDGET ui_itemlist_create(UiObject *obj, UiItemListContainerArgs *args) {
     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;
index f224aadd0736034a28f4cedd8c2c9581e83dff10..a5e46b2febf605ee407484900fb7342def5f0bfb 100644 (file)
@@ -53,7 +53,6 @@ struct UiContainerPrivate {
     UiContainerX container;
     GtkWidget *widget;
     UIMENU menu;
-    GtkWidget *current; // TODO: remove
     
     void (*add)(UiContainerPrivate*, GtkWidget*, UiLayout *layout);
     UiLayout layout;
index 8cb03b04b749c2b8aa6bb1d3ed047dae6e90ae27..2d599ccdacbb16f6bf51c408a6db05291bf29420 100644 (file)
@@ -184,7 +184,7 @@ UiFileList ui_selection_geturis(UiDnD *sel) {
 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;
index 9e61e4e109b40f09e4877381cf7966bf84ab32d2..ec9d190db988ebeb697dc3f52b949efe5407ea8b 100644 (file)
@@ -125,6 +125,7 @@ UIWIDGET ui_spinbox_create(UiObject *obj, UiSpinBoxArgs *args) {
     }
     
     UiVarEventData *event = malloc(sizeof(UiVarEventData));
+    memset(event, 0, sizeof(UiVarEventData));
     event->obj = obj;
     event->var = var;
     event->observers = obs;
index 392eae7fb581045a5e9cfe7fd3d88dcba864fa8e..d3786c8cd15972664980a1eff2c84acc84d7b6c0 100644 (file)
@@ -56,10 +56,10 @@ extern "C" {
 #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) gtk_header_bar_set_show_title(GTK_HEADER_BAR(h), b)
+#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) gtk_header_bar_set_show_title(GTK_HEADER_BAR(h), b)
+#define UI_HEADERBAR_SHOW_TITLE_WIDGET(h, b) 
 #endif
 #endif
     
index 608ad04d4e95002c809d712bd0e4f3eadccece84..16568eed5028fc013aa6f8c1b9287c60a1d740b1 100644 (file)
@@ -46,7 +46,7 @@ static GtkIconTheme *icon_theme;
 #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();
 }
index 7a08ffccd764f455e4073c58bbb6648dce901eb4..e8b649cdc261c09361aeda5a4d40a03786c44dea 100644 (file)
@@ -822,20 +822,19 @@ static void listview_update_selection(UiListView *view) {
     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);
@@ -1022,7 +1021,7 @@ static void update_list_row(UiListView *listview, GtkListStore *store, GtkTreeIt
         void *data = listview->getvalue(list, elm, row, c, listview->getvaluedata, &freevalue);
         
         UiModelType type = model->types[i];
-        
+        UiTextStyle cstyle = { 0, 0 };
         if(getstyle) {
             // in case the column is icon+text, only get a style for the text column
             int style_col = c;
@@ -1033,8 +1032,15 @@ static void update_list_row(UiListView *listview, GtkListStore *store, GtkTreeIt
             // Get the individual column style 
             // The column style overrides the row style, however if no column style
             // is provided, we stick with the row style
-            if(getstyle(list, elm, row, style_col, listview->getstyledata, &style)) {
+            if(getstyle(list, elm, row, style_col, listview->getstyledata, &cstyle)) {
                 style_set = TRUE;
+                cstyle.text_style |= style.text_style;
+                if(!cstyle.fg_set) {
+                    cstyle.fg_set = style.fg_set;
+                    cstyle.fg = style.fg;
+                }
+            } else {
+                cstyle = style;
             }
         }
 
@@ -1109,6 +1115,23 @@ static void update_list_row(UiListView *listview, GtkListStore *store, GtkTreeIt
                 }
                 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);
@@ -1123,7 +1146,7 @@ static void update_list_row(UiListView *listview, GtkListStore *store, GtkTreeIt
             
             GValue style_weight_value = G_VALUE_INIT;
             g_value_init(&style_weight_value, G_TYPE_INT);
-            if(style.text_style & UI_TEXT_STYLE_BOLD) {
+            if(cstyle.text_style & UI_TEXT_STYLE_BOLD) {
                 g_value_set_int(&style_weight_value, 600);
             } else {
                 g_value_set_int(&style_weight_value, 400);
@@ -1132,7 +1155,7 @@ static void update_list_row(UiListView *listview, GtkListStore *store, GtkTreeIt
             
             GValue style_underline_value = G_VALUE_INIT;
             g_value_init(&style_underline_value, G_TYPE_INT);
-            if(style.text_style & UI_TEXT_STYLE_UNDERLINE) {
+            if(cstyle.text_style & UI_TEXT_STYLE_UNDERLINE) {
                 g_value_set_int(&style_underline_value, PANGO_UNDERLINE_SINGLE);
             } else {
                 g_value_set_int(&style_underline_value, PANGO_UNDERLINE_NONE);
@@ -1141,7 +1164,7 @@ static void update_list_row(UiListView *listview, GtkListStore *store, GtkTreeIt
             
             GValue style_italic_value = G_VALUE_INIT;
             g_value_init(&style_italic_value, G_TYPE_INT);
-            if(style.text_style & UI_TEXT_STYLE_ITALIC) {
+            if(cstyle.text_style & UI_TEXT_STYLE_ITALIC) {
                 g_value_set_int(&style_italic_value, PANGO_STYLE_ITALIC);
             } else {
                 g_value_set_int(&style_italic_value, PANGO_STYLE_NORMAL);
@@ -1150,12 +1173,12 @@ static void update_list_row(UiListView *listview, GtkListStore *store, GtkTreeIt
             
             GValue style_fgset_value = G_VALUE_INIT;
             g_value_init(&style_fgset_value, G_TYPE_BOOLEAN);
-            g_value_set_boolean(&style_fgset_value, style.fg_set);
+            g_value_set_boolean(&style_fgset_value, cstyle.fg_set);
             gtk_list_store_set_value(store, iter, soff + 4, &style_fgset_value);
             
-            if(style.fg_set) {
+            if(cstyle.fg_set) {
                 char buf[8];
-                snprintf(buf, 8, "#%02X%02X%02X", (int)style.fg.red, (int)style.fg.green, (int)style.fg.blue);
+                snprintf(buf, 8, "#%02X%02X%02X", (int)cstyle.fg.red, (int)cstyle.fg.green, (int)cstyle.fg.blue);
                 
                 GValue style_fg_value = G_VALUE_INIT;
                 g_value_init(&style_fg_value, G_TYPE_STRING);
@@ -1182,6 +1205,8 @@ static GtkListStore* create_list_store(UiListView *listview, UiList *list) {
                 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;
@@ -1340,6 +1365,23 @@ void ui_dropdown_select(UIWIDGET dropdown, int index) {
     gtk_combo_box_set_active(GTK_COMBO_BOX(dropdown), index);
 }
 
+static void table_cell_toggled(
+        GtkCellRendererToggle *renderer,
+        gchar *path,
+        gpointer user_data)
+{
+    printf("cell toggled\n");
+}
+
+static void table_cell_edited(
+        GtkCellRendererText *renderer,
+        gchar *path,
+        gchar *new_text,
+        gpointer user_data)
+{
+    printf("cell edited\n");
+}
+
 UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) {
     // create treeview
     GtkWidget *view = gtk_tree_view_new();
@@ -1398,8 +1440,20 @@ UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) {
                 "pixbuf",
                 i + addi,
                 NULL);
+        } else if (model->types[i] == UI_BOOL_EDITABLE) {
+            GtkCellRenderer *renderer = gtk_cell_renderer_toggle_new();
+            column = gtk_tree_view_column_new_with_attributes(
+                model->titles[i],
+                renderer,
+                "active",
+                i + addi,
+                NULL);
+            g_signal_connect(renderer, "toggled", G_CALLBACK(table_cell_toggled), NULL);
         } else {
             GtkCellRenderer *textrenderer = gtk_cell_renderer_text_new();
+            if(model->types[i] == UI_STRING_EDITABLE) {
+                g_object_set(textrenderer, "editable", TRUE, NULL);
+            }
             column = gtk_tree_view_column_new_with_attributes(
                 model->titles[i],
                 textrenderer,
@@ -1560,7 +1614,7 @@ void ui_listview_update(UiList *list, int i) {
 
 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;
@@ -1713,7 +1767,7 @@ void ui_listview_activate_event(
         GtkTreeViewColumn *column,
         UiTreeEventData *event)
 {
-    UiListSelection selection = ui_listview_selection(
+    UiListSelection selection = ui_listview_get_selection(
             gtk_tree_view_get_selection(treeview),
             event);
     
@@ -1735,7 +1789,7 @@ void ui_listview_selection_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;
@@ -1751,7 +1805,7 @@ void ui_listview_selection_event(
     }
 }
 
-UiListSelection ui_listview_selection(
+UiListSelection ui_listview_get_selection(
         GtkTreeSelection *selection,
         UiTreeEventData *event)
 {
@@ -2215,7 +2269,7 @@ static void add_sublist(UiListBox *uilistbox, CxList *sublists, UiSubList *subli
     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);
@@ -2266,7 +2320,7 @@ UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs *args) {
     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;
@@ -2354,7 +2408,7 @@ void ui_listbox_dynamic_update(UiList *list, int x) {
     }
     
     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);
@@ -2442,7 +2496,11 @@ static void listbox_button_clicked(GtkWidget *button, UiEventDataExt *data) {
         
         UIMENU menu = data->customdata3;
         g_object_set_data(G_OBJECT(button), "ui-button-popup", menu);
+#if GTK_CHECK_VERSION(4, 0, 0)
         gtk_popover_popup(GTK_POPOVER(menu));
+#else
+        ui_contextmenu_popup(menu, button, 0, 0);
+#endif
     }
 }
 
@@ -2534,6 +2592,7 @@ static void listbox_fill_row(UiListBox *listbox, GtkWidget *row, UiListBoxSubLis
                 event
                 );
         gtk_widget_set_visible(button, FALSE);
+        WIDGET_NO_SHOW_ALL(button);
         
         g_object_set_data(G_OBJECT(row), "ui-listbox-row-button", button);
         
@@ -2573,6 +2632,7 @@ static void update_sublist_item(UiListBox *listbox, UiListBoxSubList *sublist, i
     LISTBOX_ROW_REMOVE_CHILD(row);
     
     listbox_fill_row(listbox, GTK_WIDGET(row), sublist, &item, index);
+    LISTBOX_ROW_SHOW(row);
     
      // cleanup
     free(item.label);
@@ -2709,6 +2769,7 @@ void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist, si
 #endif
         
         listbox_fill_row(listbox, row, sublist, &item, index);
+        LISTBOX_ROW_SHOW(row);
         if(index == first_index) {
             // first row in the sublist, set ui_listbox data to the row
             // which is then used by the headerfunc
index ef828d47131e48aeeb34dff6950a7aaa6aba9ac8..e7beb0e79e2861bbcef9e316b04513a3b6b7baec 100644 (file)
@@ -177,7 +177,7 @@ void ui_listview_activate_event(
 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);
index 44d32ca4989e6b7a97131a582b65bb3140f9e3e6..8bfbbbfe493608bc5617ea9741f9dbff59be66c9 100644 (file)
@@ -130,7 +130,7 @@ void add_menuitem_widget(GtkWidget *parent, int index, UiMenuItemI *item, UiObje
     gtk_menu_shell_append(GTK_MENU_SHELL(parent), widget);
     
     if(i->states) {
-        CxList *states = cxArrayListCreateSimple(sizeof(int), i->nstates);
+        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);
@@ -153,29 +153,100 @@ void add_checkitem_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObject *
     GtkWidget *widget = gtk_check_menu_item_new_with_mnemonic(ci->label);
     gtk_menu_shell_append(GTK_MENU_SHELL(p), widget);
     
-    if(ci->callback) {
-        UiEventData *event = malloc(sizeof(UiEventData));
-        event->obj = obj;
-        event->userdata = ci->userdata;
-        event->callback = ci->callback;
-        event->value = 0;
-        event->customdata = NULL;
-        
-        g_signal_connect(
-                widget,
-                "toggled",
-                G_CALLBACK(ui_menu_event_toggled),
-                event);
-        g_signal_connect(
-                widget,
-                "destroy",
-                G_CALLBACK(ui_destroy_userdata),
-                event);
+    UiVarEventData *event = malloc(sizeof(UiVarEventData));
+    memset(event, 0, sizeof(UiVarEventData));
+    event->obj = obj;
+    event->userdata = ci->userdata;
+    event->callback = ci->callback;
+    event->var = uic_widget_var(obj->ctx, obj->ctx, NULL, ci->varname, UI_VAR_INTEGER);
+    if(event->var) {
+        UiInteger *v = event->var->value;
+        v->obj = widget;
+        v->get = ui_checkitem_get;
+        v->set = ui_checkitem_set;
+        if(v->value != 0) {
+            gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), TRUE);
+        }
+    }
+
+    g_signal_connect(
+            widget,
+            "toggled",
+            G_CALLBACK(ui_menu_event_toggled),
+            event);
+    g_signal_connect(
+            widget,
+            "destroy",
+            G_CALLBACK(ui_destroy_userdata),
+            event);
+}
+
+static void ui_menu_event_radio_item_toggled(GtkRadioMenuItem *ri, UiVarEventData *event) {
+    UiInteger *i = event->var->value;
+    
+    UiEvent evt;
+    evt.obj = event->obj;
+    evt.window = event->obj->window;
+    evt.document = event->obj->ctx->document;
+    evt.eventdata = i;
+    evt.eventdatatype = i ? UI_EVENT_DATA_INTEGER_VALUE : 0;
+    evt.intval = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(ri));
+    
+    if(event->callback) {
+        event->callback(&evt, event->userdata);
+    }
+    
+    if(evt.intval) {
+        evt.intval = ui_get(i);
+        ui_notify_evt(i->observers, &evt);
     }
 }
 
+static void ui_destroy_menu_radio_item(GtkWidget *unused, UiVarEventData *event) {
+    if(event->customint1) {
+        uic_unbind_var(event->var);
+    }
+    free(event);
+}
+
 void add_radioitem_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObject *obj) {
-    // TODO
+    UiMenuRadioItem *ri = (UiMenuRadioItem*)item;
+    
+    UiVar *var = uic_widget_var(obj->ctx, obj->ctx, NULL, ri->varname, UI_VAR_INTEGER);
+    if(!var) {
+        fprintf(stderr, "Error: menu radioitem varname is null\n");
+        return;
+    }
+    int first = 0;
+    UiInteger *i = var->value;
+    GSList *group = i->obj;
+    GtkWidget *widget = gtk_radio_menu_item_new_with_label(group, ri->label);
+    gtk_menu_shell_append(GTK_MENU_SHELL(p), widget);
+    if(!group) {
+        i->get = ui_radioitem_get;
+        i->set = ui_radioitem_set;
+        first = 1;
+    }
+    i->obj = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(widget));
+    
+    UiVarEventData *event = malloc(sizeof(UiVarEventData));
+    memset(event, 0, sizeof(UiVarEventData));
+    event->obj = obj;
+    event->var = var;
+    event->callback = ri->callback;
+    event->userdata = ri->userdata;
+    event->customint1 = first;
+    
+    g_signal_connect(
+            widget,
+            "toggled",
+            G_CALLBACK(ui_menu_event_radio_item_toggled),
+            event);
+    g_signal_connect(
+            widget,
+            "destroy",
+            G_CALLBACK(ui_destroy_menu_radio_item),
+            event);
 }
 
 static void menuitem_list_remove_binding(void *obj) {
@@ -220,7 +291,7 @@ void add_menuitem_list_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObje
         // 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);
@@ -323,14 +394,23 @@ void ui_menu_event_wrapper(GtkMenuItem *item, UiEventData *event) {
     uic_set_tmp_eventdata(NULL, 0);
 }
 
-void ui_menu_event_toggled(GtkCheckMenuItem *ci, UiEventData *event) {
+void ui_menu_event_toggled(GtkCheckMenuItem *ci, UiVarEventData *event) {
+    UiInteger *i = event->var ? event->var->value : NULL;
+    
     UiEvent evt;
     evt.obj = event->obj;
     evt.window = event->obj->window;
     evt.document = event->obj->ctx->document;
-    evt.eventdata = NULL;
+    evt.eventdata = i;
+    evt.eventdatatype = i ? UI_EVENT_DATA_INTEGER_VALUE : 0;
     evt.intval = gtk_check_menu_item_get_active(ci);
-    event->callback(&evt, event->userdata);    
+    if(event->callback) {
+        event->callback(&evt, event->userdata);
+    }
+    
+    if(i) {
+        ui_notify_evt(i->observers, &evt);
+    }
 }
 
 int64_t ui_checkitem_get(UiInteger *i) {
@@ -341,7 +421,48 @@ int64_t ui_checkitem_get(UiInteger *i) {
 
 void ui_checkitem_set(UiInteger *i, int64_t value) {
     i->value = value;
-    gtk_check_menu_item_set_active(i->obj, value);
+    gtk_check_menu_item_set_active(i->obj, (gboolean)value);
+}
+
+int64_t ui_radioitem_get(UiInteger *value) {
+    int selection = 0;
+    GSList *ls = value->obj;
+    guint len = g_slist_length(ls);
+    int i = 0;
+    while(ls) {
+        if(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(ls->data))) {
+            selection = len - i;
+            break;
+        }
+        ls = ls->next;
+        i++;
+    }
+    
+    value->value = selection;
+    return selection;
+}
+
+void ui_radioitem_set(UiInteger *i, int64_t value) {
+    GSList *ls = i->obj;
+    
+    int len = g_slist_length(ls);
+    if(value > len) {
+        value = len;
+    }
+    int s = len - value;
+    int j = 0;
+    int unset = 1;
+    while(ls) {
+        if(j == s) {
+            gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(ls->data), TRUE);
+            unset = 0;
+            break;
+        }
+        ls = ls->next;
+        j++;
+    }
+    
+    i->value = value;
 }
 
 
@@ -463,7 +584,7 @@ void ui_gmenu_add_menuitem(GMenu *parent, int index, UiMenuItemI *item, UiObject
     g_object_unref(action);
     
     if(i->states) {
-        CxList *groups = cxArrayListCreateSimple(sizeof(int), i->nstates);
+        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);
@@ -525,22 +646,25 @@ static void radiogroup_remove_binding(void *obj) {
 }
 
 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) {
@@ -569,7 +693,7 @@ static int64_t ui_action_get_state(UiInteger *i) {
 
 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;
@@ -577,7 +701,7 @@ static UiMenuRadioGroup* create_radio_group(UiObject *obj, UiMenuRadioItem *item
         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;
@@ -711,7 +835,7 @@ void ui_gmenu_add_menuitem_list(GMenu *p, int index, UiMenuItemI *item, UiObject
         // 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);
index a7f65e7316f946efce7e1006f833f0f9c570dc0d..4cfa419d81e176d30f12b3612d1767043165e95a 100644 (file)
@@ -75,9 +75,11 @@ void add_menuitem_list_widget(GtkWidget *p, int i, UiMenuItemI *item, UiObject *
 void ui_menulist_update(UiList *list, int ignored);
 void ui_update_menuitem_list(UiActiveMenuItemList *list);
 void ui_menu_event_wrapper(GtkMenuItem *item, UiEventData *event);
-void ui_menu_event_toggled(GtkCheckMenuItem *ci, UiEventData *event);
+void ui_menu_event_toggled(GtkCheckMenuItem *ci, UiVarEventData *event);
 int64_t ui_checkitem_get(UiInteger *i);
 void ui_checkitem_set(UiInteger *i, int64_t value);
+int64_t ui_radioitem_get(UiInteger *i);
+void ui_radioitem_set(UiInteger *i, int64_t value);
 
 #endif /* GTK_MAJOR_VERSION <= 3 */
 
index cb52e4a90363a8191180552512ca4ac26e8bf7e9..0bcd54df3e4c3eb071e718be050bf731c6f20512 100644 (file)
@@ -756,7 +756,7 @@ void ui_textfield_set(UiString *str, const char *value) {
 // 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;
@@ -1042,7 +1042,7 @@ int ui_pathtextfield_update_widget(UiPathTextField* pathtf) {
         
         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);
index 0e195ff9046cd28482e2805d0c9f77aa5575e11d..33efbcd274eb1cbf575094b90985dfe84b2e7a13 100644 (file)
@@ -44,7 +44,7 @@
 
 #if UI_GTK2 || UI_GTK3
 
-GtkWidget* ui_create_toolbar(UiObject *obj) {
+GtkWidget* ui_create_toolbar(UiObject *obj, UiBool sidebar) {
     GtkWidget *toolbar = gtk_toolbar_new();
 #ifdef UI_GTK3
     gtk_style_context_add_class(
@@ -56,25 +56,24 @@ GtkWidget* ui_create_toolbar(UiObject *obj) {
     CxList *left_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_LEFT);
     CxList *center_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_CENTER);
     CxList *right_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHT);
+    CxList *sidebar_left = uic_get_toolbar_defaults(UI_TOOLBAR_SIDEBAR_LEFT);
+    CxList *sidebar_right = uic_get_toolbar_defaults(UI_TOOLBAR_SIDEBAR_RIGHT);
+    CxList *rightpanel_left = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHTPANEL_LEFT);
+    CxList *rightpanel_center = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHTPANEL_CENTER);
+    CxList *rightpanel_right = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHTPANEL_RIGHT);
+    
+    if(sidebar) {
+        ui_toolbar_add_items(obj, toolbar, items, sidebar_left);
+        ui_toolbar_add_items(obj, toolbar, items, sidebar_right);
+    }
     
     ui_toolbar_add_items(obj, toolbar, items, left_defaults);
     ui_toolbar_add_items(obj, toolbar, items, center_defaults);
     ui_toolbar_add_items(obj, toolbar, items, right_defaults);
     
-    /*
-    GtkToolbar *tb = GTK_TOOLBAR(toolbar);
-    CxIterator i = cxListIterator(defaults);
-    cx_foreach(char *, def, i) {
-        UiToolItemI *item = cxMapGet(toolbar_items, def);
-        if(item) {
-            item->add_to(tb, item, obj);
-        } else if(!strcmp(def, "@separator")) {
-            gtk_toolbar_insert(tb, gtk_separator_tool_item_new(), -1);
-        } else {
-            fprintf(stderr, "UI Error: Unknown toolbar item: %s\n", def);
-        }
-    }
-    */
+    ui_toolbar_add_items(obj, toolbar, items, rightpanel_left);
+    ui_toolbar_add_items(obj, toolbar, items, rightpanel_center);
+    ui_toolbar_add_items(obj, toolbar, items, rightpanel_right);
     
     return toolbar;
 }
@@ -197,7 +196,7 @@ void add_toolitem_toggle_widget(GtkToolbar *tb, UiToolbarToggleItem *item, UiObj
         }
     }
     
-    UiVarEventData *event = cxMalloc(
+    UiVarEventData *event = cxZalloc(
                 obj->ctx->allocator,
                 sizeof(UiVarEventData));
     event->obj = obj;
index 80bd1130a79670acb6408b4f8cc097890453e483..7534c63ec6caea97858e57f237de3dcc37da755b 100644 (file)
@@ -113,7 +113,7 @@ void ui_toolitem_vstgr(
         void *userdata,
         va_list ap);
 
-GtkWidget* ui_create_toolbar(UiObject *obj);
+GtkWidget* ui_create_toolbar(UiObject *obj, UiBool sidebar);
 
 void ui_toolbar_add_items(UiObject *obj, GtkWidget *toolbar, CxMap *items, CxList *defaults);
 
index e0a30c2ab697c4f535db78eb41a87f76c3413bba..10cba07f253f00e366c6ba1cc94c5b12b9a535de 100644 (file)
@@ -239,9 +239,10 @@ void ui_set_visible(UIWIDGET widget, UiBool visible) {
     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_set_no_show_all(widget, TRUE);
         gtk_widget_hide(widget);
     }
 #endif
@@ -281,7 +282,7 @@ void ui_destroy_userdata(GtkWidget *object, void *userdata) {
     free(userdata);
 }
 
-void ui_destroy_vardata(GtkWidget *object, UiVarEventData *data) {
+void ui_destroy_vardata(GtkWidget *unused, UiVarEventData *data) {
     if(data->var) {
         ui_destroy_boundvar(data->obj->ctx, data->var);
     }
@@ -489,7 +490,7 @@ void ui_set_name_and_style(GtkWidget *widget, const char *name, const char *styl
     }
     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
index 8d1ee573e5ad881b911077538004ba5ae5e555d3..0e421b9c996906306e14664b32f5c8ec396634a4 100644 (file)
@@ -58,6 +58,7 @@ extern "C" {
 #define WINDOW_SHOW(window) gtk_window_present(GTK_WINDOW(window))
 #define WINDOW_DESTROY(window) gtk_window_destroy(GTK_WINDOW(window))
 #define WINDOW_SET_CONTENT(window, child) gtk_window_set_child(GTK_WINDOW(window), child)
+#define WIDGET_NO_SHOW_ALL(widget)
 #define BOX_ADD(box, child) gtk_box_append(GTK_BOX(box), child)
 #define BOX_ADD_EXPAND(box, child) gtk_widget_set_hexpand(child, TRUE); gtk_widget_set_vexpand(child, TRUE); gtk_box_append(GTK_BOX(box), child)
 #define BOX_ADD_NO_EXPAND(box, child) gtk_box_append(GTK_BOX(box), child)
@@ -75,12 +76,14 @@ extern "C" {
 #define LISTBOX_REMOVE(listbox, row) gtk_list_box_remove(GTK_LIST_BOX(listbox), row)
 #define LISTBOX_ROW_SET_CHILD(row, child) gtk_list_box_row_set_child(GTK_LIST_BOX_ROW(row), child)
 #define LISTBOX_ROW_REMOVE_CHILD(row) gtk_list_box_row_set_child(GTK_LIST_BOX_ROW(row), NULL)
+#define LISTBOX_ROW_SHOW(row) 
 #define PANED_SET_CHILD1(paned, child) gtk_paned_set_start_child(GTK_PANED(paned), child)
 #define PANED_SET_CHILD2(paned, child) gtk_paned_set_end_child(GTK_PANED(paned), child)
 #else
 #define WINDOW_SHOW(window) gtk_widget_show_all(window)
 #define WINDOW_DESTROY(window) gtk_widget_destroy(window)
 #define WINDOW_SET_CONTENT(window, child) gtk_container_add(GTK_CONTAINER(window), child)
+#define WIDGET_NO_SHOW_ALL(widget) gtk_widget_set_no_show_all(widget, TRUE)
 #define BOX_ADD(box, child) gtk_container_add(GTK_CONTAINER(box), child)
 #define BOX_ADD_EXPAND(box, child) gtk_box_pack_start(GTK_BOX(box), child, TRUE, TRUE, 0)
 #define BOX_ADD_NO_EXPAND(box, child) gtk_box_pack_start(GTK_BOX(box), child, TRUE, FALSE, 0)  
@@ -98,6 +101,7 @@ extern "C" {
 #define LISTBOX_REMOVE(listbox, row) gtk_container_remove(GTK_CONTAINER(listbox), row)
 #define LISTBOX_ROW_SET_CHILD(row, child) gtk_container_add(GTK_CONTAINER(row), child)
 #define LISTBOX_ROW_REMOVE_CHILD(row) gtk_container_remove(GTK_CONTAINER(row), gtk_bin_get_child(GTK_BIN(row)))
+#define LISTBOX_ROW_SHOW(row) gtk_widget_show_all(GTK_WIDGET(row))
 #define PANED_SET_CHILD1(paned, child) gtk_paned_pack1(GTK_PANED(paned), child, TRUE, TRUE)
 #define PANED_SET_CHILD2(paned, child) gtk_paned_pack2(GTK_PANED(paned), child, TRUE, TRUE)
 #endif
@@ -159,6 +163,8 @@ typedef struct UiVarEventData {
     UiObserver  **observers;
     ui_callback callback;
     void        *userdata;
+    int         customint1;
+    int         customint2;
 } UiVarEventData;
 
 typedef enum UiOrientation UiOrientation;
@@ -184,7 +190,7 @@ void ui_set_widget_visibility_states(UiContext *ctx, GtkWidget *widget, const in
 void ui_set_widget_nvisibility_states(UiContext *ctx, GtkWidget *widget, const int *states, size_t ngroups);
 
 void ui_destroy_userdata(GtkWidget *object, void *userdata);
-void ui_destroy_vardata(GtkWidget *object, UiVarEventData *data);
+void ui_destroy_vardata(GtkWidget *unused, UiVarEventData *data);
 void ui_destroy_widget_var(GtkWidget *object, UiVar *var);
 void ui_destroy_boundvar(UiContext *ctx, UiVar *var);
 
index 2081de29c9879278d47a7eb95fdc8dc0bc9185fb..10463914976923d0a62a3c8d9689a74ffd2d7950 100644 (file)
@@ -294,45 +294,27 @@ static UiObject* create_window(const char *title, UiBool sidebar, UiBool splitvi
     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);
+            GtkWidget *tb = ui_create_toolbar(obj, sidebar);
             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);
     }
@@ -343,7 +325,7 @@ static UiObject* create_window(const char *title, UiBool sidebar, UiBool splitvi
         GtkWidget *paned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
         if(sidebar) {
             GtkWidget *sidebar_vbox = ui_gtk_vbox_new(0);
-            gtk_paned_add1(GTK_PANED(paned), sidebar_vbox);
+            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);
         }
@@ -351,17 +333,17 @@ static UiObject* create_window(const char *title, UiBool sidebar, UiBool splitvi
         if(splitview) {
             GtkWidget *content_paned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
             gtk_paned_set_position(GTK_PANED(content_paned), splitview_pos);
-            gtk_paned_add2(GTK_PANED(paned), content_paned);
+            PANED_SET_CHILD2(paned, content_paned);
             
             GtkWidget *right_content_box = ui_gtk_vbox_new(0);
-            gtk_paned_add1(GTK_PANED(content_paned), content_box);
-            gtk_paned_add2(GTK_PANED(content_paned), right_content_box);
+            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 {
-            gtk_paned_add2(GTK_PANED(paned), content_box);
+            PANED_SET_CHILD2(paned, content_box);
         }
         
         BOX_ADD_EXPAND(GTK_BOX(vbox), paned);
index 54b5ed2427a590f10f7f3369aa450ca2b254c807..45e7a1be1ab25c8b6d5db48bf358acdf10a3422b 100644 (file)
@@ -304,7 +304,7 @@ void ui_bind_radiobutton(UiObject *obj, Widget rbutton, UiInteger *value, const
         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;
@@ -374,7 +374,7 @@ UIWIDGET ui_radiobutton_create(UiObject* obj, UiToggleArgs *args) {
         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;
index 34648626b7314e14c7cdce46ce4cc5140c3f1ee2..c5c7c4df8f760b24c043234c60ad72400a861d24 100644 (file)
@@ -448,7 +448,7 @@ UIWIDGET ui_tabview_create(UiObject *obj, UiTabViewArgs *args) {
     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));
index b6f68badaa2fd003ea22fc4fa5b3f29e2768c722..6d935a9ac5075c25504b8d2fdb8f182158cc2017 100644 (file)
@@ -244,7 +244,7 @@ void add_menuitem_list_widget(Widget p, int i, 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);
index 4e0cde69bb6f8e0bff0960758476e836a93a0507..e236c2359d38435ef00df09e6574324b95bacb76 100644 (file)
@@ -170,7 +170,7 @@ void pathbar_losingfocus(Widget w, PathBar *p, XtPointer c)
 
 static cxmutstr concat_path_s(cxstring base, cxstring path) {
     if(!path.ptr) {
-        path = CX_STR("");
+        path = cx_str("");
     }
     
     int add_separator = 0;
@@ -186,9 +186,9 @@ static cxmutstr concat_path_s(cxstring base, cxstring path) {
     
     cxmutstr url;
     if(add_separator) {
-        url = cx_strcat(3, base, CX_STR("/"), path);
+        url = cx_strcat(CX_NULLSTR, 3, base, cx_str("/"), path);
     } else {
-        url = cx_strcat(2, base, path);
+        url = cx_strcat(CX_NULLSTR, 2, base, path);
     }
     
     return url;
index 628e3e8907ec5f69efc13c490a1552bb677b1df0..94c77f01b103693af936b6b2367848dfcdbd24b1 100644 (file)
@@ -642,8 +642,18 @@ 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 void ui_int_add_observer(UiInteger *i, ui_callback f, void *data);
+UIEXPORT void ui_double_add_observer(UiDouble *d, ui_callback f, void *data);
+UIEXPORT void ui_range_add_observer(UiRange *r, ui_callback f, void *data);
+UIEXPORT void ui_string_add_observer(UiString *s, ui_callback f, void *data);
+UIEXPORT void ui_text_add_observer(UiText *t, ui_callback f, void *data);
+UIEXPORT void ui_list_add_observer(UiList *l, 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);
index fca53bd064c71f3691d55311f8eeb75cce9f44b1..e5e07377a2002fc39c28825356a9ab2acc500100 100644 (file)
@@ -242,7 +242,7 @@ UIWIDGET ui_radiobutton_create(UiObject *obj, UiToggleArgs *args) {
         if (i->obj) {\r
             group = i->obj;\r
         } else {\r
-            group = cxArrayListCreate(obj->ctx->allocator, NULL, CX_STORE_POINTERS, 8);\r
+            group = cxArrayListCreate(obj->ctx->allocator, CX_STORE_POINTERS, 8);\r
             i->obj = group;\r
         }\r
 \r
index aad2d5da11ad52912141b25ec5cbc205ceadfde9..90da4eff592a37b2bec9984c9a256a6e646eae57 100644 (file)
@@ -36,7 +36,7 @@
 \r
 UiGridLayout* ui_grid_layout_create(const CxAllocator *a, short columnspacing, short rowspacing) {\r
     UiGridLayout *grid = cxZalloc(a, sizeof(UiGridLayout));\r
-    grid->widgets = cxArrayListCreate(a, NULL, sizeof(GridElm), 32);\r
+    grid->widgets = cxArrayListCreate(a, sizeof(GridElm), 32);\r
     grid->columnspacing = columnspacing;\r
     grid->rowspacing = rowspacing;\r
     return grid;\r
index 2681a2e41dcc96c473aec16347cfe5455bd0b2f3..b2c1495d59ecc2b4cf2cba35edc924b7d6dfe841 100644 (file)
@@ -191,17 +191,17 @@ static UIWIDGET listview_create(UiObject *obj, UiListArgs *args, UiBool table) {
 static UiListSelection listview_get_selection2(HWND hwnd) {\r
     UiListSelection sel = { 0, NULL };\r
 \r
-    CX_ARRAY_DECLARE(int, indices);\r
-    cx_array_initialize(indices, 8);\r
+    CX_ARRAY(int, indices);\r
+    cx_array_init(indices, 8);\r
 \r
     int index = -1;\r
     while ((index = ListView_GetNextItem(hwnd, index, LVNI_SELECTED)) != -1) {\r
-        cx_array_simple_add(indices, index);\r
+        cx_array_add(indices, index);\r
     }\r
 \r
-    if (indices_size > 0) {\r
-        sel.rows = indices;\r
-        sel.count = indices_size;\r
+    if (indices.size > 0) {\r
+        sel.rows = indices.data;\r
+        sel.count = indices.size;\r
     }\r
 \r
     return sel;\r
index c8258667acb1c7f8404967319b281a16b42f7f3a..82bcf2b4d742d0f3e1db36f3204d6412351bb78b 100644 (file)
@@ -28,6 +28,8 @@
 \r
 #include "menu.h"\r
 \r
+#include <cx/array_list.h>\r
+\r
 static ui_menu_add_f createMenuItem[] = {\r
     /* UI_MENU                 */ ui_add_menu,\r
     /* UI_MENU_ITEM            */ ui_add_menu_item,\r
@@ -58,7 +60,6 @@ void ui_add_menu(HMENU parent, int pos, UiMenuItemI *item, UiObject *obj) {
     UiMenu *menu = (UiMenu*)item;\r
     HMENU hMenu = CreatePopupMenu();\r
     AppendMenu(parent, MF_POPUP, (UINT_PTR)hMenu, menu->label);\r
-\r
     int i = 0;\r
     UiMenuItemI *child = menu->items_begin;\r
     while (child) {\r
@@ -67,17 +68,192 @@ void ui_add_menu(HMENU parent, int pos, UiMenuItemI *item, UiObject *obj) {
     }\r
 }\r
 \r
+static void menu_item_clicked(UiObject *obj, uint64_t id, UiMenuItem *item) {\r
+    UiEvent event;\r
+    event.obj = obj;\r
+    event.window = obj->window;\r
+    event.document = obj->ctx->document;\r
+    event.eventdata = NULL;\r
+    event.eventdatatype = 0;\r
+    event.intval = 0;\r
+    event.set = 0;\r
+    if (item->callback) {\r
+        item->callback(&event, item->userdata);\r
+    }\r
+}\r
+\r
 void ui_add_menu_item(HMENU parent, int pos, UiMenuItemI *item, UiObject *obj) {\r
+    uint64_t id = ++obj->ctx->command_id_counter;\r
+\r
     UiMenuItem *i = (UiMenuItem*)item;\r
-    AppendMenu(parent, MF_STRING, 0, i->label);\r
+    AppendMenu(parent, MF_STRING, id, i->label);\r
+\r
+    UiCommand cmd;\r
+    cmd.callback = (ui_command_func)menu_item_clicked;\r
+    cmd.userdata = i;\r
+    cxMapPut(obj->ctx->command_map, id, &cmd);\r
+}\r
+\r
+static void menu_stateitem_update(UiStateMenuItem *item) {\r
+    MENUITEMINFO mi = { 0 };\r
+    mi.cbSize = sizeof(mi);\r
+    mi.fMask = MIIM_STATE;\r
+    mi.fState = item->state ? MFS_CHECKED : MFS_UNCHECKED;\r
+    SetMenuItemInfo(item->menu, item->id, FALSE, &mi);\r
+}\r
+\r
+static void menu_checkitem_clicked(UiObject *obj, uint64_t id, UiStateMenuItem *item) {\r
+    item->state = !item->state;\r
+    menu_stateitem_update(item);\r
+\r
+    UiEvent event;\r
+    event.obj = obj;\r
+    event.window = obj->window;\r
+    event.document = obj->ctx->document;\r
+    event.eventdata = NULL;\r
+    event.eventdatatype = 0;\r
+    event.intval = 0;\r
+    event.set = 0;\r
+    if (item->onchange) {\r
+        item->onchange(&event, item->userdata);\r
+    }\r
+\r
+    if (item->var) {\r
+        UiInteger *i = item->var->value;\r
+        ui_notify_evt(i->observers, &event);\r
+    }\r
 }\r
 \r
 void ui_add_menu_checkitem(HMENU parent, int pos, UiMenuItemI *item, UiObject *obj) {\r
+    uint64_t id = ++obj->ctx->command_id_counter;\r
+\r
+    UiMenuCheckItem *i = (UiMenuCheckItem*)item;\r
+    AppendMenu(parent, MF_STRING, id, i->label);\r
+\r
+    // create an UiStateMenuItem with the same lifetime as the UiObject\r
+    UiStateMenuItem *sitem = ui_malloc(obj->ctx, sizeof(UiStateMenuItem));\r
+    memset(sitem, 0, sizeof(UiStateMenuItem));\r
+    sitem->obj = obj;\r
+    sitem->menu = parent;\r
+    sitem->id = id;\r
+    sitem->onchange = i->callback;\r
+    sitem->userdata = i->userdata;\r
+    sitem->var = uic_widget_var(obj->ctx, obj->ctx, NULL, i->varname, UI_VAR_INTEGER);\r
+    // bind to var\r
+    if (sitem->var) {\r
+        UiInteger *v = sitem->var->value;\r
+        sitem->state = v->value != 0;\r
+        v->obj = sitem;\r
+        v->get = ui_checkitem_get;\r
+        v->set = ui_checkitem_set;\r
+    }\r
+\r
+    // register command id\r
+    UiCommand cmd;\r
+    cmd.callback = (ui_command_func)menu_checkitem_clicked;\r
+    cmd.userdata = sitem;\r
+    cxMapPut(obj->ctx->command_map, id, &cmd);\r
+\r
+    menu_stateitem_update(sitem);\r
+}\r
+\r
+int64_t ui_checkitem_get(UiInteger *i) {\r
+    return i->value;\r
+}\r
 \r
+void ui_checkitem_set(UiInteger *i, int64_t value) {\r
+    i->value = value;\r
+    menu_stateitem_update(i->obj);\r
+}\r
+\r
+static void menu_radioitem_clicked(UiObject *obj, uint64_t id, UiStateMenuItem *item) {\r
+    UiInteger *i = item->var->value; // UiVar is always not NULL for radio items\r
+    ui_set(i, item->index+1);\r
+\r
+    UiEvent event;\r
+    event.obj = obj;\r
+    event.window = obj->window;\r
+    event.document = obj->ctx->document;\r
+    event.eventdata = i;\r
+    event.eventdatatype = UI_EVENT_DATA_INTEGER_VALUE;\r
+    event.intval = item->state;\r
+    event.set = ui_get_setop();\r
+\r
+    if (item->onchange) {\r
+        item->onchange(&event, item->userdata);\r
+    }\r
+\r
+    event.intval = (int)ui_get(i);\r
+    ui_notify_evt(i->observers, &event);\r
 }\r
 \r
 void ui_add_menu_radioitem(HMENU parent, int pos, UiMenuItemI *item, UiObject *obj) {\r
+    uint64_t id = ++obj->ctx->command_id_counter;\r
+\r
+    UiMenuRadioItem *i = (UiMenuRadioItem*)item;\r
+    AppendMenu(parent, MF_STRING, id, i->label);\r
+\r
+    UiVar *var = uic_widget_var(obj->ctx, obj->ctx, NULL, i->varname, UI_VAR_INTEGER);\r
+    if (!var) {\r
+        return; // radio item without var is useless\r
+    }\r
+\r
+    UiInteger *v = var->value;\r
+    CxList *group = v->obj;\r
+    if (!group) {\r
+        // first radio button in this group\r
+        group = cxArrayListCreate(obj->ctx->allocator, sizeof(UiStateMenuItem), 4);\r
+        v->obj = group;\r
+        v->get = ui_radioitem_get;\r
+        v->set = ui_radioitem_set;\r
+    }\r
 \r
+    UiStateMenuItem sitem = { 0 };\r
+    sitem.obj = obj;\r
+    sitem.menu = parent;\r
+    sitem.id = id;\r
+    sitem.onchange = i->callback;\r
+    sitem.userdata = i->userdata;\r
+    sitem.var = var;\r
+    sitem.index = (int)cxListSize(group);\r
+    cxListAdd(group, &sitem);\r
+\r
+    if (v->value == sitem.index+1) {\r
+        sitem.state = 1;\r
+        menu_stateitem_update(&sitem);\r
+    }\r
+\r
+\r
+    UiStateMenuItem *sitem_ptr = cxListAt(group, sitem.index);\r
+    // register command id\r
+    UiCommand cmd;\r
+    cmd.callback = (ui_command_func)menu_radioitem_clicked;\r
+    cmd.userdata = sitem_ptr;\r
+    cxMapPut(obj->ctx->command_map, id, &cmd);\r
+}\r
+\r
+int64_t ui_radioitem_get(UiInteger *i) {\r
+    return i->value;\r
+}\r
+\r
+void ui_radioitem_set(UiInteger *i, int64_t value) {\r
+    CxList *group = i->obj;\r
+    // de-select all items\r
+    CxIterator it = cxListIterator(group);\r
+    cx_foreach(UiStateMenuItem *, item, it) {\r
+        if (item->state) {\r
+            item->state = FALSE;\r
+            menu_stateitem_update(item);\r
+        }\r
+    }\r
+\r
+    if (value > 0) {\r
+        UiStateMenuItem *item = cxListAt(group, value-1);\r
+        if (item) {\r
+            item->state = TRUE;\r
+            menu_stateitem_update(item);\r
+        }\r
+    }\r
 }\r
 \r
 void ui_add_menu_list(HMENU parent, int pos, UiMenuItemI *item, UiObject *obj) {\r
index 13bbf0c18b5437e8919fea65bd19d6041964df9f..48b0c5f49f0e3f4c1d63f01e23565590cb013b32 100644 (file)
 extern "C" {\r
 #endif\r
 \r
+typedef struct UiStateMenuItem {\r
+    UiObject *obj;\r
+    HMENU menu;\r
+    uint64_t id;\r
+    UiVar *var;\r
+    ui_callback onchange;\r
+    void *userdata;\r
+    int index;\r
+    UiBool state;\r
+} UiStateMenuItem;\r
+\r
 typedef void(*ui_menu_add_f)(HMENU, int, UiMenuItemI*, UiObject*);\r
 \r
 HMENU ui_create_main_menu(UiObject *obj);\r
@@ -50,6 +61,12 @@ void ui_add_menu_checklist(HMENU parent, int pos, UiMenuItemI *item, UiObject *o
 void ui_add_menu_radiolist(HMENU parent, int pos, UiMenuItemI *item, UiObject *obj);\r
 void ui_add_menu_separator(HMENU parent, int pos, UiMenuItemI *item, UiObject *obj);\r
 \r
+int64_t ui_checkitem_get(UiInteger *i);\r
+void ui_checkitem_set(UiInteger *i, int64_t value);\r
+\r
+int64_t ui_radioitem_get(UiInteger *i);\r
+void ui_radioitem_set(UiInteger *i, int64_t value);\r
+\r
 #ifdef __cplusplus\r
 }\r
 #endif\r
index 2ed15357d890c355b7bd9daf1f17b2b5fe8877c9..24346f06d1c99fe90b05db1fa64171b3d53defd6 100644 (file)
@@ -117,6 +117,16 @@ LRESULT CALLBACK ui_default_eventproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARA
             break;
         }
         case WM_COMMAND: {
+            UiWindow *win = ui_widget2window(widget);
+            if (win) {
+                uint64_t id = LOWORD(wParam);
+                UiCommand *cmd = cxMapGet(win->obj->ctx->command_map, id);
+                if (cmd) {
+                    cmd->callback(win->obj, id, cmd->userdata);
+                    break;
+                }
+            }
+
             HWND hwndCtrl = (HWND)lParam;
             W32Widget *cmdWidget = (W32Widget*)GetWindowLongPtr(hwndCtrl, GWLP_USERDATA);
             if (cmdWidget && cmdWidget->wclass->eventproc) {
index 53e2b3ea733415e134ef89765e79ec8f159bf261..1021f33dcb4a3022f096bf3b422d7477d6538bc7 100644 (file)
@@ -55,6 +55,12 @@ typedef struct UiWidget {
 
 LRESULT CALLBACK ui_default_eventproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
 
+typedef void(*ui_command_func)(UiObject *, uint64_t id, void *userdata);
+typedef struct UiCommand {
+    ui_command_func callback;
+    void *userdata;
+} UiCommand;
+
 HFONT ui_win32_get_font(void);
 void ui_win32_set_ui_font(HWND control);
 
index 1f661d791204cd4b209cd63ec7603c6fd16675d4..c0a51b0c75d5b47d299fd48221eef84bd71555cd 100644 (file)
@@ -29,6 +29,7 @@
 #include "window.h"
 #include <Windows.h>
 
+#include "toolkit.h"
 #include "container.h"
 
 #include "../common/object.h"
@@ -70,8 +71,16 @@ void ui_window_init(void) {
     }
 }
 
+UiWindow* ui_widget2window(W32Widget *widget) {
+    if (widget->wclass != &w32_toplevel_widget_class) {
+        return NULL;
+    }
+    return (UiWindow*)widget;
+}
+
 static UiObject* create_window(const char *title, bool simple) {
     UiObject *obj = uic_object_new_toplevel();
+    obj->ctx->command_map = cxHashMapCreate(obj->ctx->allocator, sizeof(UiCommand), 64);
 
     HWND hwnd = CreateWindowExA(
             0,
index 4562b016a233d1b0c9d2a61c57d7eec34de2bbcf..8ba4ef2387e2aeb2cdce5f1a989f7a7c7a41cfcb 100644 (file)
@@ -51,6 +51,8 @@ void ui_window_init(void);
 int ui_window_widget_event(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
 void ui_window_widget_show(W32Widget *w, BOOLEAN show);
 
+UiWindow* ui_widget2window(W32Widget *widget);
+
 #ifdef __cplusplus
 }
 #endif
index 70b958930c42590750da23d6542bd90c4c64b1b7..0ad59285ae0368d449f8d5fc55c0d7e369e60310 100644 (file)
@@ -327,7 +327,7 @@ UiPathTextField::~UiPathTextField() {
 \r
 static UiPathElm* default_pathelm_func(const char* full_path, size_t len, size_t* ret_nelm, void* data) {\r
     cxstring *pathelms;\r
-    size_t nelm = cx_strsplit_a(cxDefaultAllocator, cx_strn(full_path, len), CX_STR("/"), 4096, &pathelms);\r
+    size_t nelm = cx_strsplit_a(cxDefaultAllocator, cx_strn(full_path, len), cx_str("/"), 4096, &pathelms);\r
 \r
     if (nelm == 0) {\r
         *ret_nelm = 0;\r