SRC += store.c
SRC += store_sqlite.c
SRC += notebook.c
+SRC += note.c
OBJ = $(SRC:%.c=../build/application/%.$(OBJ_EXT))
#define APP_STATE_NOTE_SELECTED 100
-
-typedef struct NotebookModel NotebookModel;
+// typedefs for NotebookModel and NoteModel are in types.h
typedef struct MainWindow {
UiObject *obj;
*/
UiList *notes;
+ /*
+ * currently attached note
+ */
+ Note *current_note;
+
/*
* The mempool used to allocate the current list of notes
*/
CxMempool *current_notes_pool;
};
+
+struct NoteModel {
+ UiContext *ctx;
+
+ UiInteger *type;
+ UiString *title;
+ UiText *text;
+ UiGeneric *html;
+};
void application_init();
--- /dev/null
+/*
+ * 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 "note.h"
+
+NoteModel* notemodel_create() {
+ NoteModel *model = ui_document_new(sizeof(NoteModel));
+ model->ctx = ui_document_context(model);
+
+ model->type = ui_int_new(model->ctx, "note_type");
+ model->title = ui_string_new(model->ctx, "note_title");
+ model->text = ui_text_new(model->ctx, "note_text");
+ model->html = ui_generic_new(model->ctx, "note_html");
+
+ ui_set(model->type, 1);
+ //ui_set_group(model->ctx, APP_STATE_NOTE_SELECTED);
+
+ return model;
+}
+
+void notemodel_set_note(NoteModel *model, Note *note) {
+ note->model = model;
+
+ ui_set(model->title, note->title);
+
+ if(note->content_loaded) {
+ // TODO: when multiple note types are implemented, chec contenttype
+ // and set model->text, model->html or something else
+ if(!note->contenttype) {
+ ui_set(model->text, note->content.ptr);
+ }
+ }
+}
--- /dev/null
+/*
+ * 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 NOTE_H
+#define NOTE_H
+
+#include "application.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+NoteModel* notemodel_create();
+void notemodel_set_note(NoteModel *model, Note *note);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NOTE_H */
+
#include "notebook.h"
#include "store.h"
+#include "note.h"
NotebookModel* notebookmodel_create() {
NotebookModel *model = ui_document_new(sizeof(NotebookModel));
void notebookmodel_reload(UiObject *obj, NotebookModel *model) {
note_store_get_notes_async(obj, model->collection_id, notebook_loaded, model);
}
+
+
+void notebookmodel_attach_note(NotebookModel *model, Note *note) {
+ if(model->current_note == note) {
+ return; // NOOP
+ }
+
+ if(note && !note->model) {
+ NoteModel *notemodel = notemodel_create();
+ notemodel_set_note(notemodel, note);
+ }
+
+ notebookmodel_detach_current_note(model);
+ ui_attach_document(model->window->obj->ctx, note->model);
+ // TODO: this is only a workaround and should be removed when
+ // sub-document groups are supported
+ ui_set_group(model->window->obj->ctx, APP_STATE_NOTE_SELECTED);
+ model->current_note = note;
+
+ if(!note->content_loaded) {
+ // TODO: load content
+ }
+}
+
+void notebookmodel_detach_current_note(NotebookModel *model) {
+ if(model->current_note && model->current_note->model) {
+ ui_detach_document2(model->window->obj->ctx, model->current_note->model);
+ model->current_note = NULL;
+ }
+}
void notebookmodel_reload(UiObject *obj, NotebookModel *model);
+void notebookmodel_attach_note(NotebookModel *model, Note *note);
+void notebookmodel_detach_current_note(NotebookModel *model);
+
#ifdef __cplusplus
}
dbuClassAdd(notes_class, Note, content);
dbuClassAdd(notes_class, Note, bin_content);
dbuClassAdd(notes_class, Note, created_by);
+ dbuClassAdd(notes_class, Note, content_loaded);
}
\ No newline at end of file
#ifdef __cplusplus
extern "C" {
#endif
+
+
+typedef struct NotebookModel NotebookModel;
+typedef struct NoteModel NoteModel;
+
typedef struct UserSettings UserSettings;
typedef struct Repository Repository;
cxmutstr content;
cxmutstr bin_content;
char *created_by;
+
+ /*
+ * indicates, whether content or bin_content was queried
+ * content_loaded is not a column in the Notes table, however it can be
+ * included in a Notes query result
+ */
+ bool content_loaded;
+
+ /*
+ * non-db member, UI model
+ */
+ NoteModel *model;
};
extern DBUClass *usersettings_class;
UiModel* model = ui_model(obj->ctx, UI_STRING, "Name", UI_STRING_FREE, "Last Modified", -1);
model->columnsize[0] = -1;
model->getvalue = window_notelist_getvalue;
- ui_table(obj, .model = model, .varname = "notes",);
+ ui_table(obj, .model = model, .varname = "notes", .onactivate = action_note_selected);
// splitpane right: content
- ui_grid(obj, .columnspacing = 10, .rowspacing = 10, .def_vfill = TRUE) {
- ui_label(obj, .label = "Title", .vfill = TRUE);
- ui_textfield(obj, .varname = "note_title", .hexpand = TRUE, .groups = UI_GROUPS(APP_STATE_NOTE_SELECTED));
- ui_newline(obj);
-
- ui_textarea(obj, .varname = "note_content", .vfill = TRUE, .hfill = TRUE, .hexpand = TRUE, .vexpand = TRUE, .colspan = 2, .groups = UI_GROUPS(APP_STATE_NOTE_SELECTED));
+ ui_tabview(obj, .tabview = UI_TABVIEW_INVISIBLE, .varname = "note_type") {
+ ui_tab(obj, "empty") {
+
+ }
+ ui_tab(obj, "textnote") {
+ ui_vbox0(obj) {
+ ui_grid(obj, .margin = 10, .columnspacing = 10, .rowspacing = 10, .def_vfill = TRUE, .fill = UI_OFF) {
+ ui_label(obj, .label = "Title", .vfill = TRUE);
+ ui_textfield(obj, .varname = "note_title", .hexpand = TRUE, .groups = UI_GROUPS(APP_STATE_NOTE_SELECTED));
+ ui_newline(obj);
+ }
+ ui_textarea(obj, .varname = "note_content", .vfill = TRUE, .hfill = TRUE, .hexpand = TRUE, .vexpand = TRUE, .colspan = 2, .groups = UI_GROUPS(APP_STATE_NOTE_SELECTED), .fill = UI_ON);
+ }
+ }
}
}
}
notebookmodel_attach(window, notebook);
}
+
+void action_note_selected(UiEvent *event, void *userdata) {
+ UiListSelection *sel = event->eventdata;
+ MainWindow *window = event->window;
+ NotebookModel *notebook = window->current_notebook;
+
+ if(!notebook) {
+ return; // should not happen
+ }
+
+ if(sel->count == 0) {
+ return; // should not happen
+ }
+
+ Note *note = ui_list_get(notebook->notes, sel->rows[0]);
+ notebookmodel_attach_note(notebook, note);
+}
void* window_notelist_getvalue(void *data, int col);
void action_notebook_selected(UiEvent *event, void *userdata);
+void action_note_selected(UiEvent *event, void *userdata);
#ifdef __cplusplus
fi
}
-dependency_error_gtk2legacy()
-{
- print_check_msg "$dep_checked_gtk2legacy" "checking for gtk2legacy... "
- # dependency gtk2legacy
- while true
- do
- if [ -z "$PKG_CONFIG" ]; then
- break
- fi
- if test_pkg_config "gtk+-2.0" "" "" "" ; then
- TEMP_CFLAGS="$TEMP_CFLAGS `"$PKG_CONFIG" --cflags gtk+-2.0`"
- TEMP_LDFLAGS="$TEMP_LDFLAGS `"$PKG_CONFIG" --libs gtk+-2.0`"
- else
- break
- fi
- TEMP_CFLAGS="$TEMP_CFLAGS -DUI_GTK2 -DUI_GTK2LEGACY"
- TEMP_LDFLAGS="$TEMP_LDFLAGS -lpthread"
- print_check_msg "$dep_checked_gtk2legacy" "yes\n"
- dep_checked_gtk2legacy=1
- return 1
- done
-
- print_check_msg "$dep_checked_gtk2legacy" "no\n"
- dep_checked_gtk2legacy=1
- return 0
-}
dependency_error_curl()
{
print_check_msg "$dep_checked_curl" "checking for curl... "
if [ -z "$PKG_CONFIG" ]; then
break
fi
- if test_pkg_config "gtk+-3.0" "" "" "" ; then
- TEMP_CFLAGS="$TEMP_CFLAGS `"$PKG_CONFIG" --cflags gtk+-3.0`"
- TEMP_LDFLAGS="$TEMP_LDFLAGS `"$PKG_CONFIG" --libs gtk+-3.0`"
+ if test_pkg_config "gtk3" "" "" "" ; then
+ TEMP_CFLAGS="$TEMP_CFLAGS `"$PKG_CONFIG" --cflags gtk3`"
+ TEMP_LDFLAGS="$TEMP_LDFLAGS `"$PKG_CONFIG" --libs gtk3`"
else
break
fi
dep_checked_gtk4=1
return 0
}
-dependency_error_openssl()
-{
- print_check_msg "$dep_checked_openssl" "checking for openssl... "
- # dependency openssl platform="windows"
- while true
- do
- if notisplatform "windows"; then
- break
- fi
- TEMP_LDFLAGS="$TEMP_LDFLAGS -lssl -lcrypto"
- print_check_msg "$dep_checked_openssl" "yes\n"
- dep_checked_openssl=1
- return 1
- done
-
- # dependency openssl platform="macos"
- while true
- do
- if notisplatform "macos"; then
- break
- fi
- TEMP_LDFLAGS="$TEMP_LDFLAGS -framework CoreFoundation"
- print_check_msg "$dep_checked_openssl" "yes\n"
- dep_checked_openssl=1
- return 1
- done
-
- # dependency openssl platform="bsd"
- while true
- do
- if notisplatform "bsd"; then
- break
- fi
- if isplatform "macos" || istoolchain "macos"; then
- break
- fi
- TEMP_LDFLAGS="$TEMP_LDFLAGS -lssl -lcrypto"
- print_check_msg "$dep_checked_openssl" "yes\n"
- dep_checked_openssl=1
- return 1
- done
-
- # dependency openssl
- while true
- do
- if [ -z "$PKG_CONFIG" ]; then
- break
- fi
- if test_pkg_config "openssl" "" "" "" ; then
- TEMP_CFLAGS="$TEMP_CFLAGS `"$PKG_CONFIG" --cflags openssl`"
- TEMP_LDFLAGS="$TEMP_LDFLAGS `"$PKG_CONFIG" --libs openssl`"
- else
- break
- fi
- print_check_msg "$dep_checked_openssl" "yes\n"
- dep_checked_openssl=1
- return 1
- done
-
- print_check_msg "$dep_checked_openssl" "no\n"
- dep_checked_openssl=1
- return 0
-}
-dependency_error_libadwaita()
+dependency_error_webkitgtk6()
{
- print_check_msg "$dep_checked_libadwaita" "checking for libadwaita... "
- # dependency libadwaita
+ print_check_msg "$dep_checked_webkitgtk6" "checking for webkitgtk6... "
+ # dependency webkitgtk6
while true
do
if [ -z "$PKG_CONFIG" ]; then
break
fi
- if test_pkg_config "libadwaita-1" "" "" "" ; then
- TEMP_CFLAGS="$TEMP_CFLAGS `"$PKG_CONFIG" --cflags libadwaita-1`"
- TEMP_LDFLAGS="$TEMP_LDFLAGS `"$PKG_CONFIG" --libs libadwaita-1`"
+ if test_pkg_config "webkitgtk-6.0" "" "" "" ; then
+ TEMP_CFLAGS="$TEMP_CFLAGS `"$PKG_CONFIG" --cflags webkitgtk-6.0`"
+ TEMP_LDFLAGS="$TEMP_LDFLAGS `"$PKG_CONFIG" --libs webkitgtk-6.0`"
else
break
fi
- TEMP_CFLAGS="$TEMP_CFLAGS -DUI_GTK4 -DUI_LIBADWAITA"
- TEMP_LDFLAGS="$TEMP_LDFLAGS -lpthread"
- print_check_msg "$dep_checked_libadwaita" "yes\n"
- dep_checked_libadwaita=1
+ TEMP_CFLAGS="$TEMP_CFLAGS -DUI_WEBVIEW"
+ print_check_msg "$dep_checked_webkitgtk6" "yes\n"
+ dep_checked_webkitgtk6=1
return 1
done
- print_check_msg "$dep_checked_libadwaita" "no\n"
- dep_checked_libadwaita=1
- return 0
-}
-dependency_error_motif()
-{
- print_check_msg "$dep_checked_motif" "checking for motif... "
- # dependency motif platform="bsd"
+ # dependency webkitgtk6
while true
do
- if notisplatform "bsd"; then
- break
- fi
- TEMP_CFLAGS="$TEMP_CFLAGS -DUI_MOTIF -I/usr/local/include/X11"
- TEMP_LDFLAGS="$TEMP_LDFLAGS -lXm -lXt -lX11 -lpthread"
- print_check_msg "$dep_checked_motif" "yes\n"
- dep_checked_motif=1
+ print_check_msg "$dep_checked_webkitgtk6" "yes\n"
+ dep_checked_webkitgtk6=1
return 1
done
- # dependency motif
- while true
- do
- TEMP_CFLAGS="$TEMP_CFLAGS -DUI_MOTIF"
- TEMP_LDFLAGS="$TEMP_LDFLAGS -lXm -lXt -lX11 -lpthread"
- print_check_msg "$dep_checked_motif" "yes\n"
- dep_checked_motif=1
- return 1
- done
-
- print_check_msg "$dep_checked_motif" "no\n"
- dep_checked_motif=1
+ print_check_msg "$dep_checked_webkitgtk6" "no\n"
+ dep_checked_webkitgtk6=1
return 0
}
dependency_error_libxml2()
dep_checked_libxml2=1
return 0
}
+dependency_error_webkit2gtk4()
+{
+ print_check_msg "$dep_checked_webkit2gtk4" "checking for webkit2gtk4... "
+ # dependency webkit2gtk4
+ while true
+ do
+ if [ -z "$PKG_CONFIG" ]; then
+ break
+ fi
+ if test_pkg_config "webkit2gtk-4.1" "" "" "" ; then
+ TEMP_CFLAGS="$TEMP_CFLAGS `"$PKG_CONFIG" --cflags webkit2gtk-4.1`"
+ TEMP_LDFLAGS="$TEMP_LDFLAGS `"$PKG_CONFIG" --libs webkit2gtk-4.1`"
+ else
+ break
+ fi
+ TEMP_CFLAGS="$TEMP_CFLAGS -DUI_WEBVIEW"
+ print_check_msg "$dep_checked_webkit2gtk4" "yes\n"
+ dep_checked_webkit2gtk4=1
+ return 1
+ done
+
+ # dependency webkit2gtk4
+ while true
+ do
+ if [ -z "$PKG_CONFIG" ]; then
+ break
+ fi
+ if test_pkg_config "webkit2gtk-4.0" "" "" "" ; then
+ TEMP_CFLAGS="$TEMP_CFLAGS `"$PKG_CONFIG" --cflags webkit2gtk-4.0`"
+ TEMP_LDFLAGS="$TEMP_LDFLAGS `"$PKG_CONFIG" --libs webkit2gtk-4.0`"
+ else
+ break
+ fi
+ TEMP_CFLAGS="$TEMP_CFLAGS -DUI_WEBVIEW"
+ print_check_msg "$dep_checked_webkit2gtk4" "yes\n"
+ dep_checked_webkit2gtk4=1
+ return 1
+ done
+
+ # dependency webkit2gtk4
+ while true
+ do
+ print_check_msg "$dep_checked_webkit2gtk4" "yes\n"
+ dep_checked_webkit2gtk4=1
+ return 1
+ done
+
+ print_check_msg "$dep_checked_webkit2gtk4" "no\n"
+ dep_checked_webkit2gtk4=1
+ return 0
+}
dependency_error_cocoa()
{
print_check_msg "$dep_checked_cocoa" "checking for cocoa... "
dep_checked_winui=1
return 0
}
+dependency_error_gtk2legacy()
+{
+ print_check_msg "$dep_checked_gtk2legacy" "checking for gtk2legacy... "
+ # dependency gtk2legacy
+ while true
+ do
+ if [ -z "$PKG_CONFIG" ]; then
+ break
+ fi
+ if test_pkg_config "gtk+-2.0" "" "" "" ; then
+ TEMP_CFLAGS="$TEMP_CFLAGS `"$PKG_CONFIG" --cflags gtk+-2.0`"
+ TEMP_LDFLAGS="$TEMP_LDFLAGS `"$PKG_CONFIG" --libs gtk+-2.0`"
+ else
+ break
+ fi
+ TEMP_CFLAGS="$TEMP_CFLAGS -DUI_GTK2 -DUI_GTK2LEGACY"
+ TEMP_LDFLAGS="$TEMP_LDFLAGS -lpthread"
+ print_check_msg "$dep_checked_gtk2legacy" "yes\n"
+ dep_checked_gtk2legacy=1
+ return 1
+ done
+
+ print_check_msg "$dep_checked_gtk2legacy" "no\n"
+ dep_checked_gtk2legacy=1
+ return 0
+}
+dependency_error_openssl()
+{
+ print_check_msg "$dep_checked_openssl" "checking for openssl... "
+ # dependency openssl platform="windows"
+ while true
+ do
+ if notisplatform "windows"; then
+ break
+ fi
+ TEMP_LDFLAGS="$TEMP_LDFLAGS -lssl -lcrypto"
+ print_check_msg "$dep_checked_openssl" "yes\n"
+ dep_checked_openssl=1
+ return 1
+ done
+
+ # dependency openssl platform="macos"
+ while true
+ do
+ if notisplatform "macos"; then
+ break
+ fi
+ TEMP_LDFLAGS="$TEMP_LDFLAGS -framework CoreFoundation"
+ print_check_msg "$dep_checked_openssl" "yes\n"
+ dep_checked_openssl=1
+ return 1
+ done
+
+ # dependency openssl platform="bsd"
+ while true
+ do
+ if notisplatform "bsd"; then
+ break
+ fi
+ if isplatform "macos" || istoolchain "macos"; then
+ break
+ fi
+ TEMP_LDFLAGS="$TEMP_LDFLAGS -lssl -lcrypto"
+ print_check_msg "$dep_checked_openssl" "yes\n"
+ dep_checked_openssl=1
+ return 1
+ done
+
+ # dependency openssl
+ while true
+ do
+ if [ -z "$PKG_CONFIG" ]; then
+ break
+ fi
+ if test_pkg_config "openssl" "" "" "" ; then
+ TEMP_CFLAGS="$TEMP_CFLAGS `"$PKG_CONFIG" --cflags openssl`"
+ TEMP_LDFLAGS="$TEMP_LDFLAGS `"$PKG_CONFIG" --libs openssl`"
+ else
+ break
+ fi
+ print_check_msg "$dep_checked_openssl" "yes\n"
+ dep_checked_openssl=1
+ return 1
+ done
+
+ print_check_msg "$dep_checked_openssl" "no\n"
+ dep_checked_openssl=1
+ return 0
+}
+dependency_error_libadwaita()
+{
+ print_check_msg "$dep_checked_libadwaita" "checking for libadwaita... "
+ # dependency libadwaita
+ while true
+ do
+ if [ -z "$PKG_CONFIG" ]; then
+ break
+ fi
+ if test_pkg_config "libadwaita-1" "" "" "" ; then
+ TEMP_CFLAGS="$TEMP_CFLAGS `"$PKG_CONFIG" --cflags libadwaita-1`"
+ TEMP_LDFLAGS="$TEMP_LDFLAGS `"$PKG_CONFIG" --libs libadwaita-1`"
+ else
+ break
+ fi
+ TEMP_CFLAGS="$TEMP_CFLAGS -DUI_GTK4 -DUI_LIBADWAITA"
+ TEMP_LDFLAGS="$TEMP_LDFLAGS -lpthread"
+ print_check_msg "$dep_checked_libadwaita" "yes\n"
+ dep_checked_libadwaita=1
+ return 1
+ done
+
+ print_check_msg "$dep_checked_libadwaita" "no\n"
+ dep_checked_libadwaita=1
+ return 0
+}
+dependency_error_motif()
+{
+ print_check_msg "$dep_checked_motif" "checking for motif... "
+ # dependency motif platform="bsd"
+ while true
+ do
+ if notisplatform "bsd"; then
+ break
+ fi
+ TEMP_CFLAGS="$TEMP_CFLAGS -DUI_MOTIF -I/usr/local/include/X11"
+ TEMP_LDFLAGS="$TEMP_LDFLAGS -lXm -lXt -lX11 -lpthread"
+ print_check_msg "$dep_checked_motif" "yes\n"
+ dep_checked_motif=1
+ return 1
+ done
+
+ # dependency motif
+ while true
+ do
+ TEMP_CFLAGS="$TEMP_CFLAGS -DUI_MOTIF"
+ TEMP_LDFLAGS="$TEMP_LDFLAGS -lXm -lXt -lX11 -lpthread"
+ print_check_msg "$dep_checked_motif" "yes\n"
+ dep_checked_motif=1
+ return 1
+ done
+
+ print_check_msg "$dep_checked_motif" "no\n"
+ dep_checked_motif=1
+ return 0
+}
# start collecting dependency information
echo > "$TEMP_DIR/flags.mk"
if dependency_error_libadwaita ; then
VERR=1
fi
+ if dependency_error_webkitgtk6 ; then
+ VERR=1
+ fi
if [ $VERR -ne 0 ]; then
return 1
fi
if dependency_error_gtk4 ; then
VERR=1
fi
+ if dependency_error_webkitgtk6 ; then
+ VERR=1
+ fi
if [ $VERR -ne 0 ]; then
return 1
fi
if dependency_error_gtk3 ; then
VERR=1
fi
+ if dependency_error_webkit2gtk4 ; then
+ VERR=1
+ fi
if [ $VERR -ne 0 ]; then
return 1
fi
int dav_config_register_keys(DavConfig *config, DavContext *ctx, dav_loadkeyfile_func loadkey) {
for(DavCfgKey *key=config->keys;key;key=key->next) {
- char *file = cx_strdup_m(key->file.value).ptr;
+ char *file = cx_strdup_a(cxDefaultAllocator, key->file.value).ptr;
cxmutstr keycontent = loadkey(file);
free(file);
}
DavKey *davkey = calloc(1, sizeof(DavKey));
- davkey->name = cx_strdup_m(key->name.value).ptr;
+ davkey->name = cx_strdup_a(cxDefaultAllocator, key->name.value).ptr;
davkey->type = dav_config_keytype(key->type);
davkey->data = keycontent.ptr;
davkey->length = keycontent.length;
}
}
- i = cxMapIteratorValues(properties);
+ CxMapIterator mi = cxMapIteratorValues(properties);
CxList *list = cxLinkedListCreateSimple(CX_STORE_POINTERS);
- cx_foreach(DavProperty*, value, i) {
+ cx_foreach(DavProperty*, value, mi) {
cxListAdd(list, value);
}
* execute a davql select statement
*/
DavResult dav_exec_select(DavSession *sn, DavQLStatement *st, va_list ap) {
- CxMempool *mp = cxBasicMempoolCreate(128);
+ CxMempool *mp = cxMempoolCreateSimple(128);
DavResult result;
result.result = NULL;
result.status = 1;
// write root element and namespaces
cx_bprintf(buf, "<D:%s xmlns:D=\"DAV:\"", rootelm);
- CxIterator mapi = cxMapIteratorValues(namespaces);
+ CxMapIterator mapi = cxMapIteratorValues(namespaces);
cx_foreach(DavNamespace*, ns, mapi) {
s = CX_STR(" xmlns:");
cxBufferWrite(s.ptr, 1, s.length, buf);
// write root element and namespaces
s = CX_STR("<D:propertyupdate xmlns:D=\"DAV:\"");
cxBufferWrite(s.ptr, 1, s.length, buf);
- CxIterator mapi = cxMapIterator(namespaces);
+ CxMapIterator mapi = cxMapIterator(namespaces);
cx_foreach(CxMapEntry*, entry, mapi) {
s = CX_STR(" xmlns:");
cxBufferWrite(s.ptr, 1, s.length, buf);
newp->encoffset = p->encoffset;
newp->isdecrypted = p->isdecrypted;
- CxIterator i = cxMapIterator(p->ids);
+ CxMapIterator i = cxMapIterator(p->ids);
cx_foreach(CxMapEntry *, e, i) {
PwdEntry *entry = e->value;
pwdstore_put(newp, entry->id, entry->user, entry->password);
write_index_entry(index, e);
}
- i = cxMapIteratorValues(p->ids);
- cx_foreach(PwdEntry*, value, i) {
+ CxMapIterator mi = cxMapIteratorValues(p->ids);
+ cx_foreach(PwdEntry*, value, mi) {
if(!value->id || !value->user || !value->password) {
continue;
}
void resource_free_properties(DavSession *sn, CxMap *properties) {
if(!properties) return;
- CxIterator i = cxMapIteratorValues(properties);
+ CxMapIterator i = cxMapIteratorValues(properties);
cx_foreach(DavProperty*, property, i) {
// TODO: free everything
dav_session_free(sn, property);
sizeof(DavPropName));
- CxIterator i = cxMapIteratorValues(data->properties);
+ CxMapIterator i = cxMapIteratorValues(data->properties);
cx_foreach(DavProperty*, value, i) {
DavPropName *name = &names[i.index];
name->ns = value->ns->name;
cxBufferPutString(content, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
cxBufferPutString(content, "<D:prop xmlns:D=\"DAV:\">\n");
- CxIterator i = cxMapIteratorValues(properties);
+ CxMapIterator i = cxMapIteratorValues(properties);
cx_foreach(DavProperty*, prop, i) {
DavXmlNode pnode;
pnode.type = DAV_XML_ELEMENT;
}
DavSession *sn = malloc(sizeof(DavSession));
memset(sn, 0, sizeof(DavSession));
- sn->mp = cxBasicMempoolCreate(DAV_SESSION_MEMPOOL_SIZE);
+ sn->mp = cxMempoolCreateSimple(DAV_SESSION_MEMPOOL_SIZE);
sn->pathcache = cxHashMapCreate(sn->mp->allocator, CX_STORE_POINTERS, DAV_PATH_CACHE_SIZE);
sn->key = NULL;
sn->errorstr = NULL;
DavSession *newsn = malloc(sizeof(DavSession));
memset(newsn, 0, sizeof(DavSession));
- newsn->mp = cxBasicMempoolCreate(DAV_SESSION_MEMPOOL_SIZE);
+ newsn->mp = cxMempoolCreateSimple(DAV_SESSION_MEMPOOL_SIZE);
newsn->pathcache = cxHashMapCreate(newsn->mp->allocator, CX_STORE_POINTERS, DAV_PATH_CACHE_SIZE);
newsn->key = sn->key;
newsn->errorstr = NULL;
value.ptr++; value.length--;
cxmutstr key_cp = cx_strdup(cx_strtrim(key));
- cx_strlower(key_cp);
+ for(int i=0;i<key_cp.length;i++) {
+ key_cp.ptr[i] = tolower(key_cp.ptr[i]);
+ }
cxmutstr value_cp = cx_strdup(cx_strtrim(value));
cxMapPut(map, cx_hash_key(key_cp.ptr, key_cp.length), value_cp.ptr);
}
if(ctx->namespaces) {
- CxIterator i = cxMapIteratorValues(ctx->namespaces);
+ CxMapIterator i = cxMapIteratorValues(ctx->namespaces);
cx_foreach(DavNamespace*, ns, i) {
if(!ns) continue;
if(ns->prefix) {
// TODO: implement
}
if(ctx->keys) {
- CxIterator i = cxMapIteratorValues(ctx->keys);
+ CxMapIterator i = cxMapIteratorValues(ctx->keys);
cx_foreach(DavKey*, key, i) {
if(!key) continue;
if(key->name) {
<ldflags>-lpthread</ldflags>
</dependency>
<dependency name="gtk3">
- <pkgconfig>gtk+-3.0</pkgconfig>
+ <pkgconfig>gtk3</pkgconfig>
<cflags>-DUI_GTK3</cflags>
<ldflags>-lpthread</ldflags>
</dependency>
<cflags>-DUI_WINUI</cflags>
</dependency>
+ <dependency name="webkitgtk6">
+ <pkgconfig>webkitgtk-6.0</pkgconfig>
+ <cflags>-DUI_WEBVIEW</cflags>
+ </dependency>
+ <dependency name="webkitgtk6">
+ <!-- webview unsupported -->
+ </dependency>
+ <dependency name="webkit2gtk4">
+ <pkgconfig>webkit2gtk-4.1</pkgconfig>
+ <cflags>-DUI_WEBVIEW</cflags>
+ </dependency>
+ <dependency name="webkit2gtk4">
+ <pkgconfig>webkit2gtk-4.0</pkgconfig>
+ <cflags>-DUI_WEBVIEW</cflags>
+ </dependency>
+ <dependency name="webkit2gtk4">
+ <!-- webview unsupported -->
+ </dependency>
+
<!--
<dependency name="qt4">
<test>which qmake-qt4</test>
<target name="tk">
<option arg="toolkit">
<value str="libadwaita">
- <dependencies>libadwaita</dependencies>
+ <dependencies>libadwaita,webkitgtk6</dependencies>
<make>TOOLKIT = gtk</make>
<make>GTKOBJ = draw_cairo.o</make>
</value>
<value str="gtk4">
- <dependencies>gtk4</dependencies>
+ <dependencies>gtk4,webkitgtk6</dependencies>
<make>TOOLKIT = gtk</make>
<make>GTKOBJ = draw_cairo.o</make>
</value>
<value str="gtk3">
- <dependencies>gtk3</dependencies>
+ <dependencies>gtk3,webkit2gtk4</dependencies>
<make>TOOLKIT = gtk</make>
<make>GTKOBJ = draw_cairo.o</make>
</value>
static void *cx_calloc_stdlib(
cx_attr_unused void *d,
- size_t nelem,
- size_t n
+ size_t nmemb,
+ size_t size
) {
- return calloc(nelem, n);
+ return calloc(nmemb, size);
}
static void cx_free_stdlib(
&cx_default_allocator_class,
NULL
};
-CxAllocator *cxDefaultAllocator = &cx_default_allocator;
+const CxAllocator * const cxDefaultAllocator = &cx_default_allocator;
-#undef cx_reallocate
-int cx_reallocate(
+int cx_reallocate_(
void **mem,
size_t n
) {
}
}
-#undef cx_reallocatearray
-int cx_reallocatearray(
+int cx_reallocatearray_(
void **mem,
size_t nmemb,
size_t size
}
}
-#undef cxReallocate
-int cxReallocate(
+int cxReallocate_(
const CxAllocator *allocator,
void **mem,
size_t n
}
}
-#undef cxReallocateArray
-int cxReallocateArray(
+int cxReallocateArray_(
const CxAllocator *allocator,
void **mem,
size_t nmemb,
void *cxCalloc(
const CxAllocator *allocator,
- size_t nelem,
- size_t n
+ size_t nmemb,
+ size_t size
) {
- return allocator->cl->calloc(allocator->data, nelem, n);
+ return allocator->cl->calloc(allocator->data, nmemb, size);
}
void cxFree(
}
}
-static ssize_t cx_arl_find_remove(
+static size_t cx_arl_find_remove(
struct cx_list_s *list,
const void *elem,
bool remove
) {
+ assert(list != NULL);
assert(list->collection.cmpfunc != NULL);
- assert(list->collection.size < SIZE_MAX / 2);
+ if (list->collection.size == 0) return 0;
char *cur = ((const cx_array_list *) list)->data;
- for (ssize_t i = 0; i < (ssize_t) list->collection.size; i++) {
+ // optimize with binary search, when sorted
+ if (list->collection.sorted) {
+ size_t i = cx_array_binary_search(
+ cur,
+ list->collection.size,
+ list->collection.elem_size,
+ elem,
+ list->collection.cmpfunc
+ );
+ if (remove && i < list->collection.size) {
+ cx_arl_remove(list, i, 1, NULL);
+ }
+ return i;
+ }
+
+ // fallback: linear search
+ for (size_t i = 0; i < list->collection.size; i++) {
if (0 == list->collection.cmpfunc(elem, cur)) {
if (remove) {
- if (1 == cx_arl_remove(list, i, 1, NULL)) {
- return i;
- } else {
- // should be unreachable
- return -1; // LCOV_EXCL_LINE
- }
- } else {
- return i;
+ cx_arl_remove(list, i, 1, NULL);
}
+ return i;
}
cur += list->collection.elem_size;
}
-
- return -1;
+ return list->collection.size;
}
static void cx_arl_sort(struct cx_list_s *list) {
cx_array_list *list = cxCalloc(allocator, 1, sizeof(cx_array_list));
if (list == NULL) return NULL;
-
- list->base.cl = &cx_array_list_class;
- list->base.collection.allocator = allocator;
+ cx_list_init((CxList*)list, &cx_array_list_class,
+ allocator, comparator, elem_size);
list->capacity = initial_capacity;
- if (elem_size > 0) {
- list->base.collection.elem_size = elem_size;
- list->base.collection.cmpfunc = comparator;
- } else {
- elem_size = sizeof(void *);
- list->base.collection.cmpfunc = comparator == NULL ? cx_cmp_ptr : comparator;
- cxListStorePointers((CxList *) list);
- }
-
// allocate the array after the real elem_size is known
- list->data = cxCalloc(allocator, initial_capacity, elem_size);
+ list->data = cxCalloc(allocator, initial_capacity,
+ list->base.collection.elem_size);
if (list->data == NULL) { // LCOV_EXCL_START
cxFree(allocator, list);
return NULL;
static size_t cx_buffer_flush_helper(
const CxBuffer *buffer,
- size_t size,
const unsigned char *src,
+ size_t size,
size_t nitems
) {
// flush data from an arbitrary source
unsigned char *space = buffer->bytes;
size_t remaining = buffer->pos / size;
size_t flushed_total = cx_buffer_flush_helper(
- buffer, size, space, remaining);
+ buffer, space, size, remaining);
// shift the buffer left after flushing
// IMPORTANT: up to this point, copy on write must have been
return nitems;
}
- size_t len;
+ size_t len, total_flushed = 0;
+cx_buffer_write_retry:
if (cx_szmul(size, nitems, &len)) {
errno = EOVERFLOW;
- return 0;
+ return total_flushed;
}
if (buffer->pos > SIZE_MAX - len) {
errno = EOVERFLOW;
- return 0;
+ return total_flushed;
}
- size_t required = buffer->pos + len;
+ size_t required = buffer->pos + len;
bool perform_flush = false;
if (required > buffer->capacity) {
if (buffer->flags & CX_BUFFER_AUTO_EXTEND) {
perform_flush = true;
} else {
if (cxBufferMinimumCapacity(buffer, required)) {
- return 0; // LCOV_EXCL_LINE
+ return total_flushed; // LCOV_EXCL_LINE
}
}
} else {
// check here and not above because of possible truncation
if (len == 0) {
- return 0;
+ return total_flushed;
}
// check if we need to copy
// perform the operation
if (perform_flush) {
- size_t items_flush;
+ size_t items_flushed;
if (buffer->pos == 0) {
// if we don't have data in the buffer, but are instructed
// to flush, it means that we are supposed to relay the data
- items_flush = cx_buffer_flush_helper(buffer, size, ptr, nitems);
- if (items_flush == 0) {
- // we needed to flush, but could not flush anything
- // give up and avoid endless trying
+ items_flushed = cx_buffer_flush_helper(buffer, ptr, size, nitems);
+ if (items_flushed == 0) {
+ // we needed to relay data, but could not flush anything
+ // i.e. we have to give up to avoid endless trying
return 0;
}
- size_t ritems = nitems - items_flush;
- const unsigned char *rest = ptr;
- rest += items_flush * size;
- return items_flush + cxBufferWrite(rest, size, ritems, buffer);
+ nitems -= items_flushed;
+ total_flushed += items_flushed;
+ if (nitems > 0) {
+ ptr = ((unsigned char*)ptr) + items_flushed * size;
+ goto cx_buffer_write_retry;
+ }
+ return total_flushed;
} else {
- items_flush = cx_buffer_flush_impl(buffer, size);
- if (items_flush == 0) {
- return 0;
+ items_flushed = cx_buffer_flush_impl(buffer, size);
+ if (items_flushed == 0) {
+ // flush target is full, let's try to truncate
+ size_t remaining_space;
+ if (buffer->flags & CX_BUFFER_AUTO_EXTEND) {
+ remaining_space = buffer->flush->threshold > buffer->pos
+ ? buffer->flush->threshold - buffer->pos
+ : 0;
+ } else {
+ remaining_space = buffer->capacity > buffer->pos
+ ? buffer->capacity - buffer->pos
+ : 0;
+ }
+ nitems = remaining_space / size;
+ if (nitems == 0) {
+ return total_flushed;
+ }
}
- return cxBufferWrite(ptr, size, nitems, buffer);
+ goto cx_buffer_write_retry;
}
} else {
memcpy(buffer->bytes + buffer->pos, ptr, len);
if (buffer->pos > buffer->size) {
buffer->size = buffer->pos;
}
- return nitems;
+ return total_flushed + nitems;
}
-
}
size_t cxBufferAppend(
CxBuffer *buffer
) {
size_t pos = buffer->pos;
- buffer->pos = buffer->size;
+ size_t append_pos = buffer->size;
+ buffer->pos = append_pos;
size_t written = cxBufferWrite(ptr, size, nitems, buffer);
- buffer->pos = pos;
+ // the buffer might have been flushed
+ // we must compute a possible delta for the position
+ // expected: pos = append_pos + written
+ // -> if this is not the case, there is a delta
+ size_t delta = append_pos + written*size - buffer->pos;
+ if (delta > pos) {
+ buffer->pos = 0;
+ } else {
+ buffer->pos = pos - delta;
+ }
return written;
}
*/
void *(*calloc)(
void *data,
- size_t nelem,
- size_t n
+ size_t nmemb,
+ size_t size
);
/**
/**
* A default allocator using standard library malloc() etc.
*/
-extern CxAllocator *cxDefaultAllocator;
+cx_attr_export
+extern const CxAllocator * const cxDefaultAllocator;
/**
* Function pointer type for destructor functions.
);
/**
- * Re-allocate a previously allocated block and changes the pointer in-place,
+ * Reallocate a previously allocated block and changes the pointer in-place,
* if necessary.
*
* @par Error handling
*/
cx_attr_nonnull
cx_attr_nodiscard
-int cx_reallocate(
+cx_attr_export
+int cx_reallocate_(
void **mem,
size_t n
);
/**
- * Re-allocate a previously allocated block and changes the pointer in-place,
+ * Reallocate a previously allocated block and changes the pointer in-place,
* if necessary.
*
* The size is calculated by multiplying @p nemb and @p size.
*/
cx_attr_nonnull
cx_attr_nodiscard
-int cx_reallocatearray(
+cx_attr_export
+int cx_reallocatearray_(
void **mem,
size_t nmemb,
size_t size
);
/**
- * Re-allocate a previously allocated block and changes the pointer in-place,
+ * Reallocate a previously allocated block and changes the pointer in-place,
* if necessary.
*
* @par Error handling
* @retval non-zero failure
* @see cx_reallocatearray()
*/
-#define cx_reallocate(mem, n) cx_reallocate((void**)(mem), n)
+#define cx_reallocate(mem, n) cx_reallocate_((void**)(mem), n)
/**
- * Re-allocate a previously allocated block and changes the pointer in-place,
+ * Reallocate a previously allocated block and changes the pointer in-place,
* if necessary.
*
* The size is calculated by multiplying @p nemb and @p size.
* @retval non-zero failure
*/
#define cx_reallocatearray(mem, nmemb, size) \
- cx_reallocatearray((void**)(mem), nmemb, size)
+ cx_reallocatearray_((void**)(mem), nmemb, size)
/**
* Free a block allocated by this allocator.
* @param mem a pointer to the block to free
*/
cx_attr_nonnull_arg(1)
+cx_attr_export
void cxFree(
const CxAllocator *allocator,
void *mem
cx_attr_malloc
cx_attr_dealloc_ucx
cx_attr_allocsize(2)
+cx_attr_export
void *cxMalloc(
const CxAllocator *allocator,
size_t n
);
/**
- * Re-allocate the previously allocated block in @p mem, making the new block
+ * Reallocate the previously allocated block in @p mem, making the new block
* @p n bytes long.
* This function may return the same pointer that was passed to it, if moving
* the memory was not necessary.
* @param allocator the allocator
* @param mem pointer to the previously allocated block
* @param n the new size in bytes
- * @return a pointer to the re-allocated memory
+ * @return a pointer to the reallocated memory
*/
cx_attr_nodiscard
cx_attr_nonnull_arg(1)
cx_attr_dealloc_ucx
cx_attr_allocsize(3)
+cx_attr_export
void *cxRealloc(
const CxAllocator *allocator,
void *mem,
);
/**
- * Re-allocate the previously allocated block in @p mem, making the new block
+ * Reallocate the previously allocated block in @p mem, making the new block
* @p n bytes long.
* This function may return the same pointer that was passed to it, if moving
* the memory was not necessary.
* @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 re-allocated memory
+ * @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_attr_export
void *cxReallocArray(
const CxAllocator *allocator,
void *mem,
);
/**
- * Re-allocate a previously allocated block and changes the pointer in-place,
+ * 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.
*
*/
cx_attr_nodiscard
cx_attr_nonnull
-int cxReallocate(
+cx_attr_export
+int cxReallocate_(
const CxAllocator *allocator,
void **mem,
size_t n
);
/**
- * Re-allocate a previously allocated block and changes the pointer in-place,
+ * 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.
*
* @retval non-zero failure
*/
#define cxReallocate(allocator, mem, n) \
- cxReallocate(allocator, (void**)(mem), n)
+ cxReallocate_(allocator, (void**)(mem), n)
/**
- * Re-allocate a previously allocated block and changes the pointer in-place,
+ * 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.
*/
cx_attr_nodiscard
cx_attr_nonnull
-int cxReallocateArray(
+cx_attr_export
+int cxReallocateArray_(
const CxAllocator *allocator,
void **mem,
size_t nmemb,
);
/**
- * Re-allocate a previously allocated block and changes the pointer in-place,
+ * 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.
* @retval non-zero failure
*/
#define cxReallocateArray(allocator, mem, nmemb, size) \
- cxReallocateArray(allocator, (void**) (mem), nmemb, size)
+ cxReallocateArray_(allocator, (void**) (mem), nmemb, size)
/**
- * Allocate @p nelem elements of @p n bytes each, all initialized to zero.
+ * Allocate @p nmemb elements of @p n bytes each, all initialized to zero.
*
* @param allocator the allocator
- * @param nelem the number of elements
- * @param n the size of each element in bytes
+ * @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_malloc
cx_attr_dealloc_ucx
cx_attr_allocsize(2, 3)
+cx_attr_export
void *cxCalloc(
const CxAllocator *allocator,
- size_t nelem,
- size_t n
+ size_t nmemb,
+ size_t size
);
#ifdef __cplusplus
* The maximum item size in an array list that fits into stack buffer
* when swapped.
*/
+cx_attr_export
extern const unsigned cx_array_swap_sbo_size;
/**
/**
* A default stdlib-based array reallocator.
*/
+cx_attr_export
extern CxArrayReallocator *cx_array_default_reallocator;
/**
* on the stack or shall not reallocated in place
* @return an array reallocator
*/
+cx_attr_export
CxArrayReallocator cx_array_reallocator(
const struct cx_allocator_s *allocator,
const void *stackmem
* @see cx_array_reallocator()
*/
cx_attr_nonnull_arg(1, 2, 3)
+cx_attr_export
int cx_array_reserve(
void **array,
void *size,
* @see cx_array_reallocator()
*/
cx_attr_nonnull_arg(1, 2, 3, 6)
+cx_attr_export
int cx_array_copy(
void **target,
void *size,
* @retval non-zero failure
*/
cx_attr_nonnull_arg(1, 2, 3, 5)
+cx_attr_export
int cx_array_insert_sorted(
void **target,
size_t *size,
* @see cx_array_binary_search()
*/
cx_attr_nonnull
+cx_attr_export
size_t cx_array_binary_search_inf(
const void *arr,
size_t size,
* @see cx_array_binary_search_sup()
*/
cx_attr_nonnull
+cx_attr_export
size_t cx_array_binary_search(
const void *arr,
size_t size,
* @see cx_array_binary_search()
*/
cx_attr_nonnull
+cx_attr_export
size_t cx_array_binary_search_sup(
const void *arr,
size_t size,
* @param idx2 index of second element
*/
cx_attr_nonnull
+cx_attr_export
void cx_array_swap(
void *arr,
size_t elem_size,
/**
* Allocates an array list for storing elements with @p elem_size bytes each.
*
- * If @p elem_size is CX_STORE_POINTERS, the created list will be created as if
- * cxListStorePointers() was called immediately after creation and the compare
- * function will be automatically set to cx_cmp_ptr(), if none is given.
+ * 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.
*
* @param allocator the allocator for allocating the list memory
* (if @c NULL, a default stdlib allocator will be used)
cx_attr_nodiscard
cx_attr_malloc
cx_attr_dealloc(cxListFree, 1)
+cx_attr_export
CxList *cxArrayListCreate(
const CxAllocator *allocator,
cx_compare_func comparator,
* 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 will be created as if
- * cxListStorePointers() was called immediately after creation and the compare
- * function will be automatically set to cx_cmp_ptr().
+ * 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.
*
* @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
*/
#define CX_BUFFER_COPY_ON_EXTEND 0x08
+/**
+ * Function pointer for cxBufferWrite that is compatible with cx_write_func.
+ * @see cx_write_func
+ */
+#define cxBufferWriteFunc ((cx_write_func) cxBufferWrite)
+/**
+ * Function pointer for cxBufferRead that is compatible with cx_read_func.
+ * @see cx_read_func
+ */
+#define cxBufferReadFunc ((cx_read_func) cxBufferRead)
+
/**
* Configuration for automatic flushing.
*/
};
/**
- * Type alais for the flush configuration struct.
+ * Type alias for the flush configuration struct.
*
* @code
* struct cx_buffer_flush_config_s {
*
* @see cxBufferEnableFlushing()
*/
- CxBufferFlushConfig* flush;
+ CxBufferFlushConfig *flush;
/** Current position of the buffer. */
size_t pos;
/** Current capacity (i.e. maximum size) of the buffer. */
* @return zero on success, non-zero if a required allocation failed
*/
cx_attr_nonnull_arg(1)
+cx_attr_export
int cxBufferInit(
CxBuffer *buffer,
void *space,
* @see cxBufferWrite()
*/
cx_attr_nonnull
+cx_attr_export
int cxBufferEnableFlushing(
CxBuffer *buffer,
CxBufferFlushConfig config
* @see cxBufferInit()
*/
cx_attr_nonnull
+cx_attr_export
void cxBufferDestroy(CxBuffer *buffer);
/**
* @param buffer the buffer to deallocate
* @see cxBufferCreate()
*/
+cx_attr_export
void cxBufferFree(CxBuffer *buffer);
/**
cx_attr_malloc
cx_attr_dealloc(cxBufferFree, 1)
cx_attr_nodiscard
+cx_attr_export
CxBuffer *cxBufferCreate(
void *space,
size_t capacity,
* @see cxBufferShiftRight()
*/
cx_attr_nonnull
+cx_attr_export
int cxBufferShift(
CxBuffer *buffer,
off_t shift
* @see cxBufferShift()
*/
cx_attr_nonnull
+cx_attr_export
int cxBufferShiftRight(
CxBuffer *buffer,
size_t shift
* @see cxBufferShift()
*/
cx_attr_nonnull
+cx_attr_export
int cxBufferShiftLeft(
CxBuffer *buffer,
size_t shift
*
*/
cx_attr_nonnull
+cx_attr_export
int cxBufferSeek(
CxBuffer *buffer,
off_t offset,
* @see cxBufferReset()
*/
cx_attr_nonnull
+cx_attr_export
void cxBufferClear(CxBuffer *buffer);
/**
* @see cxBufferClear()
*/
cx_attr_nonnull
+cx_attr_export
void cxBufferReset(CxBuffer *buffer);
/**
*/
cx_attr_nonnull
cx_attr_nodiscard
+cx_attr_export
bool cxBufferEof(const CxBuffer *buffer);
* @retval non-zero on allocation failure
*/
cx_attr_nonnull
+cx_attr_export
int cxBufferMinimumCapacity(
CxBuffer *buffer,
size_t capacity
* the target until the target signals that it cannot take more data by
* returning zero via the respective write function. In that case, the remaining
* data in this buffer is shifted to the beginning of this buffer so that the
- * newly available space can be used to append as much data as possible. This
- * function only stops writing more elements, when the flush target and this
+ * newly available space can be used to append as much data as possible.
+ *
+ * This function only stops writing more elements, when the flush target and this
* buffer are both incapable of taking more data or all data has been written.
- * If number of items that shall be written is larger than the buffer can hold,
- * the first items from @c ptr are directly relayed to the flush target, if
- * possible.
- * The number returned by this function is only the number of elements from
- * @c ptr that could be written to either the flush target or the buffer.
+ *
+ * If, after flushing, the number of items that shall be written still exceeds
+ * the capacity or flush threshold, this function tries to write all items directly
+ * to the flush target, if possible.
+ *
+ * The number returned by this function is the number of elements from
+ * @c ptr that could be written to either the flush target or the buffer
+ * (so it does not include the number of items that had been already in the buffer
+ * in were flushed during the process).
+ *
+ * @attention
+ * When @p size is larger than one and the contents of the buffer are not aligned
+ * with @p size, flushing stops after all complete items have been flushed, leaving
+ * the mis-aligned part in the buffer.
+ * Afterward, this function only writes as many items as possible to the buffer.
*
* @note The signature is compatible with the fwrite() family of functions.
*
* @see cxBufferRead()
*/
cx_attr_nonnull
+cx_attr_export
size_t cxBufferWrite(
const void *ptr,
size_t size,
* @see cxBufferRead()
*/
cx_attr_nonnull
+cx_attr_export
size_t cxBufferAppend(
const void *ptr,
size_t size,
* @see cxBufferEnableFlushing()
*/
cx_attr_nonnull
+cx_attr_export
size_t cxBufferFlush(CxBuffer *buffer);
/**
* @see cxBufferAppend()
*/
cx_attr_nonnull
+cx_attr_export
size_t cxBufferRead(
void *ptr,
size_t size,
* @see cxBufferTerminate()
*/
cx_attr_nonnull
+cx_attr_export
int cxBufferPut(
CxBuffer *buffer,
int c
* @return zero, if the terminator could be written, non-zero otherwise
*/
cx_attr_nonnull
+cx_attr_export
int cxBufferTerminate(CxBuffer *buffer);
/**
*/
cx_attr_nonnull
cx_attr_cstr_arg(2)
+cx_attr_export
size_t cxBufferPutString(
CxBuffer *buffer,
const char *str
* @return the character or @c EOF, if the end of the buffer is reached
*/
cx_attr_nonnull
+cx_attr_export
int cxBufferGet(CxBuffer *buffer);
#ifdef __cplusplus
* instead of copies of the actual objects.
*/
bool store_pointer;
+ /**
+ * Indicates if this collection is guaranteed to be sorted.
+ * Note that the elements can still be sorted, even when the collection is not aware of that.
+ */
+ bool sorted;
};
/**
*/
#define CX_COLLECTION_BASE struct cx_collection_s collection
+/**
+ * Returns the number of elements currently stored.
+ *
+ * @param c a pointer to a struct that contains #CX_COLLECTION_BASE
+ * @return (@c size_t) the number of currently stored elements
+ */
+#define cxCollectionSize(c) ((c)->collection.size)
+
+/**
+ * Returns the size of one element.
+ *
+ * If #cxCollectionStoresPointers() returns true, this is the size of a pointer.
+ *
+ * @param c a pointer to a struct that contains #CX_COLLECTION_BASE
+ * @return (@c size_t) the size of one element in bytes
+ */
+#define cxCollectionElementSize(c) ((c)->collection.elem_size)
+
+/**
+ * Indicates whether this collection only stores pointers instead of the actual data.
+ *
+ * @param c a pointer to a struct that contains #CX_COLLECTION_BASE
+ * @retval true if this collection stores only pointers to data
+ * @retval false if this collection stores the actual element's data
+ */
+#define cxCollectionStoresPointers(c) ((c)->collection.store_pointer)
+
+/**
+ * Indicates whether the collection can guarantee that the stored elements are currently sorted.
+ *
+ * This may return false even when the elements are sorted.
+ * It is totally up to the implementation of the collection whether it keeps track of the order of its elements.
+ *
+ * @param c a pointer to a struct that contains #CX_COLLECTION_BASE
+ * @retval true if the elements are currently sorted wrt. the collection's compare function
+ * @retval false if the order of elements is unknown
+ */
+#define cxCollectionSorted(c) ((c)->collection.sorted)
+
/**
* Sets a simple destructor function for this collection.
*
#error Unknown pointer size or missing size macros!
#endif
-// ---------------------------------------------------------------------------
-// Missing Defines
-// ---------------------------------------------------------------------------
-
-#ifndef SSIZE_MAX // not defined in glibc since C23 and MSVC
-#if CX_WORDSIZE == 64
-/**
- * The maximum representable value in ssize_t.
- */
-#define SSIZE_MAX 0x7fffffffffffffffll
-#else
-#define SSIZE_MAX 0x7fffffffl
-#endif
-#endif
-
-
// ---------------------------------------------------------------------------
// Attribute definitions
// ---------------------------------------------------------------------------
#endif // __STDC_VERSION__
+
+// ---------------------------------------------------------------------------
+// MSVC specifics
+// ---------------------------------------------------------------------------
+
+#ifdef _MSC_VER
+// fix missing _Thread_local support
+#define _Thread_local __declspec(thread)
+#endif // _MSC_VER
+
+#if defined(CX_WINDLL_EXPORT)
+#define cx_attr_export __declspec(dllexport)
+#elif defined(CX_WINDLL)
+#define cx_attr_export __declspec(dllimport)
+#else
+/** Only used for building Windows DLLs. */
+#define cx_attr_export
+#endif // CX_WINDLL / CX_WINDLL_EXPORT
+
// ---------------------------------------------------------------------------
// Useful function pointers
// ---------------------------------------------------------------------------
#if __cplusplus
extern "C"
#endif
-int cx_szmul_impl(size_t a, size_t b, size_t *result);
+cx_attr_export int cx_szmul_impl(size_t a, size_t b, size_t *result);
#endif // cx_szmul
-// ---------------------------------------------------------------------------
-// Fixes for MSVC incompatibilities
-// ---------------------------------------------------------------------------
-
-#ifdef _MSC_VER
-// fix missing ssize_t definition
-#include <BaseTsd.h>
-typedef SSIZE_T ssize_t;
-
-// fix missing _Thread_local support
-#define _Thread_local __declspec(thread)
-#endif // _MSC_VER
#endif // UCX_COMMON_H
*/
cx_attr_nonnull
cx_attr_nodiscard
+cx_attr_export
typedef int (*cx_compare_func)(
const void *left,
const void *right
*/
cx_attr_nonnull
cx_attr_nodiscard
+cx_attr_export
int cx_cmp_int(const void *i1, const void *i2);
/**
- * Compares two ints.
+ * Compares two integers of type int.
*
* @param i1 integer one
* @param i2 integer two
* @retval 1 if the left argument is greater than the right argument
*/
cx_attr_nodiscard
+cx_attr_export
int cx_vcmp_int(int i1, int i2);
/**
*/
cx_attr_nonnull
cx_attr_nodiscard
+cx_attr_export
int cx_cmp_longint(const void *i1, const void *i2);
/**
- * Compares two long ints.
+ * Compares two integers of type long int.
*
* @param i1 long integer one
* @param i2 long integer two
* @retval 1 if the left argument is greater than the right argument
*/
cx_attr_nodiscard
+cx_attr_export
int cx_vcmp_longint(long int i1, long int i2);
/**
*/
cx_attr_nonnull
cx_attr_nodiscard
+cx_attr_export
int cx_cmp_longlong(const void *i1, const void *i2);
/**
- * Compares twolong long ints.
+ * Compares two integers of type long long.
*
* @param i1 long long int one
* @param i2 long long int two
* @retval 1 if the left argument is greater than the right argument
*/
cx_attr_nodiscard
+cx_attr_export
int cx_vcmp_longlong(long long int i1, long long int i2);
/**
*/
cx_attr_nonnull
cx_attr_nodiscard
+cx_attr_export
int cx_cmp_int16(const void *i1, const void *i2);
/**
* @retval 1 if the left argument is greater than the right argument
*/
cx_attr_nodiscard
+cx_attr_export
int cx_vcmp_int16(int16_t i1, int16_t i2);
/**
*/
cx_attr_nonnull
cx_attr_nodiscard
+cx_attr_export
int cx_cmp_int32(const void *i1, const void *i2);
/**
* @retval 1 if the left argument is greater than the right argument
*/
cx_attr_nodiscard
+cx_attr_export
int cx_vcmp_int32(int32_t i1, int32_t i2);
/**
*/
cx_attr_nonnull
cx_attr_nodiscard
+cx_attr_export
int cx_cmp_int64(const void *i1, const void *i2);
/**
* @retval 1 if the left argument is greater than the right argument
*/
cx_attr_nodiscard
+cx_attr_export
int cx_vcmp_int64(int64_t i1, int64_t i2);
/**
*/
cx_attr_nonnull
cx_attr_nodiscard
+cx_attr_export
int cx_cmp_uint(const void *i1, const void *i2);
/**
- * Compares two unsigned ints.
+ * Compares two integers of type unsigned int.
*
* @param i1 unsigned integer one
* @param i2 unsigned integer two
* @retval 1 if the left argument is greater than the right argument
*/
cx_attr_nodiscard
+cx_attr_export
int cx_vcmp_uint(unsigned int i1, unsigned int i2);
/**
*/
cx_attr_nonnull
cx_attr_nodiscard
+cx_attr_export
int cx_cmp_ulongint(const void *i1, const void *i2);
/**
- * Compares two unsigned long ints.
+ * Compares two integers of type unsigned long int.
*
* @param i1 unsigned long integer one
* @param i2 unsigned long integer two
* @retval 1 if the left argument is greater than the right argument
*/
cx_attr_nodiscard
+cx_attr_export
int cx_vcmp_ulongint(unsigned long int i1, unsigned long int i2);
/**
*/
cx_attr_nonnull
cx_attr_nodiscard
+cx_attr_export
int cx_cmp_ulonglong(const void *i1, const void *i2);
/**
- * Compares two unsigned long long ints.
+ * Compares two integers of type unsigned long long.
*
* @param i1 unsigned long long one
* @param i2 unsigned long long two
* @retval 1 if the left argument is greater than the right argument
*/
cx_attr_nodiscard
+cx_attr_export
int cx_vcmp_ulonglong(unsigned long long int i1, unsigned long long int i2);
/**
*/
cx_attr_nonnull
cx_attr_nodiscard
+cx_attr_export
int cx_cmp_uint16(const void *i1, const void *i2);
/**
* @retval 1 if the left argument is greater than the right argument
*/
cx_attr_nodiscard
+cx_attr_export
int cx_vcmp_uint16(uint16_t i1, uint16_t i2);
/**
*/
cx_attr_nonnull
cx_attr_nodiscard
+cx_attr_export
int cx_cmp_uint32(const void *i1, const void *i2);
/**
* @retval 1 if the left argument is greater than the right argument
*/
cx_attr_nodiscard
+cx_attr_export
int cx_vcmp_uint32(uint32_t i1, uint32_t i2);
/**
*/
cx_attr_nonnull
cx_attr_nodiscard
+cx_attr_export
int cx_cmp_uint64(const void *i1, const void *i2);
/**
* @retval 1 if the left argument is greater than the right argument
*/
cx_attr_nodiscard
+cx_attr_export
int cx_vcmp_uint64(uint64_t i1, uint64_t i2);
/**
*/
cx_attr_nonnull
cx_attr_nodiscard
+cx_attr_export
int cx_cmp_float(const void *f1, const void *f2);
/**
* @retval 1 if the left argument is greater than the right argument
*/
cx_attr_nodiscard
+cx_attr_export
int cx_vcmp_float(float f1, float f2);
/**
*/
cx_attr_nonnull
cx_attr_nodiscard
+cx_attr_export
int cx_cmp_double(const void *d1, const void *d2);
/**
- * Convenience function
+ * Compares two real numbers of type double with precision 1e-14.
*
* @param d1 double one
* @param d2 double two
* @retval 1 if the left argument is greater than the right argument
*/
cx_attr_nodiscard
+cx_attr_export
int cx_vcmp_double(double d1, double d2);
/**
*/
cx_attr_nonnull
cx_attr_nodiscard
+cx_attr_export
int cx_cmp_intptr(const void *ptr1, const void *ptr2);
/**
* @retval 1 if the left argument is greater than the right argument
*/
cx_attr_nodiscard
+cx_attr_export
int cx_vcmp_intptr(intptr_t ptr1, intptr_t ptr2);
/**
*/
cx_attr_nonnull
cx_attr_nodiscard
+cx_attr_export
int cx_cmp_uintptr(const void *ptr1, const void *ptr2);
/**
* @retval 1 if the left argument is greater than the right argument
*/
cx_attr_nodiscard
+cx_attr_export
int cx_vcmp_uintptr(uintptr_t ptr1, uintptr_t ptr2);
/**
- * Compares the pointers specified in the arguments without de-referencing.
+ * Compares the pointers specified in the arguments without dereferencing.
*
* @param ptr1 pointer one
* @param ptr2 pointer two
*/
cx_attr_nonnull
cx_attr_nodiscard
+cx_attr_export
int cx_cmp_ptr(const void *ptr1, const void *ptr2);
#ifdef __cplusplus
* @see cx_hash_key()
*/
cx_attr_nonnull
+cx_attr_export
void cx_hash_murmur(CxHashKey *key);
/**
*/
cx_attr_nodiscard
cx_attr_cstr_arg(1)
+cx_attr_export
CxHashKey cx_hash_key_str(const char *str);
/**
*/
cx_attr_nodiscard
cx_attr_access_r(1, 2)
+cx_attr_export
CxHashKey cx_hash_key_bytes(
const unsigned char *bytes,
size_t len
*/
cx_attr_nodiscard
cx_attr_access_r(1, 2)
+cx_attr_export
CxHashKey cx_hash_key(
const void *obj,
size_t len
*
* If @p buckets is zero, an implementation defined default will be used.
*
- * If @p elem_size is CX_STORE_POINTERS, the created map will be created as if
- * cxMapStorePointers() was called immediately after creation.
+ * 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 removal.
cx_attr_nodiscard
cx_attr_malloc
cx_attr_dealloc(cxMapFree, 1)
+cx_attr_export
CxMap *cxHashMapCreate(
const CxAllocator *allocator,
size_t itemsize,
/**
* Creates a new hash map with a default number of buckets.
*
- * If @p elem_size is CX_STORE_POINTERS, the created map will be created as if
- * cxMapStorePointers() was called immediately after creation.
+ * 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 removal.
* @retval non-zero if a memory allocation error occurred
*/
cx_attr_nonnull
+cx_attr_export
int cxMapRehash(CxMap *map);
*/
struct cx_iterator_base_s {
/**
- * True iff the iterator points to valid data.
+ * True if the iterator points to valid data.
*/
- cx_attr_nonnull
bool (*valid)(const void *);
/**
*
* When valid returns false, the behavior of this function is undefined.
*/
- cx_attr_nonnull
- cx_attr_nodiscard
void *(*current)(const void *);
/**
* Original implementation in case the function needs to be wrapped.
*/
- cx_attr_nonnull
- cx_attr_nodiscard
void *(*current_impl)(const void *);
/**
*
* When valid returns false, the behavior of this function is undefined.
*/
- cx_attr_nonnull
void (*next)(void *);
/**
* Indicates whether this iterator may remove elements.
bool remove;
};
+/**
+ * Convenience type definition for the base structure of an iterator.
+ * @see #CX_ITERATOR_BASE
+ */
+typedef struct cx_iterator_base_s CxIteratorBase;
+
/**
* Declares base attributes for an iterator.
* Must be the first member of an iterator structure.
const void *c;
} src_handle;
- /**
- * Field for storing a key-value pair.
- * May be used by iterators that iterate over k/v-collections.
- */
- struct {
- /**
- * A pointer to the key.
- */
- const void *key;
- /**
- * A pointer to the value.
- */
- void *value;
- } kv_data;
-
- /**
- * Field for storing a slot number.
- * May be used by iterators that iterate over multi-bucket collections.
- */
- size_t slot;
-
/**
* If the iterator is position-aware, contains the index of the element in the underlying collection.
* Otherwise, this field is usually uninitialized.
/**
* Checks if the iterator points to valid data.
*
- * This is especially false for past-the-end iterators.
- *
* @param iter the iterator
* @retval true if the iterator points to valid data
* @retval false if the iterator already moved past the end
* This is useful for APIs that expect some iterator as an argument.
*
* @param iter the iterator
- * @return (@c CxIterator*) a pointer to the iterator
+ * @return (@c struct @c cx_iterator_base_s*) a pointer to the iterator
*/
#define cxIteratorRef(iter) &((iter).base)
* @see cxIteratorPtr()
*/
cx_attr_nodiscard
+cx_attr_export
CxIterator cxIterator(
const void *array,
size_t elem_size,
* @return an iterator for the specified array
*/
cx_attr_nodiscard
+cx_attr_export
CxIterator cxMutIterator(
void *array,
size_t elem_size,
* @see cxIterator()
*/
cx_attr_nodiscard
+cx_attr_export
CxIterator cxIteratorPtr(
const void *array,
size_t elem_count
* @see cxIteratorPtr()
*/
cx_attr_nodiscard
+cx_attr_export
CxIterator cxMutIteratorPtr(
void *array,
size_t elem_count,
*/
CxJsonTokenType tokentype;
/**
- * True, iff the @c content must be passed to cx_strfree().
+ * True, if the @c content must be passed to cx_strfree().
*/
bool allocated;
/**
* Internally reserved memory for the value buffer stack.
*/
CxJsonValue* vbuf_internal[8];
-
- /**
- * Used internally.
- */
- bool tokenizer_escape; // TODO: check if it can be replaced with look-behind
};
/**
bool sort_members;
/**
* The maximum number of fractional digits in a number value.
+ * The default value is 6 and values larger than 15 are reduced to 15.
+ * Note, that the actual number of digits may be lower, depending on the concrete number.
*/
uint8_t frac_max_digits;
/**
* Indentation is only used in pretty output.
*/
uint8_t indent;
+ /**
+ * Set true to enable escaping of the slash character (solidus).
+ */
+ bool escape_slash;
};
/**
* @return new JSON writer settings
*/
cx_attr_nodiscard
+cx_attr_export
CxJsonWriter cxJsonWriterCompact(void);
/**
* @return new JSON writer settings
*/
cx_attr_nodiscard
+cx_attr_export
CxJsonWriter cxJsonWriterPretty(bool use_spaces);
/**
* Writes a JSON value to a buffer or stream.
*
- * This function blocks until all data is written or an error when trying
- * to write data occurs.
+ * This function blocks until either all data is written, or an error occurs.
* The write operation is not atomic in the sense that it might happen
* that the data is only partially written when an error occurs with no
* way to indicate how much data was written.
* @retval non-zero when no or not all data could be written
*/
cx_attr_nonnull_arg(1, 2, 3)
+cx_attr_export
int cxJsonWrite(
void* target,
const CxJsonValue* value,
* @see cxJsonDestroy()
*/
cx_attr_nonnull_arg(1)
+cx_attr_export
void cxJsonInit(CxJson *json, const CxAllocator *allocator);
/**
* @see cxJsonInit()
*/
cx_attr_nonnull
+cx_attr_export
void cxJsonDestroy(CxJson *json);
/**
*/
cx_attr_nonnull
cx_attr_access_r(2, 3)
+cx_attr_export
int cxJsonFilln(CxJson *json, const char *buf, size_t len);
#ifdef __cplusplus
* @see cxJsonArrAddValues()
*/
cx_attr_nodiscard
+cx_attr_export
CxJsonValue* cxJsonCreateObj(const CxAllocator* allocator);
/**
* @see cxJsonArrAddValues()
*/
cx_attr_nodiscard
+cx_attr_export
CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator);
/**
* @see cxJsonArrAddNumbers()
*/
cx_attr_nodiscard
+cx_attr_export
CxJsonValue* cxJsonCreateNumber(const CxAllocator* allocator, double num);
/**
* @see cxJsonArrAddIntegers()
*/
cx_attr_nodiscard
+cx_attr_export
CxJsonValue* cxJsonCreateInteger(const CxAllocator* allocator, int64_t num);
/**
cx_attr_nodiscard
cx_attr_nonnull_arg(2)
cx_attr_cstr_arg(2)
+cx_attr_export
CxJsonValue* cxJsonCreateString(const CxAllocator* allocator, const char *str);
/**
* @see cxJsonArrAddCxStrings()
*/
cx_attr_nodiscard
+cx_attr_export
CxJsonValue* cxJsonCreateCxString(const CxAllocator* allocator, cxstring str);
/**
* @see cxJsonArrAddLiterals()
*/
cx_attr_nodiscard
+cx_attr_export
CxJsonValue* cxJsonCreateLiteral(const CxAllocator* allocator, CxJsonLiteral lit);
/**
*/
cx_attr_nonnull
cx_attr_access_r(2, 3)
+cx_attr_export
int cxJsonArrAddNumbers(CxJsonValue* arr, const double* num, size_t count);
/**
*/
cx_attr_nonnull
cx_attr_access_r(2, 3)
+cx_attr_export
int cxJsonArrAddIntegers(CxJsonValue* arr, const int64_t* num, size_t count);
/**
*/
cx_attr_nonnull
cx_attr_access_r(2, 3)
+cx_attr_export
int cxJsonArrAddStrings(CxJsonValue* arr, const char* const* str, size_t count);
/**
*/
cx_attr_nonnull
cx_attr_access_r(2, 3)
+cx_attr_export
int cxJsonArrAddCxStrings(CxJsonValue* arr, const cxstring* str, size_t count);
/**
*/
cx_attr_nonnull
cx_attr_access_r(2, 3)
+cx_attr_export
int cxJsonArrAddLiterals(CxJsonValue* arr, const CxJsonLiteral* lit, size_t count);
/**
*/
cx_attr_nonnull
cx_attr_access_r(2, 3)
+cx_attr_export
int cxJsonArrAddValues(CxJsonValue* arr, CxJsonValue* const* val, size_t count);
/**
* @retval non-zero allocation failure
*/
cx_attr_nonnull
+cx_attr_export
int cxJsonObjPut(CxJsonValue* obj, cxstring name, CxJsonValue* child);
/**
* @see cxJsonCreateObj()
*/
cx_attr_nonnull
+cx_attr_export
CxJsonValue* cxJsonObjPutObj(CxJsonValue* obj, cxstring name);
/**
* @see cxJsonCreateArr()
*/
cx_attr_nonnull
+cx_attr_export
CxJsonValue* cxJsonObjPutArr(CxJsonValue* obj, cxstring name);
/**
* @see cxJsonCreateNumber()
*/
cx_attr_nonnull
+cx_attr_export
CxJsonValue* cxJsonObjPutNumber(CxJsonValue* obj, cxstring name, double num);
/**
* @see cxJsonCreateInteger()
*/
cx_attr_nonnull
+cx_attr_export
CxJsonValue* cxJsonObjPutInteger(CxJsonValue* obj, cxstring name, int64_t num);
/**
*/
cx_attr_nonnull
cx_attr_cstr_arg(3)
+cx_attr_export
CxJsonValue* cxJsonObjPutString(CxJsonValue* obj, cxstring name, const char* str);
/**
* @see cxJsonCreateCxString()
*/
cx_attr_nonnull
+cx_attr_export
CxJsonValue* cxJsonObjPutCxString(CxJsonValue* obj, cxstring name, cxstring str);
/**
* @see cxJsonCreateLiteral()
*/
cx_attr_nonnull
+cx_attr_export
CxJsonValue* cxJsonObjPutLiteral(CxJsonValue* obj, cxstring name, CxJsonLiteral lit);
/**
*
* @param value the value
*/
+cx_attr_export
void cxJsonValueFree(CxJsonValue *value);
/**
*/
cx_attr_nonnull
cx_attr_access_w(2)
+cx_attr_export
CxJsonStatus cxJsonNext(CxJson *json, CxJsonValue **value);
/**
*/
cx_attr_nonnull
cx_attr_returns_nonnull
+cx_attr_export
CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index);
/**
*/
cx_attr_nonnull
cx_attr_nodiscard
+cx_attr_export
CxIterator cxJsonArrIter(const CxJsonValue *value);
/**
*/
cx_attr_nonnull
cx_attr_nodiscard
+cx_attr_export
CxIterator cxJsonObjIter(const CxJsonValue *value);
/**
*/
cx_attr_nonnull
cx_attr_returns_nonnull
+cx_attr_export
CxJsonValue *cx_json_obj_get_cxstr(const CxJsonValue *value, cxstring name);
#ifdef __cplusplus
} // extern "C"
-CxJsonValue *cxJsonObjGet(const CxJsonValue *value, cxstring name) {
+static inline CxJsonValue *cxJsonObjGet(const CxJsonValue *value, cxstring name) {
return cx_json_obj_get_cxstr(value, name);
}
-CxJsonValue *cxJsonObjGet(const CxJsonValue *value, cxmutstr name) {
+static inline CxJsonValue *cxJsonObjGet(const CxJsonValue *value, cxmutstr name) {
return cx_json_obj_get_cxstr(value, cx_strcast(name));
}
-CxJsonValue *cxJsonObjGet(const CxJsonValue *value, const char *name) {
+static inline CxJsonValue *cxJsonObjGet(const CxJsonValue *value, const char *name) {
return cx_json_obj_get_cxstr(value, cx_str(name));
}
extern "C" {
#endif
-/**
- * The maximum item size that uses SBO swap instead of relinking.
- *
- */
-extern const unsigned cx_linked_list_swap_sbo_size;
-
/**
* Allocates a linked list for storing elements with @p elem_size bytes each.
*
- * If @p elem_size is CX_STORE_POINTERS, the created list will be created as if
- * cxListStorePointers() was called immediately after creation and the compare
- * function will be automatically set to cx_cmp_ptr(), if none is given.
+ * 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.
*
* @param allocator the allocator for allocating the list nodes
* (if @c NULL, a default stdlib allocator will be used)
cx_attr_nodiscard
cx_attr_malloc
cx_attr_dealloc(cxListFree, 1)
+cx_attr_export
CxList *cxLinkedListCreate(
const CxAllocator *allocator,
cx_compare_func comparator,
* 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 will be created as if
- * cxListStorePointers() was called immediately after creation and the compare
- * function will be automatically set to cx_cmp_ptr().
+ * 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.
*
* @param elem_size (@c size_t) the size of each element in bytes
* @return (@c CxList*) the created list
*/
cx_attr_nonnull
cx_attr_nodiscard
+cx_attr_export
void *cx_linked_list_at(
const void *start,
size_t start_index,
size_t index
);
-/**
- * Finds the index of 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 cmp_func a compare function to compare @p elem against the node data
- * @param elem a pointer to the element to find
- * @return the index of the element or a negative value if it could not be found
- */
-cx_attr_nonnull
-ssize_t cx_linked_list_find(
- const void *start,
- ptrdiff_t loc_advance,
- ptrdiff_t loc_data,
- cx_compare_func cmp_func,
- const void *elem
-);
-
/**
* Finds the node containing an element within a linked list.
*
- * @param result a pointer to the memory where the node pointer (or @c NULL if the element
- * could not be found) shall be stored to
* @param start a pointer to the start node
* @param loc_advance the location of the pointer to advance
* @param loc_data the location of the @c data pointer within your node struct
* @param cmp_func a compare function to compare @p elem against the node data
* @param elem a pointer to the element to find
- * @return the index of the element or a negative value if it could not be found
+ * @param found_index an optional pointer where the index of the found node
+ * (given that @p start has index 0) is stored
+ * @return the index of the element, if found - unspecified if not found
*/
-cx_attr_nonnull
-ssize_t cx_linked_list_find_node(
- void **result,
+cx_attr_nonnull_arg(1, 4, 5)
+cx_attr_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
+ const void *elem,
+ size_t *found_index
);
/**
*/
cx_attr_nonnull
cx_attr_returns_nonnull
+cx_attr_export
void *cx_linked_list_first(
const void *node,
ptrdiff_t loc_prev
*/
cx_attr_nonnull
cx_attr_returns_nonnull
+cx_attr_export
void *cx_linked_list_last(
const void *node,
ptrdiff_t loc_next
* @return the node or @c NULL if @p node has no predecessor
*/
cx_attr_nonnull
+cx_attr_export
void *cx_linked_list_prev(
const void *begin,
ptrdiff_t loc_next,
* @param new_node a pointer to the node that shall be appended
*/
cx_attr_nonnull_arg(5)
+cx_attr_export
void cx_linked_list_add(
void **begin,
void **end,
* @param new_node a pointer to the node that shall be prepended
*/
cx_attr_nonnull_arg(5)
+cx_attr_export
void cx_linked_list_prepend(
void **begin,
void **end,
* @param loc_next the location of a @c next pointer within your node struct (required)
*/
cx_attr_nonnull
+cx_attr_export
void cx_linked_list_link(
void *left,
void *right,
* @param loc_next the location of a @c next pointer within your node struct (required)
*/
cx_attr_nonnull
+cx_attr_export
void cx_linked_list_unlink(
void *left,
void *right,
* @param new_node a pointer to the node that shall be inserted
*/
cx_attr_nonnull_arg(6)
+cx_attr_export
void cx_linked_list_insert(
void **begin,
void **end,
* @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_attr_export
void cx_linked_list_insert_chain(
void **begin,
void **end,
* @param cmp_func a compare function that will receive the node pointers
*/
cx_attr_nonnull_arg(1, 5, 6)
+cx_attr_export
void cx_linked_list_insert_sorted(
void **begin,
void **end,
* @param cmp_func a compare function that will receive the node pointers
*/
cx_attr_nonnull_arg(1, 5, 6)
+cx_attr_export
void cx_linked_list_insert_sorted_chain(
void **begin,
void **end,
* @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_attr_export
size_t cx_linked_list_remove_chain(
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_attr_export
size_t cx_linked_list_size(
const void *node,
ptrdiff_t loc_next
* @param cmp_func the compare function defining the sort order
*/
cx_attr_nonnull_arg(1, 6)
+cx_attr_export
void cx_linked_list_sort(
void **begin,
void **end,
* 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_attr_export
int cx_linked_list_compare(
const void *begin_left,
const void *begin_right,
* @param loc_next the location of a @c next pointer within your node struct (required)
*/
cx_attr_nonnull_arg(1)
+cx_attr_export
void cx_linked_list_reverse(
void **begin,
void **end,
/**
* Member function for inserting a single element.
- * Implementors SHOULD see to performant implementations for corner cases.
*/
int (*insert_element)(
struct cx_list_s *list,
/**
* Member function for inserting multiple elements.
- * Implementors SHOULD see to performant implementations for corner cases.
*
* @see cx_list_default_insert_array()
*/
/**
* Member function for finding and optionally removing an element.
*/
- ssize_t (*find_remove)(
+ size_t (*find_remove)(
struct cx_list_s *list,
const void *elem,
bool remove
* @return the number of elements actually inserted
*/
cx_attr_nonnull
+cx_attr_export
size_t cx_list_default_insert_array(
struct cx_list_s *list,
size_t index,
* @return the number of elements actually inserted
*/
cx_attr_nonnull
+cx_attr_export
size_t cx_list_default_insert_sorted(
struct cx_list_s *list,
const void *sorted_data,
* @param list the list that shall be sorted
*/
cx_attr_nonnull
+cx_attr_export
void cx_list_default_sort(struct cx_list_s *list);
/**
* allocation for the temporary buffer fails
*/
cx_attr_nonnull
+cx_attr_export
int cx_list_default_swap(struct cx_list_s *list, size_t i, size_t j);
/**
- * Common type for all list implementations.
- */
-typedef struct cx_list_s CxList;
-
-/**
- * Advises the list to store copies of the objects (default mode of operation).
- *
- * Retrieving objects from this list will yield pointers to the copies stored
- * within this list.
- *
- * @param list the list
- * @see cxListStorePointers()
- */
-cx_attr_nonnull
-void cxListStoreObjects(CxList *list);
-
-/**
- * Advises the list to only store pointers to the objects.
- *
- * Retrieving objects from this list will yield the original pointers stored.
- *
- * @note This function forcibly sets the element size to the size of a pointer.
- * Invoking this function on a non-empty list that already stores copies of
- * objects is undefined.
- *
- * @param list the list
- * @see cxListStoreObjects()
- */
-cx_attr_nonnull
-void cxListStorePointers(CxList *list);
+ * Initializes a list struct.
+ *
+ * Only use this function if you are creating your own list implementation.
+ * 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.
+ *
+ * @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));
+ * if (list == NULL) return NULL;
+ *
+ * // initialize
+ * cx_list_init((CxList*)list, &my_custom_list_class,
+ * allocator, comparator, elem_size);
+ *
+ * // ... some more custom stuff ...
+ *
+ * return (CxList *) list;
+ * }
+ * @endcode
+ *
+ * @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_attr_export
+void cx_list_init(
+ struct cx_list_s *list,
+ struct cx_list_class_s *cl,
+ const struct cx_allocator_s *allocator,
+ cx_compare_func comparator,
+ size_t elem_size
+);
/**
- * Returns true, if this list is storing pointers instead of the actual data.
- *
- * @param list
- * @return true, if this list is storing pointers
- * @see cxListStorePointers()
+ * Common type for all list implementations.
*/
-cx_attr_nonnull
-static inline bool cxListIsStoringPointers(const CxList *list) {
- return list->collection.store_pointer;
-}
+typedef struct cx_list_s CxList;
/**
* Returns the number of elements currently stored in the list.
CxList *list,
const void *elem
) {
+ list->collection.sorted = false;
return list->cl->insert_element(list, list->collection.size, elem);
}
const void *array,
size_t n
) {
+ list->collection.sorted = false;
return list->cl->insert_array(list, list->collection.size, array, n);
}
size_t index,
const void *elem
) {
+ list->collection.sorted = false;
return list->cl->insert_element(list, index, elem);
}
/**
* Inserts an item into a sorted list.
*
+ * If the list is not sorted already, the behavior is undefined.
+ *
* @param list the list
* @param elem a pointer to the element to add
* @retval zero success
CxList *list,
const void *elem
) {
+ list->collection.sorted = true; // guaranteed by definition
const void *data = list->collection.store_pointer ? &elem : elem;
return list->cl->insert_sorted(list, data, 1) == 0;
}
const void *array,
size_t n
) {
+ list->collection.sorted = false;
return list->cl->insert_array(list, index, array, n);
}
* If this list is storing pointers instead of objects @p array is expected to
* be an array of pointers.
*
+ * If the list is not sorted already, the behavior is undefined.
+ *
* @param list the list
* @param array a pointer to the elements to add
* @param n the number of elements to add
const void *array,
size_t n
) {
+ list->collection.sorted = true; // guaranteed by definition
return list->cl->insert_sorted(list, array, n);
}
CxIterator *iter,
const void *elem
) {
- return ((struct cx_list_s *) iter->src_handle.m)->cl->insert_iter(iter, elem, 0);
+ CxList* list = (CxList*)iter->src_handle.m;
+ list->collection.sorted = false;
+ return list->cl->insert_iter(iter, elem, 0);
}
/**
CxIterator *iter,
const void *elem
) {
- return ((struct cx_list_s *) iter->src_handle.m)->cl->insert_iter(iter, elem, 1);
+ CxList* list = (CxList*)iter->src_handle.m;
+ list->collection.sorted = false;
+ return list->cl->insert_iter(iter, elem, 1);
}
/**
*/
cx_attr_nonnull
static inline void cxListClear(CxList *list) {
+ list->collection.sorted = true; // empty lists are always sorted
list->cl->clear(list);
}
size_t i,
size_t j
) {
+ list->collection.sorted = false;
return list->cl->swap(list, i, j);
}
*/
cx_attr_nonnull
cx_attr_nodiscard
+cx_attr_export
CxIterator cxListMutIteratorAt(
CxList *list,
size_t index
*/
cx_attr_nonnull
cx_attr_nodiscard
+cx_attr_export
CxIterator cxListMutBackwardsIteratorAt(
CxList *list,
size_t index
*
* @param list the list
* @param elem the element to find
- * @return the index of the element or a negative
- * value when the element is not found
+ * @return the index of the element or the size of the list when the element is not found
+ * @see cxListIndexValid()
*/
cx_attr_nonnull
cx_attr_nodiscard
-static inline ssize_t cxListFind(
+static inline size_t cxListFind(
const CxList *list,
const void *elem
) {
return list->cl->find_remove((CxList*)list, elem, false);
}
+/**
+ * Checks if the specified index is within bounds.
+ *
+ * @param list the list
+ * @param index the index
+ * @retval true if the index is within bounds
+ * @retval false if the index is out of bounds
+ */
+cx_attr_nonnull
+cx_attr_nodiscard
+static inline bool cxListIndexValid(const CxList *list, size_t index) {
+ return index < list->collection.size;
+}
+
/**
* Removes and returns the index of the first element that equals @p elem.
*
*
* @param list the list
* @param elem the element to find and remove
- * @return the index of the now removed element or a negative
- * value when the element is not found or could not be removed
+ * @return the index of the now removed element or the list size
+ * when the element is not found or could not be removed
+ * @see cxListIndexValid()
*/
cx_attr_nonnull
-static inline ssize_t cxListFindRemove(
+static inline size_t cxListFindRemove(
CxList *list,
const void *elem
) {
cx_attr_nonnull
static inline void cxListSort(CxList *list) {
list->cl->sort(list);
+ list->collection.sorted = true;
}
/**
*/
cx_attr_nonnull
static inline void cxListReverse(CxList *list) {
+ // still sorted, but not according to the cmp_func
+ list->collection.sorted = false;
list->cl->reverse(list);
}
*/
cx_attr_nonnull
cx_attr_nodiscard
+cx_attr_export
int cxListCompare(
const CxList *list,
const CxList *other
*
* @param list the list which shall be freed
*/
+cx_attr_export
void cxListFree(CxList *list);
/**
* You can use this is a placeholder for initializing CxList pointers
* for which you do not want to reserve memory right from the beginning.
*/
+cx_attr_export
extern CxList *const cxEmptyList;
/** Type for a map entry. */
typedef struct cx_map_entry_s CxMapEntry;
+/** Type for a map iterator. */
+typedef struct cx_map_iterator_s CxMapIterator;
+
/** Type for map class definitions. */
typedef struct cx_map_class_s cx_map_class;
cx_map_class *cl;
};
+/**
+ * A map entry.
+ */
+struct cx_map_entry_s {
+ /**
+ * A pointer to the key.
+ */
+ const CxHashKey *key;
+ /**
+ * A pointer to the value.
+ */
+ void *value;
+};
+
/**
* The type of iterator for a map.
*/
CX_MAP_ITERATOR_VALUES
};
+/**
+ * Internal iterator struct - use CxMapIterator.
+ */
+struct cx_map_iterator_s {
+ /**
+ * Inherited common data for all iterators.
+ */
+ CX_ITERATOR_BASE;
+
+ /**
+ * Handle for the source map.
+ */
+ union {
+ /**
+ * Access for mutating iterators.
+ */
+ CxMap *m;
+ /**
+ * Access for normal iterators.
+ */
+ const CxMap *c;
+ } map;
+
+ /**
+ * Handle for the current element.
+ *
+ * @attention Depends on the map implementation, do not assume a type (better: do not use!).
+ */
+ void *elem;
+
+ /**
+ * Reserved memory for a map entry.
+ *
+ * If a map implementation uses an incompatible layout, the iterator needs something
+ * to point to during iteration which @em is compatible.
+ */
+ CxMapEntry entry;
+
+ /**
+ * Field for storing the current slot number.
+ *
+ * (Used internally)
+ */
+ size_t slot;
+
+ /**
+ * Counts the elements successfully.
+ * It usually does not denote a stable index within the map as it would be for arrays.
+ */
+ size_t index;
+
+ /**
+ * The size of a value stored in this map.
+ */
+ size_t elem_size;
+
+ /**
+ * May contain the total number of elements, if known.
+ * Set to @c SIZE_MAX when the total number is unknown during iteration.
+ *
+ * @remark The UCX implementations of #CxMap always know the number of elements they store.
+ */
+ size_t elem_count;
+
+ /**
+ * The type of this iterator.
+ */
+ enum cx_map_iterator_type type;
+};
+
/**
* The class definition for arbitrary maps.
*/
/**
* Creates an iterator for this map.
*/
- CxIterator (*iterator)(const CxMap *map, enum cx_map_iterator_type type);
-};
-
-/**
- * A map entry.
- */
-struct cx_map_entry_s {
- /**
- * A pointer to the key.
- */
- const CxHashKey *key;
- /**
- * A pointer to the value.
- */
- void *value;
+ CxMapIterator (*iterator)(const CxMap *map, enum cx_map_iterator_type type);
};
/**
* You can use this is a placeholder for initializing CxMap pointers
* for which you do not want to reserve memory right from the beginning.
*/
+cx_attr_export
extern CxMap *const cxEmptyMap;
-/**
- * Advises the map to store copies of the objects (default mode of operation).
- *
- * Retrieving objects from this map will yield pointers to the copies stored
- * within this list.
- *
- * @param map the map
- * @see cxMapStorePointers()
- */
-cx_attr_nonnull
-static inline void cxMapStoreObjects(CxMap *map) {
- map->collection.store_pointer = false;
-}
-
-/**
- * Advises the map to only store pointers to the objects.
- *
- * Retrieving objects from this list will yield the original pointers stored.
- *
- * @note This function forcibly sets the element size to the size of a pointer.
- * Invoking this function on a non-empty map that already stores copies of
- * objects is undefined.
- *
- * @param map the map
- * @see cxMapStoreObjects()
- */
-cx_attr_nonnull
-static inline void cxMapStorePointers(CxMap *map) {
- map->collection.store_pointer = true;
- map->collection.elem_size = sizeof(void *);
-}
-
-/**
- * Returns true, if this map is storing pointers instead of the actual data.
- *
- * @param map
- * @return true, if this map is storing pointers
- * @see cxMapStorePointers()
- */
-cx_attr_nonnull
-static inline bool cxMapIsStoringPointers(const CxMap *map) {
- return map->collection.store_pointer;
-}
-
/**
* Deallocates the memory of the specified map.
*
*
* @param map the map to be freed
*/
+cx_attr_export
void cxMapFree(CxMap *map);
/**
* Creates a value iterator for a map.
*
+ * When the map is storing pointers, those pointers are returned.
+ * Otherwise, the iterator iterates over pointers to the memory within the map where the
+ * respective elements are stored.
+ *
* @note An iterator iterates over all elements successively. Therefore, the order
* highly depends on the map implementation and may change arbitrarily when the contents change.
*
*/
cx_attr_nonnull
cx_attr_nodiscard
-static inline CxIterator cxMapIteratorValues(const CxMap *map) {
+static inline CxMapIterator cxMapIteratorValues(const CxMap *map) {
return map->cl->iterator(map, CX_MAP_ITERATOR_VALUES);
}
/**
* Creates a key iterator for a map.
*
- * The elements of the iterator are keys of type CxHashKey.
+ * The elements of the iterator are keys of type CxHashKey and the pointer returned
+ * during iterator shall be treated as @c const @c CxHashKey* .
*
* @note An iterator iterates over all elements successively. Therefore, the order
* highly depends on the map implementation and may change arbitrarily when the contents change.
*/
cx_attr_nonnull
cx_attr_nodiscard
-static inline CxIterator cxMapIteratorKeys(const CxMap *map) {
+static inline CxMapIterator cxMapIteratorKeys(const CxMap *map) {
return map->cl->iterator(map, CX_MAP_ITERATOR_KEYS);
}
/**
* Creates an iterator for a map.
*
- * The elements of the iterator are key/value pairs of type CxMapEntry.
+ * The elements of the iterator are key/value pairs of type CxMapEntry and the pointer returned
+ * during iterator shall be treated as @c const @c CxMapEntry* .
*
* @note An iterator iterates over all elements successively. Therefore, the order
* highly depends on the map implementation and may change arbitrarily when the contents change.
*/
cx_attr_nonnull
cx_attr_nodiscard
-static inline CxIterator cxMapIterator(const CxMap *map) {
+static inline CxMapIterator cxMapIterator(const CxMap *map) {
return map->cl->iterator(map, CX_MAP_ITERATOR_PAIRS);
}
/**
* Creates a mutating iterator over the values of a map.
*
+ * When the map is storing pointers, those pointers are returned.
+ * Otherwise, the iterator iterates over pointers to the memory within the map where the
+ * respective elements are stored.
+ *
* @note An iterator iterates over all elements successively. Therefore, the order
* highly depends on the map implementation and may change arbitrarily when the contents change.
*
*/
cx_attr_nonnull
cx_attr_nodiscard
-CxIterator cxMapMutIteratorValues(CxMap *map);
+cx_attr_export
+CxMapIterator cxMapMutIteratorValues(CxMap *map);
/**
* Creates a mutating iterator over the keys of a map.
*
- * The elements of the iterator are keys of type CxHashKey.
+ * The elements of the iterator are keys of type CxHashKey and the pointer returned
+ * during iterator shall be treated as @c const @c CxHashKey* .
*
* @note An iterator iterates over all elements successively. Therefore, the order
* highly depends on the map implementation and may change arbitrarily when the contents change.
*/
cx_attr_nonnull
cx_attr_nodiscard
-CxIterator cxMapMutIteratorKeys(CxMap *map);
+cx_attr_export
+CxMapIterator cxMapMutIteratorKeys(CxMap *map);
/**
* Creates a mutating iterator for a map.
*
- * The elements of the iterator are key/value pairs of type CxMapEntry.
+ * The elements of the iterator are key/value pairs of type CxMapEntry and the pointer returned
+ * during iterator shall be treated as @c const @c CxMapEntry* .
*
* @note An iterator iterates over all elements successively. Therefore, the order
* highly depends on the map implementation and may change arbitrarily when the contents change.
*/
cx_attr_nonnull
cx_attr_nodiscard
-CxIterator cxMapMutIterator(CxMap *map);
+cx_attr_export
+CxMapIterator cxMapMutIterator(CxMap *map);
#ifdef __cplusplus
} // end the extern "C" block here, because we want to start overloading
* Puts a key/value-pair into the map.
*
* A possible existing value will be overwritten.
+ * If destructor functions are specified, they are called for
+ * the overwritten element.
*
* If this map is storing pointers, the @p value pointer is written
* to the map. Otherwise, the memory is copied from @p value with
* Removes a key/value-pair from the map by using the key.
*
* This function will copy the contents of the removed element
- * to the target buffer must be guaranteed to be large enough
+ * to the target buffer, which must be guaranteed to be large enough
* to hold the element (the map's element size).
* The destructor functions, if any, will @em not be called.
*
* @param targetbuf (@c void*) the buffer where the element shall be copied to
* @retval zero success
* @retval non-zero the key was not found
- *
- * @see cxMapStorePointers()
+ *
* @see cxMapRemove()
*/
#define cxMapRemoveAndGet(map, key, targetbuf) _Generic((key), \
*
* @param pool the memory pool to free
*/
+cx_attr_export
void cxMempoolFree(CxMempool *pool);
/**
cx_attr_nodiscard
cx_attr_malloc
cx_attr_dealloc(cxMempoolFree, 1)
+cx_attr_export
CxMempool *cxMempoolCreate(size_t capacity, cx_destructor_func destr);
/**
* @param capacity (@c size_t) the initial capacity of the pool
* @return (@c CxMempool*) the created memory pool or @c NULL if allocation failed
*/
-#define cxBasicMempoolCreate(capacity) cxMempoolCreate(capacity, NULL)
+#define cxMempoolCreateSimple(capacity) cxMempoolCreate(capacity, NULL)
/**
* Sets the destructor function for a specific allocated memory object.
* @param fnc the destructor function
*/
cx_attr_nonnull
+cx_attr_export
void cxMempoolSetDestructor(
void *memory,
cx_destructor_func fnc
* @param memory the object allocated in the pool
*/
cx_attr_nonnull
+cx_attr_export
void cxMempoolRemoveDestructor(void *memory);
/**
* @retval non-zero failure
*/
cx_attr_nonnull
+cx_attr_export
int cxMempoolRegister(
CxMempool *pool,
void *memory,
/**
* The maximum string length that fits into stack memory.
*/
+cx_attr_export
extern const unsigned cx_printf_sbo_size;
/**
cx_attr_nonnull_arg(1, 2, 3)
cx_attr_printf(3, 4)
cx_attr_cstr_arg(3)
+cx_attr_export
int cx_fprintf(
void *stream,
cx_write_func wfc,
*/
cx_attr_nonnull
cx_attr_cstr_arg(3)
+cx_attr_export
int cx_vfprintf(
void *stream,
cx_write_func wfc,
cx_attr_nonnull_arg(1, 2)
cx_attr_printf(2, 3)
cx_attr_cstr_arg(2)
+cx_attr_export
cxmutstr cx_asprintf_a(
const CxAllocator *allocator,
const char *fmt,
*/
cx_attr_nonnull
cx_attr_cstr_arg(2)
+cx_attr_export
cxmutstr cx_vasprintf_a(
const CxAllocator *allocator,
const char *fmt,
* @see cxBufferWrite()
*/
#define cx_bprintf(buffer, fmt, ...) cx_fprintf((void*)buffer, \
- (cx_write_func) cxBufferWrite, fmt, __VA_ARGS__)
+ cxBufferWriteFunc, fmt, __VA_ARGS__)
/**
cx_attr_nonnull_arg(1, 2, 3, 4)
cx_attr_printf(4, 5)
cx_attr_cstr_arg(4)
+cx_attr_export
int cx_sprintf_a(
CxAllocator *alloc,
char **str,
cx_attr_cstr_arg(4)
cx_attr_access_rw(2)
cx_attr_access_rw(3)
+cx_attr_export
int cx_vsprintf_a(
CxAllocator *alloc,
char **str,
cx_attr_access_rw(2)
cx_attr_access_rw(3)
cx_attr_access_rw(4)
+cx_attr_export
int cx_sprintf_sa(
CxAllocator *alloc,
char *buf,
*/
cx_attr_nonnull
cx_attr_cstr_arg(5)
+cx_attr_export
int cx_vsprintf_sa(
CxAllocator *alloc,
char *buf,
*/
char delimiter;
- /**
- * The character, when appearing at the end of a line, continues that line.
- * This is '\' by default.
- */
- // char continuation; // TODO: line continuation in properties
-
/**
* The first comment character.
* This is '#' by default.
* This is not set by default.
*/
char comment3;
+
+ /*
+ * The character, when appearing at the end of a line, continues that line.
+ * This is '\' by default.
+ */
+ /**
+ * Reserved for future use.
+ */
+ char continuation;
};
/**
/**
* Default properties configuration.
*/
+cx_attr_export
extern const CxPropertiesConfig cx_properties_config_default;
/**
* @see cxPropertiesInitDefault()
*/
cx_attr_nonnull
+cx_attr_export
void cxPropertiesInit(CxProperties *prop, CxPropertiesConfig config);
/**
* @param prop the properties interface
*/
cx_attr_nonnull
+cx_attr_export
void cxPropertiesDestroy(CxProperties *prop);
/**
*/
cx_attr_nonnull
cx_attr_access_r(2, 3)
+cx_attr_export
int cxPropertiesFilln(
CxProperties *prop,
const char *buf,
* @param capacity the capacity of the stack memory
*/
cx_attr_nonnull
+cx_attr_export
void cxPropertiesUseStack(
CxProperties *prop,
char *buf,
*/
cx_attr_nonnull
cx_attr_nodiscard
+cx_attr_export
CxPropertiesStatus cxPropertiesNext(
CxProperties *prop,
cxstring *key,
*/
cx_attr_nonnull
cx_attr_nodiscard
+cx_attr_export
CxPropertiesSink cxPropertiesMapSink(CxMap *map);
/**
* @see cxPropertiesLoad()
*/
cx_attr_nodiscard
+cx_attr_export
CxPropertiesSource cxPropertiesStringSource(cxstring str);
/**
cx_attr_nonnull
cx_attr_nodiscard
cx_attr_access_r(1, 2)
+cx_attr_export
CxPropertiesSource cxPropertiesCstrnSource(const char *str, size_t len);
/**
cx_attr_nonnull
cx_attr_nodiscard
cx_attr_cstr_arg(1)
+cx_attr_export
CxPropertiesSource cxPropertiesCstrSource(const char *str);
/**
cx_attr_nonnull
cx_attr_nodiscard
cx_attr_access_r(1)
+cx_attr_export
CxPropertiesSource cxPropertiesFileSource(FILE *file, size_t chunk_size);
* the return value will be #CX_PROPERTIES_NO_ERROR.
* When the source was consumed but no k/v-pairs were found, the return value
* will be #CX_PROPERTIES_NO_DATA.
+ * In case the source data ends unexpectedly, the #CX_PROPERTIES_INCOMPLETE_DATA
+ * is returned. In that case you should call this function again with the same
+ * sink and either an updated source or the same source if the source is able to
+ * yield the missing data.
+ *
* The other result codes apply, according to their description.
*
* @param prop the properties interface
* @retval CX_PROPERTIES_READ_FAILED reading from the source failed
* @retval CX_PROPERTIES_SINK_FAILED sinking the properties into the sink failed
* @retval CX_PROPERTIES_NO_DATA the source did not provide any key/value pairs
+ * @retval CX_PROPERTIES_INCOMPLETE_DATA the source did not provide enough data
* @retval CX_PROPERTIES_INVALID_EMPTY_KEY the properties data contains an illegal empty key
* @retval CX_PROPERTIES_INVALID_MISSING_DELIMITER the properties data contains a line without delimiter
* @retval CX_PROPERTIES_BUFFER_ALLOC_FAILED an internal allocation was necessary but failed
*/
cx_attr_nonnull
+cx_attr_export
CxPropertiesStatus cxPropertiesLoad(
CxProperties *prop,
CxPropertiesSink sink,
cx_attr_access_r(1)
cx_attr_access_w(2)
cx_attr_access_w(5)
+cx_attr_export
size_t cx_stream_bncopy(
void *src,
void *dest,
cx_attr_nonnull
cx_attr_access_r(1)
cx_attr_access_w(2)
+cx_attr_export
size_t cx_stream_ncopy(
void *src,
void *dest,
/**
* The maximum length of the "needle" in cx_strstr() that can use SBO.
*/
+cx_attr_export
extern const unsigned cx_strstr_sbo_size;
/**
/**
* A pointer to the string.
* @note The string is not necessarily @c NULL terminated.
- * Always use the length.
*/
char *ptr;
/** The length of the string */
/**
* A pointer to the immutable string.
* @note The string is not necessarily @c NULL terminated.
- * Always use the length.
*/
const char *ptr;
/** The length of the string */
cx_attr_nonnull
cx_attr_nodiscard
cx_attr_cstr_arg(1)
+cx_attr_export
cxmutstr cx_mutstr(char *cstring);
/**
*/
cx_attr_nodiscard
cx_attr_access_rw(1, 2)
+cx_attr_export
cxmutstr cx_mutstrn(
char *cstring,
size_t length
cx_attr_nonnull
cx_attr_nodiscard
cx_attr_cstr_arg(1)
+cx_attr_export
cxstring cx_str(const char *cstring);
*/
cx_attr_nodiscard
cx_attr_access_r(1, 2)
+cx_attr_export
cxstring cx_strn(
const char *cstring,
size_t length
/**
* Passes the pointer in this string to @c free().
*
- * The pointer in the struct is set to @c NULL and the length is set to zero.
+ * The pointer in the struct is set to @c NULL and the length is set to zero
+ * which means that this function protects you against double-free.
*
* @note There is no implementation for cxstring, because it is unlikely that
* you ever have a <code>const char*</code> you are really supposed to free.
*
* @param str the string to free
*/
+cx_attr_export
void cx_strfree(cxmutstr *str);
/**
* Passes the pointer in this string to the allocators free function.
*
- * The pointer in the struct is set to @c NULL and the length is set to zero.
+ * The pointer in the struct is set to @c NULL and the length is set to zero
+ * which means that this function protects you against double-free.
*
* @note There is no implementation for cxstring, because it is unlikely that
* you ever have a <code>const char*</code> you are really supposed to free.
* @param str the string to free
*/
cx_attr_nonnull_arg(1)
+cx_attr_export
void cx_strfree_a(
const CxAllocator *alloc,
cxmutstr *str
* @return the accumulated length of all strings
*/
cx_attr_nodiscard
+cx_attr_export
size_t cx_strlen(
size_t count,
...
*/
cx_attr_nodiscard
cx_attr_nonnull
+cx_attr_export
cxmutstr cx_strcat_ma(
const CxAllocator *alloc,
cxmutstr str,
* @see cx_strsubsl_m()
*/
cx_attr_nodiscard
+cx_attr_export
cxstring cx_strsubs(
cxstring string,
size_t start
* @see cx_strsubsl_m()
*/
cx_attr_nodiscard
+cx_attr_export
cxstring cx_strsubsl(
cxstring string,
size_t start,
* @see cx_strsubsl()
*/
cx_attr_nodiscard
+cx_attr_export
cxmutstr cx_strsubs_m(
cxmutstr string,
size_t start
* @see cx_strsubsl()
*/
cx_attr_nodiscard
+cx_attr_export
cxmutstr cx_strsubsl_m(
cxmutstr string,
size_t start,
* @see cx_strchr_m()
*/
cx_attr_nodiscard
+cx_attr_export
cxstring cx_strchr(
cxstring string,
int chr
* @see cx_strchr()
*/
cx_attr_nodiscard
+cx_attr_export
cxmutstr cx_strchr_m(
cxmutstr string,
int chr
* @see cx_strrchr_m()
*/
cx_attr_nodiscard
+cx_attr_export
cxstring cx_strrchr(
cxstring string,
int chr
* @see cx_strrchr()
*/
cx_attr_nodiscard
+cx_attr_export
cxmutstr cx_strrchr_m(
cxmutstr string,
int chr
* @see cx_strstr_m()
*/
cx_attr_nodiscard
+cx_attr_export
cxstring cx_strstr(
cxstring haystack,
cxstring needle
* @see cx_strstr()
*/
cx_attr_nodiscard
+cx_attr_export
cxmutstr cx_strstr_m(
cxmutstr haystack,
cxstring needle
* @param string the string to split
* @param delim the delimiter
* @param limit the maximum number of split items
- * @param output a pre-allocated array of at least @p limit length
+ * @param output a preallocated array of at least @p limit length
* @return the actual number of split items
*/
cx_attr_nodiscard
cx_attr_nonnull
cx_attr_access_w(4, 3)
+cx_attr_export
size_t cx_strsplit(
cxstring string,
cxstring delim,
cx_attr_nodiscard
cx_attr_nonnull
cx_attr_access_w(5)
+cx_attr_export
size_t cx_strsplit_a(
const CxAllocator *allocator,
cxstring string,
* @param string the string to split
* @param delim the delimiter
* @param limit the maximum number of split items
- * @param output a pre-allocated array of at least @p limit length
+ * @param output a preallocated array of at least @p limit length
* @return the actual number of split items
*/
cx_attr_nodiscard
cx_attr_nonnull
cx_attr_access_w(4, 3)
+cx_attr_export
size_t cx_strsplit_m(
cxmutstr string,
cxstring delim,
cx_attr_nodiscard
cx_attr_nonnull
cx_attr_access_w(5)
+cx_attr_export
size_t cx_strsplit_ma(
const CxAllocator *allocator,
cxmutstr string,
* than @p s2, zero if both strings equal
*/
cx_attr_nodiscard
+cx_attr_export
int cx_strcmp(
cxstring s1,
cxstring s2
* than @p s2, zero if both strings equal ignoring case
*/
cx_attr_nodiscard
+cx_attr_export
int cx_strcasecmp(
cxstring s1,
cxstring s2
*/
cx_attr_nodiscard
cx_attr_nonnull
+cx_attr_export
int cx_strcmp_p(
const void *s1,
const void *s2
*/
cx_attr_nodiscard
cx_attr_nonnull
+cx_attr_export
int cx_strcasecmp_p(
const void *s1,
const void *s2
*/
cx_attr_nodiscard
cx_attr_nonnull
-cxmutstr cx_strdup_a(
+cx_attr_export
+cxmutstr cx_strdup_a_(
const CxAllocator *allocator,
cxstring string
);
-/**
- * Creates a duplicate of the specified string.
- *
- * The new string will contain a copy allocated by standard
- * @c malloc(). So developers @em must pass the return value to cx_strfree().
- *
- * @note The returned string is guaranteed to be zero-terminated.
- *
- * @param string (@c cxstring) the string to duplicate
- * @return (@c cxmutstr) a duplicate of the string
- * @see cx_strdup_a()
- */
-#define cx_strdup(string) cx_strdup_a(cxDefaultAllocator, string)
-
-
/**
* Creates a duplicate of the specified string.
*
* @note The returned string is guaranteed to be zero-terminated.
*
* @param allocator (@c CxAllocator*) the allocator to use
- * @param string (@c cxmutstr) the string to duplicate
+ * @param string the string to duplicate
* @return (@c cxmutstr) a duplicate of the string
- * @see cx_strdup_m()
+ * @see cx_strdup()
+ * @see cx_strfree_a()
*/
-#define cx_strdup_ma(allocator, string) cx_strdup_a(allocator, cx_strcast(string))
+#define cx_strdup_a(allocator, string) \
+ cx_strdup_a_((allocator), cx_strcast((string)))
/**
* Creates a duplicate of the specified string.
*
* @note The returned string is guaranteed to be zero-terminated.
*
- * @param string (@c cxmutstr) the string to duplicate
+ * @param string the string to duplicate
* @return (@c cxmutstr) a duplicate of the string
- * @see cx_strdup_ma()
+ * @see cx_strdup_a()
+ * @see cx_strfree()
*/
-#define cx_strdup_m(string) cx_strdup_a(cxDefaultAllocator, cx_strcast(string))
+#define cx_strdup(string) cx_strdup_a_(cxDefaultAllocator, string)
/**
* Omits leading and trailing spaces.
* @return the trimmed string
*/
cx_attr_nodiscard
+cx_attr_export
cxstring cx_strtrim(cxstring string);
/**
* @return the trimmed string
*/
cx_attr_nodiscard
+cx_attr_export
cxmutstr cx_strtrim_m(cxmutstr string);
/**
* @c false otherwise
*/
cx_attr_nodiscard
+cx_attr_export
bool cx_strprefix(
cxstring string,
cxstring prefix
* @c false otherwise
*/
cx_attr_nodiscard
+cx_attr_export
bool cx_strsuffix(
cxstring string,
cxstring suffix
* @c false otherwise
*/
cx_attr_nodiscard
+cx_attr_export
bool cx_strcaseprefix(
cxstring string,
cxstring prefix
* @c false otherwise
*/
cx_attr_nodiscard
+cx_attr_export
bool cx_strcasesuffix(
cxstring string,
cxstring suffix
);
/**
- * Converts the string to lower case.
- *
- * The change is made in-place. If you want a copy, use cx_strdup(), first.
- *
- * @param string the string to modify
- * @see cx_strdup()
- */
-void cx_strlower(cxmutstr string);
-
-/**
- * Converts the string to upper case.
- *
- * The change is made in-place. If you want a copy, use cx_strdup(), first.
- *
- * @param string the string to modify
- * @see cx_strdup()
- */
-void cx_strupper(cxmutstr string);
-
-/**
- * Replaces a pattern in a string with another string.
+ * Replaces a string with another string.
*
- * The pattern is taken literally and is no regular expression.
* Replaces at most @p replmax occurrences.
*
* The returned string will be allocated by @p allocator and is guaranteed
*
* @param allocator the allocator to use
* @param str the string where replacements should be applied
- * @param pattern the pattern to search for
+ * @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
*/
cx_attr_nodiscard
cx_attr_nonnull
+cx_attr_export
cxmutstr cx_strreplacen_a(
const CxAllocator *allocator,
cxstring str,
- cxstring pattern,
+ cxstring search,
cxstring replacement,
size_t replmax
);
/**
- * Replaces a pattern in a string with another string.
+ * Replaces a string with another string.
*
- * The pattern is taken literally and is no regular expression.
* Replaces at most @p replmax occurrences.
*
* The returned string will be allocated by @c malloc() and is guaranteed
* the returned string will be empty.
*
* @param str (@c cxstring) the string where replacements should be applied
- * @param pattern (@c cxstring) the pattern to search for
+ * @param search (@c cxstring) the string to search for
* @param replacement (@c cxstring) the replacement string
* @param replmax (@c size_t) maximum number of replacements
* @return (@c cxmutstr) the resulting string after applying the replacements
*/
-#define cx_strreplacen(str, pattern, replacement, replmax) \
-cx_strreplacen_a(cxDefaultAllocator, str, pattern, replacement, replmax)
+#define cx_strreplacen(str, search, replacement, replmax) \
+cx_strreplacen_a(cxDefaultAllocator, str, search, replacement, replmax)
/**
- * Replaces a pattern in a string with another string.
- *
- * The pattern is taken literally and is no regular expression.
+ * Replaces a string with another string.
*
* The returned string will be allocated by @p allocator and is guaranteed
* to be zero-terminated.
*
* @param allocator (@c CxAllocator*) the allocator to use
* @param str (@c cxstring) the string where replacements should be applied
- * @param pattern (@c cxstring) the pattern to search for
+ * @param search (@c cxstring) the string to search for
* @param replacement (@c cxstring) the replacement string
* @return (@c cxmutstr) the resulting string after applying the replacements
*/
-#define cx_strreplace_a(allocator, str, pattern, replacement) \
-cx_strreplacen_a(allocator, str, pattern, replacement, SIZE_MAX)
+#define cx_strreplace_a(allocator, str, search, replacement) \
+cx_strreplacen_a(allocator, str, search, replacement, SIZE_MAX)
/**
- * Replaces a pattern in a string with another string.
- *
- * The pattern is taken literally and is no regular expression.
- * Replaces at most @p replmax occurrences.
+ * Replaces a string with another string.
*
* The returned string will be allocated by @c malloc() and is guaranteed
* to be zero-terminated.
* the returned string will be empty.
*
* @param str (@c cxstring) the string where replacements should be applied
- * @param pattern (@c cxstring) the pattern to search for
+ * @param search (@c cxstring) the string to search for
* @param replacement (@c cxstring) the replacement string
* @return (@c cxmutstr) the resulting string after applying the replacements
*/
-#define cx_strreplace(str, pattern, replacement) \
-cx_strreplacen_a(cxDefaultAllocator, str, pattern, replacement, SIZE_MAX)
+#define cx_strreplace(str, search, replacement) \
+cx_strreplacen_a(cxDefaultAllocator, str, search, replacement, SIZE_MAX)
/**
* Creates a string tokenization context.
* @return a new string tokenization context
*/
cx_attr_nodiscard
-CxStrtokCtx cx_strtok(
+cx_attr_export
+CxStrtokCtx cx_strtok_(
cxstring str,
cxstring delim,
size_t limit
);
/**
-* Creates a string tokenization context for a mutable string.
-*
-* @param str the string to tokenize
-* @param delim the delimiter (must not be empty)
-* @param limit the maximum number of tokens that shall be returned
-* @return a new string tokenization context
-*/
-cx_attr_nodiscard
-CxStrtokCtx cx_strtok_m(
- cxmutstr str,
- cxstring delim,
- size_t limit
-);
+ * Creates a string tokenization context.
+ *
+ * @param str the string to tokenize
+ * @param delim the delimiter string (must not be empty)
+ * @param limit (@c size_t) the maximum number of tokens that shall be returned
+ * @return (@c CxStrtokCtx) a new string tokenization context
+ */
+#define cx_strtok(str, delim, limit) \
+ cx_strtok_(cx_strcast((str)), cx_strcast((delim)), (limit))
/**
* Returns the next token.
cx_attr_nonnull
cx_attr_nodiscard
cx_attr_access_w(2)
+cx_attr_export
bool cx_strtok_next(
CxStrtokCtx *ctx,
cxstring *token
* Returns the next token of a mutable string.
*
* 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.
*
cx_attr_nonnull
cx_attr_nodiscard
cx_attr_access_w(2)
+cx_attr_export
bool cx_strtok_next_m(
CxStrtokCtx *ctx,
cxmutstr *token
*/
cx_attr_nonnull
cx_attr_access_r(2, 3)
+cx_attr_export
void cx_strtok_delim(
CxStrtokCtx *ctx,
const cxstring *delim,
* ------------------------------------------------------------------------- */
/**
- * @copydoc cx_strtouz_lc()
- */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-int cx_strtos_lc(cxstring str, short *output, int base, const char *groupsep);
-/**
- * @copydoc cx_strtouz_lc()
- */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-int cx_strtoi_lc(cxstring str, int *output, int base, const char *groupsep);
-/**
- * @copydoc cx_strtouz_lc()
- */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-int cx_strtol_lc(cxstring str, long *output, int base, const char *groupsep);
-/**
- * @copydoc cx_strtouz_lc()
- */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-int cx_strtoll_lc(cxstring str, long long *output, int base, const char *groupsep);
-/**
- * @copydoc cx_strtouz_lc()
- */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-int cx_strtoi8_lc(cxstring str, int8_t *output, int base, const char *groupsep);
-/**
- * @copydoc cx_strtouz_lc()
- */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-int cx_strtoi16_lc(cxstring str, int16_t *output, int base, const char *groupsep);
-/**
- * @copydoc cx_strtouz_lc()
- */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-int cx_strtoi32_lc(cxstring str, int32_t *output, int base, const char *groupsep);
-/**
- * @copydoc cx_strtouz_lc()
- */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-int cx_strtoi64_lc(cxstring str, int64_t *output, int base, const char *groupsep);
-/**
- * @copydoc cx_strtouz_lc()
- */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-int cx_strtoz_lc(cxstring str, ssize_t *output, int base, const char *groupsep);
-/**
- * @copydoc cx_strtouz_lc()
- */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-int cx_strtous_lc(cxstring str, unsigned short *output, int base, const char *groupsep);
-/**
- * @copydoc cx_strtouz_lc()
- */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-int cx_strtou_lc(cxstring str, unsigned int *output, int base, const char *groupsep);
-/**
- * @copydoc cx_strtouz_lc()
- */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-int cx_strtoul_lc(cxstring str, unsigned long *output, int base, const char *groupsep);
-/**
- * @copydoc cx_strtouz_lc()
- */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-int cx_strtoull_lc(cxstring str, unsigned long long *output, int base, const char *groupsep);
-/**
- * @copydoc cx_strtouz_lc()
- */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-int cx_strtou8_lc(cxstring str, uint8_t *output, int base, const char *groupsep);
-/**
- * @copydoc cx_strtouz_lc()
- */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-int cx_strtou16_lc(cxstring str, uint16_t *output, int base, const char *groupsep);
-/**
- * @copydoc cx_strtouz_lc()
- */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-int cx_strtou32_lc(cxstring str, uint32_t *output, int base, const char *groupsep);
-/**
- * @copydoc cx_strtouz_lc()
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @retval zero success
+ * @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-int cx_strtou64_lc(cxstring str, uint64_t *output, int base, const char *groupsep);
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
+int cx_strtos_lc_(cxstring str, short *output, int base, const char *groupsep);
/**
* Converts a string to a number.
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-int cx_strtouz_lc(cxstring str, size_t *output, int base, const char *groupsep);
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
+int cx_strtoi_lc_(cxstring str, int *output, int base, const char *groupsep);
/**
- * Converts a string to a single precision floating point number.
+ * Converts a string to a number.
*
* The function returns non-zero when conversion is not possible.
- * In that case the function sets errno to EINVAL when the reason is an invalid character.
- * It sets errno to ERANGE when the necessary representation would exceed the limits defined in libc's float.h.
- *
- * The decimal separator is assumed to be a dot character.
- * The comma character is treated as group separator and ignored during parsing.
- * If you want to choose a different format, use cx_strtof_lc().
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
*
* @param str the string to convert
- * @param output a pointer to the float variable where the result shall be stored
- * @param decsep the decimal separator
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
* @param groupsep each character in this string is treated as group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-int cx_strtof_lc(cxstring str, float *output, char decsep, const char *groupsep);
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
+int cx_strtol_lc_(cxstring str, long *output, int base, const char *groupsep);
/**
- * Converts a string to a double precision floating point number.
+ * Converts a string to a number.
*
* The function returns non-zero when conversion is not possible.
- * In that case the function sets errno to EINVAL when the reason is an invalid character.
- * It sets errno to ERANGE when the necessary representation would exceed the limits defined in libc's float.h.
- *
- * The decimal separator is assumed to be a dot character.
- * The comma character is treated as group separator and ignored during parsing.
- * If you want to choose a different format, use cx_strtof_lc().
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
*
* @param str the string to convert
- * @param output a pointer to the float variable where the result shall be stored
- * @param decsep the decimal separator
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
* @param groupsep each character in this string is treated as group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-int cx_strtod_lc(cxstring str, double *output, char decsep, const char *groupsep);
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
+int cx_strtoll_lc_(cxstring str, long long *output, int base, const char *groupsep);
-#ifndef CX_STR_IMPLEMENTATION
-/**
- * @copydoc cx_strtouz_lc()
- */
-#define cx_strtos_lc(str, output, base, groupsep) cx_strtos_lc(cx_strcast(str), output, base, groupsep)
-/**
- * @copydoc cx_strtouz_lc()
- */
-#define cx_strtoi_lc(str, output, base, groupsep) cx_strtoi_lc(cx_strcast(str), output, base, groupsep)
-/**
- * @copydoc cx_strtouz_lc()
- */
-#define cx_strtol_lc(str, output, base, groupsep) cx_strtol_lc(cx_strcast(str), output, base, groupsep)
-/**
- * @copydoc cx_strtouz_lc()
- */
-#define cx_strtoll_lc(str, output, base, groupsep) cx_strtoll_lc(cx_strcast(str), output, base, groupsep)
-/**
- * @copydoc cx_strtouz_lc()
- */
-#define cx_strtoi8_lc(str, output, base, groupsep) cx_strtoi8_lc(cx_strcast(str), output, base, groupsep)
-/**
- * @copydoc cx_strtouz_lc()
- */
-#define cx_strtoi16_lc(str, output, base, groupsep) cx_strtoi16_lc(cx_strcast(str), output, base, groupsep)
-/**
- * @copydoc cx_strtouz_lc()
- */
-#define cx_strtoi32_lc(str, output, base, groupsep) cx_strtoi32_lc(cx_strcast(str), output, base, groupsep)
-/**
- * @copydoc cx_strtouz_lc()
- */
-#define cx_strtoi64_lc(str, output, base, groupsep) cx_strtoi64_lc(cx_strcast(str), output, base, groupsep)
-/**
- * @copydoc cx_strtouz_lc()
- */
-#define cx_strtoz_lc(str, output, base, groupsep) cx_strtoz_lc(cx_strcast(str), output, base, groupsep)
-/**
- * @copydoc cx_strtouz_lc()
- */
-#define cx_strtous_lc(str, output, base, groupsep) cx_strtous_lc(cx_strcast(str), output, base, groupsep)
-/**
- * @copydoc cx_strtouz_lc()
- */
-#define cx_strtou_lc(str, output, base, groupsep) cx_strtou_lc(cx_strcast(str), output, base, groupsep)
-/**
- * @copydoc cx_strtouz_lc()
- */
-#define cx_strtoul_lc(str, output, base, groupsep) cx_strtoul_lc(cx_strcast(str), output, base, groupsep)
-/**
- * @copydoc cx_strtouz_lc()
- */
-#define cx_strtoull_lc(str, output, base, groupsep) cx_strtoull_lc(cx_strcast(str), output, base, groupsep)
-/**
- * @copydoc cx_strtouz_lc()
- */
-#define cx_strtou8_lc(str, output, base, groupsep) cx_strtou8_lc(cx_strcast(str), output, base, groupsep)
-/**
- * @copydoc cx_strtouz_lc()
- */
-#define cx_strtou16_lc(str, output, base, groupsep) cx_strtou16_lc(cx_strcast(str), output, base, groupsep)
-/**
- * @copydoc cx_strtouz_lc()
- */
-#define cx_strtou32_lc(str, output, base, groupsep) cx_strtou32_lc(cx_strcast(str), output, base, groupsep)
-/**
- * @copydoc cx_strtouz_lc()
- */
-#define cx_strtou64_lc(str, output, base, groupsep) cx_strtou64_lc(cx_strcast(str), output, base, groupsep)
/**
* Converts a string to a number.
*
* @retval zero success
* @retval non-zero conversion was not possible
*/
-#define cx_strtouz_lc(str, output, base, groupsep) cx_strtouz_lc(cx_strcast(str), output, base, groupsep)
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
+int cx_strtoi8_lc_(cxstring str, int8_t *output, int base, const char *groupsep);
/**
- * @copydoc cx_strtouz()
- */
-#define cx_strtos(str, output, base) cx_strtos_lc(str, output, base, ",")
-/**
- * @copydoc cx_strtouz()
- */
-#define cx_strtoi(str, output, base) cx_strtoi_lc(str, output, base, ",")
-/**
- * @copydoc cx_strtouz()
- */
-#define cx_strtol(str, output, base) cx_strtol_lc(str, output, base, ",")
-/**
- * @copydoc cx_strtouz()
- */
-#define cx_strtoll(str, output, base) cx_strtoll_lc(str, output, base, ",")
-/**
- * @copydoc cx_strtouz()
- */
-#define cx_strtoi8(str, output, base) cx_strtoi8_lc(str, output, base, ",")
-/**
- * @copydoc cx_strtouz()
- */
-#define cx_strtoi16(str, output, base) cx_strtoi16_lc(str, output, base, ",")
-/**
- * @copydoc cx_strtouz()
- */
-#define cx_strtoi32(str, output, base) cx_strtoi32_lc(str, output, base, ",")
-/**
- * @copydoc cx_strtouz()
- */
-#define cx_strtoi64(str, output, base) cx_strtoi64_lc(str, output, base, ",")
-/**
- * @copydoc cx_strtouz()
- */
-#define cx_strtoz(str, output, base) cx_strtoz_lc(str, output, base, ",")
-/**
- * @copydoc cx_strtouz()
- */
-#define cx_strtous(str, output, base) cx_strtous_lc(str, output, base, ",")
-/**
- * @copydoc cx_strtouz()
- */
-#define cx_strtou(str, output, base) cx_strtou_lc(str, output, base, ",")
-/**
- * @copydoc cx_strtouz()
- */
-#define cx_strtoul(str, output, base) cx_strtoul_lc(str, output, base, ",")
-/**
- * @copydoc cx_strtouz()
- */
-#define cx_strtoull(str, output, base) cx_strtoull_lc(str, output, base, ",")
-/**
- * @copydoc cx_strtouz()
- */
-#define cx_strtou8(str, output, base) cx_strtou8_lc(str, output, base, ",")
-/**
- * @copydoc cx_strtouz()
- */
-#define cx_strtou16(str, output, base) cx_strtou16_lc(str, output, base, ",")
-/**
- * @copydoc cx_strtouz()
- */
-#define cx_strtou32(str, output, base) cx_strtou32_lc(str, output, base, ",")
-/**
- * @copydoc cx_strtouz()
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @retval zero success
+ * @retval non-zero conversion was not possible
*/
-#define cx_strtou64(str, output, base) cx_strtou64_lc(str, output, base, ",")
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
+int cx_strtoi16_lc_(cxstring str, int16_t *output, int base, const char *groupsep);
+
/**
* Converts a string to a number.
*
* In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
* It sets errno to ERANGE when the target datatype is too small.
*
- * The comma character is treated as group separator and ignored during parsing.
- * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtouz_lc()).
- *
* @param str the string to convert
* @param output a pointer to the integer variable where the result shall be stored
* @param base 2, 8, 10, or 16
+ * @param groupsep each character in this string is treated as group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
-#define cx_strtouz(str, output, base) cx_strtouz_lc(str, output, base, ",")
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
+int cx_strtoi32_lc_(cxstring str, int32_t *output, int base, const char *groupsep);
/**
- * Converts a string to a single precision floating point number.
+ * Converts a string to a number.
*
* The function returns non-zero when conversion is not possible.
- * In that case the function sets errno to EINVAL when the reason is an invalid character.
- * It sets errno to ERANGE when the necessary representation would exceed the limits defined in libc's float.h.
- *
- * The decimal separator is assumed to be a dot character.
- * The comma character is treated as group separator and ignored during parsing.
- * If you want to choose a different format, use cx_strtof_lc().
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
*
* @param str the string to convert
- * @param output a pointer to the float variable where the result shall be stored
- * @param decsep the decimal separator
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
* @param groupsep each character in this string is treated as group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
-#define cx_strtof_lc(str, output, decsep, groupsep) cx_strtof_lc(cx_strcast(str), output, decsep, groupsep)
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
+int cx_strtoi64_lc_(cxstring str, int64_t *output, int base, const char *groupsep);
+
/**
- * Converts a string to a double precision floating point number.
+ * Converts a string to a number.
*
* The function returns non-zero when conversion is not possible.
- * In that case the function sets errno to EINVAL when the reason is an invalid character.
- *
- * The decimal separator is assumed to be a dot character.
- * The comma character is treated as group separator and ignored during parsing.
- * If you want to choose a different format, use cx_strtof_lc().
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
*
* @param str the string to convert
- * @param output a pointer to the double variable where the result shall be stored
- * @param decsep the decimal separator
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
* @param groupsep each character in this string is treated as group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
-#define cx_strtod_lc(str, output, decsep, groupsep) cx_strtod_lc(cx_strcast(str), output, decsep, groupsep)
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
+int cx_strtous_lc_(cxstring str, unsigned short *output, int base, const char *groupsep);
/**
- * Converts a string to a single precision floating point number.
+ * Converts a string to a number.
*
* The function returns non-zero when conversion is not possible.
- * In that case the function sets errno to EINVAL when the reason is an invalid character.
- * It sets errno to ERANGE when the necessary representation would exceed the limits defined in libc's float.h.
- *
- * The decimal separator is assumed to be a dot character.
- * The comma character is treated as group separator and ignored during parsing.
- * If you want to choose a different format, use cx_strtof_lc().
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
*
* @param str the string to convert
- * @param output a pointer to the float variable where the result shall be stored
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @param groupsep each character in this string is treated as group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
-#define cx_strtof(str, output) cx_strtof_lc(str, output, '.', ",")
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
+int cx_strtou_lc_(cxstring str, unsigned int *output, int base, const char *groupsep);
+
/**
- * Converts a string to a double precision floating point number.
+ * Converts a string to a number.
*
* The function returns non-zero when conversion is not possible.
- * In that case the function sets errno to EINVAL when the reason is an invalid character.
- *
- * The decimal separator is assumed to be a dot character.
- * The comma character is treated as group separator and ignored during parsing.
- * If you want to choose a different format, use cx_strtof_lc().
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
*
* @param str the string to convert
- * @param output a pointer to the double variable where the result shall be stored
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @param groupsep each character in this string is treated as group separator and ignored during conversion
* @retval zero success
* @retval non-zero conversion was not possible
*/
-#define cx_strtod(str, output) cx_strtod_lc(str, output, '.', ",")
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
+int cx_strtoul_lc_(cxstring str, unsigned long *output, int base, const char *groupsep);
-#endif
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
+int cx_strtoull_lc_(cxstring str, unsigned long long *output, int base, const char *groupsep);
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
+int cx_strtou8_lc_(cxstring str, uint8_t *output, int base, const char *groupsep);
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
+int cx_strtou16_lc_(cxstring str, uint16_t *output, int base, const char *groupsep);
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
+int cx_strtou32_lc_(cxstring str, uint32_t *output, int base, const char *groupsep);
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
+int cx_strtou64_lc_(cxstring str, uint64_t *output, int base, const char *groupsep);
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
+int cx_strtoz_lc_(cxstring str, size_t *output, int base, const char *groupsep);
+
+/**
+ * Converts a string to a single precision floating point number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character.
+ * It sets errno to ERANGE when the necessary representation would exceed the limits defined in libc's float.h.
+ *
+ * @param str the string to convert
+ * @param output a pointer to the float variable where the result shall be stored
+ * @param decsep the decimal separator
+ * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
+int cx_strtof_lc_(cxstring str, float *output, char decsep, const char *groupsep);
+
+/**
+ * Converts a string to a double precision floating point number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character.
+ * It sets errno to ERANGE when the necessary representation would exceed the limits defined in libc's float.h.
+ *
+ * @param str the string to convert
+ * @param output a pointer to the float variable where the result shall be stored
+ * @param decsep the decimal separator
+ * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
+int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupsep);
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtos_lc(str, output, base, groupsep) cx_strtos_lc_(cx_strcast(str), output, base, groupsep)
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtoi_lc(str, output, base, groupsep) cx_strtoi_lc_(cx_strcast(str), output, base, groupsep)
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtol_lc(str, output, base, groupsep) cx_strtol_lc_(cx_strcast(str), output, base, groupsep)
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtoll_lc(str, output, base, groupsep) cx_strtoll_lc_(cx_strcast(str), output, base, groupsep)
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtoi8_lc(str, output, base, groupsep) cx_strtoi8_lc_(cx_strcast(str), output, base, groupsep)
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtoi16_lc(str, output, base, groupsep) cx_strtoi16_lc_(cx_strcast(str), output, base, groupsep)
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtoi32_lc(str, output, base, groupsep) cx_strtoi32_lc_(cx_strcast(str), output, base, groupsep)
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtoi64_lc(str, output, base, groupsep) cx_strtoi64_lc_(cx_strcast(str), output, base, groupsep)
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtous_lc(str, output, base, groupsep) cx_strtous_lc_(cx_strcast(str), output, base, groupsep)
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtou_lc(str, output, base, groupsep) cx_strtou_lc_(cx_strcast(str), output, base, groupsep)
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtoul_lc(str, output, base, groupsep) cx_strtoul_lc_(cx_strcast(str), output, base, groupsep)
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtoull_lc(str, output, base, groupsep) cx_strtoull_lc_(cx_strcast(str), output, base, groupsep)
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtou8_lc(str, output, base, groupsep) cx_strtou8_lc_(cx_strcast(str), output, base, groupsep)
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtou16_lc(str, output, base, groupsep) cx_strtou16_lc_(cx_strcast(str), output, base, groupsep)
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtou32_lc(str, output, base, groupsep) cx_strtou32_lc_(cx_strcast(str), output, base, groupsep)
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtou64_lc(str, output, base, groupsep) cx_strtou64_lc_(cx_strcast(str), output, base, groupsep)
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtoz_lc(str, output, base, groupsep) cx_strtoz_lc_(cx_strcast(str), output, base, groupsep)
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * The comma character is treated as group separator and ignored during parsing.
+ * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtos(str, output, base) cx_strtos_lc_(cx_strcast(str), output, base, ",")
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * The comma character is treated as group separator and ignored during parsing.
+ * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtoi(str, output, base) cx_strtoi_lc_(cx_strcast(str), output, base, ",")
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * The comma character is treated as group separator and ignored during parsing.
+ * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtol(str, output, base) cx_strtol_lc_(cx_strcast(str), output, base, ",")
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * The comma character is treated as group separator and ignored during parsing.
+ * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtoll(str, output, base) cx_strtoll_lc_(cx_strcast(str), output, base, ",")
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * The comma character is treated as group separator and ignored during parsing.
+ * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtoi8(str, output, base) cx_strtoi8_lc_(cx_strcast(str), output, base, ",")
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * The comma character is treated as group separator and ignored during parsing.
+ * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtoi16(str, output, base) cx_strtoi16_lc_(cx_strcast(str), output, base, ",")
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * The comma character is treated as group separator and ignored during parsing.
+ * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtoi32(str, output, base) cx_strtoi32_lc_(cx_strcast(str), output, base, ",")
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * The comma character is treated as group separator and ignored during parsing.
+ * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtoi64(str, output, base) cx_strtoi64_lc_(cx_strcast(str), output, base, ",")
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * The comma character is treated as group separator and ignored during parsing.
+ * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtoz(str, output, base) cx_strtoz_lc_(cx_strcast(str), output, base, ",")
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * The comma character is treated as group separator and ignored during parsing.
+ * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtous(str, output, base) cx_strtous_lc_(cx_strcast(str), output, base, ",")
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * The comma character is treated as group separator and ignored during parsing.
+ * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtou(str, output, base) cx_strtou_lc_(cx_strcast(str), output, base, ",")
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * The comma character is treated as group separator and ignored during parsing.
+ * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtoul(str, output, base) cx_strtoul_lc_(cx_strcast(str), output, base, ",")
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * The comma character is treated as group separator and ignored during parsing.
+ * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtoull(str, output, base) cx_strtoull_lc_(cx_strcast(str), output, base, ",")
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * The comma character is treated as group separator and ignored during parsing.
+ * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtou8(str, output, base) cx_strtou8_lc_(cx_strcast(str), output, base, ",")
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * The comma character is treated as group separator and ignored during parsing.
+ * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtou16(str, output, base) cx_strtou16_lc_(cx_strcast(str), output, base, ",")
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * The comma character is treated as group separator and ignored during parsing.
+ * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtou32(str, output, base) cx_strtou32_lc_(cx_strcast(str), output, base, ",")
+
+/**
+ * Converts a string to a number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base.
+ * It sets errno to ERANGE when the target datatype is too small.
+ *
+ * The comma character is treated as group separator and ignored during parsing.
+ * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()).
+ *
+ * @param str the string to convert
+ * @param output a pointer to the integer variable where the result shall be stored
+ * @param base 2, 8, 10, or 16
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtou64(str, output, base) cx_strtou64_lc_(cx_strcast(str), output, base, ",")
+
+/**
+ * Converts a string to a single precision floating point number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character.
+ * It sets errno to ERANGE when the necessary representation would exceed the limits defined in libc's float.h.
+ *
+ * @param str the string to convert
+ * @param output a pointer to the float variable where the result shall be stored
+ * @param decsep the decimal separator
+ * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtof_lc(str, output, decsep, groupsep) cx_strtof_lc_(cx_strcast(str), output, decsep, groupsep)
+
+/**
+ * Converts a string to a double precision floating point number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character.
+ *
+ * @param str the string to convert
+ * @param output a pointer to the double variable where the result shall be stored
+ * @param decsep the decimal separator
+ * @param groupsep each character in this string is treated as group separator and ignored during conversion
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtod_lc(str, output, decsep, groupsep) cx_strtod_lc_(cx_strcast(str), output, decsep, groupsep)
+
+/**
+ * Converts a string to a single precision floating point number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character.
+ * It sets errno to ERANGE when the necessary representation would exceed the limits defined in libc's float.h.
+ *
+ * The decimal separator is assumed to be a dot character.
+ * The comma character is treated as group separator and ignored during parsing.
+ * If you want to choose a different format, use cx_strtof_lc().
+ *
+ * @param str the string to convert
+ * @param output a pointer to the float variable where the result shall be stored
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtof(str, output) cx_strtof_lc_(cx_strcast(str), output, '.', ",")
+
+/**
+ * Converts a string to a double precision floating point number.
+ *
+ * The function returns non-zero when conversion is not possible.
+ * In that case the function sets errno to EINVAL when the reason is an invalid character.
+ *
+ * The decimal separator is assumed to be a dot character.
+ * The comma character is treated as group separator and ignored during parsing.
+ * If you want to choose a different format, use cx_strtof_lc().
+ *
+ * @param str the string to convert
+ * @param output a pointer to the double variable where the result shall be stored
+ * @retval zero success
+ * @retval non-zero conversion was not possible
+ */
+#define cx_strtod(str, output) cx_strtod_lc_(cx_strcast(str), output, '.', ",")
#ifdef __cplusplus
} // extern "C"
* @see cx_tree_unlink()
*/
cx_attr_nonnull
+cx_attr_export
void cx_tree_link(
void *parent,
void *node,
* @see cx_tree_link()
*/
cx_attr_nonnull
+cx_attr_export
void cx_tree_unlink(
void *node,
ptrdiff_t loc_parent,
*/
cx_attr_nonnull
cx_attr_access_w(5)
+cx_attr_export
int cx_tree_search_data(
const void *root,
size_t depth,
*/
cx_attr_nonnull
cx_attr_access_w(5)
+cx_attr_export
int cx_tree_search(
const void *root,
size_t depth,
* @see cxTreeIteratorDispose()
*/
cx_attr_nodiscard
+cx_attr_export
CxTreeIterator cx_tree_iterator(
void *root,
bool visit_on_exit,
* @see cxTreeVisitorDispose()
*/
cx_attr_nodiscard
+cx_attr_export
CxTreeVisitor cx_tree_visitor(
void *root,
ptrdiff_t loc_children,
* 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_attr_export
extern unsigned int cx_tree_add_look_around_depth;
/**
*/
cx_attr_nonnull_arg(1, 3, 4, 6, 7)
cx_attr_access_w(6)
+cx_attr_export
size_t cx_tree_add_iter(
struct cx_iterator_base_s *iter,
size_t num,
*/
cx_attr_nonnull_arg(1, 4, 5, 7, 8)
cx_attr_access_w(7)
+cx_attr_export
size_t cx_tree_add_array(
const void *src,
size_t num,
*/
cx_attr_nonnull_arg(1, 2, 3, 5, 6)
cx_attr_access_w(5)
+cx_attr_export
int cx_tree_add(
const void *src,
cx_tree_search_func sfunc,
* @see cxTreeFree()
*/
cx_attr_nonnull
+cx_attr_export
void cxTreeDestroySubtree(CxTree *tree, void *node);
*
* @param tree the tree to free
*/
+cx_attr_export
void cxTreeFree(CxTree *tree);
/**
cx_attr_nodiscard
cx_attr_malloc
cx_attr_dealloc(cxTreeFree, 1)
+cx_attr_export
CxTree *cxTreeCreate(
const CxAllocator *allocator,
cx_tree_node_create_func create_func,
cx_attr_nodiscard
cx_attr_malloc
cx_attr_dealloc(cxTreeFree, 1)
+cx_attr_export
CxTree *cxTreeCreateWrapped(
const CxAllocator *allocator,
void *root,
cx_attr_nonnull
static inline size_t cxTreeInsertIter(
CxTree *tree,
- struct cx_iterator_base_s *iter,
+ CxIteratorBase *iter,
size_t n
) {
return tree->cl->insert_many(tree, iter, n);
*/
cx_attr_nonnull
cx_attr_nodiscard
+cx_attr_export
size_t cxTreeSubtreeSize(CxTree *tree, void *subtree_root);
/**
*/
cx_attr_nonnull
cx_attr_nodiscard
+cx_attr_export
size_t cxTreeSubtreeDepth(CxTree *tree, void *subtree_root);
/**
*/
cx_attr_nonnull
cx_attr_nodiscard
+cx_attr_export
size_t cxTreeDepth(CxTree *tree);
/**
* @see cxTreeAddChildNode()
*/
cx_attr_nonnull
+cx_attr_export
void cxTreeSetParent(
CxTree *tree,
void *parent,
* @see cxTreeSetParent()
*/
cx_attr_nonnull
+cx_attr_export
void cxTreeAddChildNode(
CxTree *tree,
void *parent,
* @see cxTreeInsert()
*/
cx_attr_nonnull
+cx_attr_export
int cxTreeAddChild(
CxTree *tree,
void *parent,
* @return zero on success, non-zero if @p node is the root node of the tree
*/
cx_attr_nonnull_arg(1, 2)
+cx_attr_export
int cxTreeRemoveNode(
CxTree *tree,
void *node,
* @param node the node to remove
*/
cx_attr_nonnull
+cx_attr_export
void cxTreeRemoveSubtree(CxTree *tree, void *node);
/**
* @return zero on success, non-zero if @p node is the root node of the tree
*/
cx_attr_nonnull_arg(1, 2)
+cx_attr_export
int cxTreeDestroyNode(
CxTree *tree,
void *node,
if (elm != NULL && elm->key.hash == hash && elm->key.len == key.len &&
memcmp(elm->key.data, key.data, key.len) == 0) {
- // overwrite existing element
+ // overwrite existing element, but call destructors first
+ cx_invoke_destructor(map, elm->data);
if (map->collection.store_pointer) {
memcpy(elm->data, &value, sizeof(void *));
} else {
}
static void *cx_hash_map_iter_current_entry(const void *it) {
- const struct cx_iterator_s *iter = it;
- // struct has to have a compatible signature
- return (struct cx_map_entry_s *) &(iter->kv_data);
+ const CxMapIterator *iter = it;
+ // we have to cast away const, because of the signature
+ return (void*) &iter->entry;
}
static void *cx_hash_map_iter_current_key(const void *it) {
- const struct cx_iterator_s *iter = it;
- struct cx_hash_map_element_s *elm = iter->elem_handle;
+ const CxMapIterator *iter = it;
+ struct cx_hash_map_element_s *elm = iter->elem;
return &elm->key;
}
static void *cx_hash_map_iter_current_value(const void *it) {
- const struct cx_iterator_s *iter = it;
- const struct cx_hash_map_s *map = iter->src_handle.c;
- struct cx_hash_map_element_s *elm = iter->elem_handle;
- if (map->base.collection.store_pointer) {
+ const CxMapIterator *iter = it;
+ const CxMap *map = iter->map.c;
+ struct cx_hash_map_element_s *elm = iter->elem;
+ if (map->collection.store_pointer) {
return *(void **) elm->data;
} else {
return elm->data;
}
static bool cx_hash_map_iter_valid(const void *it) {
- const struct cx_iterator_s *iter = it;
- return iter->elem_handle != NULL;
+ const CxMapIterator *iter = it;
+ return iter->elem != NULL;
}
static void cx_hash_map_iter_next(void *it) {
- struct cx_iterator_s *iter = it;
- struct cx_hash_map_element_s *elm = iter->elem_handle;
- struct cx_hash_map_s *map = iter->src_handle.m;
+ CxMapIterator *iter = it;
+ CxMap *map = iter->map.m;
+ struct cx_hash_map_s *hmap = (struct cx_hash_map_s *) map;
+ struct cx_hash_map_element_s *elm = iter->elem;
// remove current element, if asked
if (iter->base.remove) {
// search the previous element
struct cx_hash_map_element_s *prev = NULL;
- if (map->buckets[iter->slot] != elm) {
- prev = map->buckets[iter->slot];
+ if (hmap->buckets[iter->slot] != elm) {
+ prev = hmap->buckets[iter->slot];
while (prev->next != elm) {
prev = prev->next;
}
}
// destroy
- cx_invoke_destructor((struct cx_map_s *) map, elm->data);
+ cx_invoke_destructor(map, elm->data);
// unlink
- cx_hash_map_unlink(map, iter->slot, prev, elm);
+ cx_hash_map_unlink(hmap, iter->slot, prev, elm);
// advance
elm = next;
}
// search the next bucket, if required
- while (elm == NULL && ++iter->slot < map->bucket_count) {
- elm = map->buckets[iter->slot];
+ while (elm == NULL && ++iter->slot < hmap->bucket_count) {
+ elm = hmap->buckets[iter->slot];
}
-
- // fill the struct with the next element
- iter->elem_handle = elm;
- if (elm == NULL) {
- iter->kv_data.key = NULL;
- iter->kv_data.value = NULL;
- } else {
- iter->kv_data.key = &elm->key;
- if (map->base.collection.store_pointer) {
- iter->kv_data.value = *(void **) elm->data;
+ iter->elem = elm;
+
+ // copy data to a location where the iterator can point to
+ // we need to do it here, because the iterator function call
+ // must not modify the iterator (the parameter is const)
+ if (elm != NULL) {
+ iter->entry.key = &elm->key;
+ if (iter->map.c->collection.store_pointer) {
+ iter->entry.value = *(void **) elm->data;
} else {
- iter->kv_data.value = elm->data;
+ iter->entry.value = elm->data;
}
}
}
-static CxIterator cx_hash_map_iterator(
+static CxMapIterator cx_hash_map_iterator(
const CxMap *map,
enum cx_map_iterator_type type
) {
- CxIterator iter;
+ CxMapIterator iter;
- iter.src_handle.c = map;
+ iter.map.c = map;
iter.elem_count = map->collection.size;
switch (type) {
while (elm == NULL) {
elm = hash_map->buckets[++iter.slot];
}
- iter.elem_handle = elm;
- iter.kv_data.key = &elm->key;
+ iter.elem = elm;
+ iter.entry.key = &elm->key;
if (map->collection.store_pointer) {
- iter.kv_data.value = *(void **) elm->data;
+ iter.entry.value = *(void **) elm->data;
} else {
- iter.kv_data.value = elm->data;
+ iter.entry.value = elm->data;
}
} else {
- iter.elem_handle = NULL;
- iter.kv_data.key = NULL;
- iter.kv_data.value = NULL;
+ iter.elem = NULL;
}
return iter;
map->base.collection.allocator = allocator;
if (itemsize > 0) {
- map->base.collection.store_pointer = false;
map->base.collection.elem_size = itemsize;
} else {
- map->base.collection.store_pointer = true;
map->base.collection.elem_size = sizeof(void *);
+ map->base.collection.store_pointer = true;
}
return (CxMap *) map;
*/
#include "cx/json.h"
-#include "cx/compare.h"
#include <string.h>
-#include <ctype.h>
#include <assert.h>
#include <stdio.h>
-#include <errno.h>
#include <inttypes.h>
/*
}
}
+static bool json_isdigit(char c) {
+ // TODO: remove once UCX has public API for this
+ return c >= '0' && c <= '9';
+}
+
+static bool json_isspace(char c) {
+ // TODO: remove once UCX has public API for this
+ return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\v' || c == '\f';
+}
+
static int num_isexp(const char *content, size_t length, size_t pos) {
if (pos >= length) {
return 0;
int ok = 0;
for (size_t i = pos; i < length; i++) {
char c = content[i];
- if (isdigit(c)) {
+ if (json_isdigit(c)) {
ok = 1;
} else if (i == pos) {
if (!(c == '+' || c == '-')) {
static CxJsonTokenType token_numbertype(const char *content, size_t length) {
if (length == 0) return CX_JSON_TOKEN_ERROR;
- if (content[0] != '-' && !isdigit(content[0])) {
+ if (content[0] != '-' && !json_isdigit(content[0])) {
return CX_JSON_TOKEN_ERROR;
}
type = CX_JSON_TOKEN_NUMBER;
} else if (content[i] == 'e' || content[i] == 'E') {
return num_isexp(content, length, i + 1) ? CX_JSON_TOKEN_NUMBER : CX_JSON_TOKEN_ERROR;
- } else if (!isdigit(content[i])) {
+ } else if (!json_isdigit(content[i])) {
return CX_JSON_TOKEN_ERROR; // char is not a digit, decimal separator or exponent sep
}
}
return CX_JSON_TOKEN_STRING;
}
default: {
- if (isspace(c)) {
+ if (json_isspace(c)) {
return CX_JSON_TOKEN_SPACE;
}
}
// current token type and start index
CxJsonTokenType ttype = json->uncompleted.tokentype;
- size_t token_start = json->buffer.pos;
+ 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] == '\\';
for (size_t i = json->buffer.pos; i < json->buffer.size; i++) {
char c = json->buffer.space[i];
} else if (ctype == CX_JSON_TOKEN_STRING) {
// begin string
ttype = CX_JSON_TOKEN_STRING;
- token_start = i;
+ token_part_start = i;
} else if (ctype != CX_JSON_NO_TOKEN) {
// single-char token
json->buffer.pos = i + 1;
return CX_JSON_NO_ERROR;
} else {
ttype = CX_JSON_TOKEN_LITERAL; // number or literal
- token_start = i;
+ token_part_start = i;
}
} else {
// finish token
if (ctype != CX_JSON_NO_TOKEN) {
- *result = token_create(json, false, token_start, i);
+ *result = token_create(json, false, token_part_start, i);
if (result->tokentype == CX_JSON_NO_TOKEN) {
return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE
}
}
} else {
// currently inside a string
- if (json->tokenizer_escape) {
- json->tokenizer_escape = false;
+ if (escape_end_of_string) {
+ escape_end_of_string = false;
} else {
if (c == '"') {
- *result = token_create(json, true, token_start, i + 1);
+ *result = token_create(json, true, token_part_start, i + 1);
if (result->tokentype == CX_JSON_NO_TOKEN) {
return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE
}
json->buffer.pos = i + 1;
return CX_JSON_NO_ERROR;
} else if (c == '\\') {
- json->tokenizer_escape = true;
+ escape_end_of_string = true;
}
}
}
if (ttype != CX_JSON_NO_TOKEN) {
// uncompleted token
- size_t uncompleted_len = json->buffer.size - token_start;
+ 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_start, uncompleted_len))
+ cx_strdup(cx_strn(json->buffer.space + token_part_start, uncompleted_len))
};
if (uncompleted.content.ptr == NULL) {
return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE
// 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_start, uncompleted_len));
+ cx_strn(json->buffer.space + token_part_start, uncompleted_len));
if (str.ptr == NULL) {
return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE
}
return CX_JSON_INCOMPLETE_DATA;
}
+// converts a Unicode codepoint to utf8
+static unsigned codepoint_to_utf8(uint32_t codepoint, char *output_buf) {
+ if (codepoint <= 0x7F) {
+ *output_buf = (char)codepoint;
+ return 1;
+ } else if (codepoint <= 0x7FF) {
+ output_buf[0] = (char)(0xC0 | ((codepoint >> 6) & 0x1F));
+ output_buf[1] = (char)(0x80 | (codepoint & 0x3F));
+ return 2;
+ } else if (codepoint <= 0xFFFF) {
+ output_buf[0] = (char)(0xE0 | ((codepoint >> 12) & 0x0F));
+ output_buf[1] = (char)(0x80 | ((codepoint >> 6) & 0x3F));
+ output_buf[2] = (char)(0x80 | (codepoint & 0x3F));
+ return 3;
+ } else if (codepoint <= 0x10FFFF) {
+ output_buf[0] = (char)(0xF0 | ((codepoint >> 18) & 0x07));
+ output_buf[1] = (char)(0x80 | ((codepoint >> 12) & 0x3F));
+ output_buf[2] = (char)(0x80 | ((codepoint >> 6) & 0x3F));
+ output_buf[3] = (char)(0x80 | (codepoint & 0x3F));
+ return 4;
+ }
+
+ return 0; // LCOV_EXCL_LINE
+}
+
+// converts a utf16 surrogate pair to utf8
+static inline uint32_t utf16pair_to_codepoint(uint16_t c0, uint16_t c1) {
+ return ((c0 - 0xD800) << 10) + (c1 - 0xDC00) + 0x10000;
+}
+
+static unsigned unescape_unicode_string(cxstring str, char *utf8buf) {
+ // str is supposed to start with "\uXXXX" or "\uXXXX\uXXXX"
+ // remaining bytes in the string are ignored (str may be larger!)
+
+ if (str.length < 6 || str.ptr[0] != '\\' || str.ptr[1] != 'u') {
+ return 0;
+ }
+
+ unsigned utf8len = 0;
+ cxstring ustr1 = { str.ptr + 2, 4};
+ uint16_t utf16a, utf16b;
+ if (!cx_strtou16_lc(ustr1, &utf16a, 16, "")) {
+ uint32_t codepoint;
+ if (utf16a < 0xD800 || utf16a > 0xE000) {
+ // character is in the Basic Multilingual Plane
+ // and encoded as a single utf16 char
+ codepoint = utf16a;
+ utf8len = codepoint_to_utf8(codepoint, utf8buf);
+ } else if (utf16a >= 0xD800 && utf16a <= 0xDBFF) {
+ // character is encoded as a surrogate pair
+ // get next 6 bytes
+ if (str.length >= 12) {
+ if (str.ptr[6] == '\\' && str.ptr[7] == 'u') {
+ cxstring ustr2 = { str.ptr+8, 4 };
+ if (!cx_strtou16_lc(ustr2, &utf16b, 16, "")
+ && utf16b >= 0xDC00 && utf16b <= 0xDFFF) {
+ codepoint = utf16pair_to_codepoint(utf16a, utf16b);
+ utf8len = codepoint_to_utf8(codepoint, utf8buf);
+ }
+ }
+ }
+ }
+ }
+ return utf8len;
+}
+
static cxmutstr unescape_string(const CxAllocator *a, cxmutstr str) {
- // TODO: support more escape sequences
- // we know that the unescaped string will be shorter by at least 2 chars
+ // note: this function expects that str contains the enclosing quotes!
+
cxmutstr result;
result.length = 0;
result.ptr = cxMalloc(a, str.length - 1);
u = false;
if (c == 'n') {
c = '\n';
+ } else if (c == '"') {
+ c = '"';
} else if (c == 't') {
c = '\t';
+ } else if (c == 'r') {
+ c = '\r';
+ } else if (c == '\\') {
+ c = '\\';
+ } else if (c == '/') {
+ c = '/'; // always unescape, we don't need settings here
+ } else if (c == 'f') {
+ c = '\f';
+ } else if (c == 'b') {
+ c = '\b';
+ } else if (c == 'u') {
+ char utf8buf[4];
+ unsigned utf8len = unescape_unicode_string(
+ cx_strn(str.ptr + i - 1, str.length + 1 - i),
+ utf8buf
+ );
+ if(utf8len > 0) {
+ i += utf8len < 4 ? 4 : 10;
+ // add all bytes from utf8buf except the last char
+ // to the result (last char will be added below)
+ utf8len--;
+ c = utf8buf[utf8len];
+ for (unsigned x = 0; x < utf8len; x++) {
+ result.ptr[result.length++] = utf8buf[x];
+ }
+ } else {
+ // decoding failed, ignore the entire sequence
+ result.ptr[result.length++] = '\\';
+ }
+ } else {
+ // TODO: discuss the behavior for unrecognized escape sequences
+ // most parsers throw an error here - we just ignore it
+ result.ptr[result.length++] = '\\';
}
+
result.ptr[result.length++] = c;
} else {
if (c == '\\') {
return result;
}
-static CxJsonValue* create_json_value(CxJson *json, CxJsonValueType type) {
+static cxmutstr escape_string(cxmutstr str, bool escape_slash) {
+ // note: this function produces the string without enclosing quotes
+ // the reason is that we don't want to allocate memory just for that
+ CxBuffer buf = {0};
+
+ bool all_printable = true;
+ for (size_t i = 0; i < str.length; i++) {
+ unsigned char c = str.ptr[i];
+ bool escape = c < 0x20 || c == '\\' || c == '"'
+ || (escape_slash && c == '/');
+
+ if (all_printable && escape) {
+ size_t capa = str.length + 32;
+ char *space = malloc(capa);
+ if (space == NULL) return cx_mutstrn(NULL, 0);
+ cxBufferInit(&buf, space, capa, NULL, CX_BUFFER_AUTO_EXTEND);
+ cxBufferWrite(str.ptr, 1, i, &buf);
+ all_printable = false;
+ }
+ if (escape) {
+ cxBufferPut(&buf, '\\');
+ if (c == '\"') {
+ cxBufferPut(&buf, '\"');
+ } else if (c == '\n') {
+ cxBufferPut(&buf, 'n');
+ } else if (c == '\t') {
+ cxBufferPut(&buf, 't');
+ } else if (c == '\r') {
+ cxBufferPut(&buf, 'r');
+ } else if (c == '\\') {
+ cxBufferPut(&buf, '\\');
+ } else if (c == '/') {
+ cxBufferPut(&buf, '/');
+ } else if (c == '\f') {
+ cxBufferPut(&buf, 'f');
+ } else if (c == '\b') {
+ cxBufferPut(&buf, 'b');
+ } else {
+ char code[6];
+ snprintf(code, sizeof(code), "u%04x", (unsigned int) c);
+ cxBufferPutString(&buf, code);
+ }
+ } else if (!all_printable) {
+ cxBufferPut(&buf, c);
+ }
+ }
+ if (!all_printable) {
+ str = cx_mutstrn(buf.space, buf.size);
+ }
+ cxBufferDestroy(&buf);
+ return str;
+}
+
+static CxJsonValue* json_create_value(CxJson *json, CxJsonValueType type) {
CxJsonValue *v = cxCalloc(json->allocator, 1, sizeof(CxJsonValue));
if (v == NULL) return NULL; // LCOV_EXCL_LINE
json_add_state(json, 10 + state);
switch (token.tokentype) {
case CX_JSON_TOKEN_BEGIN_ARRAY: {
- if (create_json_value(json, CX_JSON_ARRAY) == NULL) {
+ if (json_create_value(json, CX_JSON_ARRAY) == NULL) {
return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE
}
json_add_state(json, JP_STATE_VALUE_BEGIN_AR);
return_rec(CX_JSON_NO_ERROR);
}
case CX_JSON_TOKEN_BEGIN_OBJECT: {
- if (create_json_value(json, CX_JSON_OBJECT) == NULL) {
+ if (json_create_value(json, CX_JSON_OBJECT) == NULL) {
return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE
}
json_add_state(json, JP_STATE_OBJ_NAME_OR_CLOSE);
return_rec(CX_JSON_NO_ERROR);
}
case CX_JSON_TOKEN_STRING: {
- if ((vbuf = create_json_value(json, CX_JSON_STRING)) == NULL) {
+ if ((vbuf = json_create_value(json, CX_JSON_STRING)) == NULL) {
return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE
}
cxmutstr str = unescape_string(json->allocator, token.content);
case CX_JSON_TOKEN_INTEGER:
case CX_JSON_TOKEN_NUMBER: {
int type = token.tokentype == CX_JSON_TOKEN_INTEGER ? CX_JSON_INTEGER : CX_JSON_NUMBER;
- if (NULL == (vbuf = create_json_value(json, type))) {
+ if (NULL == (vbuf = json_create_value(json, type))) {
return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE
}
if (type == CX_JSON_INTEGER) {
return_rec(CX_JSON_NO_ERROR);
}
case CX_JSON_TOKEN_LITERAL: {
- if ((vbuf = create_json_value(json, CX_JSON_LITERAL)) == NULL) {
+ 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"))) {
}
CxJsonValue* cxJsonCreateObj(const CxAllocator* allocator) {
+ if (allocator == NULL) allocator = cxDefaultAllocator;
CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue));
if (v == NULL) return NULL;
v->allocator = allocator;
}
CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator) {
+ if (allocator == NULL) allocator = cxDefaultAllocator;
CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue));
if (v == NULL) return NULL;
v->allocator = allocator;
}
CxJsonValue* cxJsonCreateNumber(const CxAllocator* allocator, double num) {
+ if (allocator == NULL) allocator = cxDefaultAllocator;
CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue));
if (v == NULL) return NULL;
v->allocator = allocator;
}
CxJsonValue* cxJsonCreateInteger(const CxAllocator* allocator, int64_t num) {
+ if (allocator == NULL) allocator = cxDefaultAllocator;
CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue));
if (v == NULL) return NULL;
v->allocator = allocator;
}
CxJsonValue* cxJsonCreateCxString(const CxAllocator* allocator, cxstring str) {
+ if (allocator == NULL) allocator = cxDefaultAllocator;
CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue));
if (v == NULL) return NULL;
v->allocator = allocator;
}
CxJsonValue* cxJsonCreateLiteral(const CxAllocator* allocator, CxJsonLiteral lit) {
+ if (allocator == NULL) allocator = cxDefaultAllocator;
CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue));
if (v == NULL) return NULL;
v->allocator = allocator;
// LCOV_EXCL_START
// never called as long as malloc() does not return NULL
-static void cx_json_arr_free_temp(CxJsonValue** values, size_t count) {
+static void json_arr_free_temp(CxJsonValue** values, size_t count) {
for (size_t i = 0; i < count; i++) {
if (values[i] == NULL) break;
cxJsonValueFree(values[i]);
if (values == NULL) return -1;
for (size_t i = 0; i < count; i++) {
values[i] = cxJsonCreateNumber(arr->allocator, num[i]);
- if (values[i] == NULL) { cx_json_arr_free_temp(values, count); return -1; }
+ if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; }
}
int ret = cxJsonArrAddValues(arr, values, count);
free(values);
if (values == NULL) return -1;
for (size_t i = 0; i < count; i++) {
values[i] = cxJsonCreateInteger(arr->allocator, num[i]);
- if (values[i] == NULL) { cx_json_arr_free_temp(values, count); return -1; }
+ if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; }
}
int ret = cxJsonArrAddValues(arr, values, count);
free(values);
if (values == NULL) return -1;
for (size_t i = 0; i < count; i++) {
values[i] = cxJsonCreateString(arr->allocator, str[i]);
- if (values[i] == NULL) { cx_json_arr_free_temp(values, count); return -1; }
+ if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; }
}
int ret = cxJsonArrAddValues(arr, values, count);
free(values);
if (values == NULL) return -1;
for (size_t i = 0; i < count; i++) {
values[i] = cxJsonCreateCxString(arr->allocator, str[i]);
- if (values[i] == NULL) { cx_json_arr_free_temp(values, count); return -1; }
+ if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; }
}
int ret = cxJsonArrAddValues(arr, values, count);
free(values);
if (values == NULL) return -1;
for (size_t i = 0; i < count; i++) {
values[i] = cxJsonCreateLiteral(arr->allocator, lit[i]);
- if (values[i] == NULL) { cx_json_arr_free_temp(values, count); return -1; }
+ if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; }
}
int ret = cxJsonArrAddValues(arr, values, count);
free(values);
}
}
-static const CxJsonWriter cx_json_writer_default = {
- false,
- true,
- 255,
- false,
- 4
-};
-
CxJsonWriter cxJsonWriterCompact(void) {
- return cx_json_writer_default;
+ return (CxJsonWriter) {
+ false,
+ true,
+ 6,
+ false,
+ 4,
+ false
+ };
}
CxJsonWriter cxJsonWriterPretty(bool use_spaces) {
return (CxJsonWriter) {
true,
true,
- 255,
+ 6,
use_spaces,
- 4
+ 4,
+ false
};
}
size_t actual = 0, expected = 0;
// small buffer for number to string conversions
- char numbuf[32];
+ char numbuf[40];
// recursively write the values
switch (value->type) {
// the name
actual += wfunc("\"", 1, 1, target);
- // TODO: escape the string
- actual += wfunc(member->name.ptr, 1,
- member->name.length, target);
+ cxmutstr name = escape_string(member->name, settings->escape_slash);
+ actual += wfunc(name.ptr, 1, name.length, target);
+ if (name.ptr != member->name.ptr) {
+ cx_strfree(&name);
+ }
actual += wfunc("\"", 1, 1, target);
const char *obj_name_sep = ": ";
if (settings->pretty) {
}
case CX_JSON_STRING: {
actual += wfunc("\"", 1, 1, target);
- // TODO: escape the string
- actual += wfunc(value->value.string.ptr, 1,
- value->value.string.length, target);
+ cxmutstr str = escape_string(value->value.string, settings->escape_slash);
+ actual += wfunc(str.ptr, 1, str.length, target);
+ if (str.ptr != value->value.string.ptr) {
+ cx_strfree(&str);
+ }
actual += wfunc("\"", 1, 1, target);
expected += 2 + value->value.string.length;
break;
}
case CX_JSON_NUMBER: {
- // TODO: locale bullshit
- // TODO: formatting settings
- snprintf(numbuf, 32, "%g", value->value.number);
- size_t len = strlen(numbuf);
- actual += wfunc(numbuf, 1, len, target);
- expected += len;
+ int precision = settings->frac_max_digits;
+ // because of the way how %g is defined, we need to
+ // double the precision and truncate ourselves
+ precision = 1 + (precision > 15 ? 30 : 2 * precision);
+ snprintf(numbuf, 40, "%.*g", precision, value->value.number);
+ char *dot, *exp;
+ unsigned char max_digits;
+ // find the decimal separator and hope that it's one of . or ,
+ dot = strchr(numbuf, '.');
+ if (dot == NULL) {
+ dot = strchr(numbuf, ',');
+ }
+ if (dot == NULL) {
+ // no decimal separator found
+ // output everything until a possible exponent
+ max_digits = 30;
+ dot = numbuf;
+ } else {
+ // found a decimal separator
+ // output everything until the separator
+ // and set max digits to what the settings say
+ size_t len = dot - numbuf;
+ actual += wfunc(numbuf, 1, len, target);
+ expected += len;
+ max_digits = settings->frac_max_digits;
+ if (max_digits > 15) {
+ max_digits = 15;
+ }
+ // locale independent separator
+ if (max_digits > 0) {
+ actual += wfunc(".", 1, 1, target);
+ expected++;
+ }
+ dot++;
+ }
+ // find the exponent
+ exp = strchr(dot, 'e');
+ if (exp == NULL) {
+ // no exponent - output the rest
+ if (max_digits > 0) {
+ size_t len = strlen(dot);
+ if (len > max_digits) {
+ len = max_digits;
+ }
+ actual += wfunc(dot, 1, len, target);
+ expected += len;
+ }
+ } else {
+ // exponent found - truncate the frac digits
+ // and then output the rest
+ if (max_digits > 0) {
+ size_t len = exp - dot - 1;
+ if (len > max_digits) {
+ len = max_digits;
+ }
+ actual += wfunc(dot, 1, len, target);
+ expected += len;
+ }
+ actual += wfunc("e", 1, 1, target);
+ expected++;
+ exp++;
+ size_t len = strlen(exp);
+ actual += wfunc(exp, 1, len, target);
+ expected += len;
+ }
break;
}
case CX_JSON_INTEGER: {
cx_write_func wfunc,
const CxJsonWriter *settings
) {
- if (settings == NULL) {
- settings = &cx_json_writer_default;
- }
assert(target != NULL);
assert(value != NULL);
assert(wfunc != NULL);
+ CxJsonWriter writer_default = cxJsonWriterCompact();
+ if (settings == NULL) {
+ settings = &writer_default;
+ }
return cx_json_write_rec(target, value, wfunc, settings, 0);
}
return (void *) cur;
}
-ssize_t cx_linked_list_find(
+void *cx_linked_list_find(
const void *start,
ptrdiff_t loc_advance,
ptrdiff_t loc_data,
cx_compare_func cmp_func,
- const void *elem
-) {
- void *dummy;
- return cx_linked_list_find_node(
- &dummy, start,
- loc_advance, loc_data,
- cmp_func, elem
- );
-}
-
-ssize_t cx_linked_list_find_node(
- void **result,
- const void *start,
- ptrdiff_t loc_advance,
- ptrdiff_t loc_data,
- cx_compare_func cmp_func,
- const void *elem
+ const void *elem,
+ size_t *found_index
) {
- assert(result != NULL);
assert(start != NULL);
assert(loc_advance >= 0);
assert(loc_data >= 0);
assert(cmp_func);
- const void *node = start;
- ssize_t index = 0;
+ void *node = (void*) start;
+ size_t index = 0;
do {
void *current = ll_data(node);
if (cmp_func(current, elem) == 0) {
- *result = (void *) node;
- return index;
+ if (found_index != NULL) {
+ *found_index = index;
+ }
+ return node;
}
node = ll_advance(node);
index++;
} while (node != NULL);
- *result = NULL;
- return -1;
+ return NULL;
}
void *cx_linked_list_first(
list->collection.size = 0;
}
-#ifndef CX_LINKED_LIST_SWAP_SBO_SIZE
-#define CX_LINKED_LIST_SWAP_SBO_SIZE 128
-#endif
-const unsigned cx_linked_list_swap_sbo_size = CX_LINKED_LIST_SWAP_SBO_SIZE;
-
static int cx_ll_swap(
struct cx_list_s *list,
size_t i,
}
}
- if (list->collection.elem_size > CX_LINKED_LIST_SWAP_SBO_SIZE) {
- cx_linked_list_node *prev = nleft->prev;
- cx_linked_list_node *next = nright->next;
- cx_linked_list_node *midstart = nleft->next;
- cx_linked_list_node *midend = nright->prev;
+ cx_linked_list_node *prev = nleft->prev;
+ cx_linked_list_node *next = nright->next;
+ cx_linked_list_node *midstart = nleft->next;
+ cx_linked_list_node *midend = nright->prev;
- if (prev == NULL) {
- ll->begin = nright;
- } else {
- prev->next = nright;
- }
- nright->prev = prev;
- if (midstart == nright) {
- // special case: both nodes are adjacent
- nright->next = nleft;
- nleft->prev = nright;
- } else {
- // likely case: a chain is between the two nodes
- nright->next = midstart;
- midstart->prev = nright;
- midend->next = nleft;
- nleft->prev = midend;
- }
- nleft->next = next;
- if (next == NULL) {
- ll->end = nleft;
- } else {
- next->prev = nleft;
- }
+ if (prev == NULL) {
+ ll->begin = nright;
+ } else {
+ prev->next = nright;
+ }
+ nright->prev = prev;
+ if (midstart == nright) {
+ // special case: both nodes are adjacent
+ nright->next = nleft;
+ nleft->prev = nright;
+ } else {
+ // likely case: a chain is between the two nodes
+ nright->next = midstart;
+ midstart->prev = nright;
+ midend->next = nleft;
+ nleft->prev = midend;
+ }
+ nleft->next = next;
+ if (next == NULL) {
+ ll->end = nleft;
} else {
- // swap payloads to avoid relinking
- char buf[CX_LINKED_LIST_SWAP_SBO_SIZE];
- memcpy(buf, nleft->payload, list->collection.elem_size);
- memcpy(nleft->payload, nright->payload, list->collection.elem_size);
- memcpy(nright->payload, buf, list->collection.elem_size);
+ next->prev = nleft;
}
return 0;
return node == NULL ? NULL : node->payload;
}
-static ssize_t cx_ll_find_remove(
+static size_t cx_ll_find_remove(
struct cx_list_s *list,
const void *elem,
bool remove
) {
+ size_t index;
+ cx_linked_list *ll = ((cx_linked_list *) list);
+ cx_linked_list_node *node = cx_linked_list_find(
+ ll->begin,
+ CX_LL_LOC_NEXT, CX_LL_LOC_DATA,
+ list->collection.cmpfunc, elem,
+ &index
+ );
+ if (node == NULL) {
+ return list->collection.size;
+ }
if (remove) {
- cx_linked_list *ll = ((cx_linked_list *) list);
- cx_linked_list_node *node;
- ssize_t index = cx_linked_list_find_node(
- (void **) &node,
- ll->begin,
- CX_LL_LOC_NEXT, CX_LL_LOC_DATA,
- list->collection.cmpfunc, elem
- );
- if (node != NULL) {
- cx_invoke_destructor(list, node->payload);
- cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end,
- CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node);
- list->collection.size--;
- cxFree(list->collection.allocator, node);
- }
- return index;
- } else {
- return cx_linked_list_find(
- ((cx_linked_list *) list)->begin,
- CX_LL_LOC_NEXT, CX_LL_LOC_DATA,
- list->collection.cmpfunc, elem
- );
+ cx_invoke_destructor(list, node->payload);
+ cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end,
+ CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node);
+ list->collection.size--;
+ cxFree(list->collection.allocator, node);
}
+ return index;
}
static void cx_ll_sort(struct cx_list_s *list) {
cx_linked_list *list = cxCalloc(allocator, 1, sizeof(cx_linked_list));
if (list == NULL) return NULL;
-
- list->base.cl = &cx_linked_list_class;
- list->base.collection.allocator = allocator;
-
- if (elem_size > 0) {
- list->base.collection.elem_size = elem_size;
- list->base.collection.cmpfunc = comparator;
- } else {
- list->base.collection.cmpfunc = comparator == NULL ? cx_cmp_ptr : comparator;
- cxListStorePointers((CxList *) list);
- }
+ cx_list_init((CxList*)list, &cx_linked_list_class,
+ allocator, comparator, elem_size);
return (CxList *) list;
}
return ptr == NULL ? NULL : *ptr;
}
-static ssize_t cx_pl_find_remove(
+static size_t cx_pl_find_remove(
struct cx_list_s *list,
const void *elem,
bool remove
) {
cx_pl_hack_cmpfunc(list);
- ssize_t ret = list->climpl->find_remove(list, &elem, remove);
+ size_t ret = list->climpl->find_remove(list, &elem, remove);
cx_pl_unhack_cmpfunc(list);
return ret;
}
cx_pl_reverse,
cx_pl_iterator,
};
-
-void cxListStoreObjects(CxList *list) {
- list->collection.store_pointer = false;
- if (list->climpl != NULL) {
- list->cl = list->climpl;
- list->climpl = NULL;
- }
-}
-
-void cxListStorePointers(CxList *list) {
- list->collection.elem_size = sizeof(void *);
- list->collection.store_pointer = true;
- list->climpl = list->cl;
- list->cl = &cx_pointer_list_class;
-}
-
// </editor-fold>
// <editor-fold desc="empty list implementation">
return NULL;
}
-static ssize_t cx_emptyl_find_remove(
+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
) {
- return -1;
+ return 0;
}
static bool cx_emptyl_iter_valid(cx_attr_unused const void *iter) {
};
CxList cx_empty_list = {
- {
- NULL,
- NULL,
- 0,
- 0,
- NULL,
- NULL,
- NULL,
- false
- },
- &cx_empty_list_class,
- NULL
+ {
+ NULL,
+ NULL,
+ 0,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ false,
+ true,
+ },
+ &cx_empty_list_class,
+ NULL
};
CxList *const cxEmptyList = &cx_empty_list;
return 0;
}
+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;
+ if (elem_size > 0) {
+ list->collection.elem_size = elem_size;
+ } else {
+ list->collection.elem_size = sizeof(void *);
+ if (list->collection.cmpfunc == NULL) {
+ list->collection.cmpfunc = cx_cmp_ptr;
+ }
+ list->collection.store_pointer = true;
+ list->climpl = list->cl;
+ list->cl = &cx_pointer_list_class;
+ }
+}
+
int cxListCompare(
const CxList *list,
const CxList *other
return false;\r
}\r
\r
-static CxIterator cx_empty_map_iterator(\r
+static CxMapIterator cx_empty_map_iterator(\r
const struct cx_map_s *map,\r
cx_attr_unused enum cx_map_iterator_type type\r
) {\r
- CxIterator iter = {0};\r
- iter.src_handle.c = map;\r
+ CxMapIterator iter = {0};\r
+ iter.map.c = map;\r
iter.base.valid = cx_empty_map_iter_valid;\r
return iter;\r
}\r
};\r
\r
CxMap cx_empty_map = {\r
- {\r
- NULL,\r
- NULL,\r
- 0,\r
- 0,\r
- NULL,\r
- NULL,\r
- NULL,\r
- false\r
- },\r
- &cx_empty_map_class\r
+ {\r
+ NULL,\r
+ NULL,\r
+ 0,\r
+ 0,\r
+ NULL,\r
+ NULL,\r
+ NULL,\r
+ false,\r
+ true\r
+ },\r
+ &cx_empty_map_class\r
};\r
\r
CxMap *const cxEmptyMap = &cx_empty_map;\r
\r
// </editor-fold>\r
\r
-CxIterator cxMapMutIteratorValues(CxMap *map) {\r
- CxIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_VALUES);\r
+CxMapIterator cxMapMutIteratorValues(CxMap *map) {\r
+ CxMapIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_VALUES);\r
it.base.mutating = true;\r
return it;\r
}\r
\r
-CxIterator cxMapMutIteratorKeys(CxMap *map) {\r
- CxIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_KEYS);\r
+CxMapIterator cxMapMutIteratorKeys(CxMap *map) {\r
+ CxMapIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_KEYS);\r
it.base.mutating = true;\r
return it;\r
}\r
\r
-CxIterator cxMapMutIterator(CxMap *map) {\r
- CxIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_PAIRS);\r
+CxMapIterator cxMapMutIterator(CxMap *map) {\r
+ CxMapIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_PAIRS);\r
it.base.mutating = true;\r
return it;\r
}\r
const CxPropertiesConfig cx_properties_config_default = {
'=',
- //'\\',
'#',
'\0',
- '\0'
+ '\0',
+ '\\',
};
void cxPropertiesInit(
return CX_PROPERTIES_INVALID_EMPTY_KEY;
}
}
- // unreachable - either we returned or skipped a blank line
- assert(false);
}
// when we come to this point, all data must have been read
CxPropertiesSink cxPropertiesMapSink(CxMap *map) {
CxPropertiesSink sink;
sink.sink = map;
- sink.data = cxDefaultAllocator;
+ sink.data = (void*) cxDefaultAllocator;
sink.sink_func = cx_properties_sink_map;
return sink;
}
) {
target->ptr = src->data_ptr;
target->length = fread(src->data_ptr, 1, src->data_size, src->src);
- return ferror(src->src);
+ return ferror((FILE*)src->src);
}
static int cx_properties_read_init_file(
// transfer the data from the source to the sink
CxPropertiesStatus status;
+ CxPropertiesStatus kv_status = CX_PROPERTIES_NO_DATA;
bool found = false;
while (true) {
// read input
// no more data - break
if (input.length == 0) {
- status = found ? CX_PROPERTIES_NO_ERROR : CX_PROPERTIES_NO_DATA;
+ if (found) {
+ // something was found, check the last kv_status
+ if (kv_status == CX_PROPERTIES_INCOMPLETE_DATA) {
+ status = CX_PROPERTIES_INCOMPLETE_DATA;
+ } else {
+ status = CX_PROPERTIES_NO_ERROR;
+ }
+ } else {
+ // nothing found
+ status = CX_PROPERTIES_NO_DATA;
+ }
break;
}
// set the input buffer and read the k/v-pairs
cxPropertiesFill(prop, input);
- CxPropertiesStatus kv_status;
do {
cxstring key, value;
kv_status = cxPropertiesNext(prop, &key, &value);
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
-#define CX_STR_IMPLEMENTATION
#include "cx/string.h"
#include <string.h>
#include <stdarg.h>
-#include <ctype.h>
#include <assert.h>
#include <errno.h>
#include <limits.h>
cxstring string,
int chr
) {
- chr = 0xFF & chr;
- // TODO: improve by comparing multiple bytes at once
- for (size_t i = 0; i < string.length; i++) {
- if (string.ptr[i] == chr) {
- return cx_strsubs(string, i);
- }
- }
- return (cxstring) {NULL, 0};
+ char *ret = memchr(string.ptr, 0xFF & chr, string.length);
+ if (ret == NULL) return (cxstring) {NULL, 0};
+ return (cxstring) {ret, string.length - (ret - string.ptr)};
}
cxmutstr cx_strchr_m(
}
#ifndef CX_STRSTR_SBO_SIZE
-#define CX_STRSTR_SBO_SIZE 512
+#define CX_STRSTR_SBO_SIZE 128
#endif
const unsigned cx_strstr_sbo_size = CX_STRSTR_SBO_SIZE;
// check needle length and use appropriate prefix table
// if the pattern exceeds static prefix table, allocate on the heap
- bool useheap = needle.length >= CX_STRSTR_SBO_SIZE;
+ const bool useheap = needle.length >= CX_STRSTR_SBO_SIZE;
register size_t *ptable = useheap ? calloc(needle.length + 1,
sizeof(size_t)) : s_prefix_table;
}
// if prefix table was allocated on the heap, free it
- if (ptable != s_prefix_table) {
+ if (useheap) {
free(ptable);
}
return cx_strcasecmp(*left, *right);
}
-cxmutstr cx_strdup_a(
+cxmutstr cx_strdup_a_(
const CxAllocator *allocator,
cxstring string
) {
return result;
}
+static bool str_isspace(char c) {
+ // TODO: remove once UCX has public API for this
+ return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\v' || c == '\f';
+}
+
cxstring cx_strtrim(cxstring string) {
cxstring result = string;
// TODO: optimize by comparing multiple bytes at once
- while (result.length > 0 && isspace(*result.ptr)) {
+ while (result.length > 0 && str_isspace(*result.ptr)) {
result.ptr++;
result.length--;
}
- while (result.length > 0 && isspace(result.ptr[result.length - 1])) {
+ while (result.length > 0 && str_isspace(result.ptr[result.length - 1])) {
result.length--;
}
return result;
#endif
}
-void cx_strlower(cxmutstr string) {
- for (size_t i = 0; i < string.length; i++) {
- string.ptr[i] = (char) tolower(string.ptr[i]);
- }
-}
-
-void cx_strupper(cxmutstr string) {
- for (size_t i = 0; i < string.length; i++) {
- string.ptr[i] = (char) toupper(string.ptr[i]);
- }
-}
-
#ifndef CX_STRREPLACE_INDEX_BUFFER_SIZE
#define CX_STRREPLACE_INDEX_BUFFER_SIZE 64
#endif
};
static void cx_strrepl_free_ibuf(struct cx_strreplace_ibuf *buf) {
+ // remember, the first data is on the stack!
+ buf = buf->next;
while (buf) {
struct cx_strreplace_ibuf *next = buf->next;
free(buf->buf);
cxmutstr cx_strreplacen_a(
const CxAllocator *allocator,
cxstring str,
- cxstring pattern,
+ cxstring search,
cxstring replacement,
size_t replmax
) {
- if (pattern.length == 0 || pattern.length > str.length || replmax == 0)
+ if (search.length == 0 || search.length > str.length || replmax == 0)
return cx_strdup_a(allocator, str);
// Compute expected buffer length
- size_t ibufmax = str.length / pattern.length;
+ size_t ibufmax = str.length / search.length;
size_t ibuflen = replmax < ibufmax ? replmax : ibufmax;
if (ibuflen > CX_STRREPLACE_INDEX_BUFFER_SIZE) {
ibuflen = CX_STRREPLACE_INDEX_BUFFER_SIZE;
}
- // Allocate first index buffer
- struct cx_strreplace_ibuf *firstbuf, *curbuf;
- firstbuf = curbuf = calloc(1, sizeof(struct cx_strreplace_ibuf));
- if (!firstbuf) return cx_mutstrn(NULL, 0);
- firstbuf->buf = calloc(ibuflen, sizeof(size_t));
- if (!firstbuf->buf) {
- free(firstbuf);
- return cx_mutstrn(NULL, 0);
- }
+ // First index buffer can be on the stack
+ struct cx_strreplace_ibuf ibuf, *curbuf = &ibuf;
+ size_t ibuf_sbo[CX_STRREPLACE_INDEX_BUFFER_SIZE];
+ ibuf.buf = ibuf_sbo;
+ ibuf.next = NULL;
+ ibuf.len = 0;
// Search occurrences
cxstring searchstr = str;
size_t found = 0;
do {
- cxstring match = cx_strstr(searchstr, pattern);
+ cxstring match = cx_strstr(searchstr, search);
if (match.length > 0) {
// Allocate next buffer in chain, if required
if (curbuf->len == ibuflen) {
struct cx_strreplace_ibuf *nextbuf =
calloc(1, sizeof(struct cx_strreplace_ibuf));
if (!nextbuf) {
- cx_strrepl_free_ibuf(firstbuf);
+ cx_strrepl_free_ibuf(&ibuf);
return cx_mutstrn(NULL, 0);
}
nextbuf->buf = calloc(ibuflen, sizeof(size_t));
if (!nextbuf->buf) {
free(nextbuf);
- cx_strrepl_free_ibuf(firstbuf);
+ cx_strrepl_free_ibuf(&ibuf);
return cx_mutstrn(NULL, 0);
}
curbuf->next = nextbuf;
found++;
size_t idx = match.ptr - str.ptr;
curbuf->buf[curbuf->len++] = idx;
- searchstr.ptr = match.ptr + pattern.length;
- searchstr.length = str.length - idx - pattern.length;
+ searchstr.ptr = match.ptr + search.length;
+ searchstr.length = str.length - idx - search.length;
} else {
break;
}
// Allocate result string
cxmutstr result;
{
- ssize_t adjlen = (ssize_t) replacement.length - (ssize_t) pattern.length;
+ long long adjlen = (long long) replacement.length - (long long) search.length;
size_t rcount = 0;
- curbuf = firstbuf;
+ curbuf = &ibuf;
do {
rcount += curbuf->len;
curbuf = curbuf->next;
result.length = str.length + rcount * adjlen;
result.ptr = cxMalloc(allocator, result.length + 1);
if (!result.ptr) {
- cx_strrepl_free_ibuf(firstbuf);
+ cx_strrepl_free_ibuf(&ibuf);
return cx_mutstrn(NULL, 0);
}
}
// Build result string
- curbuf = firstbuf;
+ curbuf = &ibuf;
size_t srcidx = 0;
char *destptr = result.ptr;
do {
}
// Copy the replacement and skip the source pattern
- srcidx += pattern.length;
+ srcidx += search.length;
memcpy(destptr, replacement.ptr, replacement.length);
destptr += replacement.length;
}
result.ptr[result.length] = '\0';
// Free index buffer
- cx_strrepl_free_ibuf(firstbuf);
+ cx_strrepl_free_ibuf(&ibuf);
return result;
}
-CxStrtokCtx cx_strtok(
+CxStrtokCtx cx_strtok_(
cxstring str,
cxstring delim,
size_t limit
return ctx;
}
-CxStrtokCtx cx_strtok_m(
- cxmutstr str,
- cxstring delim,
- size_t limit
-) {
- return cx_strtok(cx_strcast(str), delim, limit);
-}
-
bool cx_strtok_next(
CxStrtokCtx *ctx,
cxstring *token
*output = (rtype) result; \
return 0
-int cx_strtos_lc(cxstring str, short *output, int base, const char *groupsep) {
+int cx_strtos_lc_(cxstring str, short *output, int base, const char *groupsep) {
cx_strtoX_signed_impl(short, SHRT_MIN, SHRT_MAX);
}
-int cx_strtoi_lc(cxstring str, int *output, int base, const char *groupsep) {
+int cx_strtoi_lc_(cxstring str, int *output, int base, const char *groupsep) {
cx_strtoX_signed_impl(int, INT_MIN, INT_MAX);
}
-int cx_strtol_lc(cxstring str, long *output, int base, const char *groupsep) {
+int cx_strtol_lc_(cxstring str, long *output, int base, const char *groupsep) {
cx_strtoX_signed_impl(long, LONG_MIN, LONG_MAX);
}
-int cx_strtoll_lc(cxstring str, long long *output, int base, const char *groupsep) {
+int cx_strtoll_lc_(cxstring str, long long *output, int base, const char *groupsep) {
// strategy: parse as unsigned, check range, negate if required
bool neg = false;
size_t start_unsigned = 0;
- // trim already, to search for a sign character
- str = cx_strtrim(str);
+ // emptiness check
if (str.length == 0) {
errno = EINVAL;
return -1;
}
}
-int cx_strtoi8_lc(cxstring str, int8_t *output, int base, const char *groupsep) {
+int cx_strtoi8_lc_(cxstring str, int8_t *output, int base, const char *groupsep) {
cx_strtoX_signed_impl(int8_t, INT8_MIN, INT8_MAX);
}
-int cx_strtoi16_lc(cxstring str, int16_t *output, int base, const char *groupsep) {
+int cx_strtoi16_lc_(cxstring str, int16_t *output, int base, const char *groupsep) {
cx_strtoX_signed_impl(int16_t, INT16_MIN, INT16_MAX);
}
-int cx_strtoi32_lc(cxstring str, int32_t *output, int base, const char *groupsep) {
+int cx_strtoi32_lc_(cxstring str, int32_t *output, int base, const char *groupsep) {
cx_strtoX_signed_impl(int32_t, INT32_MIN, INT32_MAX);
}
-int cx_strtoi64_lc(cxstring str, int64_t *output, int base, const char *groupsep) {
+int cx_strtoi64_lc_(cxstring str, int64_t *output, int base, const char *groupsep) {
assert(sizeof(long long) == sizeof(int64_t)); // should be true on all platforms
return cx_strtoll_lc(str, (long long*) output, base, groupsep);
}
-int cx_strtoz_lc(cxstring str, ssize_t *output, int base, const char *groupsep) {
-#if SSIZE_MAX == INT32_MAX
- return cx_strtoi32_lc(str, (int32_t*) output, base, groupsep);
-#elif SSIZE_MAX == INT64_MAX
- return cx_strtoll_lc(str, (long long*) output, base, groupsep);
-#else
-#error "unsupported ssize_t size"
-#endif
-}
-
#define cx_strtoX_unsigned_impl(rtype, rmax) \
uint64_t result; \
if (cx_strtou64_lc(str, &result, base, groupsep)) { \
*output = (rtype) result; \
return 0
-int cx_strtous_lc(cxstring str, unsigned short *output, int base, const char *groupsep) {
+int cx_strtous_lc_(cxstring str, unsigned short *output, int base, const char *groupsep) {
cx_strtoX_unsigned_impl(unsigned short, USHRT_MAX);
}
-int cx_strtou_lc(cxstring str, unsigned int *output, int base, const char *groupsep) {
+int cx_strtou_lc_(cxstring str, unsigned int *output, int base, const char *groupsep) {
cx_strtoX_unsigned_impl(unsigned int, UINT_MAX);
}
-int cx_strtoul_lc(cxstring str, unsigned long *output, int base, const char *groupsep) {
+int cx_strtoul_lc_(cxstring str, unsigned long *output, int base, const char *groupsep) {
cx_strtoX_unsigned_impl(unsigned long, ULONG_MAX);
}
-int cx_strtoull_lc(cxstring str, unsigned long long *output, int base, const char *groupsep) {
+int cx_strtoull_lc_(cxstring str, unsigned long long *output, int base, const char *groupsep) {
// some sanity checks
- str = cx_strtrim(str);
if (str.length == 0) {
errno = EINVAL;
return -1;
return 0;
}
-int cx_strtou8_lc(cxstring str, uint8_t *output, int base, const char *groupsep) {
+int cx_strtou8_lc_(cxstring str, uint8_t *output, int base, const char *groupsep) {
cx_strtoX_unsigned_impl(uint8_t, UINT8_MAX);
}
-int cx_strtou16_lc(cxstring str, uint16_t *output, int base, const char *groupsep) {
+int cx_strtou16_lc_(cxstring str, uint16_t *output, int base, const char *groupsep) {
cx_strtoX_unsigned_impl(uint16_t, UINT16_MAX);
}
-int cx_strtou32_lc(cxstring str, uint32_t *output, int base, const char *groupsep) {
+int cx_strtou32_lc_(cxstring str, uint32_t *output, int base, const char *groupsep) {
cx_strtoX_unsigned_impl(uint32_t, UINT32_MAX);
}
-int cx_strtou64_lc(cxstring str, uint64_t *output, int base, const char *groupsep) {
+int cx_strtou64_lc_(cxstring str, uint64_t *output, int base, const char *groupsep) {
assert(sizeof(unsigned long long) == sizeof(uint64_t)); // should be true on all platforms
return cx_strtoull_lc(str, (unsigned long long*) output, base, groupsep);
}
-int cx_strtouz_lc(cxstring str, size_t *output, int base, const char *groupsep) {
+int cx_strtoz_lc_(cxstring str, size_t *output, int base, const char *groupsep) {
#if SIZE_MAX == UINT32_MAX
- return cx_strtou32_lc(str, (uint32_t*) output, base, groupsep);
+ return cx_strtou32_lc_(str, (uint32_t*) output, base, groupsep);
#elif SIZE_MAX == UINT64_MAX
- return cx_strtoull_lc(str, (unsigned long long *) output, base, groupsep);
+ return cx_strtoull_lc_(str, (unsigned long long *) output, base, groupsep);
#else
#error "unsupported size_t size"
#endif
}
-int cx_strtof_lc(cxstring str, float *output, char decsep, const char *groupsep) {
+int cx_strtof_lc_(cxstring str, float *output, char decsep, const char *groupsep) {
// use string to double and add a range check
double d;
- int ret = cx_strtod_lc(str, &d, decsep, groupsep);
+ int ret = cx_strtod_lc_(str, &d, decsep, groupsep);
if (ret != 0) return ret;
// note: FLT_MIN is the smallest POSITIVE number that can be represented
double test = d < 0 ? -d : d;
return 0;
}
-int cx_strtod_lc(cxstring str, double *output, char decsep, const char *groupsep) {
+static bool str_isdigit(char c) {
+ // TODO: remove once UCX has public API for this
+ return c >= '0' && c <= '9';
+}
+
+int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupsep) {
// TODO: overflow check
// TODO: increase precision
- // trim and check
- str = cx_strtrim(str);
+ // emptiness check
if (str.length == 0) {
errno = EINVAL;
return -1;
// parse all digits until we find the decsep
size_t pos = 0;
do {
- if (isdigit(str.ptr[pos])) {
+ if (str_isdigit(str.ptr[pos])) {
result = result * 10 + (str.ptr[pos] - '0');
} else if (strchr(groupsep, str.ptr[pos]) == NULL) {
break;
// parse everything until exponent or end
double factor = 1.;
do {
- if (isdigit(str.ptr[pos])) {
+ if (str_isdigit(str.ptr[pos])) {
factor *= 0.1;
result = result + factor * (str.ptr[pos] - '0');
} else if (strchr(groupsep, str.ptr[pos]) == NULL) {
// parse the exponent
unsigned int exp = 0;
do {
- if (isdigit(str.ptr[pos])) {
+ if (str_isdigit(str.ptr[pos])) {
exp = 10 * exp + (str.ptr[pos] - '0');
} else if (strchr(groupsep, str.ptr[pos]) == NULL) {
errno = EINVAL;
static size_t cx_tree_default_insert_many(
CxTree *tree,
- struct cx_iterator_base_s *iter,
+ CxIteratorBase *iter,
size_t n
) {
size_t ins = 0;
+++ /dev/null
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2021 Mike Becker, 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 "cx/utils.h"
-
-#ifndef CX_STREAM_BCOPY_BUF_SIZE
-#define CX_STREAM_BCOPY_BUF_SIZE 8192
-#endif
-
-#ifndef CX_STREAM_COPY_BUF_SIZE
-#define CX_STREAM_COPY_BUF_SIZE 1024
-#endif
-
-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
-) {
- if (n == 0) {
- return 0;
- }
-
- char *lbuf;
- size_t ncp = 0;
-
- if (buf) {
- if (bufsize == 0) return 0;
- lbuf = buf;
- } else {
- if (bufsize == 0) bufsize = CX_STREAM_BCOPY_BUF_SIZE;
- lbuf = malloc(bufsize);
- if (lbuf == NULL) {
- return 0;
- }
- }
-
- size_t r;
- size_t rn = bufsize > n ? n : bufsize;
- while ((r = rfnc(lbuf, 1, rn, src)) != 0) {
- r = wfnc(lbuf, 1, r, dest);
- ncp += r;
- n -= r;
- rn = bufsize > n ? n : bufsize;
- if (r == 0 || n == 0) {
- break;
- }
- }
-
- if (lbuf != buf) {
- free(lbuf);
- }
-
- return ncp;
-}
-
-size_t cx_stream_ncopy(
- void *src,
- void *dest,
- cx_read_func rfnc,
- cx_write_func wfnc,
- size_t n
-) {
- char buf[CX_STREAM_COPY_BUF_SIZE];
- return cx_stream_bncopy(src, dest, rfnc, wfnc,
- buf, CX_STREAM_COPY_BUF_SIZE, n);
-}
-
-#ifndef CX_SZMUL_BUILTIN
-#include "szmul.c"
-#endif
#include <cx/mempool.h>
static UiObject* create_window(const char *title, BOOL simple) {
- CxMempool *mp = cxBasicMempoolCreate(256);
+ CxMempool *mp = cxMempoolCreateSimple(256);
UiObject *obj = cxCalloc(mp->allocator, 1, sizeof(UiObject));
obj->ref = 0;
static UiContext* global_context;
void uic_init_global_context(void) {
- CxMempool *mp = cxBasicMempoolCreate(32);
+ CxMempool *mp = cxMempoolCreateSimple(32);
global_context = uic_context(NULL, mp);
}
ctx->obj = toplevel;
ctx->vars = cxHashMapCreate(mp->allocator, CX_STORE_POINTERS, 16);
- ctx->documents = cxLinkedListCreate(mp->allocator, cx_cmp_intptr, CX_STORE_POINTERS);
+ ctx->documents = cxLinkedListCreate(mp->allocator, cx_cmp_ptr, CX_STORE_POINTERS);
ctx->group_widgets = cxLinkedListCreate(mp->allocator, cx_cmp_ptr, sizeof(UiGroupWidget));
ctx->groups = cxArrayListCreate(mp->allocator, cx_cmp_int, sizeof(int), 32);
UiContext *var_ctx = ctx;
while(var_ctx) {
if(var_ctx->vars_unbound && cxMapSize(var_ctx->vars_unbound) > 0) {
- CxIterator i = cxMapIterator(var_ctx->vars_unbound);
+ CxMapIterator i = cxMapIterator(var_ctx->vars_unbound);
cx_foreach(CxMapEntry*, entry, i) {
printf("attach %s\n", entry->key->data);
UiVar *var = entry->value;
}
static void uic_context_unbind_vars(UiContext *ctx) {
- CxIterator i = cxMapIterator(ctx->vars);
- cx_foreach(CxMapEntry*, entry, i) {
+ CxMapIterator mi = cxMapIterator(ctx->vars);
+ cx_foreach(CxMapEntry*, entry, mi) {
UiVar *var = entry->value;
if(var->from && var->from_ctx) {
uic_save_var2(var);
}
if(ctx->documents) {
- i = cxListIterator(ctx->documents);
+ CxIterator i = cxListIterator(ctx->documents);
cx_foreach(void *, doc, i) {
UiContext *subctx = ui_document_context(doc);
uic_context_unbind_vars(subctx);
void ui_set_group(UiContext *ctx, int group) {
- if(cxListFind(ctx->groups, &group) == -1) {
+ if(!cxListIndexValid(ctx->groups, cxListFind(ctx->groups, &group))) {
cxListAdd(ctx->groups, &group);
}
void* ui_document_new(size_t size) {
CxMempool *mp = cxMempoolCreate(256, NULL);
const CxAllocator *a = mp->allocator;
- UiContext *ctx = cxCalloc(a, 1, sizeof(UiContext));
- ctx->mp = mp;
- ctx->attach_document = uic_context_attach_document;
- ctx->detach_document2 = uic_context_detach_document2;
- ctx->allocator = a;
- ctx->vars = cxHashMapCreate(a, CX_STORE_POINTERS, 16);
+ UiContext *ctx = uic_context(NULL, mp);
void *document = cxCalloc(a, size, 1);
cxMapPut(documents, cx_hash_key(&document, sizeof(void*)), ctx);
void ui_int_set(UiInteger* i, int64_t value) {
- if (i && i->set) {
- i->set(i, value);
+ if (i) {
+ if (i->set) {
+ i->set(i, value);
+ } else {
+ i->value = value;
+ }
}
}
}
void ui_double_set(UiDouble* d, double value) {
- if (d && d->set) {
- d->set(d, value);
+ if (d) {
+ if (d->set) {
+ d->set(d, value);
+ } else {
+ d->value = value;
+ }
}
}
}
void ui_string_set(UiString* s, const char* value) {
- if (s && s->set) {
- s->set(s, value);
+ if (s) {
+ if (s->set) {
+ s->set(s, value);
+ } else {
+ if(s->value.free) {
+ s->value.free(s->value.ptr);
+ }
+ if (value) {
+ s->value.ptr = strdup(value);
+ s->value.free = free;
+ } else {
+ s->value.ptr = NULL;
+ s->value.free = NULL;
+ }
+ }
}
}
}
void ui_text_set(UiText* s, const char* value) {
- if (s && s->set) {
- s->set(s, value);
+ if (s) {
+ if (s->set) {
+ s->set(s, value);
+ } else {
+ if(s->value.free) {
+ s->value.free(s->value.ptr);
+ }
+ if (value) {
+ s->value.ptr = strdup(value);
+ s->value.free = free;
+ } else {
+ s->value.ptr = NULL;
+ s->value.free = NULL;
+ }
+ }
}
}
}
int ucx_properties_store(CxMap *map, FILE *file) {
- CxIterator iter = cxMapIterator(map);
+ CxMapIterator iter = cxMapIterator(map);
cxstring value;
size_t written;
ui_box_container_add(ct->container, item_obj->widget, FALSE);
} else {
// create new widget and object for this list element
- CxMempool *mp = cxBasicMempoolCreate(256);
+ CxMempool *mp = cxMempoolCreateSimple(256);
const CxAllocator *a = mp->allocator;
UiObject *obj = cxCalloc(a, 1, sizeof(UiObject));
obj->ctx = uic_context(obj, mp);
#include "toolkit.h"
#include "container.h"
-#include "../ui/webview.h"
-#ifdef UI_WEBVIEW
+#include "webview.h"
-#include <webkit/webkit.h>
+#ifdef UI_WEBVIEW
UIWIDGET ui_webview_create(UiObject *obj, UiWebviewArgs args) {
UiObject* current = uic_current_obj(obj);
GtkWidget *webview = webkit_web_view_new();
- webkit_web_view_load_uri(WEBKIT_WEB_VIEW(webview), "https://code.unixwork.de");
ui_set_name_and_style(webview, args.name, args.style_class);
+ UiVar *var = uic_widget_var(obj->ctx, current->ctx, args.value, args.varname, UI_VAR_GENERIC);
+ if(var) {
+ WebViewData *data = malloc(sizeof(WebViewData));
+ memset(data, 0, sizeof(WebViewData));
+ data->webview = WEBKIT_WEB_VIEW(webview);
+ WebKitSettings *settings = webkit_web_view_get_settings(data->webview);
+ data->javascript = webkit_settings_get_enable_javascript(settings);
+ data->zoom = webkit_web_view_get_zoom_level(data->webview);
+
+ UiGeneric *value = var->value;
+ value->get = ui_webview_get;
+ value->get_type = ui_webview_get_type;
+ value->set = ui_webview_set;
+ value->obj = data;
+ if(value->value && value->type && !strcmp(value->type, UI_WEBVIEW_OBJECT_TYPE)) {
+ // TODO
+ }
+ }
+
ui_set_widget_groups(obj->ctx, webview, args.groups);
UI_APPLY_LAYOUT1(current, args);
current->container->add(current->container, webview, FALSE);
return webview;
}
+void* ui_webview_get(UiGeneric *g) {
+ return g->value;
+}
+
+const char* ui_webview_get_type(UiGeneric *g) {
+ return UI_WEBVIEW_OBJECT_TYPE;
+}
+
+int ui_webview_set(UiGeneric *g, void *value, const char *type) {
+ if(!type || strcmp(type, UI_WEBVIEW_OBJECT_TYPE)) {
+ return 1;
+ }
+
+ WebViewData *obj = g->obj;
+ if(!obj->webview) {
+ return 1;
+ }
+
+ WebViewData *data = value;
+ if(data->type == WEBVIEW_CONTENT_URL) {
+ webkit_web_view_load_uri(obj->webview, data->uri);
+ } else {
+ if(!data->content) {
+ return 1;
+ }
+
+ GBytes *bytes = g_bytes_new(data->content, data->contentlength);
+ webkit_web_view_load_bytes(obj->webview, bytes, data->mimetype, data->encoding, data->uri);
+ }
+
+ ui_webview_enable_javascript(g, data->javascript);
+ webkit_web_view_set_zoom_level(data->webview, data->zoom);
+
+ return 0;
+}
+
+void ui_webview_load_url(UiGeneric *g, const char *url) {
+ WebViewData data = { .uri = (char*)url, .type = WEBVIEW_CONTENT_URL };
+ g->set(g, &data, UI_WEBVIEW_OBJECT_TYPE);
+}
+
+void ui_webview_load_content(
+ UiGeneric *g,
+ const char *uri,
+ const char *content,
+ size_t contentlength,
+ const char *mimetype,
+ const char *encoding)
+{
+ WebViewData data;
+ data.uri = (char*)uri;
+ data.content = (char*)content;
+ data.contentlength = contentlength;
+ data.mimetype = (char*)mimetype;
+ data.encoding = (char*)encoding;
+ data.type = WEBVIEW_CONTENT_CONTENT;
+ g->set(g, &data, UI_WEBVIEW_OBJECT_TYPE);
+}
+
+void ui_webview_reload(UiGeneric *g) {
+ WebViewData *webview = g->obj;
+ webkit_web_view_reload(webview->webview);
+}
+
+UiBool ui_webview_can_go_back(UiGeneric *g) {
+ WebViewData *webview = g->obj;
+ return webkit_web_view_can_go_back(webview->webview);
+}
+
+UiBool ui_webview_can_go_forward(UiGeneric *g) {
+ WebViewData *webview = g->obj;
+ return webkit_web_view_can_go_forward(webview->webview);
+}
+
+void ui_webview_go_back(UiGeneric *g) {
+ WebViewData *webview = g->obj;
+ webkit_web_view_go_back(webview->webview);
+}
+
+void ui_webview_go_forward(UiGeneric *g) {
+ WebViewData *webview = g->obj;
+ webkit_web_view_go_forward(webview->webview);
+}
+
+const char* ui_webview_get_uri(UiGeneric *g) {
+ WebViewData *webview = g->obj;
+ return webkit_web_view_get_uri(webview->webview);
+}
+
+void ui_webview_enable_javascript(UiGeneric *g, UiBool enable) {
+ WebViewData *webview = g->obj;
+ WebKitSettings *settings = webkit_web_view_get_settings(webview->webview);
+ webkit_settings_set_enable_javascript(settings, enable);
+}
+
+void ui_webview_set_zoom(UiGeneric *g, double zoom) {
+ WebViewData *webview = g->obj;
+ webkit_web_view_set_zoom_level(webview->webview, zoom);
+ webview->zoom = zoom;
+}
+
+double ui_webview_get_zoom(UiGeneric *g) {
+ WebViewData *webview = g->obj;
+ webview->zoom = webkit_web_view_get_zoom_level(webview->webview);
+ return webview->zoom;
+}
+
#endif
#ifndef WEBVIEW_H
#define WEBVIEW_H
+#include "../ui/webview.h"
+#include <webkit/webkit.h>
+
#ifdef __cplusplus
extern "C" {
#endif
+
+enum WebViewDataType {
+ WEBVIEW_CONTENT_URL,
+ WEBVIEW_CONTENT_CONTENT
+};
+
+typedef struct WebViewData {
+ WebKitWebView *webview;
+ char *uri;
+ char *mimetype;
+ char *encoding;
+ char *content;
+ size_t contentlength;
+ enum WebViewDataType type;
+
+ double zoom;
+ UiBool javascript;
+} WebViewData;
-
+void* ui_webview_get(UiGeneric *g);
+const char* ui_webview_get_type(UiGeneric *g);
+int ui_webview_set(UiGeneric *g, void *value, const char *type);
#ifdef __cplusplus
#endif
static UiObject* create_window(const char *title, void *window_data, UiBool sidebar, UiBool simple) {
- CxMempool *mp = cxBasicMempoolCreate(256);
+ CxMempool *mp = cxMempoolCreateSimple(256);
UiObject *obj = cxCalloc(mp->allocator, 1, sizeof(UiObject));
obj->ref = 0;
gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
}
- CxMempool *mp = cxBasicMempoolCreate(256);
+ CxMempool *mp = cxMempoolCreateSimple(256);
UiObject *obj = cxCalloc(mp->allocator, 1, sizeof(UiObject));
obj->ctx = uic_context(obj, mp);
obj->widget = dialog;
static UiObject* create_window(const char *title, void *window_data, Boolean simple) {
- CxMempool *mp = cxBasicMempoolCreate(256);
+ CxMempool *mp = cxMempoolCreateSimple(256);
const CxAllocator *a = mp->allocator;
UiObject *obj = cxCalloc(a, 1, sizeof(UiObject));
obj->ctx = uic_context(obj, mp);
extern "C" {
#endif
+#define UI_WEBVIEW_OBJECT_TYPE "webview"
+
typedef struct UiWebviewArgs {
UiTri fill;
UiBool hexpand;
const char *name;
const char *style_class;
+ UiGeneric *value;
+ const char *varname;
+
const int* groups;
} UiWebviewArgs;
UIWIDGET ui_webview_create(UiObject *obj, UiWebviewArgs args);
+void ui_webview_load_url(UiGeneric *g, const char *url);
+
+void ui_webview_load_content(
+ UiGeneric *g,
+ const char *uri,
+ const char *content,
+ size_t contentlength,
+ const char *mimetype,
+ const char *encoding);
+
+void ui_webview_reload(UiGeneric *g);
+UiBool ui_webview_can_go_back(UiGeneric *g);
+UiBool ui_webview_can_go_forward(UiGeneric *g);
+void ui_webview_go_back(UiGeneric *g);
+void ui_webview_go_forward(UiGeneric *g);
+const char * ui_webview_get_uri(UiGeneric *g);
+void ui_webview_enable_javascript(UiGeneric *g, UiBool enable);
+void ui_webview_set_zoom(UiGeneric *g, double zoom);
+double ui_webview_get_zoom(UiGeneric *g);
#ifdef __cplusplus
}\r
\r
UIEXPORT UiObject* ui_simple_window(const char *title, void *window_data) {\r
- CxMempool* mp = cxBasicMempoolCreate(256);\r
+ CxMempool* mp = cxMempoolCreateSimple(256);\r
UiObject* obj = (UiObject*)cxCalloc(mp->allocator, 1, sizeof(UiObject));\r
\r
obj->ctx = uic_context(obj, mp);\r
return NULL;\r
}\r
\r
- CxMempool* mp = cxBasicMempoolCreate(256);\r
+ CxMempool* mp = cxMempoolCreateSimple(256);\r
UiObject* obj = (UiObject*)cxCalloc(mp->allocator, 1, sizeof(UiObject));\r
\r
obj->ctx = uic_context(obj, mp);\r