From: Olaf Wintermann Date: Thu, 1 Jan 2026 17:10:55 +0000 (+0100) Subject: update to ucx version 4.0 X-Git-Url: https://uap-core.de/gitweb/?a=commitdiff_plain;h=HEAD;p=note.git update to ucx version 4.0 --- diff --git a/application/attachment.c b/application/attachment.c index 00ad270..fc2ce68 100644 --- a/application/attachment.c +++ b/application/attachment.c @@ -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); diff --git a/application/editor.c b/application/editor.c index 00da427..9dbcbd1 100644 --- a/application/editor.c +++ b/application/editor.c @@ -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) { diff --git a/application/gtk-text.c b/application/gtk-text.c index bd25a2d..266d334 100644 --- a/application/gtk-text.c +++ b/application/gtk-text.c @@ -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; diff --git a/application/nbconfig.c b/application/nbconfig.c index c61a108..3f96d28 100644 --- a/application/nbconfig.c +++ b/application/nbconfig.c @@ -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 diff --git a/application/note.c b/application/note.c index 372bc4d..0e65d6d 100644 --- a/application/note.c +++ b/application/note.c @@ -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); } diff --git a/application/notebook.c b/application/notebook.c index 79fdca6..37a9e9f 100644 --- a/application/notebook.c +++ b/application/notebook.c @@ -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); diff --git a/application/store.c b/application/store.c index 4323911..f061cb2 100644 --- a/application/store.c +++ b/application/store.c @@ -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); } diff --git a/application/tests/test-editor.c b/application/tests/test-editor.c index a0b274b..bb9c6cd 100644 --- a/application/tests/test-editor.c +++ b/application/tests/test-editor.c @@ -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)); diff --git a/application/window.c b/application/window.c index 84d272e..12b7d11 100644 --- a/application/window.c +++ b/application/window.c @@ -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); diff --git a/dbutils/Makefile b/dbutils/Makefile index caaa056..8a4d1aa 100644 --- a/dbutils/Makefile +++ b/dbutils/Makefile @@ -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)) diff --git a/dbutils/class.c b/dbutils/class.c index 65dec0d..fcd2751 100644 --- a/dbutils/class.c +++ b/dbutils/class.c @@ -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; } diff --git a/dbutils/db.c b/dbutils/db.c index 6f6b998..a2a0d89 100644 --- a/dbutils/db.c +++ b/dbutils/db.c @@ -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; +} diff --git a/dbutils/dbutils.c b/dbutils/dbutils.c index 68b013e..df7392b 100644 --- a/dbutils/dbutils.c +++ b/dbutils/dbutils.c @@ -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; } diff --git a/dbutils/dbutils/db.h b/dbutils/dbutils/db.h index 8a04a08..3162704 100644 --- a/dbutils/dbutils/db.h +++ b/dbutils/dbutils/db.h @@ -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 } diff --git a/dbutils/dbutils/dbutils.h b/dbutils/dbutils/dbutils.h index e11ac4c..c757401 100644 --- a/dbutils/dbutils/dbutils.h +++ b/dbutils/dbutils/dbutils.h @@ -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 index 0000000..0617ef8 --- /dev/null +++ b/dbutils/dbutils/json.h @@ -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 +#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 */ + diff --git a/dbutils/field.c b/dbutils/field.c index e2d4804..bebf221 100644 --- a/dbutils/field.c +++ b/dbutils/field.c @@ -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); } diff --git a/dbutils/field.h b/dbutils/field.h index 21d1843..4cd6a51 100644 --- a/dbutils/field.h +++ b/dbutils/field.h @@ -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 index 0000000..52cdc8a --- /dev/null +++ b/dbutils/json.c @@ -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 index 0000000..debdad3 --- /dev/null +++ b/dbutils/json.h @@ -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 */ + diff --git a/dbutils/object.c b/dbutils/object.c index 0a54c30..6c25e66 100644 --- a/dbutils/object.c +++ b/dbutils/object.c @@ -42,11 +42,11 @@ 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;ifieldName(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 // ____ // __
(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;igetText(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;cobj_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;igetText(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;ifree(result); diff --git a/libidav/config.c b/libidav/config.c index 715f98b..a055f67 100644 --- a/libidav/config.c +++ b/libidav/config.c @@ -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("/")); } } diff --git a/libidav/crypto.c b/libidav/crypto.c index fe3651a..bbf679e 100644 --- a/libidav/crypto.c +++ b/libidav/crypto.c @@ -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; } diff --git a/libidav/davqlexec.c b/libidav/davqlexec.c index afe62d1..9320e96 100644 --- a/libidav/davqlexec.c +++ b/libidav/davqlexec.c @@ -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;iname = 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; } diff --git a/libidav/davqlparser.c b/libidav/davqlparser.c index eb2dd46..33c8a11 100644 --- a/libidav/davqlparser.c +++ b/libidav/davqlparser.c @@ -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) { diff --git a/libidav/methods.c b/libidav/methods.c index 90e7597..a76ed4c 100644 --- a/libidav/methods.c +++ b/libidav/methods.c @@ -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 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("\n"); + s = cx_str("\n"); cxBufferWrite(s.ptr, 1, s.length, buf); - s = CX_STR("\n"); + s = cx_str("\n"); cxBufferWrite(s.ptr, 1, s.length, buf); - s = CX_STR("\n"); + s = cx_str("\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("\n"); + s = cx_str("\n"); cxBufferWrite(s.ptr, 1, s.length, buf); - s = CX_STR("\n"); + s = cx_str("\n"); cxBufferWrite(s.ptr, 1, s.length, buf); - s = CX_STR("\n"); + s = cx_str("\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("\n"); - cxBufferWrite(s.ptr, 1, s.length, buf); + cxBufferPutString(buf, "\n"); // write root element and namespaces cx_bprintf(buf, "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("\n"); - cxBufferWrite(s.ptr, 1, s.length, buf); - - s = CX_STR("\n\n"); - cxBufferWrite(s.ptr, 1, s.length, buf); - - s = CX_STR("\n\n"); - cxBufferWrite(s.ptr, 1, s.length, buf); + cxBufferPutString(buf, "\n"); + cxBufferPutString(buf, "\n\n"); + cxBufferPutString(buf, "\n\n"); - s = CX_STR("\n"); - cxBufferWrite(s.ptr, 1, s.length, buf); + cxBufferPutString(buf, "\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("\n"); - cxBufferWrite(s.ptr, 1, s.length, buf); + cxBufferPutString(buf, "\n"); - s = CX_STR("\n"); - cxBufferWrite(s.ptr, 1, s.length, buf); + cxBufferPutString(buf, "\n"); // properties - s = CX_STR("\n"); - cxBufferWrite(s.ptr, 1, s.length, buf); - s = CX_STR("\n"); - cxBufferWrite(s.ptr, 1, s.length, buf); - s = CX_STR("\n"); - cxBufferWrite(s.ptr, 1, s.length, buf); - s = CX_STR("\n"); - cxBufferWrite(s.ptr, 1, s.length, buf); - s = CX_STR("\n"); - cxBufferWrite(s.ptr, 1, s.length, buf); - s = CX_STR("\n"); - cxBufferWrite(s.ptr, 1, s.length, buf); + cxBufferPutString(buf, "\n"); + cxBufferPutString(buf, "\n"); + cxBufferPutString(buf, "\n"); + cxBufferPutString(buf, "\n"); + cxBufferPutString(buf, "\n"); + cxBufferPutString(buf, "\n"); // end - s = CX_STR("\n"); - cxBufferWrite(s.ptr, 1, s.length, buf); + cxBufferPutString(buf, "\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("\n"); - cxBufferWrite(s.ptr, 1, s.length, buf); + cxBufferPutString(buf, "\n"); // write root element and namespaces - s = CX_STR("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("\n\n"); - cxBufferWrite(s.ptr, 1, s.length, buf); + cxBufferPutString(buf, "\n\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("name); - cxBufferWrite(s.ptr, 1, s.length, buf); - s = CX_STR(">\n"); - cxBufferWrite(s.ptr, 1, s.length, buf); + cxBufferPutString(buf, "name); + cxBufferPutString(buf, ">\n"); } - s = CX_STR("\n\n"); - cxBufferWrite(s.ptr, 1, s.length, buf); + cxBufferPutString(buf, "\n\n"); } if(data->remove) { - s = CX_STR("\n\n"); - cxBufferWrite(s.ptr, 1, s.length, buf); + cxBufferPutString(buf, "\n\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("\n\n"); - cxBufferWrite(s.ptr, 1, s.length, buf); + cxBufferPutString(buf, "\n\n"); } - s = CX_STR("\n"); - cxBufferWrite(s.ptr, 1, s.length, buf); + cxBufferPutString(buf, "\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("\n"); - cxBufferWrite(s.ptr, 1, s.length, buf); + cxBufferPutString(buf, "\n"); - s = CX_STR("\n"); - cxBufferWrite(s.ptr, 1, s.length, buf); + cxBufferPutString(buf, "\n"); - s = CX_STR("\n\n"); - cxBufferWrite(s.ptr, 1, s.length, buf); + cxBufferPutString(buf, "\n\n"); if(DAV_ENCRYPT_NAME(sn)) { - s = CX_STR(""); - cxBufferWrite(s.ptr, 1, s.length, buf); + cxBufferPutString(buf, ""); char *crname = aes_encrypt(name, strlen(name), key); cxBufferPutString(buf, crname); free(crname); - s = CX_STR("\n"); - cxBufferWrite(s.ptr, 1, s.length, buf); + cxBufferPutString(buf, "\n"); } - s = CX_STR(""); - cxBufferWrite(s.ptr, 1, s.length, buf); + cxBufferPutString(buf, ""); cxBufferPutString(buf, key->name); - s = CX_STR("\n"); - cxBufferWrite(s.ptr, 1, s.length, buf); + cxBufferPutString(buf, "\n"); if(hash) { - s = CX_STR(""); - cxBufferWrite(s.ptr, 1, s.length, buf); + cxBufferPutString(buf, ""); cxBufferPutString(buf, hash); - s = CX_STR("\n"); - cxBufferWrite(s.ptr, 1, s.length, buf); + cxBufferPutString(buf, "\n"); } - s = CX_STR("\n\n\n"); - cxBufferWrite(s.ptr, 1, s.length, buf); + cxBufferPutString(buf, "\n\n\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("\n"); - cxBufferWrite(s.ptr, 1, s.length, buf); + cxBufferPutString(buf, "\n"); - s = CX_STR("\n" + s = cx_str("\n" "\n" "\n" "http://davutils.org/libidav/\n"); - cxBufferWrite(s.ptr, 1, s.length, buf); + cxBufferPutString(buf, s); - s = CX_STR("\n"); - cxBufferWrite(s.ptr, 1, s.length, buf); + cxBufferPutString(buf, "\n"); return buf; } diff --git a/libidav/pwdstore.c b/libidav/pwdstore.c index 6a134d6..01d8438 100644 --- a/libidav/pwdstore.c +++ b/libidav/pwdstore.c @@ -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 diff --git a/libidav/resource.c b/libidav/resource.c index b82473b..bf57861 100644 --- a/libidav/resource.c +++ b/libidav/resource.c @@ -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;iread, 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, "\n"); cxBufferPutString(content, "\n"); diff --git a/libidav/session.c b/libidav/session.c index e7b983f..bf66e79 100644 --- a/libidav/session.c +++ b/libidav/session.c @@ -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; } diff --git a/libidav/utils.c b/libidav/utils.c index 767f52a..c24c772 100644 --- a/libidav/utils.c +++ b/libidav/utils.c @@ -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;ibase_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;isessions = 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; diff --git a/libidav/xml.c b/libidav/xml.c index cfea172..9dbb15e 100644 --- a/libidav/xml.c +++ b/libidav/xml.c @@ -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; } diff --git a/ucx/allocator.c b/ucx/allocator.c index bb01129..c07406b 100644 --- a/ucx/allocator.c +++ b/ucx/allocator.c @@ -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); diff --git a/ucx/array_list.c b/ucx/array_list.c index 102ec58..98b0ca7 100644 --- a/ucx/array_list.c +++ b/ucx/array_list.c @@ -26,82 +26,16 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#ifdef WITH_MEMRCHR +#define _GNU_SOURCE +#endif + #include "cx/array_list.h" #include "cx/compare.h" #include #include #include -// 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; } diff --git a/ucx/buffer.c b/ucx/buffer.c index a727ee4..be3fad7 100644 --- a/ucx/buffer.c +++ b/ucx/buffer.c @@ -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 diff --git a/ucx/compare.c b/ucx/compare.c index 1fc036d..ae03987 100644 --- a/ucx/compare.c +++ b/ucx/compare.c @@ -29,6 +29,7 @@ #include "cx/compare.h" #include +#include 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); +} diff --git a/ucx/cx/allocator.h b/ucx/cx/allocator.h index 11750f3..ff6b287 100644 --- a/ucx/cx/allocator.h +++ b/ucx/cx/allocator.h @@ -35,10 +35,6 @@ #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 diff --git a/ucx/cx/array_list.h b/ucx/cx/array_list.h index da27615..e415f45 100644 --- a/ucx/cx/array_list.h +++ b/ucx/cx/array_list.h @@ -39,10 +39,6 @@ #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 diff --git a/ucx/cx/buffer.h b/ucx/cx/buffer.h index 4efa52c..4c2bdbd 100644 --- a/ucx/cx/buffer.h +++ b/ucx/cx/buffer.h @@ -50,10 +50,6 @@ #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 diff --git a/ucx/cx/collection.h b/ucx/cx/collection.h index c7c7c92..607d6e6 100644 --- a/ucx/cx/collection.h +++ b/ucx/cx/collection.h @@ -40,10 +40,6 @@ #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 diff --git a/ucx/cx/common.h b/ucx/cx/common.h index 953edb5..5938ce3 100644 --- a/ucx/cx/common.h +++ b/ucx/cx/common.h @@ -133,27 +133,27 @@ /** * 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 /** @@ -163,59 +163,59 @@ * @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__ @@ -227,7 +227,7 @@ * 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. @@ -235,7 +235,7 @@ * 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. @@ -243,27 +243,38 @@ * 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 @@ -278,6 +289,16 @@ #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 diff --git a/ucx/cx/compare.h b/ucx/cx/compare.h index 1b66e62..931bd9c 100644 --- a/ucx/cx/compare.h +++ b/ucx/cx/compare.h @@ -38,10 +38,6 @@ #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 diff --git a/ucx/cx/hash_key.h b/ucx/cx/hash_key.h index 23145bb..e0fa205 100644 --- a/ucx/cx/hash_key.h +++ b/ucx/cx/hash_key.h @@ -40,10 +40,6 @@ #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); } diff --git a/ucx/cx/hash_map.h b/ucx/cx/hash_map.h index 3c9a303..153d49c 100644 --- a/ucx/cx/hash_map.h +++ b/ucx/cx/hash_map.h @@ -38,10 +38,6 @@ #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 diff --git a/ucx/cx/iterator.h b/ucx/cx/iterator.h index 4fe304a..74d7e58 100644 --- a/ucx/cx/iterator.h +++ b/ucx/cx/iterator.h @@ -38,10 +38,6 @@ #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 diff --git a/ucx/cx/json.h b/ucx/cx/json.h index c70a7f7..7268164 100644 --- a/ucx/cx/json.h +++ b/ucx/cx/json.h @@ -43,13 +43,6 @@ #include "array_list.h" #include "map.h" -#include - -#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 */ diff --git a/ucx/cx/kv_list.h b/ucx/cx/kv_list.h index c66e46c..5e8962c 100644 --- a/ucx/cx/kv_list.h +++ b/ucx/cx/kv_list.h @@ -40,16 +40,12 @@ #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 diff --git a/ucx/cx/linked_list.h b/ucx/cx/linked_list.h index 0d29004..b828725 100644 --- a/ucx/cx/linked_list.h +++ b/ucx/cx/linked_list.h @@ -39,10 +39,6 @@ #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 diff --git a/ucx/cx/list.h b/ucx/cx/list.h index 22681d6..7ea0983 100644 --- a/ucx/cx/list.h +++ b/ucx/cx/list.h @@ -39,10 +39,6 @@ #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 diff --git a/ucx/cx/map.h b/ucx/cx/map.h index 871bc41..7492b4e 100644 --- a/ucx/cx/map.h +++ b/ucx/cx/map.h @@ -46,10 +46,6 @@ 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 diff --git a/ucx/cx/mempool.h b/ucx/cx/mempool.h index 6530ea7..ea90ff7 100644 --- a/ucx/cx/mempool.h +++ b/ucx/cx/mempool.h @@ -39,10 +39,6 @@ #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 diff --git a/ucx/cx/printf.h b/ucx/cx/printf.h index 7311ab4..9d4ea7f 100644 --- a/ucx/cx/printf.h +++ b/ucx/cx/printf.h @@ -45,14 +45,9 @@ * @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 diff --git a/ucx/cx/properties.h b/ucx/cx/properties.h index 8301275..e7bf840 100644 --- a/ucx/cx/properties.h +++ b/ucx/cx/properties.h @@ -41,10 +41,6 @@ #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 diff --git a/ucx/cx/streams.h b/ucx/cx/streams.h index 298ca3a..c778f7e 100644 --- a/ucx/cx/streams.h +++ b/ucx/cx/streams.h @@ -41,10 +41,6 @@ #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 diff --git a/ucx/cx/string.h b/ucx/cx/string.h index 4dd8f17..81ad00d 100644 --- a/ucx/cx/string.h +++ b/ucx/cx/string.h @@ -39,6 +39,11 @@ #include "common.h" #include "allocator.h" +#include + +/** 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(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(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(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 diff --git a/ucx/cx/test.h b/ucx/cx/test.h index ff86b33..5980f49 100644 --- a/ucx/cx/test.h +++ b/ucx/cx/test.h @@ -73,10 +73,6 @@ #include #include -#ifdef __cplusplus -extern "C" { -#endif - #ifndef __FUNCTION__ /** * Alias for the __func__ 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 */ diff --git a/ucx/cx/tree.h b/ucx/cx/tree.h index 23ab58e..d9e6fe1 100644 --- a/ucx/cx/tree.h +++ b/ucx/cx/tree.h @@ -40,93 +40,6 @@ #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 diff --git a/ucx/hash_key.c b/ucx/hash_key.c index 3eb7299..5884b1f 100644 --- a/ucx/hash_key.c +++ b/ucx/hash_key.c @@ -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); +} diff --git a/ucx/hash_map.c b/ucx/hash_map.c index 504784e..456494c 100644 --- a/ucx/hash_map.c +++ b/ucx/hash_map.c @@ -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; diff --git a/ucx/iterator.c b/ucx/iterator.c index 292c11e..f7d1872 100644 --- a/ucx/iterator.c +++ b/ucx/iterator.c @@ -29,6 +29,7 @@ #include "cx/iterator.h" #include +#include 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; } diff --git a/ucx/json.c b/ucx/json.c index 967356b..b9b3d7c 100644 --- a/ucx/json.c +++ b/ucx/json.c @@ -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 +} diff --git a/ucx/kv_list.c b/ucx/kv_list.c index d6375d9..d1ee515 100644 --- a/ucx/kv_list.c +++ b/ucx/kv_list.c @@ -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; } diff --git a/ucx/linked_list.c b/ucx/linked_list.c index aa36de0..5ee5ade 100644 --- a/ucx/linked_list.c +++ b/ucx/linked_list.c @@ -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; } diff --git a/ucx/list.c b/ucx/list.c index 75268c5..769a9cc 100644 --- a/ucx/list.c +++ b/ucx/list.c @@ -31,228 +31,65 @@ #include #include -// - -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, -}; -// +#define cx_list_compare_wrapper(l, r, c) cx_list_compare_wrapper(l, r, (void*)c) // -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; // -#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) { diff --git a/ucx/map.c b/ucx/map.c index a83df9e..eeb98c2 100644 --- a/ucx/map.c +++ b/ucx/map.c @@ -33,24 +33,24 @@ // -static void cx_empty_map_noop(cx_attr_unused CxMap *map) { +static void cx_empty_map_noop(CX_UNUSED CxMap *map) { // this is a noop, but MUST be implemented } static void *cx_empty_map_get( - cx_attr_unused const CxMap *map, - cx_attr_unused CxHashKey key + CX_UNUSED const CxMap *map, + CX_UNUSED CxHashKey key ) { return NULL; } -static bool cx_empty_map_iter_valid(cx_attr_unused const void *iter) { +static bool cx_empty_map_iter_valid(CX_UNUSED const void *iter) { return false; } static CxMapIterator cx_empty_map_iterator( const struct cx_map_s *map, - cx_attr_unused enum cx_map_iterator_type type + CX_UNUSED enum cx_map_iterator_type type ) { CxMapIterator iter = {0}; iter.map = (CxMap*) map; @@ -69,13 +69,15 @@ static struct cx_map_class_s cx_empty_map_class = { CxMap cx_empty_map = { { - NULL, NULL, 0, 0, NULL, NULL, NULL, + NULL, + NULL, + NULL, false, true }, @@ -110,11 +112,13 @@ CxMapIterator cxMapIterator(const CxMap *map) { } int cx_map_put(CxMap *map, CxHashKey key, void *value) { - return map->cl->put(map, key, value) == NULL; + return map->cl->put(map, key, value).key == NULL; } void *cx_map_emplace(CxMap *map, CxHashKey key) { - return map->cl->put(map, key, NULL); + const CxMapEntry entry = map->cl->put(map, key, NULL); + if (entry.key == NULL) return NULL; + return entry.value; } void *cx_map_get(const CxMap *map, CxHashKey key) { @@ -140,14 +144,14 @@ static void cx_map_remove_uninitialized_entry(CxMap *map, CxHashKey key) { map->collection.advanced_destructor = destr2_bak; } -static void* cx_map_simple_clone_func(void *dst, const void *src, const CxAllocator *al, void *data) { +static void* cx_map_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(map) cx_map_simple_clone_func, NULL, (void*)&((map)->collection.elem_size) +#define use_shallow_clone_func(map) cx_map_shallow_clone_func, NULL, (void*)&((map)->collection.elem_size) int cxMapClone(CxMap *dst, const CxMap *src, cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) { @@ -303,26 +307,55 @@ int cxMapUnion(CxMap *dst, const CxMap *src, return 0; } -int cxMapCloneSimple(CxMap *dst, const CxMap *src) { - return cxMapClone(dst, src, use_simple_clone_func(src)); +int cxMapCloneShallow(CxMap *dst, const CxMap *src) { + return cxMapClone(dst, src, use_shallow_clone_func(src)); } -int cxMapDifferenceSimple(CxMap *dst, const CxMap *minuend, const CxMap *subtrahend) { - return cxMapDifference(dst, minuend, subtrahend, use_simple_clone_func(minuend)); +int cxMapDifferenceShallow(CxMap *dst, const CxMap *minuend, const CxMap *subtrahend) { + return cxMapDifference(dst, minuend, subtrahend, use_shallow_clone_func(minuend)); } -int cxMapListDifferenceSimple(CxMap *dst, const CxMap *src, const CxList *keys) { - return cxMapListDifference(dst, src, keys, use_simple_clone_func(src)); +int cxMapListDifferenceShallow(CxMap *dst, const CxMap *src, const CxList *keys) { + return cxMapListDifference(dst, src, keys, use_shallow_clone_func(src)); } -int cxMapIntersectionSimple(CxMap *dst, const CxMap *src, const CxMap *other) { - return cxMapIntersection(dst, src, other, use_simple_clone_func(src)); +int cxMapIntersectionShallow(CxMap *dst, const CxMap *src, const CxMap *other) { + return cxMapIntersection(dst, src, other, use_shallow_clone_func(src)); } -int cxMapListIntersectionSimple(CxMap *dst, const CxMap *src, const CxList *keys) { - return cxMapListIntersection(dst, src, keys, use_simple_clone_func(src)); +int cxMapListIntersectionShallow(CxMap *dst, const CxMap *src, const CxList *keys) { + return cxMapListIntersection(dst, src, keys, use_shallow_clone_func(src)); } -int cxMapUnionSimple(CxMap *dst, const CxMap *src) { - return cxMapUnion(dst, src, use_simple_clone_func(src)); +int cxMapUnionShallow(CxMap *dst, const CxMap *src) { + return cxMapUnion(dst, src, use_shallow_clone_func(src)); +} + +int cxMapCompare(const CxMap *map, const CxMap *other) { + // compare map sizes + const size_t size_left = cxMapSize(map); + const size_t size_right = cxMapSize(other); + if (size_left < size_right) { + return -1; + } else if (size_left > size_right) { + return 1; + } + + // iterate through the first map + CxMapIterator iter = cxMapIterator(map); + cx_foreach(const CxMapEntry *, entry, iter) { + const void *value_left = entry->value; + const void *value_right = cxMapGet(other, *entry->key); + // if the other map does not have the key, we are done + if (value_right == NULL) { + return -1; + } + // compare the values + const int d = cx_invoke_compare_func(map, value_left, value_right); + if (d != 0) { + return d; + } + } + + return 0; } diff --git a/ucx/properties.c b/ucx/properties.c index c8cdc5c..5a32828 100644 --- a/ucx/properties.c +++ b/ucx/properties.c @@ -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++; } diff --git a/ucx/string.c b/ucx/string.c index 4367594..70c0f03 100644 --- a/ucx/string.c +++ b/ucx/string.c @@ -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 @@ -40,34 +41,17 @@ #include #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 #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, diff --git a/ucx/tree.c b/ucx/tree.c index 3905cc0..37dab50 100644 --- a/ucx/tree.c +++ b/ucx/tree.c @@ -28,9 +28,8 @@ #include "cx/tree.h" -#include "cx/array_list.h" - #include +#include #define CX_TREE_PTR(cur, off) (*(void**)(((char*)(cur))+(off))) #define tree_parent(node) CX_TREE_PTR(node, loc_parent) @@ -39,36 +38,14 @@ #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); } diff --git a/ui/cocoa/GridLayout.m b/ui/cocoa/GridLayout.m index c27ba1d..5564cfc 100644 --- a/ui/cocoa/GridLayout.m +++ b/ui/cocoa/GridLayout.m @@ -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; diff --git a/ui/cocoa/ListDataSource.h b/ui/cocoa/ListDataSource.h index 439cae1..63ba079 100644 --- a/ui/cocoa/ListDataSource.h +++ b/ui/cocoa/ListDataSource.h @@ -40,3 +40,11 @@ - (id) init:(NSArray*) columns var:(UiVar*)var getvalue:(ui_getvaluefunc2) getvaluefunc getvaluedata:(void*)userdata; @end + +@interface ArrayDataSource : NSObject + +@property NSMutableArray *data; + +- (id)init:(char**)elements size:(size_t)nelm; + +@end diff --git a/ui/cocoa/ListDataSource.m b/ui/cocoa/ListDataSource.m index 8894b20..781f415 100644 --- a/ui/cocoa/ListDataSource.m +++ b/ui/cocoa/ListDataSource.m @@ -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;insimage = (__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); } diff --git a/ui/cocoa/list.m b/ui/cocoa/list.m index 98d4a6f..5e72e92 100644 --- a/ui/cocoa/list.m +++ b/ui/cocoa/list.m @@ -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;irbutton4 = 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) { diff --git a/ui/common/args.h b/ui/common/args.h index 41d33f6..12c0160 100644 --- a/ui/common/args.h +++ b/ui/common/args.h @@ -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); diff --git a/ui/common/context.c b/ui/common/context.c index bd1d855..9ddfdb7 100644 --- a/ui/common/context.c +++ b/ui/common/context.c @@ -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;itype) { + 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; diff --git a/ui/common/context.h b/ui/common/context.h index 5efcc2e..407c051 100644 --- a/ui/common/context.h +++ b/ui/common/context.h @@ -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 diff --git a/ui/common/menu.c b/ui/common/menu.c index 64c49c1..b0a5d2e 100644 --- a/ui/common/menu.c +++ b/ui/common/menu.c @@ -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; diff --git a/ui/common/message.c b/ui/common/message.c index 30ef304..a77ad99 100644 --- a/ui/common/message.c +++ b/ui/common/message.c @@ -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); diff --git a/ui/common/object.c b/ui/common/object.c index 9b0a8b7..f158230 100644 --- a/ui/common/object.c +++ b/ui/common/object.c @@ -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; } diff --git a/ui/common/properties.c b/ui/common/properties.c index 138be62..642603d 100644 --- a/ui/common/properties.c +++ b/ui/common/properties.c @@ -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] != '/') { diff --git a/ui/common/toolbar.c b/ui/common/toolbar.c index eed1246..f87e02a 100644 --- a/ui/common/toolbar.c +++ b/ui/common/toolbar.c @@ -42,7 +42,7 @@ static UiToolbarMenuItem* ui_appmenu; void uic_toolbar_init(void) { toolbar_items = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); for(int i=0;i<8;i++) { - toolbar_defaults[i] = cxLinkedListCreateSimple(CX_STORE_POINTERS); + toolbar_defaults[i] = cxLinkedListCreate(NULL, CX_STORE_POINTERS); } } diff --git a/ui/common/types.c b/ui/common/types.c index e8680b7..2f65c16 100644 --- a/ui/common/types.c +++ b/ui/common/types.c @@ -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*); diff --git a/ui/common/utils.c b/ui/common/utils.c index 8d3b5dd..4b9c07a 100644 --- a/ui/common/utils.c +++ b/ui/common/utils.c @@ -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; } diff --git a/ui/gtk/button.c b/ui/gtk/button.c index cf911cf..d9acdbf 100644 --- a/ui/gtk/button.c +++ b/ui/gtk/button.c @@ -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); diff --git a/ui/gtk/container.c b/ui/gtk/container.c index a8df7e8..7da333d 100644 --- a/ui/gtk/container.c +++ b/ui/gtk/container.c @@ -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; diff --git a/ui/gtk/container.h b/ui/gtk/container.h index f224aad..a5e46b2 100644 --- a/ui/gtk/container.h +++ b/ui/gtk/container.h @@ -53,7 +53,6 @@ struct UiContainerPrivate { UiContainerX container; GtkWidget *widget; UIMENU menu; - GtkWidget *current; // TODO: remove void (*add)(UiContainerPrivate*, GtkWidget*, UiLayout *layout); UiLayout layout; diff --git a/ui/gtk/dnd.c b/ui/gtk/dnd.c index 8cb03b0..2d599cc 100644 --- a/ui/gtk/dnd.c +++ b/ui/gtk/dnd.c @@ -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; diff --git a/ui/gtk/entry.c b/ui/gtk/entry.c index 9e61e4e..ec9d190 100644 --- a/ui/gtk/entry.c +++ b/ui/gtk/entry.c @@ -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; diff --git a/ui/gtk/headerbar.h b/ui/gtk/headerbar.h index 392eae7..d3786c8 100644 --- a/ui/gtk/headerbar.h +++ b/ui/gtk/headerbar.h @@ -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 diff --git a/ui/gtk/icon.c b/ui/gtk/icon.c index 608ad04..16568ee 100644 --- a/ui/gtk/icon.c +++ b/ui/gtk/icon.c @@ -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(); } diff --git a/ui/gtk/list.c b/ui/gtk/list.c index 7a08ffc..e8b649c 100644 --- a/ui/gtk/list.c +++ b/ui/gtk/list.c @@ -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;iselectionmodel, 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 diff --git a/ui/gtk/list.h b/ui/gtk/list.h index ef828d4..e7beb0e 100644 --- a/ui/gtk/list.h +++ b/ui/gtk/list.h @@ -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); diff --git a/ui/gtk/menu.c b/ui/gtk/menu.c index 44d32ca..8bfbbbf 100644 --- a/ui/gtk/menu.c +++ b/ui/gtk/menu.c @@ -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); diff --git a/ui/gtk/menu.h b/ui/gtk/menu.h index a7f65e7..4cfa419 100644 --- a/ui/gtk/menu.h +++ b/ui/gtk/menu.h @@ -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 */ diff --git a/ui/gtk/text.c b/ui/gtk/text.c index cb52e4a..0bcd54d 100644 --- a/ui/gtk/text.c +++ b/ui/gtk/text.c @@ -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); diff --git a/ui/gtk/toolbar.c b/ui/gtk/toolbar.c index 0e195ff..33efbcd 100644 --- a/ui/gtk/toolbar.c +++ b/ui/gtk/toolbar.c @@ -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; diff --git a/ui/gtk/toolbar.h b/ui/gtk/toolbar.h index 80bd113..7534c63 100644 --- a/ui/gtk/toolbar.h +++ b/ui/gtk/toolbar.h @@ -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); diff --git a/ui/gtk/toolkit.c b/ui/gtk/toolkit.c index e0a30c2..10cba07 100644 --- a/ui/gtk/toolkit.c +++ b/ui/gtk/toolkit.c @@ -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= 4 diff --git a/ui/gtk/toolkit.h b/ui/gtk/toolkit.h index 8d1ee57..0e421b9 100644 --- a/ui/gtk/toolkit.h +++ b/ui/gtk/toolkit.h @@ -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); diff --git a/ui/gtk/window.c b/ui/gtk/window.c index 2081de2..1046391 100644 --- a/ui/gtk/window.c +++ b/ui/gtk/window.c @@ -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); diff --git a/ui/motif/button.c b/ui/motif/button.c index 54b5ed2..45e7a1b 100644 --- a/ui/motif/button.c +++ b/ui/motif/button.c @@ -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; diff --git a/ui/motif/container.c b/ui/motif/container.c index 3464862..c5c7c4d 100644 --- a/ui/motif/container.c +++ b/ui/motif/container.c @@ -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)); diff --git a/ui/motif/menu.c b/ui/motif/menu.c index b6f68ba..6d935a9 100644 --- a/ui/motif/menu.c +++ b/ui/motif/menu.c @@ -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); diff --git a/ui/motif/pathbar.c b/ui/motif/pathbar.c index 4e0cde6..e236c23 100644 --- a/ui/motif/pathbar.c +++ b/ui/motif/pathbar.c @@ -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; diff --git a/ui/ui/toolkit.h b/ui/ui/toolkit.h index 628e3e8..94c77f0 100644 --- a/ui/ui/toolkit.h +++ b/ui/ui/toolkit.h @@ -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); diff --git a/ui/win32/button.c b/ui/win32/button.c index fca53bd..e5e0737 100644 --- a/ui/win32/button.c +++ b/ui/win32/button.c @@ -242,7 +242,7 @@ UIWIDGET ui_radiobutton_create(UiObject *obj, UiToggleArgs *args) { if (i->obj) { group = i->obj; } else { - group = cxArrayListCreate(obj->ctx->allocator, NULL, CX_STORE_POINTERS, 8); + group = cxArrayListCreate(obj->ctx->allocator, CX_STORE_POINTERS, 8); i->obj = group; } diff --git a/ui/win32/grid.c b/ui/win32/grid.c index aad2d5d..90da4ef 100644 --- a/ui/win32/grid.c +++ b/ui/win32/grid.c @@ -36,7 +36,7 @@ UiGridLayout* ui_grid_layout_create(const CxAllocator *a, short columnspacing, short rowspacing) { UiGridLayout *grid = cxZalloc(a, sizeof(UiGridLayout)); - grid->widgets = cxArrayListCreate(a, NULL, sizeof(GridElm), 32); + grid->widgets = cxArrayListCreate(a, sizeof(GridElm), 32); grid->columnspacing = columnspacing; grid->rowspacing = rowspacing; return grid; diff --git a/ui/win32/list.c b/ui/win32/list.c index 2681a2e..b2c1495 100644 --- a/ui/win32/list.c +++ b/ui/win32/list.c @@ -191,17 +191,17 @@ static UIWIDGET listview_create(UiObject *obj, UiListArgs *args, UiBool table) { static UiListSelection listview_get_selection2(HWND hwnd) { UiListSelection sel = { 0, NULL }; - CX_ARRAY_DECLARE(int, indices); - cx_array_initialize(indices, 8); + CX_ARRAY(int, indices); + cx_array_init(indices, 8); int index = -1; while ((index = ListView_GetNextItem(hwnd, index, LVNI_SELECTED)) != -1) { - cx_array_simple_add(indices, index); + cx_array_add(indices, index); } - if (indices_size > 0) { - sel.rows = indices; - sel.count = indices_size; + if (indices.size > 0) { + sel.rows = indices.data; + sel.count = indices.size; } return sel; diff --git a/ui/win32/menu.c b/ui/win32/menu.c index c825866..82bcf2b 100644 --- a/ui/win32/menu.c +++ b/ui/win32/menu.c @@ -28,6 +28,8 @@ #include "menu.h" +#include + static ui_menu_add_f createMenuItem[] = { /* UI_MENU */ ui_add_menu, /* UI_MENU_ITEM */ ui_add_menu_item, @@ -58,7 +60,6 @@ void ui_add_menu(HMENU parent, int pos, UiMenuItemI *item, UiObject *obj) { UiMenu *menu = (UiMenu*)item; HMENU hMenu = CreatePopupMenu(); AppendMenu(parent, MF_POPUP, (UINT_PTR)hMenu, menu->label); - int i = 0; UiMenuItemI *child = menu->items_begin; while (child) { @@ -67,17 +68,192 @@ void ui_add_menu(HMENU parent, int pos, UiMenuItemI *item, UiObject *obj) { } } +static void menu_item_clicked(UiObject *obj, uint64_t id, UiMenuItem *item) { + UiEvent event; + event.obj = obj; + event.window = obj->window; + event.document = obj->ctx->document; + event.eventdata = NULL; + event.eventdatatype = 0; + event.intval = 0; + event.set = 0; + if (item->callback) { + item->callback(&event, item->userdata); + } +} + void ui_add_menu_item(HMENU parent, int pos, UiMenuItemI *item, UiObject *obj) { + uint64_t id = ++obj->ctx->command_id_counter; + UiMenuItem *i = (UiMenuItem*)item; - AppendMenu(parent, MF_STRING, 0, i->label); + AppendMenu(parent, MF_STRING, id, i->label); + + UiCommand cmd; + cmd.callback = (ui_command_func)menu_item_clicked; + cmd.userdata = i; + cxMapPut(obj->ctx->command_map, id, &cmd); +} + +static void menu_stateitem_update(UiStateMenuItem *item) { + MENUITEMINFO mi = { 0 }; + mi.cbSize = sizeof(mi); + mi.fMask = MIIM_STATE; + mi.fState = item->state ? MFS_CHECKED : MFS_UNCHECKED; + SetMenuItemInfo(item->menu, item->id, FALSE, &mi); +} + +static void menu_checkitem_clicked(UiObject *obj, uint64_t id, UiStateMenuItem *item) { + item->state = !item->state; + menu_stateitem_update(item); + + UiEvent event; + event.obj = obj; + event.window = obj->window; + event.document = obj->ctx->document; + event.eventdata = NULL; + event.eventdatatype = 0; + event.intval = 0; + event.set = 0; + if (item->onchange) { + item->onchange(&event, item->userdata); + } + + if (item->var) { + UiInteger *i = item->var->value; + ui_notify_evt(i->observers, &event); + } } void ui_add_menu_checkitem(HMENU parent, int pos, UiMenuItemI *item, UiObject *obj) { + uint64_t id = ++obj->ctx->command_id_counter; + + UiMenuCheckItem *i = (UiMenuCheckItem*)item; + AppendMenu(parent, MF_STRING, id, i->label); + + // create an UiStateMenuItem with the same lifetime as the UiObject + UiStateMenuItem *sitem = ui_malloc(obj->ctx, sizeof(UiStateMenuItem)); + memset(sitem, 0, sizeof(UiStateMenuItem)); + sitem->obj = obj; + sitem->menu = parent; + sitem->id = id; + sitem->onchange = i->callback; + sitem->userdata = i->userdata; + sitem->var = uic_widget_var(obj->ctx, obj->ctx, NULL, i->varname, UI_VAR_INTEGER); + // bind to var + if (sitem->var) { + UiInteger *v = sitem->var->value; + sitem->state = v->value != 0; + v->obj = sitem; + v->get = ui_checkitem_get; + v->set = ui_checkitem_set; + } + + // register command id + UiCommand cmd; + cmd.callback = (ui_command_func)menu_checkitem_clicked; + cmd.userdata = sitem; + cxMapPut(obj->ctx->command_map, id, &cmd); + + menu_stateitem_update(sitem); +} + +int64_t ui_checkitem_get(UiInteger *i) { + return i->value; +} +void ui_checkitem_set(UiInteger *i, int64_t value) { + i->value = value; + menu_stateitem_update(i->obj); +} + +static void menu_radioitem_clicked(UiObject *obj, uint64_t id, UiStateMenuItem *item) { + UiInteger *i = item->var->value; // UiVar is always not NULL for radio items + ui_set(i, item->index+1); + + UiEvent event; + event.obj = obj; + event.window = obj->window; + event.document = obj->ctx->document; + event.eventdata = i; + event.eventdatatype = UI_EVENT_DATA_INTEGER_VALUE; + event.intval = item->state; + event.set = ui_get_setop(); + + if (item->onchange) { + item->onchange(&event, item->userdata); + } + + event.intval = (int)ui_get(i); + ui_notify_evt(i->observers, &event); } void ui_add_menu_radioitem(HMENU parent, int pos, UiMenuItemI *item, UiObject *obj) { + uint64_t id = ++obj->ctx->command_id_counter; + + UiMenuRadioItem *i = (UiMenuRadioItem*)item; + AppendMenu(parent, MF_STRING, id, i->label); + + UiVar *var = uic_widget_var(obj->ctx, obj->ctx, NULL, i->varname, UI_VAR_INTEGER); + if (!var) { + return; // radio item without var is useless + } + + UiInteger *v = var->value; + CxList *group = v->obj; + if (!group) { + // first radio button in this group + group = cxArrayListCreate(obj->ctx->allocator, sizeof(UiStateMenuItem), 4); + v->obj = group; + v->get = ui_radioitem_get; + v->set = ui_radioitem_set; + } + UiStateMenuItem sitem = { 0 }; + sitem.obj = obj; + sitem.menu = parent; + sitem.id = id; + sitem.onchange = i->callback; + sitem.userdata = i->userdata; + sitem.var = var; + sitem.index = (int)cxListSize(group); + cxListAdd(group, &sitem); + + if (v->value == sitem.index+1) { + sitem.state = 1; + menu_stateitem_update(&sitem); + } + + + UiStateMenuItem *sitem_ptr = cxListAt(group, sitem.index); + // register command id + UiCommand cmd; + cmd.callback = (ui_command_func)menu_radioitem_clicked; + cmd.userdata = sitem_ptr; + cxMapPut(obj->ctx->command_map, id, &cmd); +} + +int64_t ui_radioitem_get(UiInteger *i) { + return i->value; +} + +void ui_radioitem_set(UiInteger *i, int64_t value) { + CxList *group = i->obj; + // de-select all items + CxIterator it = cxListIterator(group); + cx_foreach(UiStateMenuItem *, item, it) { + if (item->state) { + item->state = FALSE; + menu_stateitem_update(item); + } + } + + if (value > 0) { + UiStateMenuItem *item = cxListAt(group, value-1); + if (item) { + item->state = TRUE; + menu_stateitem_update(item); + } + } } void ui_add_menu_list(HMENU parent, int pos, UiMenuItemI *item, UiObject *obj) { diff --git a/ui/win32/menu.h b/ui/win32/menu.h index 13bbf0c..48b0c5f 100644 --- a/ui/win32/menu.h +++ b/ui/win32/menu.h @@ -37,6 +37,17 @@ extern "C" { #endif +typedef struct UiStateMenuItem { + UiObject *obj; + HMENU menu; + uint64_t id; + UiVar *var; + ui_callback onchange; + void *userdata; + int index; + UiBool state; +} UiStateMenuItem; + typedef void(*ui_menu_add_f)(HMENU, int, UiMenuItemI*, UiObject*); HMENU ui_create_main_menu(UiObject *obj); @@ -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); void ui_add_menu_separator(HMENU parent, int pos, UiMenuItemI *item, UiObject *obj); +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); + #ifdef __cplusplus } #endif diff --git a/ui/win32/toolkit.c b/ui/win32/toolkit.c index 2ed1535..24346f0 100644 --- a/ui/win32/toolkit.c +++ b/ui/win32/toolkit.c @@ -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) { diff --git a/ui/win32/toolkit.h b/ui/win32/toolkit.h index 53e2b3e..1021f33 100644 --- a/ui/win32/toolkit.h +++ b/ui/win32/toolkit.h @@ -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); diff --git a/ui/win32/window.c b/ui/win32/window.c index 1f661d7..c0a51b0 100644 --- a/ui/win32/window.c +++ b/ui/win32/window.c @@ -29,6 +29,7 @@ #include "window.h" #include +#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, diff --git a/ui/win32/window.h b/ui/win32/window.h index 4562b01..8ba4ef2 100644 --- a/ui/win32/window.h +++ b/ui/win32/window.h @@ -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 diff --git a/ui/winui/text.cpp b/ui/winui/text.cpp index 70b9589..0ad5928 100644 --- a/ui/winui/text.cpp +++ b/ui/winui/text.cpp @@ -327,7 +327,7 @@ UiPathTextField::~UiPathTextField() { 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;