# HG changeset patch # User Olaf Wintermann # Date 1682105132 -7200 # Node ID efbd59642577c827351cba92e4548936bac12297 # Parent a569148841ff03ee096525fb2ff8d2ff6d8e7545 ucx 3 update, basic dav commands work, most stuff is still broken diff -r a569148841ff -r efbd59642577 dav/Makefile --- a/dav/Makefile Sun Apr 16 14:12:24 2023 +0200 +++ b/dav/Makefile Fri Apr 21 21:25:32 2023 +0200 @@ -67,14 +67,14 @@ all: ../build/bin/dav ../build/bin/dav-sync ../build/bin/xattrtool -$(DAV_BIN): $(DAV_OBJ) ../build/libidav$(LIB_EXT) +$(DAV_BIN): $(DAV_OBJ) ../build/lib/libidav$(LIB_EXT) $(LD) -o $(DAV_BIN) $(DAV_OBJ) \ - ../build/libidav$(LIB_EXT) ../build/libucx$(LIB_EXT) \ + ../build/lib/libidav$(LIB_EXT) ../build/lib/libucx$(LIB_EXT) \ $(LDFLAGS) $(DAV_LDFLAGS) -$(DAV_SYNC_BIN): $(SYNC_OBJ) ../build/libidav$(LIB_EXT) +$(DAV_SYNC_BIN): $(SYNC_OBJ) ../build/lib/libidav$(LIB_EXT) $(LD) -o $(DAV_SYNC_BIN) $(SYNC_OBJ) \ - ../build/libidav$(LIB_EXT) ../build/libucx$(LIB_EXT) \ + ../build/lib/libidav$(LIB_EXT) ../build/lib/libucx$(LIB_EXT) \ $(LDFLAGS) $(DAV_LDFLAGS) $(XATTRTOOL_BIN): $(XATTR_OBJ) diff -r a569148841ff -r efbd59642577 dav/assistant.c --- a/dav/assistant.c Sun Apr 16 14:12:24 2023 +0200 +++ b/dav/assistant.c Fri Apr 21 21:25:32 2023 +0200 @@ -28,16 +28,17 @@ #include #include +#include -#include -#include +#include +#include #include #include "assistant.h" -char* assistant_getcfg(char *cfgname) { - sstr_t line; +char* assistant_getcfg(const char *cfgname) { + cxmutstr line; char *value = NULL; while(!value) { printf("%s: ", cfgname); @@ -54,8 +55,8 @@ return value; } -char* assistant_getoptcfg(char *cfgname) { - sstr_t line; +char* assistant_getoptcfg(const char *cfgname) { + cxmutstr line; char *value = NULL; while(!value) { printf("%s (optional): ", cfgname); @@ -74,7 +75,7 @@ return value; } -char* assistant_gethiddenoptcfg(char *cfgname) { +char* assistant_gethiddenoptcfg(const char *cfgname) { printf("%s (optional): ", cfgname); fflush(stdout); char *pw = util_password_input(""); @@ -86,8 +87,8 @@ } -char* assistant_getdefcfg(char *cfgname, char *defval) { - sstr_t line; +char* assistant_getdefcfg(const char *cfgname, const char *defval) { + cxmutstr line; char *value = NULL; while(!value) { printf("%s (default: %s): ", cfgname, defval); diff -r a569148841ff -r efbd59642577 dav/assistant.h --- a/dav/assistant.h Sun Apr 16 14:12:24 2023 +0200 +++ b/dav/assistant.h Fri Apr 21 21:25:32 2023 +0200 @@ -37,23 +37,23 @@ * ask for a configuration value with the name cfgname * which must be not empty */ -char* assistant_getcfg(char *cfgname); +char* assistant_getcfg(const char *cfgname); /* * ask for an optional configuration value with the name cfgname */ -char* assistant_getoptcfg(char *cfgname); +char* assistant_getoptcfg(const char *cfgname); /* * ask for an optional configuration value with the name cfgname * with hidden input */ -char* assistant_gethiddenoptcfg(char *cfgname); +char* assistant_gethiddenoptcfg(const char *cfgname); /* * ask for an configuration value with the default value defval */ -char* assistant_getdefcfg(char *cfgname, char *defval); +char* assistant_getdefcfg(const char *cfgname, const char *defval); #ifdef __cplusplus } diff -r a569148841ff -r efbd59642577 dav/config.c --- a/dav/config.c Sun Apr 16 14:12:24 2023 +0200 +++ b/dav/config.c Fri Apr 21 21:25:32 2023 +0200 @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include @@ -61,8 +61,8 @@ #define ENV_HOME getenv("HOME") #endif /* _WIN32 */ -static UcxMap *repos; -static UcxMap *keys; +static CxMap *repos; +static CxMap *keys; static PwdStore *pstore; @@ -104,8 +104,8 @@ int load_config(DavContext *ctx) { context = ctx; // TODO: free the config somewhere - repos = ucx_map_new(16); - keys = ucx_map_new(16); + repos = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); + keys = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); char *pwfile = util_concat_path(ENV_HOME, ".dav/secrets.crypt"); pstore = pwdstore_open(pwfile); @@ -164,10 +164,9 @@ void free_config(void) { if(repos) { - UcxMapIterator i = ucx_map_iterator(repos); - UcxKey k; + CxIterator i = cxMapIteratorValues(repos); Repository *repo; - UCX_MAP_FOREACH(k, repo, i) { + cx_foreach(Repository*, repo, i) { if(repo->default_key) { free(repo->default_key); } @@ -188,10 +187,10 @@ } free(repo); } - ucx_map_free(repos); + cxMapDestroy(repos); } if(keys) { - ucx_map_free(keys); + cxMapDestroy(keys); } } @@ -348,7 +347,7 @@ return 1; } - ucx_map_cstr_put(repos, repo->name, repo); + cxMapPut(repos, cx_hash_key_str(repo->name), repo); return 0; } @@ -426,7 +425,7 @@ key->name = strdup(value); } else if(xstreq(node->name, "file")) { // load key file - sstr_t key_data = load_key_file(value); + cxmutstr key_data = load_key_file(value); if(key_data.length > 0) { key->data = key_data.ptr; key->length = key_data.length; @@ -469,7 +468,7 @@ // add key to context if(!error) { - ucx_map_cstr_put(keys, key->name, key); + cxMapPut(keys, cx_hash_key_str(key->name), key); dav_context_add_key(context, key); } } @@ -486,8 +485,8 @@ } } -sstr_t load_key_file(char *filename) { - sstr_t k; +cxmutstr load_key_file(const char *filename) { + cxmutstr k; k.ptr = NULL; k.length = 0; @@ -597,11 +596,11 @@ return error; } -Repository* get_repository(sstr_t name) { +Repository* get_repository(cxstring name) { if(!name.ptr) { return NULL; } - return ucx_map_sstr_get(repos, name); + return cxMapGet(repos, cx_hash_key(name.ptr, name.length)); } int get_repository_flags(Repository *repo) { @@ -628,11 +627,11 @@ } -Key* get_key(char *name) { +Key* get_key(const char *name) { if(!name) { return NULL; } - return ucx_map_cstr_get(keys, name); + return cxMapGet(keys, cx_hash_key_str(name)); } int add_repository(Repository *repo) { @@ -753,17 +752,17 @@ xmlNodePtr prev = matchedRepoNode->prev; xmlNodePtr next = matchedRepoNode->next; if(prev && prev->type == XML_TEXT_NODE) { - sstr_t content = sstr((char*)prev->content); - sstr_t lf = sstrrchr(content, '\n'); + cxstring content = cx_str((char*)prev->content); + cxstring lf = cx_strrchr(content, '\n'); if(lf.length > 0) { - *lf.ptr = '\0'; - char* newcontent = sstrdup(content).ptr; + content.length = lf.ptr - content.ptr; + char* newcontent = cx_strdup(content).ptr; xmlNodeSetContent(prev, (xmlChar*)newcontent); free(newcontent); } } if(next && next->type == XML_TEXT_NODE) { - sstr_t lf = sstrchr(sstr((char*)next->content), '\n'); + cxstring lf = cx_strchr(cx_str((char*)next->content), '\n'); if(lf.length > 0) { char* newcontent = malloc(lf.length); memcpy(newcontent, lf.ptr+1, lf.length-1); @@ -785,22 +784,16 @@ } int list_repositories(void) { - UcxMapIterator i = ucx_map_iterator(repos); + CxIterator i = cxMapIteratorValues(repos); Repository *repo; - UCX_MAP_FOREACH(key, repo, i) { + cx_foreach(Repository *, repo, i) { printf("%s\n", repo->name); } return 0; } -UcxList* get_repositories(void) { - UcxList *list = NULL; - UcxMapIterator i = ucx_map_iterator(repos); - Repository *repo; - UCX_MAP_FOREACH(key, repo, i) { - list = ucx_list_append(list, repo); - } - return list; +CxIterator get_repositories(void) { + return cxMapIteratorValues(repos); } PwdStore* get_pwdstore(void) { @@ -821,29 +814,29 @@ -Repository* url2repo_s(sstr_t url, char **path) { +Repository* url2repo_s(cxstring url, char **path) { *path = NULL; int s; - if(sstrprefix(url, SC("http://"))) { + if(cx_strprefix(url, CX_STR("http://"))) { s = 7; - } else if(sstrprefix(url, SC("https://"))) { + } else if(cx_strprefix(url, CX_STR("https://"))) { s = 8; } else { s = 1; } // split URL into repository and path - sstr_t r = sstrsubs(url, s); - sstr_t p = sstrchr(r, '/'); - r = sstrsubsl(url, 0, url.length-p.length); + cxstring r = cx_strsubs(url, s); + cxstring p = cx_strchr(r, '/'); + r = cx_strsubsl(url, 0, url.length-p.length); if(p.length == 0) { - p = sstrn("/", 1); + p = cx_strn("/", 1); } Repository *repo = get_repository(r); if(repo) { - *path = sstrdup(p).ptr; + *path = cx_strdup(p).ptr; } else { // TODO: who is responsible for freeing this repository? // how can the callee know, if he has to call free()? @@ -853,17 +846,17 @@ repo->verification = true; repo->authmethods = CURLAUTH_BASIC; if(url.ptr[url.length-1] == '/') { - repo->url = sstrdup(url).ptr; + repo->url = cx_strdup(url).ptr; *path = strdup("/"); - } else if (sstrchr(url, '/').length > 0) { + } else if (cx_strchr(url, '/').length > 0) { // TODO: fix the following workaround after // fixing the inconsistent behavior of util_url_*() repo->url = util_url_base_s(url); - sstr_t truncated = sstrdup(url); + cxmutstr truncated = cx_strdup(url); *path = strdup(util_url_path(truncated.ptr)); free(truncated.ptr); } else { - repo->url = sstrdup(url).ptr; + repo->url = cx_strdup(url).ptr; *path = strdup("/"); } } @@ -871,8 +864,8 @@ return repo; } -Repository* url2repo(char *url, char **path) { - return url2repo_s(sstr(url), path); +Repository* url2repo(const char *url, char **path) { + return url2repo_s(cx_str(url), path); } static int decrypt_secrets(CmdArgs *a, PwdStore *secrets) { @@ -882,7 +875,7 @@ char *ps_password = NULL; if(secrets->unlock_cmd && strlen(secrets->unlock_cmd) > 0) { - UcxBuffer *cmd_out = ucx_buffer_new(NULL, 128, UCX_BUFFER_AUTOEXTEND); + CxBuffer *cmd_out = cxBufferCreate(NULL, 128, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); if(!util_exec_command(secrets->unlock_cmd, cmd_out)) { // command successful, get first line from output without newline // and use that as password for the secretstore @@ -899,7 +892,7 @@ ps_password[len] = 0; } } - ucx_buffer_free(cmd_out); + cxBufferFree(cmd_out); } if(!ps_password) { @@ -977,47 +970,48 @@ * The list secrets->location contains urls or repo names as * location strings. We need a list, that contains only urls */ - UcxList *locations = NULL; - UCX_FOREACH(elm, secrets->locations) { - PwdIndexEntry *e = elm->data; - - UCX_FOREACH(loc, e->locations) { + CxList *locations = cxLinkedListCreate(cxDefaultAllocator, (cx_compare_func)cmp_url_cred_entry, CX_STORE_POINTERS); + locations->simple_destructor = (cx_destructor_func)free_cred_location; + CxIterator i = cxListIterator(secrets->locations); + cx_foreach(PwdIndexEntry*, e, i) { + CxIterator entry_iter = cxListIterator(e->locations); + cx_foreach(char *, loc, entry_iter) { char *path; - Repository *r = url2repo(loc->data, &path); + Repository *r = url2repo(loc, &path); CredLocation *urlentry = calloc(1, sizeof(CredLocation)); urlentry->id = e->id; urlentry->location = util_concat_path(r->url, path); - locations = ucx_list_append(locations, urlentry); + cxListAdd(locations, urlentry); } } // the list must be sorted - locations = ucx_list_sort(locations, (cmp_func)cmp_url_cred_entry, NULL); + cxListSort(locations); // create full request url string and remove protocol prefix - sstr_t req_url_proto = sstr(util_concat_path(repo->url, path)); - sstr_t req_url = req_url_proto; - if(sstrprefix(req_url, S("http://"))) { - req_url = sstrsubs(req_url, 7); - } else if(sstrprefix(req_url, S("https://"))) { - req_url = sstrsubs(req_url, 8); + cxmutstr req_url_proto = cx_mutstr(util_concat_path(repo->url, path)); + cxstring req_url = cx_strcast(req_url_proto); + if(cx_strprefix(req_url, CX_STR("http://"))) { + req_url = cx_strsubs(req_url, 7); + } else if(cx_strprefix(req_url, CX_STR("https://"))) { + req_url = cx_strsubs(req_url, 8); } // iterate over sorted locations and check if a location is a prefix // of the requested url char *id = NULL; int ret = 0; - UCX_FOREACH(elm, locations) { - CredLocation *cred = elm->data; - sstr_t cred_url = sstr(cred->location); + i = cxListIterator(locations); + cx_foreach(CredLocation*, cred, i) { + cxstring cred_url = cx_str(cred->location); // remove protocol prefix - if(sstrprefix(cred_url, S("http://"))) { - cred_url = sstrsubs(cred_url, 7); - } else if(sstrprefix(cred_url, S("https://"))) { - cred_url = sstrsubs(cred_url, 8); + if(cx_strprefix(cred_url, CX_STR("http://"))) { + cred_url = cx_strsubs(cred_url, 7); + } else if(cx_strprefix(cred_url, CX_STR("https://"))) { + cred_url = cx_strsubs(cred_url, 8); } - if(sstrprefix(req_url, cred_url)) { + if(cx_strprefix(req_url, cred_url)) { id = cred->id; break; } @@ -1035,8 +1029,7 @@ } free(req_url_proto.ptr); - ucx_list_free_content(locations, (ucx_destructor)free_cred_location); - ucx_list_free(locations); + cxListDestroy(locations); return ret; } diff -r a569148841ff -r efbd59642577 dav/config.h --- a/dav/config.h Sun Apr 16 14:12:24 2023 +0200 +++ b/dav/config.h Fri Apr 21 21:25:32 2023 +0200 @@ -29,7 +29,7 @@ #ifndef CONFIG_H #define CONFIG_H -#include +#include #include #include #include "pwd.h" @@ -81,28 +81,28 @@ int load_repository(const xmlNode *reponode); int load_key(const xmlNode *keynode); int load_proxy(DavProxy*, const xmlNode *proxynode, int type); -sstr_t load_key_file(char *filename); +cxmutstr load_key_file(const char *filename); int load_namespace(const xmlNode *node); int load_secretstore(const xmlNode *node); Repository* repository_new(void); -Repository* get_repository(sstr_t name); +Repository* get_repository(cxstring name); int get_repository_flags(Repository *repo); DavSession *repository_session(Repository *repo); -Key* get_key(char *name); +Key* get_key(const char *name); int add_repository(Repository *repo); int remove_repository(Repository *repo); int list_repositories(void); -UcxList* get_repositories(void); +CxIterator get_repositories(void); PwdStore* get_pwdstore(void); int pwdstore_save(PwdStore *pwdstore); -Repository* url2repo_s(sstr_t url, char **path); -Repository* url2repo(char *url, char **path); +Repository* url2repo_s(cxstring url, char **path); +Repository* url2repo(const char *url, char **path); DavSession* connect_to_repo(DavContext *ctx, Repository *repo, char *path, dav_auth_func authfunc, CmdArgs *a); diff -r a569148841ff -r efbd59642577 dav/db.c --- a/dav/db.c Sun Apr 16 14:12:24 2023 +0200 +++ b/dav/db.c Fri Apr 21 21:25:32 2023 +0200 @@ -33,7 +33,7 @@ #include "db.h" -#include +#include #include @@ -55,8 +55,11 @@ free(dav_dir); SyncDatabase *db = malloc(sizeof(SyncDatabase)); - db->resources = ucx_map_new(2048); - db->conflict = ucx_map_new(16); + db->resources = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 2048); + db->conflict = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); + + db->resources->destructor_data = (cx_destructor_func)local_resource_free; + db->conflict->destructor_data = (cx_destructor_func)local_resource_free; xmlTextReaderPtr reader = xmlReaderForFile(db_file, NULL, 0); if(!reader) { @@ -82,7 +85,7 @@ if(xstreq(name, "resource")) { LocalResource *res = process_resource(reader); if(res) { - ucx_map_cstr_put(db->resources, res->path, res); + cxMapPut(db->resources, cx_hash_key_str(res->path), res); } else { error = 1; break; @@ -90,7 +93,7 @@ } else if(xstreq(name, "conflict")) { LocalResource *res = process_conflict(reader); if(res) { - ucx_map_cstr_put(db->conflict, res->path, res); + cxMapPut(db->conflict, cx_hash_key_str(res->path), res); } else { error = 1; break; @@ -109,7 +112,10 @@ } void process_parts(xmlTextReaderPtr reader, LocalResource *res) { - UcxList *parts = NULL; + // TODO: rewrite using low level array + + CxList *parts = cxLinkedListCreateSimple(CX_STORE_POINTERS); + parts->destructor_data = (cx_destructor_func)filepart_free; FilePart *current_part = NULL; @@ -121,12 +127,11 @@ const xmlChar *name = xmlTextReaderConstName(reader); int depth = xmlTextReaderDepth(reader); - int part = TRUE; if(type == XML_READER_TYPE_ELEMENT) { if(depth == 3 && xstreq(name, "part")) { current_part = calloc(1, sizeof(FilePart)); current_part->block = count; - parts = ucx_list_append(parts, current_part); + cxListAdd(parts, current_part); count++; } else if(depth == 4) { if(xstreq(name, "hash")) { @@ -159,25 +164,21 @@ } } - if(err) { - ucx_list_free_content(parts, (ucx_destructor)filepart_free); - ucx_list_free(parts); - return; - } + if(!err) { + FilePart *file_parts = calloc(count, sizeof(FilePart)); + size_t i = 0; + CxIterator iter = cxListIterator(parts); + cx_foreach(FilePart*, p, iter) { + file_parts[i] = *p; + free(p); + i++; + } + + res->parts = file_parts; + res->numparts = count; + } - FilePart *file_parts = calloc(count, sizeof(FilePart)); - size_t i = 0; - UCX_FOREACH(elm, parts) { - FilePart *p = elm->data; - file_parts[i] = *p; - free(p); - i++; - } - - ucx_list_free(parts); - - res->parts = file_parts; - res->numparts = count; + cxListDestroy(parts); } LocalResource* process_resource(xmlTextReaderPtr reader) { @@ -410,9 +411,9 @@ xmlTextWriterStartElement(writer, BAD_CAST "directory"); // write all resource entries - UcxMapIterator i = ucx_map_iterator(db->resources); + CxIterator i = cxMapIteratorValues(db->resources); LocalResource *res; - UCX_MAP_FOREACH(key, res, i) { + cx_foreach(LocalResource*, res, i) { // xmlTextWriterStartElement(writer, BAD_CAST "resource"); @@ -687,8 +688,8 @@ */ // write all conflict entries - i = ucx_map_iterator(db->conflict); - UCX_MAP_FOREACH(key, res, i) { + i = cxMapIteratorValues(db->conflict); + cx_foreach(LocalResource*, res, i) { // xmlTextWriterStartElement(writer, BAD_CAST "conflict"); @@ -720,10 +721,8 @@ } void destroy_db(SyncDatabase *db) { - ucx_map_free_content(db->resources, (ucx_destructor)local_resource_free); - ucx_map_free_content(db->conflict, (ucx_destructor)local_resource_free); - ucx_map_free(db->resources); - ucx_map_free(db->conflict); + cxMapDestroy(db->resources); + cxMapDestroy(db->conflict); free(db); } @@ -741,7 +740,7 @@ free(res->etag); } if(res->cached_tags) { - ucx_buffer_free(res->cached_tags); + cxBufferFree(res->cached_tags); } if(res->tags_hash) { free(res->tags_hash); @@ -790,10 +789,10 @@ xattr->hash = nullstrdup(src->xattr->hash); xattr->nattr = src->xattr->nattr; xattr->names = calloc(xattr->nattr, sizeof(char*)); - xattr->values = calloc(xattr->nattr, sizeof(sstr_t)); + xattr->values = calloc(xattr->nattr, sizeof(cxmutstr)); for(int i=0;inattr;i++) { xattr->names[i] = strdup(src->xattr->names[i]); - xattr->values[i] = sstrdup(src->xattr->values[i]); + xattr->values[i] = cx_strdup(cx_strcast(src->xattr->values[i])); } newres->xattr = xattr; } @@ -825,15 +824,14 @@ free(part); } -UcxMap* create_hash_index(SyncDatabase *db) { - UcxMap *hmap = ucx_map_new(db->resources->count + 64); +CxMap* create_hash_index(SyncDatabase *db) { + CxMap *hmap = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, db->resources->size + 64); - UcxMapIterator i = ucx_map_iterator(db->resources); - UcxKey key; + CxIterator i = cxMapIteratorValues(db->resources); LocalResource *res; - UCX_MAP_FOREACH(key, res, i) { + cx_foreach(LocalResource*, res, i) { if(res->hash) { - ucx_map_cstr_put(hmap, res->hash, res); + cxMapPut(hmap, cx_hash_key_str(res->hash), res); } } diff -r a569148841ff -r efbd59642577 dav/db.h --- a/dav/db.h Sun Apr 16 14:12:24 2023 +0200 +++ b/dav/db.h Fri Apr 21 21:25:32 2023 +0200 @@ -31,8 +31,9 @@ #include #include -#include -#include +#include +#include +#include #include #include @@ -62,7 +63,7 @@ off_t size; DavBool isdirectory; DavBool skipped; - UcxBuffer *cached_tags; + CxBuffer *cached_tags; XAttributes *xattr; char *tags_hash; char *xattr_hash; @@ -103,8 +104,8 @@ }; struct SyncDatabase { - UcxMap *resources; - UcxMap *conflict; + CxMap *resources; + CxMap *conflict; }; SyncDatabase* load_db(char *name); @@ -119,7 +120,7 @@ void filepart_free(FilePart *part); -UcxMap* create_hash_index(SyncDatabase *db); +CxMap* create_hash_index(SyncDatabase *db); LocalResource* process_resource(xmlTextReaderPtr reader); LocalResource* process_conflict(xmlTextReaderPtr reader); diff -r a569148841ff -r efbd59642577 dav/error.c --- a/dav/error.c Sun Apr 16 14:12:24 2023 +0200 +++ b/dav/error.c Fri Apr 21 21:25:32 2023 +0200 @@ -33,7 +33,7 @@ #include "error.h" -void print_resource_error(DavSession *sn, char *path) { +void print_resource_error(DavSession *sn, const char *path) { char *res_url = util_concat_path(sn->base_url, path); switch(sn->error) { default: { diff -r a569148841ff -r efbd59642577 dav/error.h --- a/dav/error.h Sun Apr 16 14:12:24 2023 +0200 +++ b/dav/error.h Fri Apr 21 21:25:32 2023 +0200 @@ -35,7 +35,7 @@ extern "C" { #endif -void print_resource_error(DavSession *sn, char *path); +void print_resource_error(DavSession *sn, const char *path); #ifdef __cplusplus diff -r a569148841ff -r efbd59642577 dav/finfo.c --- a/dav/finfo.c Sun Apr 16 14:12:24 2023 +0200 +++ b/dav/finfo.c Fri Apr 21 21:25:32 2023 +0200 @@ -34,45 +34,42 @@ #include #include #include +#include -#include -#include +#include +#include +#include #include #include #include "libxattr.h" uint32_t parse_finfo_settings(const char *str, char **error) { - scstr_t s = scstr(str); + cxstring s = cx_str(str); + + if(!cx_strcmp(s, CX_STR("*")) || !cx_strcmp(s, CX_STR("a")) || !cx_strcmp(s, CX_STR("all"))) { + return FINFO_MTIME|FINFO_OWNER|FINFO_MODE|FINFO_XATTR; + } - if(!sstrcmp(s, SC("*")) || !sstrcmp(s, SC("a")) || !sstrcmp(s, SC("all"))) { - return FINFO_MTIME|FINFO_OWNER|FINFO_MODE|FINFO_XATTR; + CxStrtokCtx fs = cx_strtok(s, CX_STR(","), INT_MAX); + cxstring f; + uint32_t finfo = 0; + char *err = NULL; + while(cx_strtok_next(&fs, &f)) { + if(!cx_strcasecmp(f, CX_STR("mtime"))) { + finfo |= FINFO_MTIME; + } else if(!cx_strcasecmp(f, CX_STR("owner"))) { + finfo |= FINFO_OWNER; + } else if(!cx_strcasecmp(f, CX_STR("mode"))) { + finfo |= FINFO_MODE; + } else if(!cx_strcasecmp(f, CX_STR("xattr"))) { + finfo |= FINFO_XATTR; + } else if(error && !err) { + err = cx_strdup(f).ptr; + continue; + } } - ssize_t count = 0; - sstr_t *fs = sstrsplit(s, SC(","), &count); - - char *err = NULL; - - uint32_t finfo = 0; - for(int i=0;inames = calloc(x_names_alloc, sizeof(char*)); + attributes->values = calloc(x_values_alloc, sizeof(cxmutstr)); + struct cx_array_reallocator_s re = { .realloc = array_realloc }; + size_t count = 0; char *hash = NULL; @@ -145,7 +158,16 @@ } else if(!strcmp(node->name, "xattr")) { char *xattr_name = dav_xml_get_attr(node, "name"); if(xattr_name) { - names = ucx_list_append(names, strdup(xattr_name)); + char *xname = strdup(xattr_name); + cx_array_copy( + (void**)&attributes->names, + &x_names_size, + &x_names_alloc, + count, + &xname, + sizeof(void*), + 1, + &re); char *text = dav_xml_getstring(node->children); if(!text) { @@ -155,11 +177,19 @@ int len = 0; char *val = util_base64decode_len(text, &len); - sstr_t *value = malloc(sizeof(sstr_t)); - value->ptr = val; - value->length = len; + cxmutstr value; + value.ptr = val; + value.length = len; - values = ucx_list_append(values, value); + cx_array_copy( + (void**)&attributes->values, + &x_values_size, + &x_values_alloc, + count, + &value, + sizeof(cxmutstr), + 1, + &re); count++; } @@ -167,24 +197,16 @@ } } - XAttributes *attributes = NULL; - if(count > 0) { - attributes = calloc(1, sizeof(XAttributes)); - attributes->hash = hash ? strdup(hash) : NULL; - attributes->nattr = count; - attributes->names = calloc(count, sizeof(char*)); - attributes->values = calloc(count, sizeof(sstr_t)); - int i=0; - UCX_FOREACH(elm, names) { - attributes->names[i] = elm->data; - i++; - } - i=0; - UCX_FOREACH(elm, values) { - attributes->values[i] = *(sstr_t*)elm->data; - i++; - } - } + if(count == 0) { + free(attributes->names); + free(attributes->values); + free(attributes); + return NULL; + } + + attributes->hash = hash ? strdup(hash) : NULL; + attributes->nattr = count; + return attributes; } @@ -202,7 +224,7 @@ XAttributes *xattr = malloc(sizeof(XAttributes)); xattr->nattr = 0; xattr->names = calloc(nelm, sizeof(char*)); - xattr->values = calloc(nelm, sizeof(sstr_t)); + xattr->values = calloc(nelm, sizeof(cxmutstr)); DAV_SHA_CTX *sha256 = dav_hash_init(); @@ -223,7 +245,7 @@ dav_hash_update(sha256, value, valuelen); // add name and value xattr->names[nattr] = attributes[i]; - sstr_t v; + cxmutstr v; v.ptr = value; v.length = valuelen; xattr->values[nattr] = v; @@ -264,7 +286,7 @@ last->next = attr; last = attr; - sstr_t value = xattr->values[i]; + cxmutstr value = xattr->values[i]; if(value.length > 0) { char *encval = util_base64encode(value.ptr, value.length); attr->children = dav_xml_createtextnode(encval); diff -r a569148841ff -r efbd59642577 dav/finfo.h --- a/dav/finfo.h Sun Apr 16 14:12:24 2023 +0200 +++ b/dav/finfo.h Fri Apr 21 21:25:32 2023 +0200 @@ -33,6 +33,8 @@ #include #include +#include + #include "system.h" #ifdef __cplusplus @@ -45,21 +47,21 @@ #define FINFO_XATTR 8 typedef struct XAttributes { - size_t nattr; - char **names; - sstr_t *values; - char *hash; + size_t nattr; + char **names; + cxmutstr *values; + char *hash; } XAttributes; typedef struct FileInfo { - time_t last_modified; - mode_t mode; - uid_t uid; - gid_t gid; - DavBool date_set; - DavBool mode_set; - DavBool uid_set; - DavBool gid_set; + time_t last_modified; + mode_t mode; + uid_t uid; + gid_t gid; + DavBool date_set; + DavBool mode_set; + DavBool uid_set; + DavBool gid_set; } FileInfo; typedef int(*xattr_filter_func)(const char*,void*); diff -r a569148841ff -r efbd59642577 dav/main.c --- a/dav/main.c Sun Apr 16 14:12:24 2023 +0200 +++ b/dav/main.c Fri Apr 21 21:25:32 2023 +0200 @@ -38,8 +38,11 @@ #ifndef _WIN32 #include #endif -#include -#include +#include +#include +#include +#include +#include #include #include @@ -160,7 +163,7 @@ } else if(!strcasecmp(cmd, "get")) { ret = cmd_get(args, FALSE); } else if(!strcasecmp(cmd, "cat")) { - ucx_map_cstr_put(args->options, "output", "-"); + cxMapPut(args->options, cx_hash_key_str("output"), "-"); ret = cmd_get(args, FALSE); } else if(!strcasecmp(cmd, "edit")) { ret = cmd_edit(args); @@ -274,14 +277,14 @@ }; char* find_usage_str(const char *cmd) { - scstr_t c = scstr(cmd); + cxstring c = cx_str(cmd); for(int i=0;;i++) { char *str = cmdusageinfo[i]; if(!str) { break; } - scstr_t u = scstr(str); - if(sstrprefix(u, c)) { + cxstring u = cx_str(str); + if(cx_strprefix(u, c)) { return str; } } @@ -591,7 +594,7 @@ return -1; } if(export) { - ucx_map_cstr_put(a->options, "recursive", ""); + cxMapPut(a->options, cx_hash_key_str("recursive"), ""); } char *url = a->argv[0]; @@ -694,7 +697,8 @@ } // get list of resources - UcxList *reslist = NULL; + CxList *reslist = cxLinkedListCreateSimple(CX_STORE_POINTERS); + reslist->simple_destructor = (cx_destructor_func)free_getres; uint64_t totalsize = 0; uint64_t rescount = 0; @@ -705,10 +709,11 @@ char *structure = cmd_getoption(a, "structure"); // iterate over resource tree - UcxList *stack = ucx_list_prepend(NULL, getres); - while(stack) { - GetResource *g = stack->data; - stack = ucx_list_remove(stack, stack); + CxList *stack = cxLinkedListCreateSimple(CX_STORE_POINTERS); + cxListInsert(stack, 0, getres); + while(stack->size > 0) { + GetResource *g = cxListAt(stack, 0); + cxListRemove(stack, 0); if(g->res->iscollection) { DavResource *child = g->res->children; @@ -720,7 +725,7 @@ newres->path = pathlen > 0 ? util_concat_path(g->path, child->name) : strdup(child->name); - stack = ucx_list_prepend(stack, newres); + cxListInsert(stack, 0, newres); child = child->next; } @@ -737,9 +742,10 @@ if(strlen(g->path) == 0) { free_getres(g); } else { - reslist = ucx_list_append(reslist, g); + cxListAdd(reslist, g); } } + cxListDestroy(stack); // download resources pdata.total = totalsize; @@ -758,9 +764,8 @@ } else { get = get_resource; } - UCX_FOREACH(elm, reslist) { - GetResource *getres = elm->data; - + CxIterator i = cxListIterator(reslist); + cx_foreach(GetResource *, getres, i) { ret = get(repo, getres, a, tout); if(ret) { break; @@ -774,8 +779,7 @@ } } - ucx_list_free_content(reslist, free_getres); - ucx_list_free(reslist); + cxListDestroy(reslist); free(path); if(pdata.out && !pdata.isstdout) { @@ -1135,7 +1139,7 @@ } if(import) { - ucx_map_cstr_put(a->options, "resursive", ""); + cxMapPut(a->options, cx_hash_key_str("resursive"), ""); } char *url = a->argv[0]; @@ -1158,7 +1162,7 @@ if(a->argc > 2) { printfile = TRUE; ignoredirerr = TRUE; - } else if(ucx_map_cstr_get(a->options, "recursive")) { + } else if(cmd_getoption(a, "recursive")) { printfile = TRUE; } @@ -1289,7 +1293,7 @@ fprintf(stderr, "cannot open input file\n"); return -1; } - char *filename = util_resource_name(file); + const char *filename = util_resource_name(file); //path = util_concat_path(path, filename); ret = put_file(repo, a, sn, path, filename, finfo, file, in, s.st_size); //free(path); @@ -1379,8 +1383,8 @@ Repository *repo, CmdArgs *a, DavSession *sn, - char *path, - char *name, + const char *path, + const char *name, uint32_t finfo, const char *fpath, FILE *in, @@ -1695,11 +1699,10 @@ char **date_str = (char**)data; //printf("header: %.*s\n", s*n, header); - sstr_t h = sstrn(header, s*n); - if(sstrprefix(h, S("Date:"))) { - sstr_t v = sstrsubs(h, 5); - v = sstrdup(sstrtrim(v)); - *date_str = v.ptr; + cxstring h = cx_strn(header, s*n); + if(cx_strprefix(h, CX_STR("Date:"))) { + cxstring v = cx_strsubs(h, 5); + *date_str = cx_strdup(cx_strtrim(v)).ptr; } return s*n; } @@ -1926,7 +1929,7 @@ char *path = NULL; Repository *repo = url2repo(url, &path); DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a); - ucx_mempool_reg_destr(sn->mp, path, free); + util_regdestr(sn->mp, path, free); if(set_session_config(sn, a)) { return -1; @@ -1935,7 +1938,7 @@ time_t timeout = 0; char *timeoutstr = cmd_getoption(a, "timeout"); if(timeoutstr) { - if(!sstrcasecmp(sstr(timeoutstr), S("infinite"))) { + if(!cx_strcasecmp(cx_str(timeoutstr), CX_STR("infinite"))) { timeout = -1; } else { uint64_t i; @@ -1970,20 +1973,21 @@ } static char* read_line() { - UcxBuffer *buf = ucx_buffer_new(NULL, 128, UCX_BUFFER_AUTOEXTEND); + CxBuffer buf; + cxBufferInit(&buf, NULL, 128, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); int c; while((c = getchar()) != EOF) { if(c == '\n') { break; } - ucx_buffer_putc(buf, c); + cxBufferPut(&buf, c); } char *str = NULL; - sstr_t line = sstrtrim(sstrn(buf->space, buf->size)); + cxstring line = cx_strtrim(cx_strn(buf.space, buf.size)); if(line.length != 0) { - str = sstrdup(line).ptr; + str = cx_strdup(line).ptr; } - ucx_buffer_free(buf); + cxBufferDestroy(&buf); return str; } @@ -1998,7 +2002,7 @@ char *path = NULL; Repository *repo = url2repo(url, &path); DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a); - ucx_mempool_reg_destr(sn->mp, path, free); + util_regdestr(sn->mp, path, free); if(set_session_config(sn, a)) { return -1; } @@ -2109,7 +2113,7 @@ DavXmlNode *xval = dav_get_property_ns(res, p.ns, p.name); if(dav_xml_isstring(xval)) { - sstr_t value = sstr(dav_xml_getstring(xval)); + cxstring value = cx_str(dav_xml_getstring(xval)); printf(" %s: %.*s\n", p.name, (int)value.length, value.ptr); } else { // find some xml elements @@ -2319,26 +2323,25 @@ char* stdin2str() { - UcxBuffer *buf = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND); - size_t size = ucx_stream_copy(stdin, buf, fread, ucx_buffer_write); + CxBuffer buf; + cxBufferInit(&buf, NULL, 1024, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); + size_t size = cx_stream_copy(stdin, &buf, (cx_read_func)fread, (cx_write_func)cxBufferWrite); if(size == 0) { - ucx_buffer_free(buf); + cxBufferDestroy(&buf); return NULL; } else { - ucx_buffer_putc(buf, '\0'); - char *str = buf->space; - free(buf); - return str; + cxBufferPut(&buf, '\0'); + return buf.space; } } -static void xml2str_i(DavXmlNode *node, UcxBuffer *buf, int indent) { +static void xml2str_i(DavXmlNode *node, CxBuffer *buf, int indent) { while(node) { if(node->type == DAV_XML_ELEMENT) { if(node->children) { if(dav_xml_isstring(node->children)) { - sstr_t s = sstrtrim(sstr(dav_xml_getstring(node->children))); - ucx_bprintf( + cxstring s = cx_strtrim(cx_str(dav_xml_getstring(node->children))); + cx_bprintf( buf, "%*s<%s>%.*s\n", indent, @@ -2348,18 +2351,18 @@ s.ptr, node->name); } else { - ucx_bprintf(buf, "%*s<%s>\n", indent, "", node->name); + cx_bprintf(buf, "%*s<%s>\n", indent, "", node->name); xml2str_i(node->children, buf, indent+2); - ucx_bprintf(buf, "%*s\n", indent, "", node->name); + cx_bprintf(buf, "%*s\n", indent, "", node->name); } } else { - ucx_bprintf(buf, "%*s<%s />", indent, "", node->name); - ucx_buffer_putc(buf, '\n'); + cx_bprintf(buf, "%*s<%s />", indent, "", node->name); + cxBufferPut(buf, '\n'); } } else if(node->type == DAV_XML_TEXT) { - sstr_t val = sstrtrim(sstrn(node->content, node->contentlength)); + cxstring val = cx_strtrim(cx_strn(node->content, node->contentlength)); if(val.length > 0) { - ucx_bprintf(buf, "%*.*s", indent, (int)val.length, val.ptr); + cx_bprintf(buf, "%*.*s", indent, (int)val.length, val.ptr); } } @@ -2368,30 +2371,28 @@ } char* xml2str(DavXmlNode *node) { - char *str = malloc(256); - UcxBuffer *buf = ucx_buffer_new(str, 256, UCX_BUFFER_AUTOEXTEND); - xml2str_i(node, buf, 0); - ucx_buffer_putc(buf, 0); - char *space = buf->space; - ucx_buffer_free(buf); - return space; + CxBuffer buf; + cxBufferInit(&buf, NULL, 256, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND); + xml2str_i(node, &buf, 0); + cxBufferPut(&buf, 0); + return buf.space; } void printxmldoc(FILE *out, char *root, char *rootns, DavXmlNode *content) { - UcxMap *nsmap = ucx_map_new(16); + CxMap *nsmap = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); + nsmap->simple_destructor = free; - ucx_map_cstr_put(nsmap, rootns, "x0"); + cxMapPut(nsmap, cx_hash_key_str(rootns), "x0"); fprintf(out, "%s", "\n"); fprintf(out, "", root, rootns); - dav_print_node(out, (write_func)fwrite, nsmap, content); + dav_print_node(out, (cx_write_func)fwrite, nsmap, content); fprintf(out, "\n", root); // cleanup namespace map - ucx_map_cstr_remove(nsmap, rootns); - ucx_map_free_content(nsmap, free); - ucx_map_free(nsmap); + cxMapRemove(nsmap, cx_hash_key_str(rootns)); + cxMapDestroy(nsmap); } @@ -2404,7 +2405,7 @@ fprintf(stderr, "Abort\n"); return -1; } - if(get_repository(sstr(name))) { + if(get_repository(cx_str(name))) { fprintf(stderr, "Repository %s already exists.\nAbort\n", name); return -1; } @@ -2460,7 +2461,7 @@ } for(int i = 0 ; i < args->argc ; i++) { - sstr_t reponame = sstr(args->argv[i]); + cxstring reponame = cx_str(args->argv[i]); Repository* repo = get_repository(reponame); if(repo) { if(remove_repository(repo)) { @@ -2484,16 +2485,16 @@ return -1; } - sstr_t reponame = sstr(args->argv[0]); + cxstring reponame = cx_str(args->argv[0]); Repository* repo = get_repository(reponame); if(repo) { - sstr_t url = sstr(repo->url); + cxstring url = cx_str(repo->url); if(repo->user && !cmd_getoption(args, "plain")) { int hostindex = 0; - if(sstrprefix(url, S("https://"))) { + if(cx_strprefix(url, CX_STR("https://"))) { printf("https://"); hostindex = 8; - } else if(sstrprefix(url, S("http://"))) { + } else if(cx_strprefix(url, CX_STR("http://"))) { printf("http://"); hostindex = 7; } @@ -2616,9 +2617,10 @@ // optionally, get one or more locations char *location = NULL; - UcxList *locations = NULL; + CxList *locations = cxLinkedListCreateSimple(CX_STORE_POINTERS); + locations->simple_destructor = free; while((location = assistant_getoptcfg("Location"))) { - locations = ucx_list_append(locations, location); + cxListAdd(locations, location); } int ret = 1; @@ -2635,8 +2637,7 @@ if(user) free(user); if(password) free(password); - ucx_list_free_content(locations, free); - ucx_list_free(locations); + cxListDestroy(locations); return ret; } @@ -2649,7 +2650,7 @@ * called before the secret store is decrypted */ static int cmd_ss_list_users_bc(CmdArgs *Args, PwdStore *secrets, int *ret) { - if(secrets->index->count == 0) { + if(secrets->index->size == 0) { return 1; // abort, because the secret store is empty } // set ret to 1, because decrypt could fail and this should be an error @@ -2663,24 +2664,28 @@ static int cmd_ss_list_users(CmdArgs *args, PwdStore *secrets, int *ret) { *ret = 0; - UcxList *list = secrets->locations; + CxList *list = secrets->locations; for(int i=0;i<2;i++) { - UCX_FOREACH(elm, list) { - PwdIndexEntry *index = elm->data; - PwdEntry *e = ucx_map_cstr_get(secrets->ids, index->id); - if(e) { - printf("Id: %s\n", e->id); - printf("User: %s\n", e->user); - UCX_FOREACH(loc, index->locations) { - char *location = loc->data; - printf("Location: %s\n", location); + if(list) { + CxIterator i = cxListIterator(list); + cx_foreach(PwdIndexEntry*, index, i) { + PwdEntry *e = cxMapGet(secrets->ids, cx_hash_key_str(index->id)); + if(e) { + printf("Id: %s\n", e->id); + printf("User: %s\n", e->user); + if(index->locations) { + CxIterator loc_iter = cxListIterator(index->locations); + cx_foreach(char *, location, loc_iter) { + printf("Location: %s\n", location); + } + printf("\n"); + } + } else { + // broken index + fprintf(stderr, + "Warning: id '%s' not in secret store.\n", + index->id); } - printf("\n"); - } else { - // broken index - fprintf(stderr, - "Warning: id '%s' not in secret store.\n", - index->id); } } list = secrets->noloc; @@ -2729,28 +2734,32 @@ return; } - PwdIndexEntry *index = ucx_map_cstr_get(secrets->index, id); + PwdIndexEntry *index = cxMapGet(secrets->index, cx_hash_key_str(id)); if(!index) { return; } printf("Id: %s\n", entry->id); printf("User: %s\n", entry->user); - UCX_FOREACH(elm, index->locations) { - printf("Location: %s\n", (char*)elm->data); + if(index->locations) { + CxIterator loc_iter = cxListIterator(index->locations); + cx_foreach(char *, location, loc_iter) { + printf("Location: %s\n", location); + } } } static void secrets_remove_location(PwdIndexEntry *index) { - if(!index->locations) { + if(!index->locations || index->locations->size == 0) { printf("no locations\n"); return; } printf("0: abort\n"); int i = 1; - UCX_FOREACH(elm, index->locations) { - printf("%d: %s\n", i, (char*)elm->data); + CxIterator loc_iter = cxListIterator(index->locations); + cx_foreach(char *, location, loc_iter) { + printf("%d: %s\n", i, location); i++; } @@ -2764,10 +2773,10 @@ if(ln == 0) { return; } else { - UcxList *elm = ucx_list_get(index->locations, ln - 1); - if(elm) { - free(elm->data); - index->locations = ucx_list_remove(index->locations, elm); + char *location = cxListAt(index->locations, ln - 1); + if(location) { + free(location); + cxListRemove(index->locations, ln - 1); } } } else { @@ -2783,7 +2792,7 @@ return 1; } PwdEntry *entry = pwdstore_get(secrets, id); - PwdIndexEntry *index = ucx_map_cstr_get(secrets->index, id); + PwdIndexEntry *index = cxMapGet(secrets->index, cx_hash_key_str(id)); if(!entry || !index) { fprintf(stderr, "Credentials with this id doesn't exist.\n"); return 1; @@ -2837,7 +2846,7 @@ // add location char *location = assistant_getoptcfg("Location"); if(location) { - index->locations = ucx_list_append(index->locations, location); + cxListAdd(index->locations, location); } break; } @@ -2848,11 +2857,12 @@ } case 4: { // list locations - if(!index->locations) { + if(!index->locations || index->locations->size == 0) { printf("no locations\n"); } else { - UCX_FOREACH(elm, index->locations) { - printf("Location: %s\n", (char*)elm->data); + CxIterator i = cxListIterator(index->locations); + cx_foreach(char *, location, i) { + printf("Location: %s\n", location); } } break; @@ -2910,12 +2920,13 @@ static char** read_args_from_stdin(int *argc) { // read stdin into buffer - UcxBuffer *in = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND); - ucx_stream_copy(stdin, in, (read_func)fread, (write_func)ucx_buffer_write); + CxBuffer *in = cxBufferCreate(NULL, 1024, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); + cx_stream_copy(stdin, in, (cx_read_func)fread, (cx_write_func)cxBufferWrite); // split input into lines ssize_t count = 0; - sstr_t *lines = scstrsplit(scstrn(in->space, in->pos), SC("\n"), &count); + cxmutstr *lines; + count = cx_strsplit_ma(cxDefaultAllocator, cx_mutstrn(in->space, in->pos), CX_STR("\n"), INT_MAX, &lines); char **args = NULL; if(count > 0) { @@ -2931,7 +2942,7 @@ } // cleanup - ucx_buffer_free(in); + cxBufferFree(in); return args; } @@ -2997,9 +3008,9 @@ int shell_completion(char *cmd, CmdArgs *args, int index) { if(index == 1) { - sstr_t prefix = { NULL, 0 }; + cxstring prefix = { NULL, 0 }; if(cmd) { - prefix = sstr(cmd); + prefix = cx_str(cmd); } for(int i=0;;i++) { char *str = cmdusageinfo[i]; @@ -3015,7 +3026,7 @@ } } if(prefix.ptr) { - if(!sstrprefix(sstrn(str, maxlen), prefix)) { + if(!cx_strprefix(cx_strn(str, maxlen), prefix)) { continue; } } @@ -3053,7 +3064,7 @@ } int url_completion(CmdArgs *args, char *u) { - sstr_t url; + cxstring url; url.ptr = u; url.length = u ? strlen(u) : 0; @@ -3082,10 +3093,9 @@ } } if(repocomp) { - UcxList *repos = get_repositories(); - UCX_FOREACH(elm, repos) { - Repository *repo = elm->data; - if(sstrprefix(sstr(repo->name), url)) { + CxIterator i = get_repositories(); + cx_foreach(Repository*, repo, i) { + if(cx_strprefix(cx_str(repo->name), url)) { if(quote == '\0') { printf("%s/\n", repo->name); } else { @@ -3096,7 +3106,7 @@ } } else { // url completion - ucx_map_cstr_put(args->options, "noinput", ""); + cxMapPut(args->options, cx_hash_key_str("noinput"), ""); char *path = NULL; Repository *repo = url2repo_s(url, &path); @@ -3110,21 +3120,21 @@ size_t plen = strlen(path); - sstr_t filter; + cxstring filter; char *lspath = NULL; if(path[plen-1] == '/') { lspath = strdup(path); - filter = S(""); + filter = CX_STR(""); } else { lspath = util_parent_path(path); - filter = sstr(util_resource_name(path)); + filter = cx_str(util_resource_name(path)); } DavResource *ls = dav_query(sn, "select - from %s order by name", lspath); DavResource *elm = ls ? ls->children : NULL; while(elm) { - sstr_t name = sstr(elm->name); - if(sstrprefix(name, filter)) { + cxstring name = cx_str(elm->name); + if(cx_strprefix(name, filter)) { int space = 0; for(int i=0;iname); + CxBuffer *out = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); + cxBufferPutString(out, repo->name); if(space) { size_t l = strlen(elm->path); for(int i=0;ipath[i]; if(quote == '\0' && NULL != strchr( "!\"#$&'()*,;<>?[\\]^`{|}~ ", nextc)) { - ucx_buffer_putc(out, '\\'); + cxBufferPut(out, '\\'); } - ucx_buffer_putc(out, nextc); + cxBufferPut(out, nextc); } } else { - ucx_buffer_puts(out, elm->path); + cxBufferPutString(out, elm->path); } if(elm->iscollection) { if(out->space[out->pos-1] != '/') { - ucx_buffer_putc(out, '/'); + cxBufferPut(out, '/'); } } if (quote == '\0') { @@ -3161,7 +3171,7 @@ quote, (int)out->pos, out->space, quote); } - ucx_buffer_free(out); + cxBufferFree(out); } elm = elm->next; } diff -r a569148841ff -r efbd59642577 dav/main.h --- a/dav/main.h Sun Apr 16 14:12:24 2023 +0200 +++ b/dav/main.h Fri Apr 21 21:25:32 2023 +0200 @@ -88,8 +88,8 @@ Repository *repo, CmdArgs *a, DavSession *sn, - char *path, - char *name, + const char *path, + const char *name, uint32_t finfo, const char *fpath, FILE *in, diff -r a569148841ff -r efbd59642577 dav/opt.h --- a/dav/opt.h Sun Apr 16 14:12:24 2023 +0200 +++ b/dav/opt.h Fri Apr 21 21:25:32 2023 +0200 @@ -34,9 +34,9 @@ #endif typedef struct { - UcxMap *options; - char **argv; - int argc; + CxMap *options; + char **argv; + int argc; } CmdArgs; #ifdef __cplusplus diff -r a569148841ff -r efbd59642577 dav/optparser.c --- a/dav/optparser.c Sun Apr 16 14:12:24 2023 +0200 +++ b/dav/optparser.c Fri Apr 21 21:25:32 2023 +0200 @@ -33,9 +33,12 @@ #include "optparser.h" #include "sopt.h" +#include + + void cmd_args_free(CmdArgs *args) { if(args) { - ucx_map_free(args->options); + cxMapDestroy(args->options); if(args->argv) { free(args->argv); } @@ -43,9 +46,13 @@ } } +static void cmd_map_put(CxMap *map, const char *key, const void *value) { + cxMapPut(map, key, (void*)value); +} + CmdArgs* cmd_parse_args(int argc, char **argv) { CmdArgs *a = malloc(sizeof(CmdArgs)); - a->options = ucx_map_new(16); + a->options = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); a->argv = argc > 0 ? calloc(argc, sizeof(char*)) : NULL; a->argc = 0; @@ -71,7 +78,7 @@ for(int c=1;coptions, option, &arg[c]); + cxMapPut(a->options, cx_hash_key_str(option), &arg[c]); option = NULL; break; } @@ -83,7 +90,7 @@ return NULL; } case 'v': { - ucx_map_cstr_put(a->options, "verbose", NOARG); + cmd_map_put(a->options, "verbose", NOARG); break; } case 'k': { @@ -92,51 +99,51 @@ break; } case 'p': { - ucx_map_cstr_put(a->options, "plain", NOARG); + cmd_map_put(a->options, "plain", NOARG); break; } case 'c': { - ucx_map_cstr_put(a->options, "crypt", NOARG); + cmd_map_put(a->options, "crypt", NOARG); break; } case 'a': { - ucx_map_cstr_put(a->options, "all", NOARG); + cmd_map_put(a->options, "all", NOARG); break; } case 'l': { - ucx_map_cstr_put(a->options, "list", NOARG); + cmd_map_put(a->options, "list", NOARG); break; } case 'd': { - ucx_map_cstr_put(a->options, "date", NOARG); + cmd_map_put(a->options, "date", NOARG); break; } case 't': { - ucx_map_cstr_put(a->options, "type", NOARG); + cmd_map_put(a->options, "type", NOARG); break; } case 'R': { - ucx_map_cstr_put(a->options, "recursive", NOARG); + cmd_map_put(a->options, "recursive", NOARG); break; } case 'O': { - ucx_map_cstr_put(a->options, "override", NOARG); + cmd_map_put(a->options, "override", NOARG); break; } case 'i': { - ucx_map_cstr_put(a->options, "insecure", NOARG); + cmd_map_put(a->options, "insecure", NOARG); break; } case 'N': { - ucx_map_cstr_put(a->options, "noinput", NOARG); + cmd_map_put(a->options, "noinput", NOARG); break; } case 'e': { - ucx_map_cstr_put(a->options, "extended", NOARG); + cmd_map_put(a->options, "extended", NOARG); break; } case 'x': { - ucx_map_cstr_put(a->options, "xml", NOARG); + cmd_map_put(a->options, "xml", NOARG); break; } case 'F': { @@ -146,11 +153,11 @@ } case 'S': { // undocumented hidden feature - ucx_map_cstr_put(a->options, "structure", NOARG); + cmd_map_put(a->options, "structure", NOARG); break; } case 'K': { - ucx_map_cstr_put(a->options, "keep", NOARG); + cmd_map_put(a->options, "keep", NOARG); break; } case 'o': { @@ -193,7 +200,7 @@ } } } else if(option) { - ucx_map_cstr_put(a->options, option, arg); + cmd_map_put(a->options, option, arg); option = NULL; } else { a->argv[a->argc++] = arg; @@ -209,6 +216,6 @@ return a; } -char* cmd_getoption(CmdArgs *arg, char *name) { - return ucx_map_cstr_get(arg->options, name); +char* cmd_getoption(CmdArgs *arg, const char *name) { + return cxMapGet(arg->options, cx_hash_key_str(name)); } diff -r a569148841ff -r efbd59642577 dav/optparser.h --- a/dav/optparser.h Sun Apr 16 14:12:24 2023 +0200 +++ b/dav/optparser.h Fri Apr 21 21:25:32 2023 +0200 @@ -29,7 +29,7 @@ #ifndef OPTPARSER_H #define OPTPARSER_H -#include +#include #include "opt.h" #ifdef __cplusplus @@ -37,7 +37,7 @@ #endif CmdArgs* cmd_parse_args(int argc, char **argv); -char* cmd_getoption(CmdArgs *arg, char *name); +char* cmd_getoption(CmdArgs *arg, const char *name); void cmd_args_free(CmdArgs *args); diff -r a569148841ff -r efbd59642577 dav/pwd.c --- a/dav/pwd.c Sun Apr 16 14:12:24 2023 +0200 +++ b/dav/pwd.c Fri Apr 21 21:25:32 2023 +0200 @@ -28,11 +28,13 @@ #include #include +#include #include "pwd.h" -#include -#include +#include +#include +#include #ifdef _WIN32 #include @@ -46,20 +48,20 @@ return NULL; } - UcxBuffer *buf = ucx_buffer_new(NULL, 2048, UCX_BUFFER_AUTOEXTEND); - ucx_stream_copy(in, buf, (read_func)fread, (write_func)ucx_buffer_write); + CxBuffer *buf = cxBufferCreate(NULL, 2048, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); + cx_stream_copy(in, buf, (cx_read_func)fread, (cx_write_func)cxBufferWrite); fclose(in); if(buf->size < PWDS_HEADER_SIZE || buf->space[0] != PWDS_MAGIC_CHAR) { - ucx_buffer_free(buf); + cxBufferFree(buf); return NULL; } PwdStore *p = malloc(sizeof(PwdStore)); - p->ids = ucx_map_new(16); - p->locations = NULL; - p->noloc = NULL; - p->index = ucx_map_new(16); + p->ids = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); + p->locations = cxLinkedListCreateSimple(CX_STORE_POINTERS); + p->noloc = cxLinkedListCreateSimple(CX_STORE_POINTERS); + p->index = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); p->content = buf; p->key = NULL; p->unlock_cmd = NULL; @@ -77,11 +79,11 @@ PwdStore* pwdstore_new(void) { PwdStore *p = calloc(1, sizeof(PwdStore)); - p->ids = ucx_map_new(16); - p->locations = NULL; - p->noloc = NULL; - p->index = ucx_map_new(16); - p->content = ucx_buffer_new(NULL, PWDS_HEADER_SIZE, UCX_BUFFER_AUTOEXTEND); + p->ids = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); + p->locations = cxLinkedListCreateSimple(CX_STORE_POINTERS); + p->noloc = cxLinkedListCreateSimple(CX_STORE_POINTERS); + p->index = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); + p->content = cxBufferCreate(NULL, PWDS_HEADER_SIZE, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); PWDS_MAGIC(p) = PWDS_MAGIC_CHAR; PWDS_VERSION(p) = 1; PWDS_ENC(p) = DAV_KEY_AES256; @@ -92,7 +94,7 @@ return p; } -static int readval(UcxBuffer *in, char **val, int allowzero) { +static int readval(CxBuffer *in, char **val, int allowzero) { // value = length string // length = uint32 // string = bytes @@ -101,7 +103,7 @@ // get length uint32_t length = 0; - if(ucx_buffer_read(&length, 1, sizeof(uint32_t), in) != sizeof(uint32_t)) { + if(cxBufferRead(&length, 1, sizeof(uint32_t), in) != sizeof(uint32_t)) { return 0; } length = ntohl(length); // convert from BE to host byte order @@ -119,7 +121,7 @@ // get value char *value = malloc(length + 1); value[length] = 0; - if(ucx_buffer_read(value, 1, length, in) != length) { + if(cxBufferRead(value, 1, length, in) != length) { free(value); return 0; } @@ -128,16 +130,17 @@ return 1; } -static int read_indexentry(PwdStore *p, UcxBuffer *in) { +static int read_indexentry(PwdStore *p, CxBuffer *in) { // read type of index element - int type = ucx_buffer_getc(in); + int type = cxBufferGet(in); if(type == EOF || type != 0) { // only type 0 supported yet return 0; } char *id = NULL; - UcxList *locations = NULL; + CxList *locations = cxLinkedListCreateSimple(CX_STORE_POINTERS); + locations->simple_destructor = free; // get id (required) int ret = 0; @@ -149,7 +152,7 @@ if(!location) { break; } - locations = ucx_list_append(locations, location); + cxListAdd(locations, location); } } @@ -157,14 +160,14 @@ pwdstore_put_index(p, id, locations); } else { if(id) free(id); - ucx_list_free_content(locations, free); + cxListDestroy(locations); } return ret; } -static int read_pwdentry(PwdStore *p, UcxBuffer *in) { - int type = ucx_buffer_getc(in); +static int read_pwdentry(PwdStore *p, CxBuffer *in) { + int type = cxBufferGet(in); if(type == EOF || type != 0) { // only type 0 supported yet return 0; @@ -196,44 +199,36 @@ static int remove_list_entries(PwdStore *s, const char *id) { int ret = 0; - UcxList *loc_entry = NULL; - UcxList *noloc_entry = NULL; - UCX_FOREACH(elm, s->locations) { - PwdIndexEntry *ie = elm->data; + CxList *loc_entry = NULL; + CxList *noloc_entry = NULL; + + CxMutIterator i = cxListMutIterator(s->locations); + cx_foreach(PwdIndexEntry*, ie, i) { if(!strcmp(ie->id, id)) { - loc_entry = elm; - ret = 1; - break; + cxIteratorFlagRemoval(i); + // TODO: break loop } } - UCX_FOREACH(elm, s->noloc) { - PwdIndexEntry *ie = elm->data; + i = cxListMutIterator(s->noloc); + cx_foreach(PwdIndexEntry*, ie, i) { if(!strcmp(ie->id, id)) { - noloc_entry = elm; - ret = 1; - break; + cxIteratorFlagRemoval(i); + // TODO: break loop } } - if(loc_entry) { - s->locations = ucx_list_remove(s->locations, loc_entry); - } - if(noloc_entry) { - s->noloc = ucx_list_remove(s->noloc, noloc_entry); - } - return ret; } void pwdstore_remove_entry(PwdStore *s, const char *id) { while(remove_list_entries(s, id)) {} - PwdIndexEntry *i = ucx_map_cstr_remove(s->index, id); - PwdEntry *e = ucx_map_cstr_remove(s->ids, id); + CxHashKey key = cx_hash_key_str(id); + PwdIndexEntry *i = cxMapRemoveAndGet(s->index, key); + PwdEntry *e = cxMapRemoveAndGet(s->ids, key); if(i) { - ucx_list_free_content(i->locations, free); - ucx_list_free(i->locations); + cxListDestroy(i->locations); free(i->id); free(i); } @@ -253,7 +248,7 @@ s->content->pos = PWDS_HEADER_SIZE - sizeof(uint32_t); // read indexlen and convert to host byte order - if(ucx_buffer_read(&netindexlen, 1, sizeof(uint32_t), s->content) != sizeof(uint32_t)) { + if(cxBufferRead(&netindexlen, 1, sizeof(uint32_t), s->content) != sizeof(uint32_t)) { return 1; } uint32_t indexlen = ntohl(netindexlen); @@ -269,14 +264,14 @@ s->encoffset = PWDS_HEADER_SIZE + indexlen; // the index starts after the header - UcxBuffer *index = ucx_buffer_new(s->content->space+PWDS_HEADER_SIZE, indexlen, 0); + CxBuffer *index = cxBufferCreate(s->content->space+PWDS_HEADER_SIZE, indexlen, cxDefaultAllocator, 0); index->size = indexlen; // read index while(read_indexentry(s, index)) {} // free index buffer structure (not the content) - ucx_buffer_free(index); + cxBufferFree(index); return 0; } @@ -291,18 +286,18 @@ // decrypt contet size_t encsz = p->content->size - p->encoffset; - UcxBuffer *enc = ucx_buffer_new(p->content->space + p->encoffset, encsz, 0); + CxBuffer *enc = cxBufferCreate(p->content->space + p->encoffset, encsz, cxDefaultAllocator, 0); enc->size = encsz; enc->size = p->content->size - p->encoffset; - UcxBuffer *content = aes_decrypt_buffer(enc, p->key); - ucx_buffer_free(enc); + CxBuffer *content = aes_decrypt_buffer(enc, p->key); + cxBufferFree(enc); if(!content) { return 1; } while(read_pwdentry(p, content)) {} - ucx_buffer_free(content); + cxBufferFree(content); return 0; } @@ -335,24 +330,24 @@ } void pwdstore_free(PwdStore* p) { - ucx_map_free_content(p->ids, (ucx_destructor)pwdstore_free_entry); - ucx_map_free(p->ids); + p->ids->simple_destructor = (cx_destructor_func)pwdstore_free_entry; + cxMapDestroy(p->ids); - ucx_list_free(p->locations); + cxListDestroy(p->locations); if(p->content) { - ucx_buffer_free(p->content); + cxBufferFree(p->content); } free(p); } int pwdstore_has_id(PwdStore *s, const char *id) { - return ucx_map_cstr_get(s->index, id) ? 1 : 0; + return cxMapGet(s->index, cx_hash_key_str(id)) ? 1 : 0; } PwdEntry* pwdstore_get(PwdStore *p, const char *id) { - PwdEntry *e = ucx_map_cstr_get(p->ids, id); + PwdEntry *e = cxMapGet(p->ids, cx_hash_key_str(id)); if(e && e->user && e->password) { return e; } else { @@ -365,11 +360,11 @@ entry->id = strdup(id); entry->user = strdup(username); entry->password = strdup(password); - ucx_map_cstr_put(p->ids, id, entry); + cxMapPut(p->ids, cx_hash_key_str(id), entry); } -void pwdstore_put_index(PwdStore *p, char *id, UcxList *locations) { - PwdIndexEntry *e = ucx_map_cstr_get(p->index, id); +void pwdstore_put_index(PwdStore *p, char *id, CxList *locations) { + PwdIndexEntry *e = cxMapGet(p->index, cx_hash_key_str(id)); if(e) { return; } @@ -377,34 +372,34 @@ newentry->id = id; if(locations) { newentry->locations = locations; - p->locations = ucx_list_append(p->locations, newentry); + cxListAdd(p->locations, newentry); } else { newentry->locations = NULL; - p->noloc = ucx_list_append(p->noloc, newentry); + cxListAdd(p->noloc, newentry); } - ucx_map_cstr_put(p->index, id, newentry); + cxMapPut(p->index, cx_hash_key_str(id), newentry); } -void write_index_entry(UcxBuffer *out, PwdIndexEntry *e) { +void write_index_entry(CxBuffer *out, PwdIndexEntry *e) { uint32_t idlen = strlen(e->id); uint32_t netidlen = htonl(idlen); - ucx_buffer_putc(out, 0); // type + cxBufferPut(out, 0); // type - ucx_buffer_write(&netidlen, 1, sizeof(uint32_t), out); - ucx_buffer_write(e->id, 1, idlen, out); + cxBufferWrite(&netidlen, 1, sizeof(uint32_t), out); + cxBufferWrite(e->id, 1, idlen, out); - UCX_FOREACH(elm, e->locations) { - char *location = elm->data; + CxIterator i = cxListIterator(e->locations); + cx_foreach(char *, location, i) { uint32_t locationlen = strlen(location); uint32_t netlocationlen = htonl(locationlen); - ucx_buffer_write(&netlocationlen, 1, sizeof(uint32_t), out); - ucx_buffer_write(location, 1, locationlen, out); + cxBufferWrite(&netlocationlen, 1, sizeof(uint32_t), out); + cxBufferWrite(location, 1, locationlen, out); } uint32_t terminate = 0; - ucx_buffer_write(&terminate, 1, sizeof(uint32_t), out); + cxBufferWrite(&terminate, 1, sizeof(uint32_t), out); } int pwdstore_store(PwdStore *p, const char *file) { @@ -412,22 +407,21 @@ return 1; } - UcxBuffer *index = ucx_buffer_new(NULL, 2048, UCX_BUFFER_AUTOEXTEND); - UcxBuffer *content = ucx_buffer_new(NULL, 2048, UCX_BUFFER_AUTOEXTEND); + CxBuffer *index = cxBufferCreate(NULL, 2048, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); + CxBuffer *content = cxBufferCreate(NULL, 2048, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); // create index - UCX_FOREACH(elm, p->noloc) { - PwdIndexEntry *e = elm->data; + CxIterator i = cxListIterator(p->noloc); + cx_foreach(PwdIndexEntry*, e, i) { write_index_entry(index, e); } - UCX_FOREACH(elm, p->locations) { - PwdIndexEntry *e = elm->data; + i = cxListIterator(p->locations); + cx_foreach(PwdIndexEntry*, e, i) { write_index_entry(index, e); } - UcxMapIterator i = ucx_map_iterator(p->ids); - PwdEntry *value; - UCX_MAP_FOREACH(key, value, i) { + i = cxMapIteratorValues(p->ids); + cx_foreach(PwdEntry*, value, i) { if(!value->id || !value->user || !value->password) { continue; } @@ -440,31 +434,31 @@ uint32_t netplen = htonl(plen); // content buffer - ucx_buffer_putc(content, 0); // type + cxBufferPut(content, 0); // type - ucx_buffer_write(&netidlen, 1, sizeof(uint32_t), content); - ucx_buffer_write(value->id, 1, idlen, content); - ucx_buffer_write(&netulen, 1, sizeof(uint32_t), content); - ucx_buffer_write(value->user, 1, ulen, content); - ucx_buffer_write(&netplen, 1, sizeof(uint32_t), content); - ucx_buffer_write(value->password, 1, plen, content); + cxBufferWrite(&netidlen, 1, sizeof(uint32_t), content); + cxBufferWrite(value->id, 1, idlen, content); + cxBufferWrite(&netulen, 1, sizeof(uint32_t), content); + cxBufferWrite(value->user, 1, ulen, content); + cxBufferWrite(&netplen, 1, sizeof(uint32_t), content); + cxBufferWrite(value->password, 1, plen, content); } content->pos = 0; - UcxBuffer *enc = aes_encrypt_buffer(content, p->key); + CxBuffer *enc = aes_encrypt_buffer(content, p->key); p->content->pos = PWDS_HEADER_SIZE - sizeof(uint32_t); p->content->size = PWDS_HEADER_SIZE; // add index after header uint32_t netindexlen = htonl((uint32_t)index->size); - ucx_buffer_write(&netindexlen, 1, sizeof(uint32_t), p->content); - ucx_buffer_write(index->space, 1, index->size, p->content); + cxBufferWrite(&netindexlen, 1, sizeof(uint32_t), p->content); + cxBufferWrite(index->space, 1, index->size, p->content); // add encrypted buffer - ucx_buffer_write(enc->space, 1, enc->size, p->content); + cxBufferWrite(enc->space, 1, enc->size, p->content); - ucx_buffer_free(enc); + cxBufferFree(enc); FILE *out = fopen(file, "w"); if(!out) { diff -r a569148841ff -r efbd59642577 dav/pwd.h --- a/dav/pwd.h Sun Apr 16 14:12:24 2023 +0200 +++ b/dav/pwd.h Fri Apr 21 21:25:32 2023 +0200 @@ -32,8 +32,9 @@ #include #include -#include -#include +#include +#include +#include #include #ifdef __cplusplus @@ -84,30 +85,30 @@ * key is the username * value is PwdEntry* */ - UcxMap *ids; + CxMap *ids; /* * list of all credentials with location * value is PwdIndexEntry* */ - UcxList *locations; + CxList *locations; /* * list of all credentials without location * value is PwdIndexEntry* */ - UcxList *noloc; + CxList *noloc; /* * index map that contains all elements from the lists * 'locations' and 'noloc' */ - UcxMap *index; + CxMap *index; /* * a buffer containing the complete file content */ - UcxBuffer *content; + CxBuffer *content; /* * key used for encryption/decryption @@ -150,7 +151,7 @@ struct PwdIndexEntry { char *id; - UcxList *locations; + CxList *locations; }; /* @@ -179,7 +180,7 @@ PwdEntry* pwdstore_get(PwdStore *p, const char *id); void pwdstore_put(PwdStore *p, const char *id, const char *username, const char *password); -void pwdstore_put_index(PwdStore *p, char *id, UcxList *locations); +void pwdstore_put_index(PwdStore *p, char *id, CxList *locations); void pwdstore_remove_entry(PwdStore *s, const char *id); diff -r a569148841ff -r efbd59642577 dav/scfg.c --- a/dav/scfg.c Sun Apr 16 14:12:24 2023 +0200 +++ b/dav/scfg.c Fri Apr 21 21:25:32 2023 +0200 @@ -31,8 +31,10 @@ #include #include #include -#include -#include +#include +#include +#include +#include #include "scfg.h" #include "config.h" @@ -58,10 +60,10 @@ #define ENV_HOME getenv("HOME") #endif /* _WIN32 */ -static UcxMap *directories; +static CxMap *directories; -UcxMapIterator scfg_directory_iterator() { - return ucx_map_iterator(directories); +CxIterator scfg_directory_iterator() { + return cxMapIteratorValues(directories); } static int create_default_sync_config(char *file) { @@ -79,24 +81,22 @@ return 0; } -static UcxList* add_regex_pattern(UcxList *list, char *value, +static void add_regex_pattern(CxList *list, char *value, unsigned short xmlline) { - regex_t *regex = malloc(sizeof(regex_t)); - if (regcomp(regex, value, REG_EXTENDED|REG_NOSUB)) { + regex_t regex; + if (regcomp(®ex, value, REG_EXTENDED|REG_NOSUB)) { print_warning(xmlline, "Invalid regular expression (%s) ... skipped\n", value); - free(regex); - return list; } else { - return ucx_list_append(list, regex); + cxListAdd(list, ®ex); } } static int scfg_load_filter( xmlNode *node, - UcxList **include, - UcxList **exclude, - UcxList **tags) + CxList *include, + CxList *exclude, + CxList *tags) { node = node->children; @@ -105,11 +105,11 @@ char *value = util_xml_get_text(node); if(xstreq(node->name, "include")) { if(value) { - *include = add_regex_pattern(*include, value, node->line); + add_regex_pattern(include, value, node->line); } } else if(xstreq(node->name, "exclude")) { if(value) { - *exclude = add_regex_pattern(*exclude, value, node->line); + add_regex_pattern(exclude, value, node->line); } } else if(xstreq(node->name, "tags")) { if(value) { @@ -143,7 +143,7 @@ } xmlFree(scope); - *tags = ucx_list_append(*tags, tagfilter); + cxListAdd(tags, tagfilter); } } } else { @@ -165,11 +165,15 @@ } Filter* parse_filter(xmlNode *node) { - UcxList *include = NULL; - UcxList *exclude = NULL; - UcxList *tags = NULL; + CxList *include = cxLinkedListCreate(cxDefaultAllocator, NULL, sizeof(regex_t)); + CxList *exclude = cxLinkedListCreate(cxDefaultAllocator, NULL, sizeof(regex_t)); + CxList *tags = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS); - if(scfg_load_filter(node, &include, &exclude, &tags)) { + include->simple_destructor = (cx_destructor_func)regfree; + exclude->simple_destructor = (cx_destructor_func)regfree; + // TODO: set tags destructor + + if(scfg_load_filter(node, include, exclude, tags)) { return NULL; } @@ -181,10 +185,10 @@ } void init_default_filter(Filter *filter) { - if(!filter->include) { - regex_t *matchall = malloc(sizeof(regex_t)); - regcomp(matchall, ".*", REG_NOSUB); - filter->include = ucx_list_append(NULL, matchall); + if(filter->include->size == 0) { + regex_t matchall; + regcomp(&matchall, ".*", REG_NOSUB); + cxListAdd(filter->include, &matchall); } /* if(!filter->exclude) { @@ -343,15 +347,15 @@ return sc; } -static UcxList* parse_splitconfig(xmlNode *node, int *error) { - UcxList *splitconfig = NULL; +static CxList* parse_splitconfig(xmlNode *node, int *error) { + CxList *splitconfig = cxLinkedListCreateSimple(CX_STORE_POINTERS); int err = 0; xmlNode *c = node->children; while(c) { if(c->type == XML_ELEMENT_NODE && xstreq(c->name, "split")) { SplitConfig *sc = parse_split(c); if(sc) { - splitconfig = ucx_list_append(splitconfig, sc); + cxListAdd(splitconfig, sc); } else { err = 1; break; @@ -427,10 +431,10 @@ char *database = NULL; TagConfig *tagconfig = NULL; Versioning *versioning = NULL; - UcxList *include = NULL; - UcxList *exclude = NULL; - UcxList *tagfilter = NULL; - UcxList *splitconfig = NULL; + CxList *include = cxLinkedListCreateSimple(sizeof(regex_t)); + CxList *exclude = cxLinkedListCreateSimple(sizeof(regex_t)); + CxList *tagfilter = cxLinkedListCreateSimple(CX_STORE_POINTERS); + CxList *splitconfig = NULL; int max_retry = 0; int allow_cmd = SYNC_CMD_PULL | SYNC_CMD_PUSH | SYNC_CMD_ARCHIVE | SYNC_CMD_RESTORE; @@ -470,7 +474,7 @@ } else if(xstreq(node->name, "repository")) { repository = value; } else if(xstreq(node->name, "filter")) { - if(scfg_load_filter(node, &include, &exclude, &tagfilter)) { + if(scfg_load_filter(node, include, exclude, tagfilter)) { return 1; } } else if(xstreq(node->name, "database")) { @@ -647,7 +651,7 @@ dir->filter.tags = tagfilter; init_default_filter(&dir->filter); - if (trash && sstrtrim(sstr(trash)).length > 0) { + if (trash && cx_strtrim(cx_str(trash)).length > 0) { if (trash[0] == '/' || trash[0] == '$') { dir->trash = scfg_create_path(trash); } else { @@ -665,13 +669,13 @@ dir->trash = NULL; } - ucx_map_cstr_put(directories, name, dir); + cxMapPut(directories, cx_hash_key_str(name), dir); return 0; } int load_sync_config() { - directories = ucx_map_new(8); + directories = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 8); if(check_config_dir()) { fprintf(stderr, "Cannot create .dav directory\n"); @@ -724,8 +728,8 @@ return ret; } -SyncDirectory* scfg_get_dir(char *name) { - return ucx_map_cstr_get(directories, name); +SyncDirectory* scfg_get_dir(const char *name) { + return cxMapGet(directories, cx_hash_key_str(name)); } int scfg_check_dir(SyncDirectory *dir) { @@ -756,7 +760,7 @@ return 0; } -char* scfg_create_path(char *cfg) { +char* scfg_create_path(const char *cfg) { if(!cfg) { return NULL; } @@ -764,15 +768,15 @@ return strdup(cfg); } - sstr_t s = sstr(cfg); - sstr_t path = sstrchr(sstr(cfg), '/'); + cxstring s = cx_str(cfg); + cxstring path = cx_strchr(cx_str(cfg), '/'); char *localpath = NULL; if(path.length > 0) { // path = $var/path/ - sstr_t var = sstrsubsl(s, 1, path.ptr - s.ptr - 1); + cxstring var = cx_strsubsl(s, 1, path.ptr - s.ptr - 1); if(var.length > 0) { - char *env = sstrdup(var).ptr; + char *env = cx_strdup(var).ptr; char *envval = getenv(env); free(env); if(envval) { @@ -786,7 +790,7 @@ exit(-1); } } else { - localpath = sstrdup(path).ptr; + localpath = cx_strdup(path).ptr; } } else { // path = $var @@ -852,20 +856,19 @@ return ret; } -char* generate_db_name(char *basename) { +char* generate_db_name(const char *basename) { char *dbname = NULL; int count = -1; while(!dbname) { - sstr_t name = count < 0 ? - ucx_sprintf("%s-db.xml", basename) : - ucx_sprintf("%s%d-db.xml", basename, count); + cxmutstr name = count < 0 ? + cx_asprintf("%s-db.xml", basename) : + cx_asprintf("%s%d-db.xml", basename, count); count++; - UcxMapIterator i = ucx_map_iterator(directories); - SyncDirectory *dir; + CxIterator i = cxMapIteratorValues(directories); bool unique = true; - UCX_MAP_FOREACH(key, dir, i) { - if(!sstrcmp(name, sstr(dir->database))) { + cx_foreach(SyncDirectory *, dir, i) { + if(!cx_strcmp(cx_strcast(name), cx_str(dir->database))) { unique = false; break; } @@ -879,23 +882,15 @@ } void free_filter(Filter filter) { - UCX_FOREACH(elm, filter.include) { - regfree(elm->data); - free(elm->data); - } - ucx_list_free(filter.include); - UCX_FOREACH(elm, filter.exclude) { - regfree(elm->data); - free(elm->data); - } - ucx_list_free(filter.exclude); + cxListDestroy(filter.include); + cxListDestroy(filter.exclude); + cxListDestroy(filter.tags); } void free_sync_config() { if(directories) { - UcxMapIterator i = ucx_map_iterator(directories); - SyncDirectory *dir; - UCX_MAP_FOREACH(elm, dir, i) { + CxIterator i = cxMapIteratorValues(directories); + cx_foreach(SyncDirectory *, dir, i) { free(dir->name); free(dir->path); free(dir->repository); @@ -913,6 +908,6 @@ free(dir); } - ucx_map_free(directories); + cxMapDestroy(directories); } } diff -r a569148841ff -r efbd59642577 dav/scfg.h --- a/dav/scfg.h Sun Apr 16 14:12:24 2023 +0200 +++ b/dav/scfg.h Fri Apr 21 21:25:32 2023 +0200 @@ -29,7 +29,7 @@ #ifndef SCFG_H #define SCFG_H -#include +#include #include #include #include @@ -73,9 +73,9 @@ typedef enum PushStrategy PushStrategy; struct Filter { - UcxList *include; - UcxList *exclude; - UcxList *tags; + CxList *include; + CxList *exclude; + CxList *tags; }; typedef struct SyncDirectory { @@ -88,7 +88,7 @@ TagConfig *tagconfig; Versioning *versioning; Filter filter; - UcxList *splitconfig; + CxList *splitconfig; uint32_t metadata; int max_retry; int allow_cmd; @@ -167,16 +167,16 @@ int load_sync_config(); -UcxMapIterator scfg_directory_iterator(); -SyncDirectory* scfg_get_dir(char *name); +CxIterator scfg_directory_iterator(); +SyncDirectory* scfg_get_dir(const char *name); int scfg_check_dir(SyncDirectory *dir); -char* scfg_create_path(char *cfg); +char* scfg_create_path(const char *cfg); int add_directory(SyncDirectory *dir); -char* generate_db_name(char *basename); +char* generate_db_name(const char *basename); void free_filter(Filter filter); diff -r a569148841ff -r efbd59642577 dav/sopt.c --- a/dav/sopt.c Sun Apr 16 14:12:24 2023 +0200 +++ b/dav/sopt.c Fri Apr 21 21:25:32 2023 +0200 @@ -32,17 +32,23 @@ #include "sopt.h" +#include + void cmd_args_free(CmdArgs *args) { if(args) { - ucx_map_free(args->options); + cxMapDestroy(args->options); free(args->argv); free(args); } } +static void cmd_map_put(CxMap *map, const char *key, const void *value) { + cxMapPut(map, key, (void*)value); +} + CmdArgs* cmd_parse_args(int argc, char **argv) { CmdArgs *a = malloc(sizeof(CmdArgs)); - a->options = ucx_map_new(16); + a->options = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); a->argv = calloc(argc, sizeof(char*)); a->argc = 0; @@ -62,23 +68,23 @@ return NULL; } case 'c': { - ucx_map_cstr_put(a->options, "conflict", NOARG); + cmd_map_put(a->options, "conflict", NOARG); break; } case 'l': { - ucx_map_cstr_put(a->options, "lock", NOARG); + cmd_map_put(a->options, "lock", NOARG); break; } case 'd': { - ucx_map_cstr_put(a->options, "nolock", NOARG); + cmd_map_put(a->options, "nolock", NOARG); break; } case 'r': { - ucx_map_cstr_put(a->options, "remove", NOARG); + cmd_map_put(a->options, "remove", NOARG); break; } case 'v': { - ucx_map_cstr_put(a->options, "verbose", NOARG); + cmd_map_put(a->options, "verbose", NOARG); break; } case 's': { @@ -97,20 +103,20 @@ break; } case 'R': { - ucx_map_cstr_put(a->options, "restore-removed", NOARG); + cmd_map_put(a->options, "restore-removed", NOARG); break; } case 'M': { - ucx_map_cstr_put(a->options, "restore-modified", NOARG); + cmd_map_put(a->options, "restore-modified", NOARG); break; } case 'S': { - ucx_map_cstr_put(a->options, "snapshot", NOARG); + cmd_map_put(a->options, "snapshot", NOARG); } } } } else if(option) { - ucx_map_cstr_put(a->options, option, arg); + cmd_map_put(a->options, option, arg); option = NULL; } else { a->argv[a->argc++] = arg; @@ -120,6 +126,6 @@ return a; } -char* cmd_getoption(CmdArgs *arg, char *name) { - return ucx_map_cstr_get(arg->options, name); +char* cmd_getoption(CmdArgs *arg, const char *name) { + return cxMapGet(arg->options, cx_hash_key_str(name)); } diff -r a569148841ff -r efbd59642577 dav/sopt.h --- a/dav/sopt.h Sun Apr 16 14:12:24 2023 +0200 +++ b/dav/sopt.h Fri Apr 21 21:25:32 2023 +0200 @@ -29,7 +29,7 @@ #ifndef OPTPARSER_H #define OPTPARSER_H -#include +#include #include "opt.h" #ifdef __cplusplus @@ -40,7 +40,7 @@ void cmd_args_free(CmdArgs *args); CmdArgs* cmd_parse_args(int argc, char **argv); -char* cmd_getoption(CmdArgs *arg, char *name); +char* cmd_getoption(CmdArgs *arg, const char *name); #ifdef __cplusplus } diff -r a569148841ff -r efbd59642577 dav/sync.c --- a/dav/sync.c Sun Apr 16 14:12:24 2023 +0200 +++ b/dav/sync.c Fri Apr 21 21:25:32 2023 +0200 @@ -36,9 +36,10 @@ #include #include #include -#include -#include -#include +#include +#include +#include +#include #include #include @@ -355,12 +356,12 @@ static int res_matches_filter(Filter *filter, char *res_path) { // include/exclude filter - UCX_FOREACH(inc, filter->include) { - regex_t* pattern = (regex_t*) inc->data; + CxIterator i = cxListIterator(filter->include); + cx_foreach(regex_t*, pattern, i) { if (regexec(pattern, res_path, 0, NULL, 0) == 0) { - UCX_FOREACH(exc, filter->exclude) { - regex_t* pattern = (regex_t*) exc->data; - if (regexec(pattern, res_path, 0, NULL, 0) == 0) { + CxIterator e = cxListIterator(filter->exclude); + cx_foreach(regex_t*, expat, e) { + if (regexec(expat, res_path, 0, NULL, 0) == 0) { return 1; } } @@ -373,7 +374,7 @@ static int res_matches_dir_filter(SyncDirectory *dir, char *res_path) { // trash filter if (dir->trash) { - sstr_t rpath = sstr(util_concat_path(dir->path, res_path)); + cxmutstr rpath = cx_mutstr(util_concat_path(dir->path, res_path)); if (util_path_isrelated(dir->trash, rpath.ptr)) { free(rpath.ptr); return 1; @@ -407,12 +408,11 @@ } DavXmlNode *tagsprop = dav_get_property_ns(res, DAV_PROPS_NS, "tags"); - UcxList *res_tags = parse_dav_xml_taglist(tagsprop); + CxList *res_tags = parse_dav_xml_taglist(tagsprop); int ret = matches_tagfilter(res_tags, tagfilter); - ucx_list_free_content(res_tags, (ucx_destructor) free_dav_tag); - ucx_list_free(res_tags); + cxListDestroy(res_tags); return ret; } @@ -436,14 +436,11 @@ } DavBool changed = 0; - UcxList *res_tags = sync_get_file_tags(dir, res, &changed, NULL); + CxList *res_tags = sync_get_file_tags(dir, res, &changed, NULL); int ret = matches_tagfilter(res_tags, tagfilter); - UCX_FOREACH(elm, res_tags) { - DavTag *t = elm->data; - free_dav_tag(t); - } - ucx_list_free(res_tags); + CxIterator i = cxListIterator(res_tags); + cxListDestroy(res_tags); return ret; } @@ -510,7 +507,7 @@ } static void localres_keep(SyncDatabase *db, const char *path) { - LocalResource *local = ucx_map_cstr_remove(db->resources, path); + LocalResource *local = cxMapRemoveAndGet(db->resources, cx_hash_key_str(path)); if(local) { local->keep = TRUE; } @@ -528,21 +525,23 @@ return 1; } -void res2map(DavResource *root, UcxMap *map) { - UcxList *stack = ucx_list_prepend(NULL, root->children); - while(stack) { - DavResource *res = stack->data; - stack = ucx_list_remove(stack, stack); +void res2map(DavResource *root, CxMap *map) { + CxList *stack = cxLinkedListCreateSimple(CX_STORE_POINTERS); + cxListInsert(stack, 0, root->children); + while(stack->size > 0) { + DavResource *res = cxListAt(stack, 0); + cxListRemove(stack, 0); while(res) { - ucx_map_cstr_put(map, res->path, res); + cxMapPut(map, cx_hash_key_str(res->path), res); if(res->children) { - stack = ucx_list_prepend(stack, res->children); + cxListInsert(stack, 0, res->children); } res = res->next; } } + cxListDestroy(stack); } int cmd_pull(CmdArgs *a, DavBool incoming) { @@ -575,7 +574,7 @@ return -1; } - Repository *repo = get_repository(sstr(dir->repository)); + Repository *repo = get_repository(cx_str(dir->repository)); if(!repo) { fprintf(stderr, "Unknown repository %s\n", dir->repository); return -1; @@ -588,13 +587,13 @@ } remove_deleted_conflicts(dir, db); - UcxMap *hashes = NULL; + CxMap *hashes = NULL; if(SYNC_HASHING(dir)) { hashes = create_hash_index(db); } DavSession *sn = create_session(a, ctx, repo, dir->collection); - ucx_mempool_reg_destr(sn->mp, db, (ucx_destructor)destroy_db); + util_regdestr(sn->mp, db, (cx_destructor_func)destroy_db); if (cmd_getoption(a, "verbose")) { curl_easy_setopt(sn->handle, CURLOPT_VERBOSE, 1L); curl_easy_setopt(sn->handle, CURLOPT_STDERR, stderr); @@ -664,31 +663,32 @@ int sync_error = 0; int sync_conflict = 0; - UcxList *res_modified = NULL; - UcxList *res_new = NULL; - UcxList *res_moved = NULL; // type: MovedFile - UcxList *res_link = NULL; - UcxList *res_conflict = NULL; - UcxList *res_mkdir = NULL; - UcxList *res_metadata = NULL; - UcxList *res_broken = NULL; - UcxMap *lres_removed = ucx_map_new(16); // type: LocalResource* + CxList *res_modified = cxLinkedListCreateSimple(CX_STORE_POINTERS); + CxList *res_new = cxLinkedListCreateSimple(CX_STORE_POINTERS); + CxList *res_moved = cxLinkedListCreateSimple(CX_STORE_POINTERS); // type: MovedFile + CxList *res_link = cxLinkedListCreateSimple(CX_STORE_POINTERS); + CxList *res_conflict = cxLinkedListCreateSimple(CX_STORE_POINTERS); + CxList *res_mkdir = cxLinkedListCreateSimple(CX_STORE_POINTERS); + CxList *res_metadata = cxLinkedListCreateSimple(CX_STORE_POINTERS); + CxList *res_broken = cxLinkedListCreateSimple(CX_STORE_POINTERS); + CxMap *lres_removed = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); // type: LocalResource* //UcxMap *svrres = ucx_map_new(db->resources->count); - UcxMap *dbres = ucx_map_clone(db->resources, NULL, NULL); - - UcxList *stack = ucx_list_prepend(NULL, ls->children); - while(stack) { - DavResource *res = stack->data; - stack = ucx_list_remove(stack, stack); + CxMap *dbres = NULL; // TODO: ucx_map_clone(db->resources, NULL, NULL); + + CxList *stack = cxLinkedListCreateSimple(CX_STORE_POINTERS); + cxListInsert(stack, 0, ls->children); + while(stack->size > 0) { + DavResource *res = cxListAt(stack, 0); + cxListRemove(stack, 0); while(res) { DavBool res_filtered = FALSE; if (res_matches_dir_filter(dir, res->path)) { res_filtered = TRUE; } else { - UCX_FOREACH(elm, dir->filter.tags) { - SyncTagFilter *tf = elm->data; + CxIterator iter = cxListIterator(dir->filter.tags); + cx_foreach(SyncTagFilter *, tf, iter) { if(!res_matches_tags(res, tf)) { res_filtered = TRUE; break; @@ -714,7 +714,7 @@ if(status && !strcmp(status, "broken")) { res = res->next; localres_keep(db, res->path); - res_broken = ucx_list_append(res_broken, res); + cxListAdd(res_broken, res); continue; } @@ -723,28 +723,28 @@ switch(change) { case REMOTE_NO_CHANGE: break; case REMOTE_CHANGE_MODIFIED: { - res_modified = ucx_list_append(res_modified, res); + cxListAdd(res_modified, res); break; } case REMOTE_CHANGE_NEW: { - res_new = ucx_list_append(res_new, res); + cxListAdd(res_new, res); break; } case REMOTE_CHANGE_DELETED: break; // never happens case REMOTE_CHANGE_CONFLICT_LOCAL_MODIFIED: { - res_conflict = ucx_list_append(res_conflict, res); + cxListAdd(res_conflict, res); break; } case REMOTE_CHANGE_METADATA: { - res_metadata = ucx_list_append(res_metadata, res); + cxListAdd(res_metadata, res); break; } case REMOTE_CHANGE_MKDIR: { - res_mkdir = ucx_list_append(res_mkdir, res); + cxListAdd(res_mkdir, res); break; } case REMOTE_CHANGE_LINK: { - res_link = ucx_list_append(res_link, res); + cxListAdd(res_link, res); break; } } @@ -752,10 +752,10 @@ // remove every server resource from dbres // all remaining elements are the resources that are removed // on the server - ucx_map_cstr_remove(dbres, res->path); + cxMapRemove(dbres, cx_hash_key_str(res->path)); if(!dav_get_property_ns(res, DAV_NS, "split") && res->children) { - stack = ucx_list_prepend(stack, res->children); + cxListInsert(stack, 0, res->children); } res = res->next; } @@ -764,16 +764,15 @@ // find deleted resources // svrres currently contains all resources from the server // and will replace the current db->resources map later - UcxMapIterator i = ucx_map_iterator(dbres); - LocalResource *local; - UCX_MAP_FOREACH(key, local, i) { + CxIterator i = cxMapIteratorValues(dbres); + cx_foreach(LocalResource *, local, i) { if (res_matches_dir_filter(dir, local->path)) { continue; } if(!local->keep) { - ucx_map_cstr_put(lres_removed, local->path, local); - if(lres_removed->count > lres_removed->size * 2) { - ucx_map_rehash(lres_removed); + cxMapPut(lres_removed, cx_hash_key_str(local->path), local); + if(lres_removed->size > lres_removed->size * 2) { + cxMapRehash(lres_removed); } } } @@ -783,31 +782,25 @@ // // the first thing we need are all directories to put the files in - UCX_FOREACH(elm, res_mkdir) { - DavResource *res = elm->data; + i = cxListIterator(res_mkdir); + cx_foreach(DavResource *, res, i) { if(sync_get_collection(a, dir, res, db)) { sync_error++; } } // we need a map for all conflicts for fast lookups - UcxMap *conflicts = ucx_map_new(ucx_list_size(res_conflict)+16); - UCX_FOREACH(elm, res_conflict) { - DavResource *res = elm->data; - ucx_map_cstr_put(conflicts, res->path, res); + CxMap *conflicts = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, res_conflict->size+16); + i = cxListIterator(res_conflict); + cx_foreach(DavResource *, res, i) { + cxMapPut(conflicts, cx_hash_key_str(res->path), res); } if(SYNC_HASHING(dir)) { // check for moved/copied files - UcxList *elm = res_new; - UcxList *prev = NULL; - UcxList *next = NULL; SYS_STAT s; - for(;elm;elm=next) { - DavResource *res = elm->data; - prev = elm->prev; - next = elm->next; - + CxMutIterator mut_iter = cxListMutIterator(res_new); + cx_foreach(DavResource *, res, mut_iter) { if(dav_get_property_ns(res, DAV_PROPS_NS, "link")) { continue; } @@ -817,7 +810,7 @@ continue; } - LocalResource *local = ucx_map_cstr_get(hashes, hash); + LocalResource *local = cxMapGet(hashes, cx_hash_key_str(hash)); if(!local) { continue; } @@ -833,35 +826,28 @@ MovedFile *mf = malloc(sizeof(MovedFile)); mf->content = local; mf->resource = res; - if(ucx_map_cstr_remove(lres_removed, local->path)) { + if(cxMapRemoveAndGet(lres_removed, cx_hash_key_str(local->path))) { mf->copy = FALSE; } else { mf->copy = TRUE; } - res_moved = ucx_list_append(res_moved, mf); + cxListAdd(res_moved, mf); // remove item from res_new - if(prev) { - prev->next = next; - } else { - res_new = next; - } - if(next) { - next->prev = prev; - } + cxIteratorFlagRemoval(mut_iter); } } // do copy/move operations - UCX_FOREACH(elm, res_moved) { - MovedFile *mf = elm->data; + i = cxListIterator(res_moved); + cx_foreach(MovedFile *, mf, i) { if(sync_shutdown) { break; } DavBool issplit = dav_get_property_ns(mf->resource, DAV_NS, "split") ? 1 : 0; - if(ucx_map_cstr_get(conflicts, mf->resource->path)) { + if(cxMapGet(conflicts, cx_hash_key_str(mf->resource->path))) { rename_conflict_file(dir, db, mf->resource->path, issplit); sync_conflict++; } @@ -874,36 +860,45 @@ } // download all new, modified and conflict files - UcxList *download = ucx_list_concat(res_modified, res_conflict); - download = ucx_list_concat(res_new, download); - download = ucx_list_concat(download, res_link); - UCX_FOREACH(elm, download) { - DavResource *res = elm->data; + for(int n=0;n<4;n++) { + CxList *ls; + if(n == 0) { + ls = res_new; + } else if(n == 1) { + ls = res_modified; + } else if(n == 2) { + ls = res_conflict; + } else { + ls = res_link; + } + CxIterator iter = cxListIterator(ls); + cx_foreach(DavResource *, res, iter) { + if(sync_shutdown) { + break; + } + + DavBool issplit = dav_get_property_ns(res, DAV_NS, "split") ? 1 : 0; + if(cxMapGet(conflicts, cx_hash_key_str(res->path))) { + rename_conflict_file(dir, db, res->path, issplit); + sync_conflict++; + } + + // download the resource + if(sync_get_resource(a, dir, res->path, res, db, TRUE, &sync_success)) { + fprintf(stderr, "resource download failed: %s\n", res->path); + sync_error++; + } + } + } + + // update metadata + i = cxListIterator(res_metadata); + cx_foreach(DavResource *, res, i) { if(sync_shutdown) { break; } - DavBool issplit = dav_get_property_ns(res, DAV_NS, "split") ? 1 : 0; - if(ucx_map_cstr_get(conflicts, res->path)) { - rename_conflict_file(dir, db, res->path, issplit); - sync_conflict++; - } - - // download the resource - if(sync_get_resource(a, dir, res->path, res, db, TRUE, &sync_success)) { - fprintf(stderr, "resource download failed: %s\n", res->path); - sync_error++; - } - } - - // update metadata - UCX_FOREACH(elm, res_metadata) { - DavResource *res = elm->data; - if(sync_shutdown) { - break; - } - - LocalResource *local = ucx_map_cstr_get(db->resources, res->path); + LocalResource *local = cxMapGet(db->resources, cx_hash_key_str(res->path)); if(local) { printf("update: %s\n", res->path); char *res_path = resource_local_path(res); @@ -929,36 +924,35 @@ } } - UcxList *rmdirs = NULL; - UcxMapIterator mi = ucx_map_iterator(lres_removed); + CxList *rmdirs = cxLinkedListCreate(cxDefaultAllocator, (cx_compare_func)resource_pathlen_cmp, CX_STORE_POINTERS); + i = cxMapIteratorValues(lres_removed); LocalResource *removed_res; - UcxKey key; - UCX_MAP_FOREACH(key, removed_res, mi) { + cx_foreach(LocalResource *, removed_res, i) { if(sync_shutdown) { break; } int ret = sync_remove_local_resource(dir, removed_res); if(ret == -1) { - rmdirs = ucx_list_append(rmdirs, removed_res); + cxListAdd(rmdirs, removed_res); } else if(ret == 0) { - LocalResource *local = ucx_map_cstr_remove(db->resources, removed_res->path); + LocalResource *local = cxMapRemoveAndGet(db->resources, cx_hash_key_str(removed_res->path)); if(local) { local_resource_free(local); } sync_delete++; } } - ucx_map_free(lres_removed); + cxMapDestroy(lres_removed); // sort dir list, we need to delete dirs with higher depth first - rmdirs = ucx_list_sort(rmdirs, (cmp_func)resource_pathlen_cmp, NULL); + cxListSort(rmdirs); // delete dirs - UCX_FOREACH(elm, rmdirs) { - LocalResource *local_dir = elm->data; + i = cxListIterator(rmdirs); + cx_foreach(LocalResource *, local_dir, i) { if(!sync_remove_local_directory(dir, local_dir)) { // dir successfully removed, now remove the related db entry - LocalResource *local = ucx_map_cstr_remove(db->resources, local_dir->path); + LocalResource *local = cxMapRemoveAndGet(db->resources, cx_hash_key_str(local_dir->path)); if(local) { local_resource_free(local); } @@ -1030,7 +1024,7 @@ RemoteChangeType type = cmd_getoption(a, "conflict") ? REMOTE_CHANGE_MODIFIED : REMOTE_CHANGE_CONFLICT_LOCAL_MODIFIED; - LocalResource *local = ucx_map_cstr_get(db->resources, res->path); + LocalResource *local = cxMapGet(db->resources, cx_hash_key_str(res->path)); char *local_path = create_local_path(dir, res->path); char *link = SYNC_SYMLINK(dir) ? @@ -1081,9 +1075,9 @@ nochange = TRUE; } } else if(local->etag) { - sstr_t e = sstr(etag); - if(sstrprefix(e, S("W/"))) { - e = sstrsubs(e, 2); + cxstring e = cx_str(etag); + if(cx_strprefix(e, CX_STR("W/"))) { + e = cx_strsubs(e, 2); } if(!strcmp(e.ptr, local->etag)) { // resource is already up-to-date on the client @@ -1164,7 +1158,7 @@ // check if tags have changed if(dir->tagconfig) { DavXmlNode *tagsprop = dav_get_property_ns(res, DAV_PROPS_NS, "tags"); - UcxList *remote_tags = NULL; + CxList *remote_tags = NULL; if(tagsprop) { remote_tags = parse_dav_xml_taglist(tagsprop); } @@ -1213,7 +1207,7 @@ local = calloc(1, sizeof(LocalResource)); local->path = strdup(res->path); - ucx_map_cstr_put(db->resources, local->path, local); + cxMapPut(db->resources, cx_hash_key_str(local->path), local); } // update local res @@ -1245,7 +1239,7 @@ local->size = s->st_size; } -static UcxList* sync_download_changed_parts( +static CxList* sync_download_changed_parts( DavResource *res, LocalResource *local, FILE *out, @@ -1254,7 +1248,8 @@ int64_t *truncate_file, int *err) { - UcxList *updates = NULL; + CxList *updates = cxLinkedListCreateSimple(CX_STORE_POINTERS); + updates->simple_destructor = (cx_destructor_func)filepart_free; size_t local_numparts = local ? local->numparts : 0; fseeko(out, 0, SEEK_END); @@ -1262,7 +1257,7 @@ int error = 0; - UcxBuffer *buf = ucx_buffer_new(NULL, blocksize, 0); + CxBuffer *buf = cxBufferCreate(NULL, blocksize, cxDefaultAllocator, 0); int64_t maxsize = -1; @@ -1306,7 +1301,7 @@ } buf->pos = 0; buf->size = 0; - if(dav_get_content(part, buf,(dav_write_func)ucx_buffer_write)) { + if(dav_get_content(part, buf,(dav_write_func)cxBufferWrite)) { fprintf(stderr, "Error: cannot download part: %s\n", part->name); error = 1; break; @@ -1321,7 +1316,7 @@ update->block = partnum; update->etag = etag ? strdup(etag) : NULL; update->hash = dav_create_hash(buf->space, buf->size); - updates = ucx_list_append(updates, update); + cxListAdd(updates, update); block_end = offset+buf->size; } else { @@ -1343,11 +1338,11 @@ part = part->next; } - ucx_buffer_free(buf); + cxBufferFree(buf); if(error) { *err = 1; - ucx_list_free_content(updates, (ucx_destructor)filepart_free); + cxListDestroy(updates); return NULL; } @@ -1373,7 +1368,7 @@ return 1; } - ucx_stream_copy(in, out, (read_func)fread, (write_func)fwrite); + cx_stream_copy(in, out, (cx_read_func)fread, (cx_write_func)fwrite); fclose(in); fclose(out); @@ -1415,7 +1410,7 @@ } else { // reuse previous LocalResource (content) // remove it from db->resources, change path and put it back - local = ucx_map_cstr_remove(db->resources, content->path); + local = cxMapRemoveAndGet(db->resources, cx_hash_key_str(content->path)); if(!local) { // can't happen, but handle it nevertheless local = content; @@ -1423,7 +1418,7 @@ free(content->path); local->path = strdup(res->path); } - ucx_map_cstr_put(db->resources, local->path, local); + cxMapPut(db->resources, cx_hash_key_str(local->path), local); if(sync_store_metadata(dir, new_path, local, res)) { fprintf(stderr, "Cannot store metadata: %s\n", res->path); @@ -1463,7 +1458,7 @@ char *link = SYNC_SYMLINK(dir) ? dav_get_string_property_ns(res, DAV_PROPS_NS, "link") : NULL; - LocalResource *local = ucx_map_cstr_get(db->resources, path); + LocalResource *local = cxMapGet(db->resources, cx_hash_key_str(path)); char *local_path; if(link) { @@ -1488,7 +1483,7 @@ } issplit = TRUE; } - UcxList *part_updates = NULL; + CxList *part_updates = NULL; uint64_t blockcount = 0; char *content_hash = NULL; @@ -1590,7 +1585,7 @@ // new local resource local = calloc(1, sizeof(LocalResource)); local->path = strdup(path); - ucx_map_cstr_put(db->resources, local->path, local); + cxMapPut(db->resources, cx_hash_key_str(local->path), local); } if(sync_store_metadata(dir, local_path, local, res)) { @@ -1673,11 +1668,11 @@ } // if it doesn't exist in the db, create an entry for the dir - LocalResource *local = ucx_map_cstr_get(db->resources, res->path); + LocalResource *local = cxMapGet(db->resources, cx_hash_key_str(res->path)); if(!local) { local = calloc(1, sizeof(LocalResource)); local->path = strdup(res->path); - ucx_map_cstr_put(db->resources, local->path, local); + cxMapPut(db->resources, cx_hash_key_str(local->path), local); } local->isdirectory = 1; @@ -1770,14 +1765,14 @@ int loop = 1; do { char *res_parent = util_parent_path(path); - char *res_name = util_resource_name(path); + const char *res_name = util_resource_name(path); - sstr_t new_path = ucx_sprintf( + cxmutstr new_path = cx_asprintf( "%sorig.%d.%s", parent, rev, res_name); - sstr_t new_res_path = ucx_sprintf( + cxmutstr new_res_path = cx_asprintf( "%sorig.%d.%s", res_parent, rev, @@ -1799,7 +1794,7 @@ LocalResource *conflict = calloc(1, sizeof(LocalResource)); conflict->path = strdup(new_res_path.ptr); conflict->conflict_source = strdup(path); - ucx_map_cstr_put(db->conflict, new_res_path.ptr, conflict); + cxMapPut(db->conflict, cx_hash_key_str(new_res_path.ptr), conflict); } } } @@ -1817,8 +1812,7 @@ char *new_path = NULL; char *parent = util_parent_path(path); for (int i=0;;i++) { - sstr_t np = ucx_asprintf( - ucx_default_allocator(), + cxmutstr np = cx_asprintf( "%sdownload%d-%s", parent, i, @@ -1841,8 +1835,7 @@ void move_to_trash(SyncDirectory *dir, char *path) { char *new_path = NULL; for (int i=0;;i++) { - sstr_t np = ucx_asprintf( - ucx_default_allocator(), + cxmutstr np = cx_asprintf( "%s%d-%s", dir->trash, i, @@ -1876,7 +1869,7 @@ } static int res_isconflict(SyncDatabase *db, LocalResource *res) { - return ucx_map_cstr_get(db->conflict, res->path) ? 1 : 0; + return cxMapGet(db->conflict, cx_hash_key_str(res->path)) ? 1 : 0; } int cmd_push(CmdArgs *a, DavBool outgoing, DavBool archive) { @@ -1917,7 +1910,7 @@ return -1; } - Repository *repo = get_repository(sstr(dir->repository)); + Repository *repo = get_repository(cx_str(dir->repository)); if(!repo) { fprintf(stderr, "Unkown repository %s\n", dir->name); return -1; @@ -1931,7 +1924,7 @@ remove_deleted_conflicts(dir, db); DavSession *sn = create_session(a, ctx, repo, dir->collection); - ucx_mempool_reg_destr(sn->mp, db, (ucx_destructor)destroy_db); + util_regdestr(sn->mp, db, (cx_destructor_func)destroy_db); if (cmd_getoption(a, "verbose")) { curl_easy_setopt(sn->handle, CURLOPT_VERBOSE, 1L); curl_easy_setopt(sn->handle, CURLOPT_STDERR, stderr); @@ -1953,9 +1946,9 @@ return -1; } - UcxMap *svrres = NULL; + CxMap *svrres = NULL; if(restore) { - svrres = ucx_map_new(1024); + svrres = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 1024); res2map(root, svrres); } @@ -1981,7 +1974,7 @@ DavBool remove_file = cmd_getoption(a, "remove") ? 1 : 0; - UcxMap *db_hashes = NULL; + CxMap *db_hashes = NULL; if(SYNC_HASHING(dir)) { db_hashes = create_hash_index(db); } @@ -1991,23 +1984,24 @@ int sync_conflict = 0; int sync_error = 0; - UcxList *ls_new = NULL; - UcxList *ls_modified = NULL; - UcxList *ls_conflict = NULL; - UcxList *ls_update = NULL; - UcxList *ls_delete = NULL; - UcxList *ls_move = NULL; - UcxList *ls_copy = NULL; - UcxList *ls_mkcol = NULL; + CxList *ls_new = cxLinkedListCreate(cxDefaultAllocator, (cx_compare_func)resource_path_cmp, CX_STORE_POINTERS); + CxList *ls_modified = cxLinkedListCreate(cxDefaultAllocator, (cx_compare_func)resource_path_cmp, CX_STORE_POINTERS); + CxList *ls_conflict = cxLinkedListCreate(cxDefaultAllocator, (cx_compare_func)resource_path_cmp, CX_STORE_POINTERS); + CxList *ls_update = cxLinkedListCreate(cxDefaultAllocator, (cx_compare_func)resource_path_cmp, CX_STORE_POINTERS); + CxList *ls_delete = cxLinkedListCreate(cxDefaultAllocator, (cx_compare_func)resource_path_cmp, CX_STORE_POINTERS); + CxList *ls_move = cxLinkedListCreate(cxDefaultAllocator, (cx_compare_func)resource_path_cmp, CX_STORE_POINTERS); + CxList *ls_copy = cxLinkedListCreate(cxDefaultAllocator, (cx_compare_func)resource_path_cmp, CX_STORE_POINTERS); + CxList *ls_mkcol = cxLinkedListCreate(cxDefaultAllocator, (cx_compare_func)resource_path_cmp, CX_STORE_POINTERS); + // upload all changed files //UcxList *resources = cmd_getoption(a, "read") ? // read_changes(dir, db) : local_scan(dir, db); - UcxList *resources = local_scan(dir, db); - UcxMap *resources_map = ucx_map_new(ucx_list_size(resources)+16); - - UCX_FOREACH(elm, resources) { - LocalResource *local_res = elm->data; + CxList *resources = local_scan(dir, db); + CxMap *resources_map = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, resources->size+16); + + CxIterator iter = cxListIterator(resources); + cx_foreach(LocalResource *, local_res, iter) { // ignore all files, that are excluded by a static filter (sync.xml) // static include/exclude filter @@ -2015,22 +2009,25 @@ continue; } // static tag filter - UCX_FOREACH(elm, dir->filter.tags) { - SyncTagFilter *tf = elm->data; - if(!localres_matches_tags(dir, local_res, tf)) { - continue; + if(dir->filter.tags) { + CxIterator tag_iter = cxListIterator(dir->filter.tags); + cx_foreach(SyncTagFilter *, tf, tag_iter) { + if(!localres_matches_tags(dir, local_res, tf)) { + continue; + } } } + // we need a fast file lookup map later to detect deleted files - ucx_map_cstr_put(resources_map, local_res->path, local_res); + cxMapPut(resources_map, cx_hash_key_str(local_res->path), local_res); // dynamic tag filter if(!localres_matches_tags(dir, local_res, tagfilter)) { if(!remove_file) { - LocalResource *dbres = ucx_map_cstr_get( + LocalResource *dbres = cxMapGet( db->resources, - local_res->path); + cx_hash_key_str(local_res->path)); if(dbres) { // this makes sure the file will not be deleted later dbres->keep = TRUE; @@ -2041,7 +2038,7 @@ // skip conflict backups silently if(res_isconflict(db, local_res)) { - ls_conflict = ucx_list_append(ls_conflict, local_res); + cxListAdd(ls_conflict, local_res); continue; } @@ -2054,14 +2051,14 @@ restore_modified); if(is_changed) { if(local_res->isdirectory) { - ls_mkcol = ucx_list_append(ls_mkcol, local_res); + cxListAdd(ls_mkcol, local_res); } else if(local_res->isnew) { - ls_new = ucx_list_append(ls_new, local_res); + cxListAdd(ls_new, local_res); } else { - ls_modified = ucx_list_append(ls_modified, local_res); + cxListAdd(ls_modified, local_res); } } else if(local_res->metadata_updated) { - ls_update = ucx_list_append(ls_update, local_res); + cxListAdd(ls_update, local_res); } if(local_res->isnew) { @@ -2077,13 +2074,9 @@ if(SYNC_STORE_HASH(dir)) { // calculate hashes of all new files and check if a file // was moved or is a copy - UcxList *elm = ls_new; - while(elm) { - LocalResource *local = elm->data; - UcxList *prev = elm->prev; - UcxList *next = elm->next; + CxMutIterator mut_iter = cxListMutIterator(ls_new); + cx_foreach(LocalResource *, local, mut_iter) { if(local->isdirectory || local->link_target) { - elm = elm->next; continue; } @@ -2091,78 +2084,70 @@ char *hash = util_file_hash(local_path); local->hash = hash; // check if a file with this hash already exists - LocalResource *origin = ucx_map_cstr_get(db_hashes, hash); + LocalResource *origin = cxMapGet(db_hashes, cx_hash_key_str(hash)); if(origin) { local->origin = local_resource_copy(origin, origin->path); // the file is a copied/moved file // check if the file is in the resources_map, because then // it still exists - if(ucx_map_cstr_get(resources_map, origin->path)) { - ls_copy = ucx_list_append(ls_copy, local); + if(cxMapGet(resources_map, cx_hash_key_str(origin->path))) { + cxListAdd(ls_copy, local); } else { - ls_move = ucx_list_append(ls_move, local); + cxListAdd(ls_move, local); // put file in resources_map to prevent deletion - ucx_map_cstr_put(resources_map, origin->path, local); + cxMapPut(resources_map, cx_hash_key_str(origin->path), local); } // remove list elemend from ls_new - if(prev) { - prev->next = next; - } else { - ls_new = next; - } - if(next) { - next->prev = prev; - } + cxIteratorFlagRemoval(mut_iter); } free(local_path); - - elm = next; } } // find all deleted files and cleanup the database - UcxMapIterator i = ucx_map_iterator(db->resources); + iter = cxMapIterator(db->resources); LocalResource *local; - UcxList *removed_res = NULL; - UCX_MAP_FOREACH(key, local, i) { + CxList *removed_res = cxLinkedListCreateSimple(CX_STORE_POINTERS); + cx_foreach(CxMapEntry *, entry, iter) { + LocalResource *local = entry->value; // all filtered files should be removed from the database if(res_matches_dir_filter(dir, local->path+1)) { - ucx_map_cstr_remove(db->resources, local->path); + cxMapRemove(db->resources, local->path); continue; } - UCX_FOREACH(elm, dir->filter.tags) { - SyncTagFilter *tf = elm->data; + CxIterator tag_iter = cxListIterator(dir->filter.tags); + cx_foreach(SyncTagFilter *, tf, tag_iter) { if(!localres_matches_tags(dir, local, tf)) { - ucx_map_cstr_remove(db->resources, local->path); + cxMapRemove(db->resources, local->path); continue; } } - if(!ucx_map_get(resources_map, key)) { + if(!cxMapGet(resources_map, *entry->key)) { // The current LocalResource is in the database but doesn't exist // in the filesystem anymore. This means the file was deleted // and should be deleted on the server if(!archive) { - ls_delete = ucx_list_append(ls_delete, local); + cxListAdd(ls_delete, local); } else { - removed_res = ucx_list_prepend(removed_res, local); + cxListInsert(removed_res, 0, local); } } } - UCX_FOREACH(elm, removed_res) { - LocalResource *local = elm->data; - ucx_map_cstr_remove(db->resources, local->path); - } - - ls_new = ucx_list_sort(ls_new, (cmp_func)resource_path_cmp, NULL); - ls_modified = ucx_list_sort(ls_modified, (cmp_func)resource_path_cmp, NULL); - ls_conflict = ucx_list_sort(ls_conflict, (cmp_func)resource_path_cmp, NULL); - ls_update = ucx_list_sort(ls_update, (cmp_func)resource_path_cmp, NULL); - ls_delete = ucx_list_sort(ls_delete, (cmp_func)resource_path_cmp, NULL); - ls_move = ucx_list_sort(ls_move, (cmp_func)resource_path_cmp, NULL); - ls_copy = ucx_list_sort(ls_copy, (cmp_func)resource_path_cmp, NULL); - ls_mkcol = ucx_list_sort(ls_mkcol, (cmp_func)resource_path_cmp, NULL); + iter = cxListIterator(removed_res); + cx_foreach(LocalResource *, local, iter) { + cxMapRemove(db->resources, local->path); + } + + cxListSort(ls_new); + cxListSort(ls_modified); + cxListSort(ls_conflict); + cxListSort(ls_update); + cxListSort(ls_delete); + cxListSort(ls_move); + cxListSort(ls_copy); + cxListSort(ls_mkcol); if(outgoing) { print_outgoing( @@ -2186,8 +2171,11 @@ int error = 0; // create collections - for(UcxList *elm=ls_mkcol;elm && !sync_shutdown;elm=elm->next) { - LocalResource *local_res = elm->data; + iter = cxListIterator(ls_mkcol); + cx_foreach(LocalResource *, local_res, iter) { + if(sync_shutdown) { + break; + } DavResource *res = dav_resource_new(sn, local_res->path); if(!res) { @@ -2227,8 +2215,9 @@ // remove old db entry (if it exists) // and add add new entry - LocalResource *dbres = ucx_map_cstr_remove(db->resources, local_res->path); - ucx_map_cstr_put(db->resources, local_res->path, local_res); + // TODO: free?? + LocalResource *dbres = cxMapGet(db->resources, cx_hash_key_str(local_res->path)); + cxMapPut(db->resources, cx_hash_key_str(local_res->path), local_res); } dav_resource_free(res); @@ -2240,163 +2229,176 @@ copy = FALSE; ls_copy = ls_move; } - for(UcxList *elm=ls_copy;elm && !sync_shutdown;elm=elm->next) { - LocalResource *local = elm->data; - - int err = 0; - DavResource *res = dav_resource_new(sn, local->path); - if(dav_exists(res)) { - printf("conflict: %s\n", local->path); - local->last_modified = 0; - nullfree(local->etag); - local->etag = NULL; - nullfree(local->hash); - local->hash = NULL; - local->skipped = TRUE; - sync_conflict++; - } else { - DavResource *origin_res = dav_resource_new(sn, local->origin->path); - int origin_changed = remote_resource_is_changed( - sn, - dir, - db, - origin_res, - local->origin, - NULL); - if(origin_changed) { - // upload with put - ls_modified = ucx_list_prepend(ls_modified, local); + iter = cxListIterator(ls_copy); + for(int i=0;i<2;i++) { + cx_foreach(LocalResource*, local, iter) { + if(sync_shutdown) { + break; + } + + int err = 0; + DavResource *res = dav_resource_new(sn, local->path); + if(dav_exists(res)) { + printf("conflict: %s\n", local->path); + local->last_modified = 0; + nullfree(local->etag); + local->etag = NULL; + nullfree(local->hash); + local->hash = NULL; + local->skipped = TRUE; + sync_conflict++; } else { - printf("%s: %s -> %s\n", copy ? "copy":"move", local->origin->path, local->path); - err = sync_move_remote_resource( + DavResource *origin_res = dav_resource_new(sn, local->origin->path); + int origin_changed = remote_resource_is_changed( + sn, dir, db, origin_res, - local, - copy, - &sync_success); + local->origin, + NULL); + if(origin_changed) { + // upload with put + cxListInsert(ls_modified, 0, local); + } else { + printf("%s: %s -> %s\n", copy ? "copy":"move", local->origin->path, local->path); + err = sync_move_remote_resource( + dir, + db, + origin_res, + local, + copy, + &sync_success); + } } - } - - if(err) { - sync_error++; - print_resource_error(sn, res->path); - ret = -1; - error = 1; - } else { - LocalResource *dbres = ucx_map_cstr_remove(db->resources, local->path); - ucx_map_cstr_put(db->resources, local->path, local); - } - - if(copy && !elm->next) { - // finished copy, begin move - elm->next = ls_move; - copy = FALSE; - } + + if(err) { + sync_error++; + print_resource_error(sn, res->path); + ret = -1; + error = 1; + } else { + LocalResource *dbres = cxMapGet(db->resources, cx_hash_key_str(local->path)); + cxMapPut(db->resources, cx_hash_key_str(local->path), local); + } + } + copy = FALSE; + iter = cxListIterator(ls_move); } // upload changed files - ls_modified = ucx_list_concat(ls_new, ls_modified); - - for(UcxList *elm=ls_modified;elm && !sync_shutdown;elm=elm->next) { - LocalResource *local_res = elm->data; - int err = 0; - - DavResource *res = dav_resource_new(sn, local_res->path); - if(!res) { - print_resource_error(sn, local_res->path); - ret = -1; - sync_error++; - } else { - DavBool equal = FALSE; - DavBool res_conflict = FALSE; - int changed = remote_resource_is_changed(sn, dir, db, res, local_res, &equal); - if(equal) { - char *etag = dav_get_string_property(res, "D:getetag"); - if(local_res->metadata_updated) { - ls_update = ucx_list_prepend(ls_update, local_res); - } else if(etag) { - // update etag in db - if(local_res->etag) { - free(local_res->etag); + //ls_modified = ucx_list_concat(ls_new, ls_modified); + iter = cxListIterator(ls_new); + for(int i=0;i<2;i++) { + cx_foreach(LocalResource*, local_res, iter) { + if(sync_shutdown) { + break; + } + + int err = 0; + + DavResource *res = dav_resource_new(sn, local_res->path); + if(!res) { + print_resource_error(sn, local_res->path); + ret = -1; + sync_error++; + } else { + DavBool equal = FALSE; + DavBool res_conflict = FALSE; + int changed = remote_resource_is_changed(sn, dir, db, res, local_res, &equal); + if(equal) { + char *etag = dav_get_string_property(res, "D:getetag"); + if(local_res->metadata_updated) { + cxListInsert(ls_update, 0, local_res); + } else if(etag) { + // update etag in db + if(local_res->etag) { + free(local_res->etag); + } + local_res->etag = strdup(etag); } - local_res->etag = strdup(etag); + } else if(cdt && changed) { + printf("conflict: %s\n", local_res->path); + local_res->last_modified = 0; + nullfree(local_res->etag); + local_res->etag = NULL; + nullfree(local_res->hash); + local_res->hash = NULL; + local_res->skipped = TRUE; + sync_conflict++; + + if(local_res->link_target) { + free(local_res->link_target); + local_res->link_target = local_res->link_target_db; + local_res->link_target_db = NULL; + } + + res_conflict = TRUE; + } else { + if(local_res->link_target) { + printf( + "link: %s -> %s\n", + local_res->path, + local_res->link_target); + } else { + printf("put: %s\n", local_res->path); + } + if(sync_put_resource(dir, res, local_res, &sync_success)) { + sync_error++; + print_resource_error(sn, res->path); + ret = -1; + error = 1; + + err = 1; + } } - } else if(cdt && changed) { - printf("conflict: %s\n", local_res->path); - local_res->last_modified = 0; - nullfree(local_res->etag); - local_res->etag = NULL; - nullfree(local_res->hash); - local_res->hash = NULL; - local_res->skipped = TRUE; - sync_conflict++; - - if(local_res->link_target) { - free(local_res->link_target); - local_res->link_target = local_res->link_target_db; - local_res->link_target_db = NULL; - } - - res_conflict = TRUE; - } else { - if(local_res->link_target) { - printf( - "link: %s -> %s\n", - local_res->path, - local_res->link_target); - } else { - printf("put: %s\n", local_res->path); - } - if(sync_put_resource(dir, res, local_res, &sync_success)) { - sync_error++; - print_resource_error(sn, res->path); - ret = -1; - error = 1; - - err = 1; + + if(!err) { + LocalResource *dbres = cxMapRemoveAndGet(db->resources, cx_hash_key_str(local_res->path)); + // in case of a conflict, don't store the resource + // in the db, if it is new + if(!res_conflict || dbres) { + cxMapPut(db->resources, cx_hash_key_str(local_res->path), local_res); + } } } - - if(!err) { - LocalResource *dbres = ucx_map_cstr_remove(db->resources, local_res->path); - // in case of a conflict, don't store the resource - // in the db, if it is new - if(!res_conflict || dbres) { - ucx_map_cstr_put(db->resources, local_res->path, local_res); - } - } - } - - dav_resource_free(res); + + dav_resource_free(res); + } + iter = cxListIterator(ls_modified); } // metadata updates - for(UcxList *elm=ls_update;elm && !sync_shutdown;elm=elm->next) { - LocalResource *local_res = elm->data; + iter = cxListIterator(ls_update); + cx_foreach(LocalResource *, local_res, iter) { + if(sync_shutdown) { + break; + } DavResource *res = dav_resource_new(sn, local_res->path); if(local_res->metadata_updated) { printf("update: %s\n", local_res->path); if(!sync_update_metadata(dir, sn, res, local_res)) { - LocalResource *dbres = ucx_map_cstr_remove(db->resources, local_res->path); - ucx_map_cstr_put(db->resources, local_res->path, local_res); + LocalResource *dbres = cxMapGet(db->resources, cx_hash_key_str(local_res->path)); + cxMapPut(db->resources, cx_hash_key_str(local_res->path), local_res); } } } // delete all removed files - ls_delete = ucx_list_sort(ls_delete, (cmp_func)resource_pathlen_cmp, NULL); - - UcxList *cols = NULL; - UcxList **col_list = &cols; - UcxList *deletelist = ls_delete; + cxListSort(ls_delete); + + CxList *cols = cxLinkedListCreateSimple(CX_STORE_POINTERS); + CxList *col_list = cols; + CxList *deletelist = ls_delete; for(int i=0;i<2;i++) { // the first iteration deletes everything from ls_delete except // all collections, which are stored in cols // in the second run all collections will be deleted - for(UcxList *elm=deletelist;elm && !sync_shutdown;elm=elm->next) { - LocalResource *local = elm->data; + iter = cxListIterator(deletelist); + cx_foreach(LocalResource *, local, iter) { + if(sync_shutdown) { + break; + } if(local->keep) { continue; } @@ -2407,7 +2409,7 @@ break; } } else { - LocalResource *dbres = ucx_map_cstr_remove(db->resources, local->path); + LocalResource *dbres = cxMapRemoveAndGet(db->resources, cx_hash_key_str(local->path)); //local_resource_free(dbres); } } @@ -2477,14 +2479,14 @@ } SyncDirectory *dir = NULL; - UcxMap *files = NULL; + CxMap *files = NULL; if(syncdir) { dir = scfg_get_dir(syncdir); } LocalResource nres; if(a->argc > 0) { - files = ucx_map_new(a->argc+8); + files = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, a->argc+8); // get all specified files and check the syncdir SyncDirectory *sd = NULL; for(int i=0;iargc;i++) { @@ -2503,7 +2505,7 @@ } } - ucx_map_cstr_put(files, f.path, &nres); + cxMapPut(files, cx_hash_key_str(f.path), &nres); } dir = sd; } @@ -2536,16 +2538,16 @@ } remove_deleted_conflicts(dir, db); - UcxList *modified = NULL; - UcxList *deleted = NULL; + CxList *modified = cxLinkedListCreate(cxDefaultAllocator, (cx_compare_func)localres_cmp_path, CX_STORE_POINTERS); + CxList *deleted = cxLinkedListCreate(cxDefaultAllocator, (cx_compare_func)localres_cmp_path, CX_STORE_POINTERS); // iterate over all db resources and check if any resource is // modified or deleted - UcxMapIterator i = ucx_map_iterator(files ? files : db->resources); - LocalResource *resource; - UCX_MAP_FOREACH(key, resource, i) { + CxIterator i = cxMapIterator(files ? files : db->resources); + cx_foreach(CxMapEntry *, entry, i) { + LocalResource *resource = entry->value; if(resource == &nres) { - resource = ucx_map_get(db->resources, key); + resource = cxMapGet(db->resources, *entry->key); if(!resource) { continue; } @@ -2556,7 +2558,7 @@ if(sys_stat(file_path, &s)) { if(errno == ENOENT) { if(restore_removed) { - deleted = ucx_list_prepend(deleted, resource); + cxListAdd(deleted, resource); } } else { fprintf(stderr, "Cannot stat file: %s\n", file_path); @@ -2564,11 +2566,11 @@ } } else { if(files) { - modified = ucx_list_prepend(modified, resource); + cxListAdd(modified, resource); } else if(!resource->isdirectory && !S_ISDIR(s.st_mode)) { if(resource->last_modified != s.st_mtime || resource->size != s.st_size) { if(restore_modified) { - modified = ucx_list_prepend(modified, resource); + cxListAdd(modified, resource); } } } @@ -2578,19 +2580,19 @@ } if(files) { - ucx_map_free(files); + cxMapDestroy(files); } int ret = 0; // create DavSession - Repository *repo = get_repository(sstr(dir->repository)); + Repository *repo = get_repository(cx_str(dir->repository)); if(!repo) { fprintf(stderr, "Unkown repository %s\n", dir->name); return -1; } DavSession *sn = create_session(a, ctx, repo, dir->collection); - ucx_mempool_reg_destr(sn->mp, db, (ucx_destructor)destroy_db); + util_regdestr(sn->mp, db, (cx_destructor_func)destroy_db); if (cmd_getoption(a, "verbose")) { curl_easy_setopt(sn->handle, CURLOPT_VERBOSE, 1L); curl_easy_setopt(sn->handle, CURLOPT_STDERR, stderr); @@ -2619,70 +2621,74 @@ int sync_success = 0; int sync_error = 0; - UcxList *resources = ucx_list_concat(modified, deleted); - resources = ucx_list_sort(resources, (cmp_func)localres_cmp_path, NULL); - - UCX_FOREACH(elm, resources) { - LocalResource *resource = elm->data; - - DavResource *res = dav_get(sn, resource->path, "D:getetag,idav:status,idav:version-collection,idav:split,`idav:content-hash`,idavprops:tags,idavprops:finfo,idavprops:xattributes,idavprops:link"); - if(!res) { - printf("skip: %s\n", resource->path); - continue; - } - char *status = dav_get_string_property(res, "idav:status"); - if(status && !strcmp(status, "broken")) { - fprintf(stderr, "Resource %s broken\n", res->path); - continue; - } - - DavResource *vres = NULL; - DavBool update_local_entry = TRUE; - if(version) { - if(dir->versioning->type == VERSIONING_SIMPLE) { - vres = versioning_simple_find(res, version); - } else if(dir->versioning->type == VERSIONING_DELTAV) { - vres = versioning_deltav_find(res, version); + // TODO: old code sorted both modified and deleted, is this necessary? + //UcxList *resources = ucx_list_concat(modified, deleted); + //resources = ucx_list_sort(resources, (cmp_func)localres_cmp_path, NULL); + cxListSort(deleted); + + CxIterator iter = cxListIterator(modified); + for(int i=0;i<2;i++) { + cx_foreach(LocalResource *, resource, iter) { + DavResource *res = dav_get(sn, resource->path, "D:getetag,idav:status,idav:version-collection,idav:split,`idav:content-hash`,idavprops:tags,idavprops:finfo,idavprops:xattributes,idavprops:link"); + if(!res) { + printf("skip: %s\n", resource->path); + continue; } - if(!vres) { - fprintf(stderr, "Cannot find specified version for resource %s\n", res->path); - ret = 1; - break; + char *status = dav_get_string_property(res, "idav:status"); + if(status && !strcmp(status, "broken")) { + fprintf(stderr, "Resource %s broken\n", res->path); + continue; } - - // By restoring an old version of a file, the local dir is not - // in sync with the server anymore. Mark this file to change - // the metadata later, to make sure, the file will be detected - // as locally modified, on the next push/pull - update_local_entry = FALSE; - } else { - vres = res; - } - - // download the resource - if(!sync_shutdown) { - if(resource->isdirectory) { - char *local_path = create_local_path(dir, res->path); - if(sys_mkdir(local_path) && errno != EEXIST) { - fprintf(stderr, - "Cannot create directory %s: %s", - local_path, strerror(errno)); + + DavResource *vres = NULL; + DavBool update_local_entry = TRUE; + if(version) { + if(dir->versioning->type == VERSIONING_SIMPLE) { + vres = versioning_simple_find(res, version); + } else if(dir->versioning->type == VERSIONING_DELTAV) { + vres = versioning_deltav_find(res, version); + } + if(!vres) { + fprintf(stderr, "Cannot find specified version for resource %s\n", res->path); + ret = 1; + break; } - free(local_path); + + // By restoring an old version of a file, the local dir is not + // in sync with the server anymore. Mark this file to change + // the metadata later, to make sure, the file will be detected + // as locally modified, on the next push/pull + update_local_entry = FALSE; } else { - if(sync_get_resource(a, dir, res->path, vres, db, update_local_entry, &sync_success)) { - fprintf(stderr, "sync_get_resource failed for resource: %s\n", res->path); - sync_error++; - } else if(!update_local_entry) { - LocalResource *lr = ucx_map_cstr_get(db->resources, res->path); - if(lr) { - lr->last_modified = 0; - nullfree(lr->hash); - lr->hash = NULL; - } // else should not happen + vres = res; + } + + // download the resource + if(!sync_shutdown) { + if(resource->isdirectory) { + char *local_path = create_local_path(dir, res->path); + if(sys_mkdir(local_path) && errno != EEXIST) { + fprintf(stderr, + "Cannot create directory %s: %s", + local_path, strerror(errno)); + } + free(local_path); + } else { + if(sync_get_resource(a, dir, res->path, vres, db, update_local_entry, &sync_success)) { + fprintf(stderr, "sync_get_resource failed for resource: %s\n", res->path); + sync_error++; + } else if(!update_local_entry) { + LocalResource *lr = cxMapGet(db->resources, cx_hash_key_str(res->path)); + if(lr) { + lr->last_modified = 0; + nullfree(lr->hash); + lr->hash = NULL; + } // else should not happen + } } } } + iter = cxListIterator(deleted); } // unlock repository @@ -2730,25 +2736,25 @@ void print_outgoing( CmdArgs *args, - UcxList *ls_new, - UcxList *ls_modified, - UcxList *ls_conflict, - UcxList *ls_update, - UcxList *ls_delete, - UcxList *ls_move, - UcxList *ls_copy, - UcxList *ls_mkcol) + CxList *ls_new, + CxList *ls_modified, + CxList *ls_conflict, + CxList *ls_update, + CxList *ls_delete, + CxList *ls_move, + CxList *ls_copy, + CxList *ls_mkcol) { int64_t total_size = 0; - size_t len_new = ucx_list_size(ls_new); - size_t len_mod = ucx_list_size(ls_modified); - size_t len_cnf = ucx_list_size(ls_conflict); - size_t len_upd = ucx_list_size(ls_update); - size_t len_del = ucx_list_size(ls_delete); - size_t len_mov = ucx_list_size(ls_move); - size_t len_cpy = ucx_list_size(ls_copy); - size_t len_mkc = ucx_list_size(ls_mkcol); + size_t len_new = ls_new->size; + size_t len_mod = ls_modified->size; + size_t len_cnf = ls_conflict->size; + size_t len_upd = ls_update->size; + size_t len_del = ls_delete->size; + size_t len_mov = ls_move->size; + size_t len_cpy = ls_copy->size; + size_t len_mkc = ls_mkcol->size; size_t total = len_new + len_mod + len_cnf + len_upd + len_del + len_mov + len_cpy + len_mkc; if(total == 0) { @@ -2761,32 +2767,32 @@ if(ls_mkcol) { printf("Directories:\n"); - UCX_FOREACH(elm, ls_mkcol) { - LocalResource *res = elm->data; + CxIterator i = cxListIterator(ls_mkcol); + cx_foreach(LocalResource *, res, i) { printf(" %-49s\n", res->path+1); total_size += res->size; } } if(ls_new) { printf("New:\n"); - UCX_FOREACH(elm, ls_new) { - LocalResource *res = elm->data; - print_outgoging_file(elm->data); + CxIterator i = cxListIterator(ls_new); + cx_foreach(LocalResource *, res, i) { + print_outgoging_file(res); total_size += res->size; } } if(ls_modified) { printf("Modified:\n"); - UCX_FOREACH(elm, ls_modified) { - LocalResource *res = elm->data; - print_outgoging_file(elm->data); + CxIterator i = cxListIterator(ls_modified); + cx_foreach(LocalResource *, res, i) { + print_outgoging_file(res); total_size += res->size; } } if(ls_update) { printf("Update:\n"); - UCX_FOREACH(elm, ls_update) { - LocalResource *res = elm->data; + CxIterator i = cxListIterator(ls_update); + cx_foreach(LocalResource *, res, i) { char *lastmodified = util_date_str(res->last_modified); printf(" %-49s %12s\n", res->path+1, lastmodified); free(lastmodified); @@ -2794,29 +2800,29 @@ } if(ls_delete) { printf("Delete:\n"); - UCX_FOREACH(elm, ls_delete) { - LocalResource *res = elm->data; + CxIterator i = cxListIterator(ls_delete); + cx_foreach(LocalResource *, res, i) { printf(" %s\n", res->path+1); } } if(ls_copy) { printf("Copy:\n"); - UCX_FOREACH(elm, ls_copy) { - LocalResource *res = elm->data; + CxIterator i = cxListIterator(ls_copy); + cx_foreach(LocalResource *, res, i) { printf("%s -> %s\n", res->origin->path+1, res->path); } } if(ls_move) { printf("Move:\n"); - UCX_FOREACH(elm, ls_move) { - LocalResource *res = elm->data; + CxIterator i = cxListIterator(ls_move); + cx_foreach(LocalResource *, res, i) { printf("%s -> %s\n", res->origin->path+1, res->path); } } if(ls_conflict) { printf("Conflict\n"); - UCX_FOREACH(elm, ls_conflict) { - LocalResource *res = elm->data; + CxIterator i = cxListIterator(ls_conflict); + cx_foreach(LocalResource *, res, i) { printf(" %s\n", res->path+1); } } @@ -2836,17 +2842,18 @@ free(total_size_str); } -UcxList* local_scan(SyncDirectory *dir, SyncDatabase *db) { - UcxList *resources = NULL; +CxList* local_scan(SyncDirectory *dir, SyncDatabase *db) { + CxList *resources = cxLinkedListCreateSimple(CX_STORE_POINTERS); char *path = strdup("/"); - UcxList *stack = ucx_list_prepend(NULL, path); - while(stack) { + CxList *stack = cxLinkedListCreateSimple(CX_STORE_POINTERS); + cxListInsert(stack, 0, path); + while(stack->size > 0) { // get a directory path from the stack and read all entries // if an entry is a directory, put it on the stack - char *p = stack->data; - stack = ucx_list_remove(stack, stack); + char *p = cxListAt(stack, 0); + cxListRemove(stack, 0); char *local_path = create_local_path(dir, p); SYS_DIR local_dir = sys_opendir(local_path); @@ -2864,11 +2871,11 @@ LocalResource *res = local_resource_new(dir, db, new_path); if(res) { if(res->isdirectory) { - resources = ucx_list_append(resources, res); - stack = ucx_list_prepend(stack, new_path); + cxListAdd(resources, res); + cxListInsert(stack, 0, new_path); free_new_path = FALSE; } else { - resources = ucx_list_append(resources, res); + cxListAdd(resources, res); } } if(free_new_path) { @@ -2885,50 +2892,6 @@ return resources; } -UcxList* read_changes(SyncDirectory *dir, SyncDatabase *db) { - UcxProperties *parser = ucx_properties_new(); - parser->delimiter = ':'; - - UcxList *resources = NULL; - sstr_t name; - sstr_t value; - - char buf[STDIN_BUF_SIZE]; - size_t r; - while(!feof(stdin)) { - r = fread(buf, 1, STDIN_BUF_SIZE, stdin); - ucx_properties_fill(parser, buf, r); - while(ucx_properties_next(parser, &name, &value)) { - if(value.length == 0) { - fprintf(stderr, "Wrong input\n"); - continue; - } - if(value.ptr[0] == '"' - && value.length > 2 - && value.ptr[value.length - 1] == '"') - { - value.ptr[value.length - 1] = '\0'; - value.ptr++; - value.length -= 2; - } - value = sstrdup(value); - - if(!sstrcmp(name, S("put"))) { - LocalResource *res = local_resource_new(dir, db, value.ptr); - if(res) { - resources = ucx_list_append(resources, res); - } - } else if(!sstrcmp(name, S("remove"))) { - ucx_map_sstr_remove(db->resources, value); - } - - free(value.ptr); - } - } - ucx_properties_free(parser); - - return resources; -} LocalResource* local_resource_new(SyncDirectory *dir, SyncDatabase *db, char *path) { char *file_path = create_local_path(dir, path); @@ -3051,11 +3014,11 @@ SyncDirectory *dir, SyncDatabase *db, LocalResource *res, - UcxMap *svrres, + CxMap *svrres, DavBool restore_removed, DavBool restore_modified) { - LocalResource *db_res = ucx_map_cstr_get(db->resources, res->path); + LocalResource *db_res = cxMapGet(db->resources, cx_hash_key_str(res->path)); res->tags_updated = 0; if(db_res) { // copy some metadata from db_res, that localscan does not deliver @@ -3088,7 +3051,7 @@ // check if the file must be restored on the server if(svrres) { - DavResource *remote = ucx_map_cstr_get(svrres, res->path); + DavResource *remote = cxMapGet(svrres, cx_hash_key_str(res->path)); if(restore_removed && !remote) { return 1; } @@ -3110,7 +3073,7 @@ res->tags_updated = 1; res->metadata_updated = 1; } else if(dir->tagconfig && dir->tagconfig->detect_changes ) { - UcxBuffer *tags = sync_get_file_tag_data(dir, res); + CxBuffer *tags = sync_get_file_tag_data(dir, res); if(tags) { if(db_res->tags_hash) { char *hash = dav_create_hash(tags->space, tags->size); @@ -3268,9 +3231,9 @@ // the resource is on the server and the client has no etag ret = 1; } else if(etag) { - sstr_t e = sstr(etag); - if(sstrprefix(e, S("W/"))) { - e = sstrsubs(e, 2); + cxstring e = cx_str(etag); + if(cx_strprefix(e, CX_STR("W/"))) { + e = cx_strsubs(e, 2); } if(strcmp(e.ptr, res->etag)) { ret = 1; @@ -3306,11 +3269,11 @@ return; } - scstr_t e = scstr(etag); - if(sstrprefix(e, S("W/"))) { - e = scstrsubs(e, 2); - } - local->etag = sstrdup(e).ptr; + cxstring e = cx_str(etag); + if(cx_strprefix(e, CX_STR("W/"))) { + e = cx_strsubs(e, 2); + } + local->etag = cx_strdup(e).ptr; } char* resource_local_path(DavResource *res) { @@ -3334,9 +3297,9 @@ return 0; } else if(local->blocksize > 0) { local_blocksize = (size_t)local->blocksize; - } else { - UCX_FOREACH(elm, dir->splitconfig) { - SplitConfig *sc = elm->data; + } else if(dir->splitconfig) { + CxIterator i = cxListIterator(dir->splitconfig); + cx_foreach(SplitConfig *, sc, i) { if(sc->filter) { if(res_matches_filter(sc->filter, local->path)) { continue; @@ -3462,38 +3425,6 @@ return ret; } -int sync_tags_equal(UcxList *tags1, UcxList *tags2) { - if(!tags1) { - return tags2 ? 0 : 1; - } - if(!tags2) { - return tags1 ? 0 : 1; - } - - UcxMap *map1 = ucx_map_new(32); - UCX_FOREACH(elm, tags1) { - DavTag *t = elm->data; - ucx_map_cstr_put(map1, t->name, t); - } - - int equal = 1; - int i = 0; - UCX_FOREACH(elm, tags2) { - DavTag *t = elm->data; - if(!ucx_map_cstr_get(map1, t->name)) { - equal = 0; - break; - } - i++; - } - - if(i != map1->count) { - equal = 0; - } - ucx_map_free(map1); - return equal; -} - int sync_store_metadata(SyncDirectory *dir, const char *path, LocalResource *local, DavResource *res) { int ret = 0; @@ -3545,12 +3476,12 @@ // create a map of all currently available local attributes ssize_t nelm = 0; char **list = xattr_list(path, &nelm); - UcxMap *current_xattr = NULL; + CxMap *current_xattr = NULL; if(nelm > 0) { - current_xattr = ucx_map_new(nelm + 8); + current_xattr = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, nelm + 8); for(int i=0;inattr : 0; for(int i=0;ivalues[i]; + cxmutstr value = xattr->values[i]; if(xattr_set(path, xattr->names[i], value.ptr, value.length)) { fprintf(stderr, "Cannot store xattr '%s' for file: %s\n", @@ -3572,7 +3503,7 @@ // to detect which xattributes are removed, we remove all new // attributes from the map and all remaining attributes must // be removed with xattr_remove - char *value = ucx_map_cstr_remove(current_xattr, xattr->names[i]); + char *value = cxMapRemoveAndGet(current_xattr, cx_hash_key_str(xattr->names[i])); if(value) { free(value); } @@ -3580,13 +3511,13 @@ } if(current_xattr) { - UcxMapIterator i = ucx_map_iterator(current_xattr); + CxIterator i = cxMapIteratorValues(current_xattr); char *value = NULL; - UCX_MAP_FOREACH(key, value, i) { + cx_foreach(char *, value, i) { (void)xattr_remove(path, value); // don't print error free(value); } - ucx_map_free(current_xattr); + cxMapDestroy(current_xattr); } return 0; @@ -3598,7 +3529,7 @@ } char *remote_hash = NULL; - UcxList *tags = NULL; + CxList *tags = NULL; if(dir->tagconfig) { DavXmlNode *tagsprop = dav_get_property_ns(res, DAV_PROPS_NS, "tags"); if(tagsprop) { @@ -3609,7 +3540,7 @@ DavBool store_tags = FALSE; DavBool tags_changed = FALSE; - UcxList *local_tags = sync_get_file_tags(dir, local, &tags_changed, NULL); + CxList *local_tags = sync_get_file_tags(dir, local, &tags_changed, NULL); if(tags_changed) { switch(dir->tagconfig->conflict) { case TAG_NO_CONFLICT: { @@ -3626,7 +3557,7 @@ break; } case TAG_MERGE: { - UcxList *new_tags = merge_tags(local_tags, tags); + CxList *new_tags = merge_tags(local_tags, tags); // TODO: free tags and local_tags tags = new_tags; store_tags = TRUE; @@ -3636,7 +3567,7 @@ } } } else { - if(!sync_tags_equal(tags, local_tags)) { + if(!compare_taglists(tags, local_tags)) { store_tags = TRUE; } // TODO: free local_tags @@ -3661,10 +3592,10 @@ return ret; } -int sync_store_tags_local(SyncDirectory *dir, LocalResource *local, const char *path, UcxList *tags) { +int sync_store_tags_local(SyncDirectory *dir, LocalResource *local, const char *path, CxList *tags) { int ret = 0; if(dir->tagconfig->store == TAG_STORE_XATTR) { - UcxBuffer *data = NULL; + CxBuffer *data = NULL; if(tags) { switch(dir->tagconfig->local_format) { default: break; @@ -3704,7 +3635,7 @@ } else { free(data_hash); } - ucx_buffer_free(data); + cxBufferFree(data); } else { ret = -1; } @@ -3724,14 +3655,14 @@ return ret; } -UcxBuffer* sync_get_file_tag_data(SyncDirectory *dir, LocalResource *res) { +CxBuffer* sync_get_file_tag_data(SyncDirectory *dir, LocalResource *res) { if(!dir->tagconfig) { return NULL; } if(res->cached_tags) { return res->cached_tags; } - UcxBuffer *buf = NULL; + CxBuffer *buf = NULL; if(dir->tagconfig->store == TAG_STORE_XATTR) { ssize_t tag_length = 0; char *local_path = create_local_path(dir, local_resource_path(res)); @@ -3742,7 +3673,7 @@ free(local_path); if(tag_length > 0) { - buf = ucx_buffer_new(tag_data, (size_t)tag_length, UCX_BUFFER_AUTOFREE); + buf = cxBufferCreate(tag_data, (size_t)tag_length, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS); buf->size = (size_t)tag_length; } } @@ -3750,10 +3681,10 @@ return buf; } -UcxList* sync_get_file_tags(SyncDirectory *dir, LocalResource *res, DavBool *changed, char **newhash) { +CxList* sync_get_file_tags(SyncDirectory *dir, LocalResource *res, DavBool *changed, char **newhash) { if(changed) *changed = FALSE; - UcxList *tags = NULL; + CxList *tags = NULL; if(!res) { return NULL; @@ -3766,7 +3697,7 @@ *changed = TRUE; } if(dir->tagconfig->store == TAG_STORE_XATTR) { - UcxBuffer *tag_buf = res->cached_tags ? + CxBuffer *tag_buf = res->cached_tags ? res->cached_tags : sync_get_file_tag_data(dir, res); @@ -4011,7 +3942,7 @@ // this macro is only a workaround for a netbeans bug #define LOG10 log10 -static UcxList* upload_parts( +static CxList* upload_parts( LocalResource *local, DavResource *res, FILE *in, @@ -4067,7 +3998,8 @@ return NULL; } - UcxMap *updated_parts_map = ucx_map_new((nblocks/2)+64); + CxMap *updated_parts_map = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, (nblocks/2)+64); + updated_parts_map->simple_destructor = (cx_destructor_func)filepart_free; int blockindex = 0; int uploaded_parts = 0; @@ -4121,7 +4053,7 @@ FilePart *f = calloc(1, sizeof(FilePart)); f->block = blockindex; f->hash = block_hash; - ucx_map_cstr_put(updated_parts_map, name, f); + cxMapPut(updated_parts_map, cx_hash_key_str(name), f); } dav_resource_free(part); uploaded_parts++; @@ -4138,8 +4070,7 @@ free(buffer); if(*err) { - ucx_map_free_content(updated_parts_map, (ucx_destructor)filepart_free); - ucx_map_free(updated_parts_map); + cxMapDestroy(updated_parts_map); return NULL; } @@ -4151,18 +4082,17 @@ // get etags from uploaded resources // also delete everything, that is not part of the file - UcxList *updated_parts = NULL; + CxList *updated_parts = cxLinkedListCreateSimple(CX_STORE_POINTERS); DavResource *parts = dav_query(res->session, "select D:getetag from %s order by name", res->path); if(!parts) { print_resource_error(res->session, parts->path); *err = 1; - ucx_map_free_content(updated_parts_map, (ucx_destructor)filepart_free); - ucx_map_free(updated_parts_map); + cxMapDestroy(updated_parts_map); return NULL; } DavResource *part = parts->children; while(part) { - FilePart *fp = ucx_map_cstr_remove(updated_parts_map, part->name); + FilePart *fp = cxMapRemoveAndGet(updated_parts_map, cx_hash_key_str(part->name)); // every part we uploaded is in the map // if we get parts that are not in the map, someone else uploaded it if(fp) { @@ -4173,7 +4103,7 @@ } fp->etag = strdup(etag); - updated_parts = ucx_list_append(updated_parts, fp); + cxListAdd(updated_parts, fp); } // else { wtf is wrong with this resource } } else { uint64_t name_partnum = 0; @@ -4200,14 +4130,13 @@ } dav_resource_free_all(parts); - ucx_map_free_content(updated_parts_map, (ucx_destructor)filepart_free); - ucx_map_free(updated_parts_map); + cxMapDestroy(updated_parts_map); *err = 0; return updated_parts; } -void update_parts(LocalResource *local, UcxList *updates, uint64_t numparts) { +void update_parts(LocalResource *local, CxList *updates, uint64_t numparts) { size_t old_num = local->numparts; if(old_num > numparts) { // free old parts @@ -4226,8 +4155,12 @@ local->numparts = numparts; } - UCX_FOREACH(elm, updates) { - FilePart *p = elm->data; + if(!updates) { + return; + } + + CxIterator i = cxListIterator(updates); + cx_foreach(FilePart *, p, i) { if(p->block > numparts) { // just make sure things don't explode in case some weird stuff // is going on @@ -4285,7 +4218,7 @@ DavBool issplit = split_blocksize == 0 ? FALSE : TRUE; int split_err = 0; - UcxList *parts = NULL; + CxList *parts = NULL; uint64_t blockcount = 0; if(islink) { @@ -4455,7 +4388,7 @@ LocalResource *local_origin = local->origin; if(!copy) { - ucx_map_cstr_remove(db->resources, local_origin->path); + cxMapRemove(db->resources, cx_hash_key_str(local_origin->path)); } // set resource metadata @@ -4496,7 +4429,7 @@ DavSession *sn, LocalResource *local_res, int *counter, - UcxList **cols) + CxList *cols) { DavResource *res = dav_get(sn, local_res->path, "D:getetag,idav:split"); if(!res) { @@ -4508,7 +4441,7 @@ if(res->iscollection) { DavXmlNode *split = dav_get_property_ns(res, DAV_NS, "split"); if(cols) { - *cols = ucx_list_append(*cols, local_res); + cxListAdd(cols, local_res); } else if(split || !res->children) { printf("delete: %s\n", res->path); if(dav_delete(res)) { @@ -4582,7 +4515,7 @@ // get local tags DavBool changed = 0; char *tags_hash = NULL; - UcxList *tags = sync_get_file_tags(dir, local, &changed, &tags_hash); + CxList *tags = sync_get_file_tags(dir, local, &changed, &tags_hash); char *new_remote_hash = nullstrdup(tags_hash); if(changed || local->tags_updated) { DavBool store_tags = TRUE; @@ -4594,7 +4527,7 @@ if(dav_load_prop(res, &p, 1) && sn->error != DAV_NOT_FOUND) { print_resource_error(sn, res->path); } - UcxList *remote_tags = NULL; + CxList *remote_tags = NULL; DavXmlNode *tagsprop = dav_get_property_ns(res, DAV_PROPS_NS, "tags"); if(tagsprop) { remote_tags = parse_dav_xml_taglist(tagsprop); @@ -4613,7 +4546,7 @@ break; } case TAG_MERGE: { - UcxList *new_tags = merge_tags(tags, remote_tags); + CxList *new_tags = merge_tags(tags, remote_tags); free_taglist(tags); tags = new_tags; @@ -4706,12 +4639,11 @@ } void remove_deleted_conflicts(SyncDirectory *dir, SyncDatabase *db) { - char **dc = calloc(sizeof(void*), db->conflict->count); + char **dc = calloc(sizeof(void*), db->conflict->size); int numdc = 0; - UcxMapIterator i = ucx_map_iterator(db->conflict); - LocalResource *res; - UCX_MAP_FOREACH(key, res, i) { + CxIterator i = cxMapIteratorValues(db->conflict); + cx_foreach(LocalResource *, res, i) { char *path = create_local_path(dir, res->path); SYS_STAT s; if(sys_stat(path, &s)) { @@ -4727,18 +4659,16 @@ } for(int i=0;iconflict, dc[i]); + cxMapRemove(db->conflict, cx_hash_key_str(dc[i])); } free(dc); } static void resolve_skipped(SyncDatabase *db) { - UcxKey k; - LocalResource *res; - UcxMapIterator i = ucx_map_iterator(db->resources); + CxIterator i = cxMapIteratorValues(db->resources); int skipped = 0; - UCX_MAP_FOREACH(k, res, i) { + cx_foreach(LocalResource *, res, i) { if(res->skipped) { skipped++; fprintf(stderr, "skipped from push: %s\n", res->path); @@ -4777,9 +4707,9 @@ int ret = 0; // remove conflicts - int num_conflict = db->conflict->count; - ucx_map_free_content(db->conflict, (ucx_destructor)local_resource_free); - ucx_map_clear(db->conflict); + int num_conflict = db->conflict->size; + //ucx_map_free_content(db->conflict, (ucx_destructor)local_resource_free); + cxMapClear(db->conflict); // store db if(store_db(db, dir->database, dir->db_settings)) { @@ -4829,9 +4759,8 @@ int ret = 0; // delete all conflict files - UcxMapIterator i = ucx_map_iterator(db->conflict); - LocalResource *res; - UCX_MAP_FOREACH(key, res, i) { + CxIterator i = cxMapIterator(db->conflict); + cx_foreach(LocalResource*, res, i) { printf("delete: %s\n", res->path); char *path = create_local_path(dir, res->path); if(sys_unlink(path)) { @@ -4844,8 +4773,8 @@ } free(path); } - ucx_map_free_content(db->conflict, (ucx_destructor)local_resource_free); - ucx_map_clear(db->conflict); + //ucx_map_free_content(db->conflict, (ucx_destructor)local_resource_free); + cxMapClear(db->conflict); // store db if(store_db(db, dir->database, dir->db_settings)) { @@ -4893,30 +4822,28 @@ remove_deleted_conflicts(dir, db); // get all conflict sources - UcxMapIterator i = ucx_map_iterator(db->conflict); - LocalResource *res; - UcxList* conflict_sources = NULL; - UCX_MAP_FOREACH(key, res, i) { - conflict_sources = ucx_list_append(conflict_sources, res->conflict_source); + CxIterator i = cxMapIteratorValues(db->conflict); + CxList* conflict_sources = cxLinkedListCreateSimple(CX_STORE_POINTERS); + cx_foreach(LocalResource *, res, i) { + cxListAdd(conflict_sources, res->conflict_source); } // print unique conflict sources - conflict_sources = ucx_list_sort(conflict_sources, ucx_cmp_str, NULL); - UCX_FOREACH(elem, conflict_sources) { - char* path = elem->data; - if(cmd_getoption(a, "verbose")) { - int confl_count = 1; - while(elem->next && !strcmp(elem->next->data, path)) { - elem = elem->next; - ++confl_count; - } - printf("%s (%d)\n", path, confl_count); - } else { - printf("%s\n", path); - while(elem->next && !strcmp(elem->next->data, path)) { - elem = elem->next; - } - } + // TODO: set cmpfunc at map creation + conflict_sources->cmpfunc = (cx_compare_func)strcmp; + cxListSort(conflict_sources); + i = cxListIterator(conflict_sources); + char *prev = ""; + cx_foreach(char *, path, i) { + // TODO: implement verbose print if(cmd_getoption(a, "verbose")) + // printf("%s (%d)\n", path, confl_count); + if(!strcmp(path, prev)) { + continue; + } + + printf("%s\n", path); + + prev = path; } // cleanup @@ -5029,7 +4956,7 @@ fprintf(stderr, "No versioning configured for syncdir %s\n", dir->name); } - Repository *repo = get_repository(sstr(dir->repository)); + Repository *repo = get_repository(cx_str(dir->repository)); if(!repo) { fprintf(stderr, "Unknown repository %s\n", dir->repository); return -1; @@ -5043,7 +4970,7 @@ remove_deleted_conflicts(dir, db); DavSession *sn = create_session(a, ctx, repo, dir->collection); - ucx_mempool_reg_destr(sn->mp, db, (ucx_destructor)destroy_db); + util_regdestr(sn->mp, db, (cx_destructor_func)destroy_db); if (cmd_getoption(a, "verbose")) { curl_easy_setopt(sn->handle, CURLOPT_VERBOSE, 1L); curl_easy_setopt(sn->handle, CURLOPT_STDERR, stderr); @@ -5079,23 +5006,23 @@ } DavResource *child = vcol->children; - UcxList *children = NULL; + CxList *children = cxLinkedListCreate(cxDefaultAllocator, (cx_compare_func)strcmp, CX_STORE_POINTERS); while(child) { - children = ucx_list_append(children, child); + cxListAdd(children, child); child = child->next; } - children = ucx_list_sort(children, ucx_cmp_str, NULL); + cxListSort(children); DavBool first = 1; - UCX_FOREACH(elm, children) { - DavResource *c = elm->data; + CxIterator i = cxListIterator(children); + cx_foreach(DavResource *, c, i) { if(!first) { putchar('\n'); } print_resource_version(c, c->name); first = 0; } - ucx_list_free(children); + cxListDestroy(children); } while(0); } else if(dir->versioning->type == VERSIONING_DELTAV) { DavResource *versions = dav_versiontree(res, NULL); @@ -5271,6 +5198,10 @@ } int cmd_tagop(CmdArgs *args, int cmd) { + // TODO: port to ucx 3 + return 1; +#if 0 + SyncFile file; int ret = 0; char *path = args->argv[0]; @@ -5293,13 +5224,13 @@ } LocalResource *newres = NULL; - LocalResource *localres = ucx_map_cstr_get(db->resources, file.path); + LocalResource *localres = cxMapGet(db->resources, cx_hash_key_str(file.path)); if(!localres) { newres = calloc(1, sizeof(LocalResource)); newres->path = strdup(file.path); localres = newres; } - UcxList *tags = NULL; + CxList *tags = NULL; DavBool store_tags = FALSE; if(cmd != CMD_TAG_SET) { @@ -5307,7 +5238,7 @@ char *tagcolor = NULL; // TODO: get color tags = sync_get_file_tags(file.dir, localres, NULL, NULL); - UcxList *x = NULL; + CxList *x = NULL; UCX_FOREACH(elm, tags) { DavTag *t = elm->data; if(cmd == CMD_TAG_LIST) { @@ -5374,6 +5305,7 @@ free(file.path); return ret; +#endif } int isfileindir(SyncDirectory *dir, const char *path, SyncFile *f) { @@ -5406,16 +5338,16 @@ // TODO: normalize path DavBool not_in_dir = 0; - scstr_t fp = scstr(fullpath); - scstr_t dp = scstr(dir->path); + cxstring fp = cx_str(fullpath); + cxstring dp = cx_str(dir->path); if(fp.length == dp.length) { - if(sstrcmp(fp, dp)) { + if(cx_strcmp(fp, dp)) { not_in_dir = 1; } } else if(fp.length < dp.length) { not_in_dir = 1; } else { - if(!sstrprefix(fp, dp)) { + if(!cx_strprefix(fp, dp)) { not_in_dir = 1; } else { if(dp.ptr[dp.length-1] == '/') { @@ -5466,10 +5398,8 @@ } else { SyncDirectory *target = NULL; - UcxMapIterator i = scfg_directory_iterator(); - UcxKey k; - SyncDirectory *dir; - UCX_MAP_FOREACH(key, dir, i) { + CxIterator i = scfg_directory_iterator(); + cx_foreach(SyncDirectory *, dir, i) { if(isfileindir(dir, path, f)) { if(target) { return 5; @@ -5500,11 +5430,17 @@ int cmd_add_directory(CmdArgs *args) { + /* if(!get_repositories()) { fprintf(stderr, "No repositories available. Run 'dav add-repository' first.\n"); fprintf(stderr, "Abort\n"); return -1; } + */ + + // TODO: port to ucx 3 + return 1; +#if 0 printf("Each sync directory must have an unique name.\n"); char *name = assistant_getcfg("name"); @@ -5525,10 +5461,9 @@ } printf("Specify webdav repository.\n"); - UcxList *repos = get_repositories(); + CxIterator repos = get_repositories(); int i = 0; - UCX_FOREACH(elm, repos) { - Repository *r = elm->data; + cx_foreach(Repository *, r, repos) { printf("%d) %s\n", i, r->name); i++; } @@ -5590,12 +5525,12 @@ free(db); return ret; +#endif } int cmd_list_dirs() { - UcxMapIterator iter = scfg_directory_iterator(); - SyncDirectory *dir; - UCX_MAP_FOREACH(key, dir, iter) { + CxIterator iter = scfg_directory_iterator(); + cx_foreach(SyncDirectory *, dir, iter) { printf("%s\n", dir->name); } return 0; @@ -5604,19 +5539,18 @@ int cmd_check_repositories(CmdArgs *a) { int ret = EXIT_SUCCESS; - UcxList *reponames = NULL; + CxList *reponames = cxLinkedListCreateSimple(CX_STORE_POINTERS); { - UcxMapIterator iter = scfg_directory_iterator(); - SyncDirectory *dir; - UCX_MAP_FOREACH(key, dir, iter) { - reponames = ucx_list_append(reponames, dir->repository); - } - } - - UCX_FOREACH(listelem, reponames) { - char *reponame = listelem->data; + CxIterator iter = scfg_directory_iterator(); + cx_foreach(SyncDirectory *, dir, iter) { + cxListAdd(reponames, dir->repository); + } + } + + CxIterator iter = cxListIterator(reponames); + cx_foreach(char *, reponame, iter) { printf("Checking %s... ", reponame); - Repository* repo = get_repository(sstr(reponame)); + Repository* repo = get_repository(cx_str(reponame)); if (!repo) { printf(" not found in config.xml!\n"); ret = EXIT_FAILURE; @@ -5640,13 +5574,13 @@ } } - ucx_list_free(reponames); + cxListDestroy(reponames); return ret; } char* create_locktoken_file(const char *syncdirname, const char *locktoken) { - sstr_t fname = ucx_sprintf("locktoken-%s.txt", syncdirname); + cxmutstr fname = cx_asprintf("locktoken-%s.txt", syncdirname); char *path = config_file_path(fname.ptr); free(fname.ptr); diff -r a569148841ff -r efbd59642577 dav/sync.h --- a/dav/sync.h Sun Apr 16 14:12:24 2023 +0200 +++ b/dav/sync.h Fri Apr 21 21:25:32 2023 +0200 @@ -31,7 +31,8 @@ #include #include -#include +#include +#include #include @@ -98,7 +99,7 @@ pthread_t start_sighandler(pthread_mutex_t *mutex) ; void stop_sighandler(pthread_mutex_t *mutex, pthread_t tid); -void res2map(DavResource *root, UcxMap *map); +void res2map(DavResource *root, CxMap *map); int cmd_pull(CmdArgs *args, DavBool incoming); int cmd_push(CmdArgs *args, DavBool outgoing, DavBool archive); @@ -106,14 +107,14 @@ void print_outgoing( CmdArgs *args, - UcxList *ls_new, - UcxList *ls_modified, - UcxList *ls_conflict, - UcxList *ls_update, - UcxList *ls_delete, - UcxList *ls_move, - UcxList *ls_copy, - UcxList *ls_mkcol); + CxList *ls_new, + CxList *ls_modified, + CxList *ls_conflict, + CxList *ls_update, + CxList *ls_delete, + CxList *ls_move, + CxList *ls_copy, + CxList *ls_mkcol); RemoteChangeType resource_get_remote_change( CmdArgs *a, @@ -149,15 +150,15 @@ char* create_tmp_download_path(char *path); void move_to_trash(SyncDirectory *dir, char *path); -UcxList* local_scan(SyncDirectory *dir, SyncDatabase *db); -UcxList* read_changes(SyncDirectory *dir, SyncDatabase *db); +CxList* local_scan(SyncDirectory *dir, SyncDatabase *db); +CxList* read_changes(SyncDirectory *dir, SyncDatabase *db); LocalResource* local_resource_new(SyncDirectory *dir, SyncDatabase *db, char *path); char* local_resource_path(LocalResource *res); int local_resource_is_changed( SyncDirectory *dir, SyncDatabase *db, LocalResource *res, - UcxMap *svrres, + CxMap *svrres, DavBool restore_deleted, DavBool restore_modified); int remote_resource_is_changed( @@ -183,13 +184,13 @@ int sync_set_status(DavResource *res, char *status); int sync_remove_status(DavResource *res); -UcxBuffer* sync_get_file_tag_data(SyncDirectory *dir, LocalResource *res); -UcxList* sync_get_file_tags(SyncDirectory *dir, LocalResource *res, DavBool *changed, char **newhash); -int sync_tags_equal(UcxList *tags1, UcxList *tags2); +CxBuffer* sync_get_file_tag_data(SyncDirectory *dir, LocalResource *res); +CxList* sync_get_file_tags(SyncDirectory *dir, LocalResource *res, DavBool *changed, char **newhash); +int sync_tags_equal(CxList *tags1, CxList *tags2); int sync_store_metadata(SyncDirectory *dir, const char *path, LocalResource *local, DavResource *res); int sync_store_xattr(SyncDirectory *dir, const char *path, XAttributes *xattr); int sync_store_tags(SyncDirectory *dir, const char *path, LocalResource *local, DavResource *res); -int sync_store_tags_local(SyncDirectory *dir, LocalResource *local, const char *path, UcxList *tags); +int sync_store_tags_local(SyncDirectory *dir, LocalResource *local, const char *path, CxList *tags); int sync_put_resource( SyncDirectory *dir, DavResource *res, @@ -203,7 +204,7 @@ LocalResource *local, DavBool copy, int *counter); -int sync_delete_remote_resource(SyncDirectory *dir, DavSession *sn, LocalResource *res, int *counter, UcxList **cols); +int sync_delete_remote_resource(SyncDirectory *dir, DavSession *sn, LocalResource *res, int *counter, CxList *cols); MetadataHashes sync_set_metadata_properties( SyncDirectory *dir, DavSession *sn, @@ -216,7 +217,7 @@ DavResource *res, LocalResource *local); -void update_parts(LocalResource *local, UcxList *updates, uint64_t numparts); +void update_parts(LocalResource *local, CxList *updates, uint64_t numparts); void remove_deleted_conflicts(SyncDirectory *dir, SyncDatabase *db); diff -r a569148841ff -r efbd59642577 dav/system.c --- a/dav/system.c Sun Apr 16 14:12:24 2023 +0200 +++ b/dav/system.c Fri Apr 21 21:25:32 2023 +0200 @@ -36,7 +36,7 @@ #include #include -#include +#include #ifndef _WIN32 #include @@ -335,8 +335,8 @@ // however, we interpret .lnk files as symlinks int ret = 0; - scstr_t path_s = scstr(path); - if(scstrsuffix(path_s, SC(".lnk"))) { + cxstring path_s = cx_str(path); + if(cx_strsuffix(path_s, CX_STR(".lnk"))) { // looks like a .lnk file // check content IShellLink *sl; diff -r a569148841ff -r efbd59642577 dav/tags.c --- a/dav/tags.c Sun Apr 16 14:12:24 2023 +0200 +++ b/dav/tags.c Fri Apr 21 21:25:32 2023 +0200 @@ -31,8 +31,10 @@ #include #include -#include -#include +#include +#include +#include +#include #include @@ -52,50 +54,53 @@ free(tag); } -void free_taglist(UcxList *list) { - ucx_list_free_content(list, (ucx_destructor)free_dav_tag); - ucx_list_free(list); +void free_taglist(CxList *list) { + if(!list) { + return; + } + cxListDestroy(list); } int compare_tagname(DavTag* left, DavTag* right, void* ignorecase) { - sstr_t leftname = sstr(left->name); - sstr_t rightname = sstr(right->name); + cxstring leftname = cx_str(left->name); + cxstring rightname = cx_str(right->name); if (ignorecase && *((int*) ignorecase)) { - return sstrcasecmp(leftname, rightname); + return cx_strcasecmp(leftname, rightname); } else { - return sstrcmp(leftname, rightname); + return cx_strcmp(leftname, rightname); } } -UcxList* parse_text_taglist(const char *buf, size_t length) { - UcxList *tags = NULL; +CxList* parse_text_taglist(const char *buf, size_t length) { + CxList *tags = cxLinkedListCreateSimple(CX_STORE_POINTERS); + tags->simple_destructor = (cx_destructor_func)free_dav_tag; int line_start = 0; for(int i=0;i 0) { DavTag *tag = calloc(1, sizeof(DavTag)); - sstr_t color = sstrchr(line, '#'); + cxstring color = cx_strchr(line, '#'); if(color.length>0) { - sstr_t name = line; + cxstring name = line; name.length = (int)(color.ptr-line.ptr); if(name.length != 0) { - tag->name = sstrdup(name).ptr; + tag->name = cx_strdup(name).ptr; color.ptr++; color.length--; if(color.length > 0) { - tag->color = sstrdup(color).ptr; + tag->color = cx_strdup(color).ptr; } } else { free(tag); } } else { - tag->name = sstrdup(line).ptr; + tag->name = cx_strdup(line).ptr; tag->color = NULL; } - tags = ucx_list_append(tags, tag); + cxListAdd(tags, tag); } line_start = i+1; } @@ -104,55 +109,61 @@ return tags; } -UcxBuffer* create_text_taglist(UcxList *tags) { +CxMap* taglist2map(CxList *tags) { + CxMap *map = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, tags->size + 8); + CxIterator iter = cxListIterator(tags); + cx_foreach(DavTag*, t, iter) { + cxMapPut(map, cx_hash_key_str(t->name), t); + } + return map; +} + +CxBuffer* create_text_taglist(CxList *tags) { if(!tags) { return NULL; } - UcxBuffer *buf = ucx_buffer_new(NULL, 128, UCX_BUFFER_AUTOEXTEND); - UCX_FOREACH(elm, tags) { - DavTag *tag = elm->data; + CxBuffer *buf = cxBufferCreate(NULL, 128, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); + CxIterator i = cxListIterator(tags); + cx_foreach(DavTag *, tag, i) { if(tag->color) { - ucx_bprintf(buf, "%s#%s\n", tag->name, tag->color); + cx_bprintf(buf, "%s#%s\n", tag->name, tag->color); } else { - ucx_bprintf(buf, "%s\n", tag->name); + cx_bprintf(buf, "%s\n", tag->name); } } return buf; } -UcxList* parse_csv_taglist(const char *buf, size_t length) { - UcxList *taglist = NULL; +CxList* parse_csv_taglist(const char *buf, size_t length) { + CxList *taglist = cxLinkedListCreateSimple(CX_STORE_POINTERS); + taglist->simple_destructor = (cx_destructor_func)free_dav_tag; - sstr_t str = sstrn((char*)buf, length); - ssize_t count = 0; - sstr_t *tags = sstrsplit(str, S(","), &count); - for(int i=0;i 0) { DavTag *tag = malloc(sizeof(DavTag)); - tag->name = sstrdup(trimmed_tag).ptr; + tag->name = cx_strdup(trimmed_tag).ptr; tag->color = NULL; - taglist = ucx_list_append(taglist, tag); + cxListAdd(taglist, tag); } - free(tags[i].ptr); - } - if(tags) { - free(tags); } return taglist; } -UcxBuffer* create_csv_taglist(UcxList *tags) { - UcxBuffer *buf = ucx_buffer_new(NULL, 128, UCX_BUFFER_AUTOEXTEND); +CxBuffer* create_csv_taglist(CxList *tags) { + CxBuffer *buf = cxBufferCreate(NULL, 128, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); int insertsep = 0; - UCX_FOREACH(elm, tags) { - DavTag *tag = elm->data; + CxIterator i = cxListIterator(tags); + cx_foreach(DavTag*, tag, i) { if(insertsep) { - ucx_buffer_putc(buf, ','); + cxBufferPut(buf, ','); } - ucx_buffer_puts(buf, tag->name); + cxBufferPutString(buf, tag->name); insertsep = 1; } return buf; @@ -196,8 +207,9 @@ return tag; } -UcxList* parse_dav_xml_taglist(DavXmlNode *taglistnode) { - UcxList *tags = NULL; +CxList* parse_dav_xml_taglist(DavXmlNode *taglistnode) { + CxList *tags = cxLinkedListCreateSimple(CX_STORE_POINTERS); + tags->simple_destructor = (cx_destructor_func)free_dav_tag; DavXmlNode *node = taglistnode; while(node) { @@ -205,7 +217,7 @@ if(!strcmp(node->namespace, DAV_PROPS_NS) && !strcmp(node->name, "tag")) { DavTag *tag = parse_xml_dav_tag(node); if(tag) { - tags = ucx_list_append(tags, tag); + cxListAdd(tags, tag); } } } @@ -215,12 +227,11 @@ return tags; } -DavXmlNode* create_xml_taglist(UcxList *tags) { +DavXmlNode* create_xml_taglist(CxList *tags) { DavXmlNode *tag1 = NULL; DavXmlNode *lasttag = NULL; - UCX_FOREACH(elm, tags) { - DavTag *tag = elm->data; - + CxIterator i = cxListIterator(tags); + cx_foreach(DavTag*, tag, i) { DavXmlNode *tagelm = dav_xml_createnode(DAV_PROPS_NS, "tag"); DavXmlNode *tagname = dav_xml_createnode_with_text(DAV_PROPS_NS, "name", tag->name); tagelm->children = tagname; @@ -278,8 +289,9 @@ return tag; } -UcxList* parse_macos_taglist(const char *buf, size_t length) { - UcxList *taglist = NULL; +CxList* parse_macos_taglist(const char *buf, size_t length) { + CxList *taglist = cxLinkedListCreate(CX_STORE_POINTERS); + taglist->simple_destructor = (cx_destructor_func)free_dav_tag; CFDataRef data = CFDataCreateWithBytesNoCopy( kCFAllocatorDefault, @@ -297,7 +309,7 @@ if(CFStringGetCString(str, cstr, cstrbuflen, kCFStringEncodingUTF8)) { DavTag *tag = tagstr2davtag(cstr); if(tag) { - taglist = ucx_list_append(taglist, tag); + cxListAdd(taglist, tag); } } free(cstr); @@ -309,7 +321,7 @@ return taglist; } -UcxBuffer* create_macos_taglist(UcxList *tags) { +CxBuffer* create_macos_taglist(CxList *tags) { size_t count = ucx_list_size(tags); if(count == 0) { return NULL; @@ -317,8 +329,8 @@ CFStringRef *strings = calloc(sizeof(CFStringRef), count); int i = 0; - UCX_FOREACH(elm, tags) { - DavTag *tag = elm->data; + CxIterator i = cxListIterator(tags); + cx_foreach(DavTag*, tag, i) { CFStringRef str = NULL; if(tag->color) { sstr_t s = sstrcat(3, sstr(tag->name), S("\n"), sstr(tag->color)); @@ -334,13 +346,13 @@ CFPropertyListRef array = CFArrayCreate(kCFAllocatorDefault, (const void**)strings, count, &kCFTypeArrayCallBacks); CFDataRef data = CFPropertyListCreateData(kCFAllocatorDefault, array, kCFPropertyListBinaryFormat_v1_0, 0, NULL); - UcxBuffer *buf = NULL; + CxBuffer *buf = NULL; if(data) { int datalen = CFDataGetLength(data); CFRange range; range.location = 0; range.length = datalen; - buf = ucx_buffer_new(NULL, datalen, 0); + buf = cxBufferCreate(NULL, datalen, cxDefaultAllocator, 0); CFDataGetBytes(data, range, (UInt8*)buf->space); buf->size = datalen; CFRelease(data); @@ -355,18 +367,18 @@ } #else -UcxList* parse_macos_taglist(const char *buf, size_t length) { +CxList* parse_macos_taglist(const char *buf, size_t length) { fprintf(stderr, "Error: macos tags not supported on this platform.\n"); return NULL; } -UcxBuffer* create_macos_taglist(UcxList *tags) { +CxBuffer* create_macos_taglist(CxList *tags) { fprintf(stderr, "Error: macos tags not supported on this platform.\n"); return NULL; } #endif -int compare_taglists(UcxList *tags1, UcxList *tags2) { +int compare_taglists(CxList *tags1, CxList *tags2) { if(!tags1) { return tags2 ? 0 : 1; } @@ -374,100 +386,93 @@ return tags1 ? 0 : 1; } - UcxMap *map1 = ucx_map_new(32); - UCX_FOREACH(elm, tags1) { - DavTag *t = elm->data; - ucx_map_cstr_put(map1, t->name, t); - } + CxMap *map1 = taglist2map(tags1); int equal = 1; int i = 0; - UCX_FOREACH(elm, tags2) { - DavTag *t = elm->data; - if(!ucx_map_cstr_get(map1, t->name)) { + CxIterator iter = cxListIterator(tags2); + cx_foreach(DavTag*, t, iter) { + if(!cxMapGet(map1, cx_hash_key_str(t->name))) { equal = 0; break; } i++; } - if(i != map1->count) { + if(i != map1->size) { equal = 0; } - ucx_map_free(map1); + cxMapDestroy(map1); return equal; } -char* create_tags_hash(UcxList *tags) { +char* create_tags_hash(CxList *tags) { if(!tags) { return NULL; } - UcxBuffer *buf = create_text_taglist(tags); + CxBuffer *buf = create_text_taglist(tags); char *hash = dav_create_hash(buf->space, buf->size); - ucx_buffer_free(buf); + cxBufferDestroy(buf); return hash; } -UcxList* merge_tags(UcxList *tags1, UcxList *tags2) { +CxList* merge_tags(CxList *tags1, CxList *tags2) { // this map is used to check the existence of tags - UcxMap *tag_map = ucx_map_new(32); + CxMap *tag_map = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 32); // merged taglist - UcxList *new_tags = NULL; + CxList *new_tags = cxLinkedListCreateSimple(CX_STORE_POINTERS); + new_tags->simple_destructor = (cx_destructor_func)free_dav_tag; // add all local tags - UCX_FOREACH(elm, tags1) { - DavTag *t = elm->data; - ucx_map_cstr_put(tag_map, t->name, t); + CxIterator iter = cxListIterator(tags1); + cx_foreach(DavTag*, t, iter) { + cxMapPut(tag_map, cx_hash_key_str(t->name), t); DavTag *newt = calloc(1, sizeof(DavTag)); newt->color = t->color ? strdup(t->color) : NULL; newt->name = strdup(t->name); - new_tags = ucx_list_append(new_tags, newt); + cxListAdd(new_tags, newt); } // check if a remote tag is already in the map // and if not add it to the new taglist - UCX_FOREACH(elm, tags2) { - DavTag *t = elm->data; - if(!ucx_map_cstr_get(tag_map, t->name)) { + iter = cxListIterator(tags2); + cx_foreach(DavTag*, t, iter) { + if(!cxMapGet(tag_map, cx_hash_key_str(t->name))) { DavTag *newt = calloc(1, sizeof(DavTag)); newt->color = t->color ? strdup(t->color) : NULL; newt->name = strdup(t->name); - new_tags = ucx_list_append(new_tags, newt); + cxListAdd(new_tags, newt); } } - ucx_map_free(tag_map); + cxMapDestroy(tag_map); return new_tags; } -void add_tag_colors(UcxList *taglist, UcxList *colored) { - UcxMap *tagmap = ucx_map_new(32); - UCX_FOREACH(elm, taglist) { - DavTag *tag = elm->data; - ucx_map_cstr_put(tagmap, tag->name, tag); - } +void add_tag_colors(CxList *taglist, CxList *colored) { + CxMap *tagmap = taglist2map(taglist); - UCX_FOREACH(elm, colored) { - DavTag *colored_tag = elm->data; + CxIterator i = cxListIterator(colored); + cx_foreach(DavTag*, colored_tag, i) { if(colored_tag->color) { - DavTag *tag = ucx_map_cstr_get(tagmap, colored_tag->name); + DavTag *tag = cxMapGet(tagmap, cx_hash_key_str(colored_tag->name)); if(tag && !tag->color) { tag->color = strdup(colored_tag->color); } } } - ucx_map_free(tagmap); + cxMapDestroy(tagmap); } /* ----------- ----------- tag filter ---------------------- */ -static size_t rtrimskip(scstr_t str, size_t skip) { +static size_t rtrimskip(cxstring str, size_t skip) { while (skip < str.length && isspace(str.ptr[skip])) skip++; return skip; } -static size_t parse_tagfilter_taglist(scstr_t fs, SyncTagFilter* tagfilter) { +static size_t parse_tagfilter_taglist(cxstring fs, SyncTagFilter* tagfilter) { size_t csvlen; for (csvlen = 0 ; csvlen < fs.length ; ++csvlen) { if (fs.ptr[csvlen] == ')') break; @@ -479,12 +484,12 @@ return csvlen; } -static size_t parse_tagfilter_subfilters(scstr_t fs, SyncTagFilter* tagfilter); +static size_t parse_tagfilter_subfilters(cxstring fs, SyncTagFilter* tagfilter); -static size_t parse_tagfilter_filter(scstr_t fs, SyncTagFilter* tagfilter) { +static size_t parse_tagfilter_filter(cxstring fs, SyncTagFilter* tagfilter) { size_t consumed = rtrimskip(fs, 0); - fs = scstrsubs(fs, consumed); + fs = cx_strsubs(fs, consumed); if (fs.length == 0) { return consumed; @@ -512,7 +517,7 @@ if (hasop) { size_t skip = rtrimskip(fs, 1); consumed += skip; - fs = scstrsubs(fs, skip); + fs = cx_strsubs(fs, skip); } if (fs.length > 0 && fs.ptr[0] == '(') { @@ -533,7 +538,7 @@ /* * Parses: ( "(" , filter , ")" )+ */ -static size_t parse_tagfilter_subfilters(scstr_t fs, SyncTagFilter* f) { +static size_t parse_tagfilter_subfilters(cxstring fs, SyncTagFilter* f) { // strategy: allocate much and give back later (instead of reallocs in loop) size_t subfilter_cap = 8; @@ -545,7 +550,7 @@ do { // skip leading parenthesis (and white spaces) c = rtrimskip(fs, 1); - fs = scstrsubs(fs, c); + fs = cx_strsubs(fs, c); total_consumed += c; // increase array capacity, if necessary @@ -572,7 +577,7 @@ // consume ')' and find the next parenthesis or the end-of-string c = rtrimskip(fs, 1+c); - fs = scstrsubs(fs, c); + fs = cx_strsubs(fs, c); total_consumed += c; if (fs.length == 0 || fs.ptr[0] == ')') { @@ -611,7 +616,7 @@ return tagfilter; } - scstr_t fs = scstr(filterstring); + cxstring fs = cx_str(filterstring); size_t consumed = parse_tagfilter_filter(fs, tagfilter); if (!consumed) { free_tagfilter(tagfilter); @@ -639,42 +644,55 @@ } -static int matches_tags_and(UcxList *dav_tags, UcxList *tags, int ignorecase) { - UCX_FOREACH(e, tags) { - if (!ucx_list_contains(dav_tags, e->data, - (cmp_func) compare_tagname, &ignorecase)) { - return 0; +static int matches_tags_and(CxList *dav_tags, CxList *tags, int ignorecase) { + // ignorecase not supported yet + int ret = 1; + CxMap *tagmap = taglist2map(dav_tags); + CxIterator i = cxListIterator(tags); + cx_foreach(DavTag *, tag, i) { + if (cxMapGet(tagmap, cx_hash_key_str(tag->name))) { + ret = 0; + break; } } - return 1; + cxMapDestroy(tagmap); + return ret; } -static int matches_tags_or(UcxList *dav_tags, UcxList *tags, int ignorecase) { - UCX_FOREACH(e, tags) { - if (ucx_list_contains(dav_tags, e->data, - (cmp_func) compare_tagname, &ignorecase)) { - return 1; +static int matches_tags_or(CxList *dav_tags, CxList *tags, int ignorecase) { + // ignorecase not supported yet + int ret = 0; + CxMap *tagmap = taglist2map(dav_tags); + CxIterator i = cxListIterator(tags); + cx_foreach(DavTag *, tag, i) { + if (cxMapGet(tagmap, cx_hash_key_str(tag->name))) { + ret = 1; + break; } } - return 0; + cxMapDestroy(tagmap); + return ret; } -static int matches_tags_one(UcxList *dav_tags, UcxList *tags, int ignorecase) { +static int matches_tags_one(CxList *dav_tags, CxList *tags, int ignorecase) { int matches_exactly_one = 0; - UCX_FOREACH(e, tags) { - if (ucx_list_contains(dav_tags, e->data, - (cmp_func) compare_tagname, &ignorecase)) { + CxMap *tagmap = taglist2map(dav_tags); + CxIterator i = cxListIterator(tags); + cx_foreach(DavTag *, tag, i) { + if (cxMapGet(tagmap, cx_hash_key_str(tag->name))) { if (matches_exactly_one) { + cxMapDestroy(tagmap); return 0; } else { matches_exactly_one = 1; } } } + cxMapDestroy(tagmap); return matches_exactly_one; } -static int matches_subfilters_and(UcxList *dav_tags, SyncTagFilter *filter) { +static int matches_subfilters_and(CxList *dav_tags, SyncTagFilter *filter) { int ret = 1; for (size_t i = 0 ; i < filter->subfilter_count ; i++) { ret &= matches_tagfilter(dav_tags, filter->subfilters[i]); @@ -682,7 +700,7 @@ return ret; } -static int matches_subfilters_or(UcxList *dav_tags, SyncTagFilter *filter) { +static int matches_subfilters_or(CxList *dav_tags, SyncTagFilter *filter) { int ret = 0; for (size_t i = 0 ; i < filter->subfilter_count ; i++) { ret |= matches_tagfilter(dav_tags, filter->subfilters[i]); @@ -690,7 +708,7 @@ return ret; } -static int matches_subfilters_one(UcxList *dav_tags, SyncTagFilter *filter) { +static int matches_subfilters_one(CxList *dav_tags, SyncTagFilter *filter) { int one = 0; for (size_t i = 0 ; i < filter->subfilter_count ; i++) { if (matches_tagfilter(dav_tags, filter->subfilters[i])) { @@ -704,7 +722,7 @@ return one; } -int matches_tagfilter(UcxList *dav_tags, SyncTagFilter *tagfilter) { +int matches_tagfilter(CxList *dav_tags, SyncTagFilter *tagfilter) { if (tagfilter->subfilter_count > 0) { switch (tagfilter->mode) { diff -r a569148841ff -r efbd59642577 dav/tags.h --- a/dav/tags.h Sun Apr 16 14:12:24 2023 +0200 +++ b/dav/tags.h Fri Apr 21 21:25:32 2023 +0200 @@ -29,9 +29,9 @@ #ifndef TAGS_H #define TAGS_H -#include -#include -#include +#include +#include +#include #include @@ -65,44 +65,48 @@ struct SyncTagFilter { int scope; int mode; - UcxList* tags; + CxList* tags; size_t subfilter_count; SyncTagFilter** subfilters; }; void free_dav_tag(DavTag* tag); -void free_taglist(UcxList *list); +void free_taglist(CxList *list); int compare_tagname(DavTag* left, DavTag* right, void* ignorecase); -UcxList* parse_text_taglist(const char *buf, size_t length); -UcxBuffer* create_text_taglist(UcxList *tags); +CxMap* taglist2map(CxList *tags); -UcxList* parse_csv_taglist(const char *buf, size_t length); -UcxBuffer* create_csv_taglist(UcxList *tags); +CxList* parse_text_taglist(const char *buf, size_t length); +CxBuffer* create_text_taglist(CxList *tags); + +CxList* parse_csv_taglist(const char *buf, size_t length); +CxBuffer* create_csv_taglist(CxList *tags); -UcxList* parse_dav_xml_taglist(DavXmlNode *taglistnode); -DavXmlNode* create_xml_taglist(UcxList *tags); +CxList* parse_dav_xml_taglist(DavXmlNode *taglistnode); +DavXmlNode* create_xml_taglist(CxList *tags); + +CxList* parse_macos_taglist(const char *buf, size_t length); +CxBuffer* create_macos_taglist(CxList *tags); -UcxList* parse_macos_taglist(const char *buf, size_t length); -UcxBuffer* create_macos_taglist(UcxList *tags); +int compare_taglists(CxList *tags1, CxList *tags2); -char* create_tags_hash(UcxList *tags); +char* create_tags_hash(CxList *tags); -UcxList* merge_tags(UcxList *tags1, UcxList *tags2); +CxList* merge_tags(CxList *tags1, CxList *tags2); /* * Adds tag colors from the colored list to taglist if tags have the same name */ -void add_tag_colors(UcxList *taglist, UcxList *colored); +void add_tag_colors(CxList *taglist, CxList *colored); /* ----------- ----------- tag filter ---------------------- */ SyncTagFilter* parse_tagfilter_string(const char* filterstring, int scope); void free_tagfilter(SyncTagFilter* filter); -int matches_tagfilter(UcxList *dav_tags, SyncTagFilter *tagfilter); +int matches_tagfilter(CxList *dav_tags, SyncTagFilter *tagfilter); #ifdef __cplusplus } diff -r a569148841ff -r efbd59642577 dav/tar.c --- a/dav/tar.c Sun Apr 16 14:12:24 2023 +0200 +++ b/dav/tar.c Fri Apr 21 21:25:32 2023 +0200 @@ -29,7 +29,7 @@ #include "tar.h" #include -#include +#include #include @@ -59,13 +59,13 @@ static int add_header(TarOutputStream *tar, char *path, uint32_t mode, uint64_t size, time_t mtime, int type) { // split path in prefix and name and check length char *p = util_parent_path(path); - char *n = util_resource_name(path); + const char *n = util_resource_name(path); if(!p || !n) { return -1; } - sstr_t prefix = sstr(p); - sstr_t name = sstr(n); + cxstring prefix = cx_str(p); + cxstring name = cx_str(n); if(prefix.ptr[prefix.length-1] == '/') { prefix.length--; @@ -73,16 +73,19 @@ if(prefix.length > 154) { tar->error = TAR_PATH_TOO_LONG; + free(p); return -1; } if(name.length > 99) { tar->error = TAR_PATH_TOO_LONG; + free(p); return -1; } // check file length if(size >= 077777777777 ) { tar->error = TAR_FILE_TOO_LARGE; + free(p); return -1; } @@ -136,6 +139,8 @@ fwrite(&h, 1, 512, tar->file); + free(p); + return 0; } diff -r a569148841ff -r efbd59642577 libidav/Makefile --- a/libidav/Makefile Sun Apr 16 14:12:24 2023 +0200 +++ b/libidav/Makefile Fri Apr 21 21:25:32 2023 +0200 @@ -44,10 +44,10 @@ OBJ = $(SRC:%.c=../build/libidav/%$(OBJ_EXT)) -all: ../build/ucx ../build/libidav$(LIB_EXT) +all: ../build/ucx ../build/lib/libidav$(LIB_EXT) -../build/libidav$(LIB_EXT): $(OBJ) - $(AR) $(ARFLAGS) $(AOFLAGS)../build/libidav$(LIB_EXT) $(OBJ) +../build/lib/libidav$(LIB_EXT): $(OBJ) + $(AR) $(ARFLAGS) $(AOFLAGS)$@ $(OBJ) ../build/libidav/%$(OBJ_EXT): %.c $(CC) $(CFLAGS) $(DAV_CFLAGS) -I.. -I../ucx -c -o $@ $< diff -r a569148841ff -r efbd59642577 libidav/crypto.c --- a/libidav/crypto.c Sun Apr 16 14:12:24 2023 +0200 +++ b/libidav/crypto.c Fri Apr 21 21:25:32 2023 +0200 @@ -1487,26 +1487,26 @@ -UcxBuffer* aes_encrypt_buffer(UcxBuffer *in, DavKey *key) { - UcxBuffer *encbuf = ucx_buffer_new( - NULL, - in->size+16, - UCX_BUFFER_AUTOEXTEND); +CxBuffer* aes_encrypt_buffer(CxBuffer *in, DavKey *key) { + CxBuffer *encbuf = cxBufferCreate(NULL, in->size, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); + if(!encbuf) { + return NULL; + } AESEncrypter *enc = aes_encrypter_new( key, in, - (dav_read_func)ucx_buffer_read, + (dav_read_func)cxBufferRead, NULL); if(!enc) { - ucx_buffer_free(encbuf); + cxBufferFree(encbuf); return NULL; } char buf[1024]; size_t r; while((r = aes_read(buf, 1, 1024, enc)) > 0) { - ucx_buffer_write(buf, 1, r, encbuf); + cxBufferWrite(buf, 1, r, encbuf); } aes_encrypter_close(enc); @@ -1514,15 +1514,19 @@ return encbuf; } -UcxBuffer* aes_decrypt_buffer(UcxBuffer *in, DavKey *key) { - UcxBuffer *decbuf = ucx_buffer_new( - NULL, - in->size, - UCX_BUFFER_AUTOEXTEND); +CxBuffer* aes_decrypt_buffer(CxBuffer *in, DavKey *key) { + CxBuffer *decbuf = cxBufferCreate(NULL, in->size, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); + if(!decbuf) { + return NULL; + } AESDecrypter *dec = aes_decrypter_new( key, decbuf, - (dav_write_func)ucx_buffer_write); + (dav_write_func)cxBufferWrite); + if(!dec) { + cxBufferFree(decbuf); + return NULL; + } aes_write(in->space, 1, in->size, dec); aes_decrypter_shutdown(dec); diff -r a569148841ff -r efbd59642577 libidav/crypto.h --- a/libidav/crypto.h Sun Apr 16 14:12:24 2023 +0200 +++ b/libidav/crypto.h Fri Apr 21 21:25:32 2023 +0200 @@ -30,7 +30,7 @@ #define DAV_CRYPTO_H #include "webdav.h" -#include +#include #ifdef __APPLE__ /* macos */ @@ -155,8 +155,8 @@ DavKey* dav_pw2key(const char *password, const unsigned char *salt, int saltlen, int pwfunc, int enc); -UcxBuffer* aes_encrypt_buffer(UcxBuffer *buf, DavKey *key); -UcxBuffer* aes_decrypt_buffer(UcxBuffer *buf, DavKey *key); +CxBuffer* aes_encrypt_buffer(CxBuffer *in, DavKey *key); +CxBuffer* aes_decrypt_buffer(CxBuffer *in, DavKey *key); #ifdef __cplusplus } diff -r a569148841ff -r efbd59642577 libidav/davqlexec.c --- a/libidav/davqlexec.c Sun Apr 16 14:12:24 2023 +0200 +++ b/libidav/davqlexec.c Fri Apr 21 21:25:32 2023 +0200 @@ -31,8 +31,12 @@ #include #include -#include -#include +#include +#include +#include +#include +#include + #include "davqlexec.h" #include "utils.h" #include "methods.h" @@ -46,9 +50,16 @@ } args->first = NULL; + if(!st->args) { + args->first = NULL; + args->current = NULL; + return args; + } + DavQLArg *cur = NULL; - UCX_FOREACH(elm, st->args) { - intptr_t type = (intptr_t)elm->data; + CxIterator i = cxListIterator(st->args); + cx_foreach(void*, data, i) { + intptr_t type = (intptr_t)data; DavQLArg *arg = calloc(1, sizeof(DavQLArg)); if(!arg) { dav_ql_free_arglist(args); @@ -167,8 +178,9 @@ return result; } -sstr_t dav_format_string(UcxAllocator *a, sstr_t fstr, DavQLArgList *ap, davqlerror_t *error) { - UcxBuffer *buf = ucx_buffer_new(NULL, 128, UCX_BUFFER_AUTOEXTEND); +cxmutstr dav_format_string(const CxAllocator *a, cxstring fstr, DavQLArgList *ap, davqlerror_t *error) { + CxBuffer buf; + cxBufferInit(&buf, NULL, 128, a, CX_BUFFER_AUTO_EXTEND); int placeholder = 0; for(int i=0;ispace, buf->size)); - ucx_buffer_free(buf); - return ret; + return cx_mutstrn(buf.space, buf.size-1); } -static int fl_add_properties(DavSession *sn, UcxMempool *mp, UcxMap *map, DavQLExpression *expression) { +static int fl_add_properties(DavSession *sn, const CxAllocator *a, CxMap *map, DavQLExpression *expression) { if(!expression) { return 0; } if(expression->type == DAVQL_IDENTIFIER) { - DavProperty *property = ucx_mempool_malloc(mp, sizeof(DavProperty)); + DavProperty *property = cxMalloc(a, sizeof(DavProperty)); char *name; DavNamespace *ns = dav_get_property_namespace( sn->context, - sstrdup_a(mp->allocator, expression->srctext).ptr, + cx_strdup_a(a, expression->srctext).ptr, &name); if(!ns) { return -1; @@ -251,16 +263,16 @@ property->name = name; property->value = NULL; - ucx_map_sstr_put(map, expression->srctext, property); + cxMapPut(map, cx_hash_key(expression->srctext.ptr, expression->srctext.length), property); } if(expression->left) { - if(fl_add_properties(sn, mp, map, expression->left)) { + if(fl_add_properties(sn, a, map, expression->left)) { return -1; } } if(expression->right) { - if(fl_add_properties(sn, mp, map, expression->right)) { + if(fl_add_properties(sn, a, map, expression->right)) { return -1; } } @@ -268,170 +280,170 @@ return 0; } -static UcxBuffer* fieldlist2propfindrequest(DavSession *sn, UcxMempool *mp, UcxList *fields, int *isallprop) { - UcxMap *properties = ucx_map_new(32); +static CxBuffer* fieldlist2propfindrequest(DavSession *sn, const CxAllocator *a, CxList *fields, int *isallprop) { + CxMap *properties = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 32); *isallprop = 0; - UCX_FOREACH(elm, fields) { - DavQLField *field = elm->data; - if(!sstrcmp(field->name, S("*"))) { - ucx_map_free(properties); + CxIterator i = cxListIterator(fields); + cx_foreach(DavQLField*, field, i) { + if(!cx_strcmp(field->name, CX_STR("*"))) { + cxMapDestroy(properties); *isallprop = 1; return create_allprop_propfind_request(); - } else if(!sstrcmp(field->name, S("-"))) { - ucx_map_free(properties); + } else if(!cx_strcmp(field->name, CX_STR("-"))) { + cxMapDestroy(properties); return create_propfind_request(sn, NULL, "propfind", 0); } else { - if(fl_add_properties(sn, mp, properties, field->expr)) { + if(fl_add_properties(sn, a, properties, field->expr)) { // TODO: set error - ucx_map_free(properties); + cxMapDestroy(properties); return NULL; } } } - UcxMapIterator i = ucx_map_iterator(properties); - UcxKey key; - DavProperty *value; - UcxList *list = NULL; - UCX_MAP_FOREACH(key, value, i) { - list = ucx_list_append(list, value); + i = cxMapIteratorValues(properties); + CxList *list = cxLinkedListCreateSimple(CX_STORE_POINTERS); + cx_foreach(DavProperty*, value, i) { + cxListAdd(list, value); } - UcxBuffer *reqbuf = create_propfind_request(sn, list, "propfind", 0); - ucx_list_free(list); - ucx_map_free(properties); + CxBuffer *reqbuf = create_propfind_request(sn, list, "propfind", 0); + cxListDestroy(list); + cxMapDestroy(properties); return reqbuf; } -static int reset_properties(DavSession *sn, DavResult *result, DavResource *res, UcxList *fields) { - UcxMap *new_properties = ucx_map_new_a(sn->mp->allocator, 32); +static int reset_properties(DavSession *sn, DavResult *result, DavResource *res, CxList *fields) { + CxMap *new_properties = cxHashMapCreate(sn->mp->allocator, CX_STORE_POINTERS, 32); DavResourceData *data = (DavResourceData*)res->data; // add basic properties void *value; - sstr_t cl_keystr = dav_property_key("DAV:", "getcontentlength"); - UcxKey cl_key = ucx_key(cl_keystr.ptr, cl_keystr.length); - value = ucx_map_get(data->properties, cl_key); + cxmutstr cl_keystr = dav_property_key("DAV:", "getcontentlength"); + CxHashKey cl_key = cx_hash_key(cl_keystr.ptr, cl_keystr.length); + value = cxMapGet(data->properties, cl_key); if(value) { - ucx_map_put(new_properties, cl_key, value); + cxMapPut(new_properties, cl_key, value); } - sstr_t cd_keystr = dav_property_key("DAV:", "creationdate"); - UcxKey cd_key = ucx_key(cd_keystr.ptr, cd_keystr.length); - value = ucx_map_get(data->properties, cd_key); + cxmutstr cd_keystr = dav_property_key("DAV:", "creationdate"); + CxHashKey cd_key = cx_hash_key(cd_keystr.ptr, cd_keystr.length); + value = cxMapGet(data->properties, cd_key); if(value) { - ucx_map_put(new_properties, cd_key, value); + cxMapPut(new_properties, cd_key, value); } - sstr_t lm_keystr = dav_property_key("DAV:", "getlastmodified"); - UcxKey lm_key = ucx_key(lm_keystr.ptr, lm_keystr.length); - value = ucx_map_get(data->properties, lm_key); + cxmutstr lm_keystr = dav_property_key("DAV:", "getlastmodified"); + CxHashKey lm_key = cx_hash_key(lm_keystr.ptr, lm_keystr.length); + value = cxMapGet(data->properties, lm_key); if(value) { - ucx_map_put(new_properties, lm_key, value); + cxMapPut(new_properties, lm_key, value); } - sstr_t ct_keystr = dav_property_key("DAV:", "getcontenttype"); - UcxKey ct_key = ucx_key(ct_keystr.ptr, ct_keystr.length); - value = ucx_map_get(data->properties, ct_key); + cxmutstr ct_keystr = dav_property_key("DAV:", "getcontenttype"); + CxHashKey ct_key = cx_hash_key(ct_keystr.ptr, ct_keystr.length); + value = cxMapGet(data->properties, ct_key); if(value) { - ucx_map_put(new_properties, ct_key, value); + cxMapPut(new_properties, ct_key, value); } - sstr_t rt_keystr = dav_property_key("DAV:", "resourcetype"); - UcxKey rt_key = ucx_key(rt_keystr.ptr, rt_keystr.length); - value = ucx_map_get(data->properties, rt_key); + cxmutstr rt_keystr = dav_property_key("DAV:", "resourcetype"); + CxHashKey rt_key = cx_hash_key(rt_keystr.ptr, rt_keystr.length); + value = cxMapGet(data->properties, rt_key); if(value) { - ucx_map_put(new_properties, rt_key, value); + cxMapPut(new_properties, rt_key, value); } - sstr_t cn_keystr = dav_property_key(DAV_NS, "crypto-name"); - UcxKey cn_key = ucx_key(cn_keystr.ptr, cn_keystr.length); - value = ucx_map_get(data->properties, cn_key); + cxmutstr cn_keystr = dav_property_key(DAV_NS, "crypto-name"); + CxHashKey cn_key = cx_hash_key(cn_keystr.ptr, cn_keystr.length); + value = cxMapGet(data->properties, cn_key); if(value) { - ucx_map_put(new_properties, cn_key, value); + cxMapPut(new_properties, cn_key, value); } - sstr_t ck_keystr = dav_property_key(DAV_NS, "crypto-key"); - UcxKey ck_key = ucx_key(ck_keystr.ptr, ck_keystr.length); - value = ucx_map_get(data->properties, ck_key); + cxmutstr ck_keystr = dav_property_key(DAV_NS, "crypto-key"); + CxHashKey ck_key = cx_hash_key(ck_keystr.ptr, ck_keystr.length); + value = cxMapGet(data->properties, ck_key); if(value) { - ucx_map_put(new_properties, ck_key, value); + cxMapPut(new_properties, ck_key, value); } - sstr_t ch_keystr = dav_property_key(DAV_NS, "crypto-hash"); - UcxKey ch_key = ucx_key(ch_keystr.ptr, ch_keystr.length); - value = ucx_map_get(data->properties, ch_key); + cxmutstr ch_keystr = dav_property_key(DAV_NS, "crypto-hash"); + CxHashKey ch_key = cx_hash_key(ch_keystr.ptr, ch_keystr.length); + value = cxMapGet(data->properties, ch_key); if(value) { - ucx_map_put(new_properties, ch_key, value); + cxMapPut(new_properties, ch_key, value); } // add properties from field list - UCX_FOREACH(elm, fields) { - DavCompiledField *field = elm->data; - DavQLStackObj field_result; - if(!dav_exec_expr(field->code, res, &field_result)) { - sstr_t str; - str.ptr = NULL; - str.length = 0; - DavXmlNode *node = NULL; - if(field_result.type == 0) { - str = ucx_asprintf( - sn->mp->allocator, - "%d", - field_result.data.integer); - } else if(field_result.type == 1) { - if(field_result.data.string) { - str = sstrdup_a(sn->mp->allocator, sstrn( - field_result.data.string, - field_result.length)); + if(fields) { + CxIterator i = cxListIterator(fields); + cx_foreach(DavCompiledField*, field, i) { + DavQLStackObj field_result; + if(!dav_exec_expr(field->code, res, &field_result)) { + cxmutstr str; + str.ptr = NULL; + str.length = 0; + DavXmlNode *node = NULL; + if(field_result.type == 0) { + str = cx_asprintf_a( + sn->mp->allocator, + "%" PRId64, + field_result.data.integer); + } else if(field_result.type == 1) { + if(field_result.data.string) { + str = cx_strdup_a(sn->mp->allocator, cx_strn( + field_result.data.string, + field_result.length)); + } + } else if(field_result.type == 2) { + node = dav_copy_node(field_result.data.node); + } else { + // unknown type + // TODO: error + resource_free_properties(sn, new_properties); + return -1; } - } else if(field_result.type == 2) { - node = dav_copy_node(field_result.data.node); + if(str.ptr) { + node = dav_session_malloc(sn, sizeof(DavXmlNode)); + memset(node, 0, sizeof(DavXmlNode)); + node->type = DAV_XML_TEXT; + node->content = str.ptr; + node->contentlength = str.length; + } + if(node) { + cxmutstr key = dav_property_key(field->ns, field->name); + + DavNamespace *namespace = dav_session_malloc(sn, sizeof(DavNamespace)); + namespace->prefix = NULL; + namespace->name = dav_session_strdup(sn, field->ns); + + DavProperty *prop = dav_session_malloc(sn, sizeof(DavProperty)); + prop->name = dav_session_strdup(sn, field->name); + prop->ns = namespace; + prop->value = node; + + cxMapPut(new_properties, cx_hash_key(key.ptr, key.length), prop); + free(key.ptr); + } } else { - // unknown type // TODO: error resource_free_properties(sn, new_properties); return -1; } - if(str.ptr) { - node = dav_session_malloc(sn, sizeof(DavXmlNode)); - memset(node, 0, sizeof(DavXmlNode)); - node->type = DAV_XML_TEXT; - node->content = str.ptr; - node->contentlength = str.length; - } - if(node) { - sstr_t key = dav_property_key(field->ns, field->name); - - DavNamespace *namespace = dav_session_malloc(sn, sizeof(DavNamespace)); - namespace->prefix = NULL; - namespace->name = dav_session_strdup(sn, field->ns); - - DavProperty *prop = dav_session_malloc(sn, sizeof(DavProperty)); - prop->name = dav_session_strdup(sn, field->name); - prop->ns = namespace; - prop->value = node; - - ucx_map_sstr_put(new_properties, key, prop); - free(key.ptr); - } - } else { - // TODO: error - resource_free_properties(sn, new_properties); - return -1; } } - ucx_map_remove(data->properties, cl_key); - ucx_map_remove(data->properties, cd_key); - ucx_map_remove(data->properties, lm_key); - ucx_map_remove(data->properties, ct_key); - ucx_map_remove(data->properties, rt_key); - ucx_map_remove(data->properties, cn_key); - ucx_map_remove(data->properties, ck_key); - ucx_map_remove(data->properties, ch_key); + cxMapRemove(data->properties, cl_key); + cxMapRemove(data->properties, cd_key); + cxMapRemove(data->properties, lm_key); + cxMapRemove(data->properties, ct_key); + cxMapRemove(data->properties, rt_key); + cxMapRemove(data->properties, cn_key); + cxMapRemove(data->properties, ck_key); + cxMapRemove(data->properties, ch_key); resource_free_properties(sn, data->properties); data->properties = new_properties; @@ -452,7 +464,7 @@ * execute a davql select statement */ DavResult dav_exec_select(DavSession *sn, DavQLStatement *st, va_list ap) { - UcxMempool *mp = ucx_mempool_new(128); + CxMempool *mp = cxBasicMempoolCreate(128); DavResult result; result.result = NULL; result.status = 1; @@ -461,157 +473,158 @@ if(!args) { return result; } - ucx_mempool_reg_destr(mp, args, (ucx_destructor)dav_ql_free_arglist); + util_regdestr(mp, args, (cx_destructor_func)dav_ql_free_arglist); int isallprop; - UcxBuffer *rqbuf = fieldlist2propfindrequest(sn, mp, st->fields, &isallprop); + CxBuffer *rqbuf = fieldlist2propfindrequest(sn, mp->allocator, st->fields, &isallprop); if(!rqbuf) { - ucx_mempool_destroy(mp); + cxMempoolDestroy(mp); return result; } - ucx_mempool_reg_destr(mp, rqbuf, (ucx_destructor)ucx_buffer_free); + util_regdestr(mp, rqbuf, (cx_destructor_func)cxBufferFree); // compile field list - UcxList *cfieldlist = NULL; - UCX_FOREACH(elm, st->fields) { - DavQLField *field = elm->data; - if(sstrcmp(field->name, S("*")) && sstrcmp(field->name, S("-"))) { - // compile field expression - UcxBuffer *code = dav_compile_expr( + CxList *cfieldlist = cxLinkedListCreate(mp->allocator, NULL, CX_STORE_POINTERS); + if(st->fields) { + CxIterator i = cxListIterator(st->fields); + cx_foreach(DavQLField*, field, i) { + if(cx_strcmp(field->name, CX_STR("*")) && cx_strcmp(field->name, CX_STR("-"))) { + // compile field expression + CxBuffer *code = dav_compile_expr( + sn->context, + mp->allocator, + field->expr, + args); + if(!code) { + // TODO: set error string + return result; + } + DavCompiledField *cfield = cxMalloc( + mp->allocator, + sizeof(DavCompiledField)); + + char *ns; + char *name; + dav_get_property_namespace_str( sn->context, - mp->allocator, - field->expr, - args); - if(!code) { - // TODO: set error string - return result; - } - ucx_mempool_reg_destr(mp, code, (ucx_destructor)ucx_buffer_free); - DavCompiledField *cfield = ucx_mempool_malloc( - mp, - sizeof(DavCompiledField)); - - char *ns; - char *name; - dav_get_property_namespace_str( - sn->context, - sstrdup_a(mp->allocator, field->name).ptr, - &ns, - &name); - if(!ns || !name) { - // TODO: set error string - return result; - } - cfield->ns = ns; - cfield->name = name; - cfield->code = code; - cfieldlist = ucx_list_append_a(mp->allocator, cfieldlist, cfield); - } + cx_strdup_a(mp->allocator, field->name).ptr, + &ns, + &name); + if(!ns || !name) { + // TODO: set error string + return result; + } + cfield->ns = ns; + cfield->name = name; + cfield->code = code; + cxListAdd(cfieldlist, cfield); + } + } } // get path string davqlerror_t error; - sstr_t path = dav_format_string(mp->allocator, st->path, args, &error); + cxmutstr path = dav_format_string(mp->allocator, st->path, args, &error); if(error) { // TODO: cleanup - ucx_mempool_destroy(mp); + cxMempoolDestroy(mp); return result; } int depth = st->depth == DAV_DEPTH_PLACEHOLDER ? dav_ql_getarg_int(args) : st->depth; - UcxBuffer *where = dav_compile_expr(sn->context, mp->allocator, st->where, args); + CxBuffer *where = dav_compile_expr(sn->context, mp->allocator, st->where, args); if(st->where && !where) { // TODO: cleanup - ucx_mempool_destroy(mp); + cxMempoolDestroy(mp); return result; } - if(where) { - ucx_mempool_reg_destr(mp, where, (ucx_destructor)ucx_buffer_free); - } // compile order criterion - UcxList *ordercr = NULL; - UCX_FOREACH(elm, st->orderby) { - DavQLOrderCriterion *oc = elm->data; - DavQLExpression *column = oc->column; - //printf("%.*s %s\n", column->srctext.length, column->srctext.ptr, oc->descending ? "desc" : "asc"); - if(column->type == DAVQL_IDENTIFIER) { - // TODO: remove code duplication (add_cmd) - davqlresprop_t resprop; - sstr_t propertyname = sstrchr(column->srctext, ':'); - if(propertyname.length > 0) { - char *ns; - char *name; - dav_get_property_namespace_str( - sn->context, - sstrdup_a(mp->allocator, column->srctext).ptr, - &ns, - &name); - if(ns && name) { - DavOrderCriterion *cr = ucx_mempool_malloc(mp, sizeof(DavOrderCriterion)); - cr->type = 1; - sstr_t keystr = dav_property_key_a(mp->allocator, ns, name); - cr->column.property = ucx_key(keystr.ptr, keystr.length); - cr->descending = oc->descending; - ordercr = ucx_list_append_a(mp->allocator, ordercr, cr); + CxList *ordercr = NULL; + if(st->orderby) { + ordercr = cxLinkedListCreate(mp->allocator, NULL, sizeof(DavOrderCriterion)); + CxIterator i = cxListIterator(st->orderby); + cx_foreach(DavQLOrderCriterion*, oc, i) { + DavQLExpression *column = oc->column; + //printf("%.*s %s\n", column->srctext.length, column->srctext.ptr, oc->descending ? "desc" : "asc"); + if(column->type == DAVQL_IDENTIFIER) { + // TODO: remove code duplication (add_cmd) + davqlresprop_t resprop; + cxstring propertyname = cx_strchr(column->srctext, ':'); + if(propertyname.length > 0) { + char *ns; + char *name; + dav_get_property_namespace_str( + sn->context, + cx_strdup_a(mp->allocator, column->srctext).ptr, + &ns, + &name); + if(ns && name) { + DavOrderCriterion cr; + cr.type = 1; + cxmutstr keystr = dav_property_key_a(mp->allocator, ns, name); + cr.column.property = cx_hash_key(keystr.ptr, keystr.length); + cr.descending = oc->descending; + cxListAdd(ordercr, &cr); + } else { + // error + // TODO: cleanup + cxMempoolDestroy(mp); + return result; + } + } else if(dav_identifier2resprop(column->srctext, &resprop)) { + DavOrderCriterion cr; + cr.type = 0; + cr.column.resprop = resprop; + cr.descending = oc->descending; + cxListAdd(ordercr, &cr); } else { // error // TODO: cleanup - ucx_mempool_destroy(mp); + cxMempoolDestroy(mp); return result; } - } else if(dav_identifier2resprop(column->srctext, &resprop)) { - DavOrderCriterion *cr = ucx_mempool_malloc(mp, sizeof(DavOrderCriterion)); - cr->type = 0; - cr->column.resprop = resprop; - cr->descending = oc->descending; - ordercr = ucx_list_append_a(mp->allocator, ordercr, cr); + + } else if(column->type == DAVQL_NUMBER) { + // TODO: implement + fprintf(stderr, "order by number not supported\n"); + return result; } else { - // error + // something is broken // TODO: cleanup - ucx_mempool_destroy(mp); + cxMempoolDestroy(mp); return result; } - - } else if(column->type == DAVQL_NUMBER) { - // TODO: implement - fprintf(stderr, "order by number not supported\n"); - return result; - } else { - // something is broken - // TODO: cleanup - ucx_mempool_destroy(mp); - return result; } } DavResource *selroot = dav_resource_new(sn, path.ptr); - UcxList *stack = NULL; // stack with DavResource* elements + CxList *stack = cxLinkedListCreateSimple(sizeof(DavQLRes)); // initialize the stack with the requested resource - DavQLRes *res = ucx_mempool_malloc(mp, sizeof(DavQLRes)); - res->resource = selroot; - res->depth = 0; - stack = ucx_list_prepend(stack, res); + DavQLRes res; + res.resource = selroot; + res.depth = 0; + cxListInsert(stack, 0, &res); // reuseable response buffer - UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND); + CxBuffer *rpbuf = cxBufferCreate(NULL, 4096, mp->allocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); if(!rpbuf) { // TODO: cleanup - ucx_mempool_destroy(mp); + cxMempoolDestroy(mp); return result; } - ucx_mempool_reg_destr(mp, rpbuf, (ucx_destructor)ucx_buffer_free); result.result = selroot; result.status = 0; // do a propfind request for each resource on the stack - while(stack) { - DavQLRes *sr = stack->data; // get first element from the stack - stack = ucx_list_remove(stack, stack); // remove first element + while(stack->size > 0) { + DavQLRes *sr = cxListAt(stack, 0); // get first element from the stack + cxListRemove(stack, 0); + cxListRemove(stack, 0); // remove first element DavResource *root = sr->resource; util_set_url(sn, dav_resource_get_href(sr->resource)); @@ -672,7 +685,7 @@ result.result = NULL; result.status = -1; dav_resource_free_all(selroot); - ucx_list_free(stack); + cxListDestroy(stack); break; } } else { @@ -691,12 +704,10 @@ if(child->iscollection && (depth < 0 || depth > sr->depth+1)) { - DavQLRes *rs = ucx_mempool_malloc( - mp, - sizeof(DavQLRes)); - rs->resource = child; - rs->depth = sr->depth + 1; - stack = ucx_list_prepend(stack, rs); + DavQLRes rs; + rs.resource = child; + rs.depth = sr->depth + 1; + cxListInsert(stack, 0, &rs); } } else { dav_resource_free(child); @@ -717,10 +728,10 @@ } // reset response buffer - ucx_buffer_seek(rpbuf, SEEK_SET, 0); + cxBufferSeek(rpbuf, SEEK_SET, 0); } - ucx_mempool_destroy(mp); + cxMempoolDestroy(mp); return result; } @@ -738,22 +749,22 @@ return count; } -int dav_identifier2resprop(sstr_t src, davqlresprop_t *prop) { - if(!sstrcmp(src, S("name"))) { +int dav_identifier2resprop(cxstring src, davqlresprop_t *prop) { + if(!cx_strcmp(src, CX_STR("name"))) { *prop = DAVQL_RES_NAME; - } else if(!sstrcmp(src, S("path"))) { + } else if(!cx_strcmp(src, CX_STR("path"))) { *prop = DAVQL_RES_PATH; - } else if(!sstrcmp(src, S("href"))) { + } else if(!cx_strcmp(src, CX_STR("href"))) { *prop = DAVQL_RES_HREF; - } else if(!sstrcmp(src, S("contentlength"))) { + } else if(!cx_strcmp(src, CX_STR("contentlength"))) { *prop = DAVQL_RES_CONTENTLENGTH; - } else if(!sstrcmp(src, S("contenttype"))) { + } else if(!cx_strcmp(src, CX_STR("contenttype"))) { *prop = DAVQL_RES_CONTENTTYPE; - } else if(!sstrcmp(src, S("creationdate"))) { + } else if(!cx_strcmp(src, CX_STR("creationdate"))) { *prop = DAVQL_RES_CREATIONDATE; - } else if(!sstrcmp(src, S("lastmodified"))) { + } else if(!cx_strcmp(src, CX_STR("lastmodified"))) { *prop = DAVQL_RES_LASTMODIFIED; - } else if(!sstrcmp(src, S("iscollection"))) { + } else if(!cx_strcmp(src, CX_STR("iscollection"))) { *prop = DAVQL_RES_ISCOLLECTION; } else { return 0; @@ -761,7 +772,7 @@ return 1; } -static int add_cmd(DavContext *ctx, UcxAllocator *a, UcxBuffer *bcode, DavQLExpression *expr, DavQLArgList *ap) { +static int add_cmd(DavContext *ctx, const CxAllocator *a, CxBuffer *bcode, DavQLExpression *expr, DavQLArgList *ap) { if(!expr) { return 0; } @@ -771,7 +782,7 @@ memset(&cmd, 0, sizeof(DavQLCmd)); davqlerror_t error; - sstr_t src = expr->srctext; + cxstring src = expr->srctext; switch(expr->type) { default: break; case DAVQL_NUMBER: { @@ -779,7 +790,7 @@ if(src.ptr[0] == '%') { cmd.data.integer = dav_ql_getarg_int(ap); } else if(util_strtoint(src.ptr, &cmd.data.integer)) { - ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); + cxBufferWrite(&cmd, sizeof(cmd), 1, bcode); } else { // error return -1; @@ -790,14 +801,14 @@ case DAVQL_STRING: { cmd.type = DAVQL_CMD_STRING; cmd.data.string = dav_format_string(a, src, ap, &error); - ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); + cxBufferWrite(&cmd, sizeof(cmd), 1, bcode); break; } case DAVQL_TIMESTAMP: { if(src.ptr[0] == '%') { cmd.type = DAVQL_CMD_TIMESTAMP; cmd.data.timestamp = dav_ql_getarg_time(ap); - ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); + cxBufferWrite(&cmd, sizeof(cmd), 1, bcode); } else { // error return -1; @@ -805,7 +816,7 @@ break; } case DAVQL_IDENTIFIER: { - sstr_t propertyname = sstrchr(src, ':'); + cxstring propertyname = cx_strchr(src, ':'); cmd.type = DAVQL_CMD_RES_IDENTIFIER; if(propertyname.length > 0) { cmd.type = DAVQL_CMD_PROP_IDENTIFIER; @@ -813,7 +824,7 @@ char *name; dav_get_property_namespace_str( ctx, - sstrdup_a(a, src).ptr, + cx_strdup_a(a, src).ptr, &ns, &name); if(ns && name) { @@ -824,10 +835,10 @@ return -1; } } else if(!dav_identifier2resprop(src, &cmd.data.resprop)) { - if(!sstrcmp(src, S("true"))) { + if(!cx_strcmp(src, CX_STR("true"))) { cmd.type = DAVQL_CMD_INT; cmd.data.integer = 1; - } else if(!sstrcmp(src, S("false"))) { + } else if(!cx_strcmp(src, CX_STR("false"))) { cmd.type = DAVQL_CMD_INT; cmd.data.integer = 0; } else { @@ -835,7 +846,7 @@ return -1; } } - ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); + cxBufferWrite(&cmd, sizeof(cmd), 1, bcode); break; } case DAVQL_UNARY: { @@ -848,12 +859,12 @@ } case DAVQL_SUB: { cmd.type = DAVQL_CMD_OP_UNARY_SUB; - ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); + cxBufferWrite(&cmd, sizeof(cmd), 1, bcode); break; } case DAVQL_NEG: { cmd.type = DAVQL_CMD_OP_UNARY_NEG; - ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); + cxBufferWrite(&cmd, sizeof(cmd), 1, bcode); break; } default: break; @@ -894,7 +905,7 @@ } default: break; } - ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); + cxBufferWrite(&cmd, sizeof(cmd), 1, bcode); break; } case DAVQL_LOGICAL: { @@ -907,12 +918,12 @@ case DAVQL_NOT: { numcmd += add_cmd(ctx, a, bcode, expr->left, ap); cmd.type = DAVQL_CMD_OP_LOGICAL_NOT; - ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); + cxBufferWrite(&cmd, sizeof(cmd), 1, bcode); break; } case DAVQL_LAND: { cmd.type = DAVQL_CMD_OP_LOGICAL_AND; - ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); + cxBufferWrite(&cmd, sizeof(cmd), 1, bcode); break; } case DAVQL_LOR: { @@ -920,61 +931,61 @@ cmd.type = DAVQL_CMD_OP_LOGICAL_OR_L; DavQLCmd *or_l = (DavQLCmd*)(bcode->space + bcode->pos); - ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); + cxBufferWrite(&cmd, sizeof(cmd), 1, bcode); int nright = add_cmd(ctx, a, bcode, expr->right, ap); or_l->data.integer = nright + 1; cmd.type = DAVQL_CMD_OP_LOGICAL_OR; cmd.data.integer = 0; - ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); + cxBufferWrite(&cmd, sizeof(cmd), 1, bcode); numcmd += nleft + nright; break; } case DAVQL_LXOR: { cmd.type = DAVQL_CMD_OP_LOGICAL_XOR; - ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); + cxBufferWrite(&cmd, sizeof(cmd), 1, bcode); break; } case DAVQL_EQ: { cmd.type = DAVQL_CMD_OP_EQ; - ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); + cxBufferWrite(&cmd, sizeof(cmd), 1, bcode); break; } case DAVQL_NEQ: { cmd.type = DAVQL_CMD_OP_NEQ; - ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); + cxBufferWrite(&cmd, sizeof(cmd), 1, bcode); break; } case DAVQL_LT: { cmd.type = DAVQL_CMD_OP_LT; - ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); + cxBufferWrite(&cmd, sizeof(cmd), 1, bcode); break; } case DAVQL_GT: { cmd.type = DAVQL_CMD_OP_GT; - ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); + cxBufferWrite(&cmd, sizeof(cmd), 1, bcode); break; } case DAVQL_LE: { cmd.type = DAVQL_CMD_OP_LE; - ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); + cxBufferWrite(&cmd, sizeof(cmd), 1, bcode); break; } case DAVQL_GE: { cmd.type = DAVQL_CMD_OP_GE; - ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); + cxBufferWrite(&cmd, sizeof(cmd), 1, bcode); break; } case DAVQL_LIKE: { cmd.type = DAVQL_CMD_OP_LIKE; - ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); + cxBufferWrite(&cmd, sizeof(cmd), 1, bcode); break; } case DAVQL_UNLIKE: { cmd.type = DAVQL_CMD_OP_UNLIKE; - ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); + cxBufferWrite(&cmd, sizeof(cmd), 1, bcode); break; } default: break; @@ -995,12 +1006,12 @@ // numargs cmd.type = DAVQL_CMD_INT; cmd.data.integer = count_func_args(expr); - ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); + cxBufferWrite(&cmd, sizeof(cmd), 1, bcode); // TODO: resolve function name cmd.type = DAVQL_CMD_CALL; cmd.data.func = NULL; - ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode); + cxBufferWrite(&cmd, sizeof(cmd), 1, bcode); numcmd = 2; numcmd += nright; @@ -1020,14 +1031,14 @@ return numcmd; } -UcxBuffer* dav_compile_expr(DavContext *ctx, UcxAllocator *a, DavQLExpression *lexpr, DavQLArgList *ap) { - UcxBuffer *bcode = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOEXTEND); +CxBuffer* dav_compile_expr(DavContext *ctx, const CxAllocator *a, DavQLExpression *lexpr, DavQLArgList *ap) { + CxBuffer *bcode = cxBufferCreate(NULL, 512, a, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); if(!bcode) { return NULL; } if(add_cmd(ctx, a, bcode, lexpr, ap) <= 0) { - ucx_buffer_free(bcode); + cxBufferFree(bcode); return NULL; } @@ -1035,53 +1046,56 @@ } static int cmd_str_cmp(DavQLStackObj obj1, DavQLStackObj obj2, davqlcmdtype_t cmd) { - sstr_t s1 = obj1.type == 1 ? - sstrn(obj1.data.string, obj1.length) : - ucx_sprintf("%" PRId64, obj1.data.integer); - sstr_t s2 = obj1.type == 1 ? - sstrn(obj2.data.string, obj2.length) : - ucx_sprintf("%" PRId64, obj2.data.integer); + cxmutstr s1m = obj1.type == 1 ? + cx_mutstrn(obj1.data.string, obj1.length) : + cx_asprintf("%" PRId64, obj1.data.integer); + cxmutstr s2m = obj1.type == 1 ? + cx_mutstrn(obj2.data.string, obj2.length) : + cx_asprintf("%" PRId64, obj2.data.integer); + + cxstring s1 = cx_strcast(s1m); + cxstring s2 = cx_strcast(s2m); int res = 0; switch(cmd) { case DAVQL_CMD_OP_EQ: { - res = sstrcmp(s1, s2) == 0; + res = cx_strcmp(s1, s2) == 0; break; } case DAVQL_CMD_OP_NEQ: { - res = sstrcmp(s1, s2) != 0; + res = cx_strcmp(s1, s2) != 0; break; } case DAVQL_CMD_OP_LT: { - res = sstrcmp(s1, s2) < 0; + res = cx_strcmp(s1, s2) < 0; break; } case DAVQL_CMD_OP_GT: { - res = sstrcmp(s1, s2) > 0; + res = cx_strcmp(s1, s2) > 0; break; } case DAVQL_CMD_OP_LE: { - res = sstrcmp(s1, s2) <= 0; + res = cx_strcmp(s1, s2) <= 0; break; } case DAVQL_CMD_OP_GE: { - res = sstrcmp(s1, s2) >= 0; + res = cx_strcmp(s1, s2) >= 0; break; } default: break; } if(obj1.type == 0) { - free(s1.ptr); + free(s1m.ptr); } if(obj2.type == 0) { - free(s2.ptr); + free(s2m.ptr); } return res; } -int dav_exec_expr(UcxBuffer *bcode, DavResource *res, DavQLStackObj *result) { +int dav_exec_expr(CxBuffer *bcode, DavResource *res, DavQLStackObj *result) { if(!bcode) { result->type = 0; result->length = 0; diff -r a569148841ff -r efbd59642577 libidav/davqlexec.h --- a/libidav/davqlexec.h Sun Apr 16 14:12:24 2023 +0200 +++ b/libidav/davqlexec.h Fri Apr 21 21:25:32 2023 +0200 @@ -32,7 +32,7 @@ #include "davqlparser.h" #include "webdav.h" -#include +#include #ifdef __cplusplus extern "C" { @@ -66,7 +66,8 @@ typedef enum { DAVQL_OK = 0, DAVQL_UNSUPPORTED_FORMATCHAR, - DAVQL_UNKNOWN_FORMATCHAR + DAVQL_UNKNOWN_FORMATCHAR, + DAVQL_OOM } davqlerror_t; typedef enum { @@ -116,7 +117,7 @@ davqlcmdtype_t type; union DavQLCmdData { int64_t integer; - sstr_t string; + cxmutstr string; time_t timestamp; davqlresprop_t resprop; DavPropName property; @@ -142,14 +143,14 @@ typedef struct DavCompiledField { char *ns; char *name; - UcxBuffer *code; + CxBuffer *code; } DavCompiledField; typedef struct DavOrderCriterion { int type; // 0: resprop, 1: property union DavQLColumn { davqlresprop_t resprop; - UcxKey property; + CxHashKey property; } column; _Bool descending; } DavOrderCriterion; @@ -165,16 +166,16 @@ DavResult dav_statement_exec(DavSession *sn, DavQLStatement *st, ...); DavResult dav_statement_execv(DavSession *sn, DavQLStatement *st, va_list ap); -UcxBuffer* dav_path_string(sstr_t src, DavQLArgList *args, davqlerror_t *error); -sstr_t dav_format_string(UcxAllocator *a, sstr_t fstr, DavQLArgList *ap, davqlerror_t *error); +CxBuffer* dav_path_string(cxmutstr src, DavQLArgList *args, davqlerror_t *error); +cxmutstr dav_format_string(const CxAllocator *a, cxstring fstr, DavQLArgList *ap, davqlerror_t *error); DavResult dav_exec_select(DavSession *sn, DavQLStatement *st, va_list ap); -int dav_identifier2resprop(sstr_t src, davqlresprop_t *prop); +int dav_identifier2resprop(cxstring src, davqlresprop_t *prop); -UcxBuffer* dav_compile_expr(DavContext *ctx, UcxAllocator *a, DavQLExpression *lexpr, DavQLArgList *ap); +CxBuffer* dav_compile_expr(DavContext *ctx, const CxAllocator *a, DavQLExpression *lexpr, DavQLArgList *ap); -int dav_exec_expr(UcxBuffer *bcode, DavResource *res, DavQLStackObj *result); +int dav_exec_expr(CxBuffer *bcode, DavResource *res, DavQLStackObj *result); diff -r a569148841ff -r efbd59642577 libidav/davqlparser.c --- a/libidav/davqlparser.c Sun Apr 16 14:12:24 2023 +0200 +++ b/libidav/davqlparser.c Fri Apr 21 21:25:32 2023 +0200 @@ -27,7 +27,9 @@ */ #include "davqlparser.h" -#include +#include +#include +#include #include #include #include @@ -93,9 +95,9 @@ static void dav_debug_ql_fnames_print(DavQLStatement *stmt) { if (stmt->fields) { printf("Field names: "); - UCX_FOREACH(field, stmt->fields) { - DavQLField *f = field->data; - printf("%.*s, ", sfmtarg(f->name)); + CxIterator i = cxListIterator(stmt->fields); + cx_foreach(DavQLField *, f, i) { + printf("%.*s, ", (int)f->name.length, f->name.ptr); } printf("\b\b \b\b\n"); } @@ -103,10 +105,10 @@ static void dav_debug_ql_stmt_print(DavQLStatement *stmt) { // Basic information - size_t fieldcount = ucx_list_size(stmt->fields); + size_t fieldcount = stmt->fields ? stmt->fields->size : 0; int specialfield = 0; - if (stmt->fields) { - DavQLField* firstfield = (DavQLField*)stmt->fields->data; + if (stmt->fields && stmt->fields->size > 0) { + DavQLField* firstfield = (DavQLField*)cxListAt(stmt->fields, 0); if (firstfield->expr->type == DAVQL_IDENTIFIER) { switch (firstfield->expr->srctext.ptr[0]) { case '*': specialfield = 1; break; @@ -118,14 +120,14 @@ fieldcount--; } printf("Statement: %.*s\nType: %s\nField count: %zu %s\n", - sfmtarg(stmt->srctext), + (int)stmt->srctext.length, stmt->srctext.ptr, _map_querytype(stmt->type), fieldcount, _map_specialfield(specialfield)); dav_debug_ql_fnames_print(stmt); printf("Path: %.*s\nHas where clause: %s\n", - sfmtarg(stmt->path), + (int)stmt->path.length, stmt->path.ptr, stmt->where ? "yes" : "no"); // WITH attributes @@ -140,11 +142,11 @@ // order by clause printf("Order by: "); if (stmt->orderby) { - UCX_FOREACH(crit, stmt->orderby) { - DavQLOrderCriterion *critdata = crit->data; - printf("%.*s %s%s", sfmtarg(critdata->column->srctext), + CxIterator i = cxListIterator(stmt->orderby); + cx_foreach(DavQLOrderCriterion*, critdata, i) { + printf("%.*s %s%s", (int)critdata->column->srctext.length, critdata->column->srctext.ptr, critdata->descending ? "desc" : "asc", - crit->next ? ", " : "\n"); + i.index+1 < stmt->orderby->size ? ", " : "\n"); } } else { printf("nothing\n"); @@ -168,7 +170,7 @@ static void dav_debug_ql_expr_print(DavQLExpression *expr) { if (dav_debug_ql_expr_selected(expr)) { - sstr_t empty = ST("(empty)"); + cxstring empty = CX_STR("(empty)"); printf( "Text: %.*s\nType: %s\nOperator: %s\n", sfmtarg(expr->srctext), @@ -280,7 +282,7 @@ } DavQLExpression *examineexpr = NULL; - UcxList *examineelem = NULL; + CxList *examineelem = NULL; int examineclause = 0; while(1) { @@ -294,8 +296,8 @@ case DQLD_CMD_F: examineclause = DQLD_CMD_F; examineelem = stmt->fields; - if (stmt->fields) { - DavQLField* field = ((DavQLField*)stmt->fields->data); + if (stmt->fields && stmt->fields->size > 0) { + DavQLField* field = cxListAt(stmt->fields, 0); examineexpr = field->expr; dav_debug_ql_field_print(field); } else { @@ -310,14 +312,16 @@ case DQLD_CMD_O: examineclause = DQLD_CMD_O; examineelem = stmt->orderby; - examineexpr = stmt->orderby ? - ((DavQLOrderCriterion*)stmt->orderby->data)->column : NULL; + examineexpr = stmt->orderby && stmt->orderby->size > 0 ? + ((DavQLOrderCriterion*)cxListAt(stmt->orderby, 0))->column : NULL; dav_debug_ql_expr_print(examineexpr); break; case DQLD_CMD_N: case DQLD_CMD_P: + printf("TODO: port code to ucx 3\n"); + /* if (examineelem) { - UcxList *newelem = (cmd == DQLD_CMD_N ? + CxList *newelem = (cmd == DQLD_CMD_N ? examineelem->next : examineelem->prev); if (newelem) { examineelem = newelem; @@ -338,6 +342,7 @@ } else { printf("Currently not examining an expression list.\n"); } + */ break; case DQLD_CMD_L: if (dav_debug_ql_expr_selected(examineexpr)) { @@ -411,28 +416,28 @@ #define _error_invalid_string "string expected " _error_context #define _error_invalid_order_criterion "invalid order criterion " _error_context -#define token_sstr(token) (((DavQLToken*)(token)->data)->value) +#define token_sstr(token) ((token)->value) static void dav_error_in_context(int errorcode, const char *errormsg, - DavQLStatement *stmt, UcxList *token) { + DavQLStatement *stmt, DavQLToken *token) { // we try to achieve two things: get as many information as possible // and recover the concrete source string (and not the token strings) - sstr_t emptystring = ST(""); - sstr_t prev = token->prev ? (token->prev->prev ? + cxstring emptystring = CX_STR(""); + cxstring prev = token->prev ? (token->prev->prev ? token_sstr(token->prev->prev) : token_sstr(token->prev)) : emptystring; - sstr_t tokenstr = token_sstr(token); - sstr_t next = token->next ? (token->next->next ? + cxstring tokenstr = token_sstr(token); + cxstring next = token->next ? (token->next->next ? token_sstr(token->next->next) : token_sstr(token->next)) : emptystring; int lp = prev.length == 0 ? 0 : tokenstr.ptr-prev.ptr; - char *pn = tokenstr.ptr + tokenstr.length; + const char *pn = tokenstr.ptr + tokenstr.length; int ln = next.ptr+next.length - pn; stmt->errorcode = errorcode; - stmt->errormessage = ucx_sprintf(errormsg, + stmt->errormessage = cx_asprintf(errormsg, lp, prev.ptr, sfmtarg(tokenstr), ln, pn).ptr; @@ -448,30 +453,18 @@ #define dqlsec_mallocz(stmt, ptr, type) \ dqlsec_alloc_failed(ptr = calloc(1, sizeof(type)), stmt) -#define dqlsec_list_append_or_free(stmt, list, data) \ - do { \ - UcxList *_dqlsecbak_ = list; \ - list = ucx_list_append(list, data); \ - if (!list) { \ - free(data); \ - data = NULL; \ - (stmt)->errorcode = DAVQL_ERROR_OUT_OF_MEMORY; \ - list = _dqlsecbak_; \ - return 0; \ - } \ - } while(0) // special symbols are single tokens - the % sign MUST NOT be a special symbol static const char *special_token_symbols = ",()+-*/&|^~=!<>"; static _Bool iskeyword(DavQLToken *token) { - sstr_t keywords[] ={ST("select"), ST("set"), ST("from"), ST("at"), ST("as"), - ST("where"), ST("anywhere"), ST("like"), ST("unlike"), ST("and"), - ST("or"), ST("not"), ST("xor"), ST("with"), ST("infinity"), - ST("order"), ST("by"), ST("asc"), ST("desc") + cxstring keywords[] ={CX_STR("select"), CX_STR("set"), CX_STR("from"), CX_STR("at"), CX_STR("as"), + CX_STR("where"), CX_STR("anywhere"), CX_STR("like"), CX_STR("unlike"), CX_STR("and"), + CX_STR("or"), CX_STR("not"), CX_STR("xor"), CX_STR("with"), CX_STR("infinity"), + CX_STR("order"), CX_STR("by"), CX_STR("asc"), CX_STR("desc") }; - for (int i = 0 ; i < sizeof(keywords)/sizeof(sstr_t) ; i++) { - if (!sstrcasecmp(token->value, keywords[i])) { + for (int i = 0 ; i < sizeof(keywords)/sizeof(cxstring) ; i++) { + if (!cx_strcasecmp(token->value, keywords[i])) { return 1; } } @@ -479,18 +472,45 @@ } static _Bool islongoperator(DavQLToken *token) { - sstr_t operators[] = {ST("and"), ST("or"), ST("not"), ST("xor"), - ST("like"), ST("unlike") + cxstring operators[] = {CX_STR("and"), CX_STR("or"), CX_STR("not"), CX_STR("xor"), + CX_STR("like"), CX_STR("unlike") }; - for (int i = 0 ; i < sizeof(operators)/sizeof(sstr_t) ; i++) { - if (!sstrcasecmp(token->value, operators[i])) { + for (int i = 0 ; i < sizeof(operators)/sizeof(cxstring) ; i++) { + if (!cx_strcasecmp(token->value, operators[i])) { return 1; } } return 0; } -static UcxList* dav_parse_add_token(UcxList *tokenlist, DavQLToken *token) { +static int dav_stmt_add_field(DavQLStatement *stmt, DavQLField *field) { + if(!stmt->fields) { + stmt->fields = cxLinkedListCreateSimple(CX_STORE_POINTERS); + if(!stmt->fields) { + stmt->errorcode = DAVQL_ERROR_OUT_OF_MEMORY; + return 1; + } + } + + if(cxListAdd(stmt->fields, field)) { + stmt->errorcode = DAVQL_ERROR_OUT_OF_MEMORY; + return 1; + } + + return 0; +} + + +static void tokenlist_free(DavQLToken *tokenlist) { + DavQLToken *token = tokenlist; + while(token) { + DavQLToken *next = token->next; + free(token); + token = next; + } +} + +static int dav_parse_add_token(DavQLToken **begin, DavQLToken **end, DavQLToken *token) { // determine token class (order of if-statements is very important!) char firstchar = token->value.ptr[0]; @@ -541,24 +561,22 @@ } } - - UcxList *ret = ucx_list_append(tokenlist, token); - if (ret) { - return ret; - } else { - ucx_list_free(tokenlist); - return NULL; - } + cx_linked_list_add((void**)begin, (void**)end, offsetof(DavQLToken, prev), offsetof(DavQLToken, next), token); + return 0; } -static UcxList* dav_parse_tokenize(sstr_t src) { -#define alloc_token() do {token = malloc(sizeof(DavQLToken));\ - if(!token) {ucx_list_free(tokens); return NULL;}} while(0) -#define add_token() do {tokens = dav_parse_add_token(tokens, token); \ - if(!tokens) {return NULL;}} while(0) - UcxList *tokens = NULL; + + +static DavQLToken* dav_parse_tokenize(cxstring src) { +#define alloc_token() do {token = calloc(1, sizeof(DavQLToken));\ + if(!token) {tokenlist_free(tokens_begin); return NULL;}} while(0) +#define add_token() if(dav_parse_add_token(&tokens_begin, &tokens_end, token)) return NULL; + + DavQLToken *tokens_begin = NULL; + DavQLToken *tokens_end = NULL; DavQLToken *token = NULL; + char insequence = '\0'; for (size_t i = 0 ; i < src.length ; i++) { // quoted strings / identifiers are a single token @@ -628,14 +646,10 @@ alloc_token(); token->tokenclass = DAVQL_TOKEN_END; - token->value = S(""); - UcxList *ret = ucx_list_append(tokens, token); - if (ret) { - return ret; - } else { - ucx_list_free(tokens); - return NULL; - } + token->value = CX_STR(""); + + cx_linked_list_add((void**)&tokens_begin, (void**)&tokens_end, offsetof(DavQLToken, prev), offsetof(DavQLToken, next), token); + return tokens_begin; #undef alloc_token #undef add_token } @@ -661,18 +675,17 @@ if (crit->column) { // do it null-safe though column is expected to be set dav_free_expression(crit->column); } - free(crit); } #define token_is(token, expectedclass) (token && \ - (((DavQLToken*)(token)->data)->tokenclass == expectedclass)) + (token->tokenclass == expectedclass)) #define tokenvalue_is(token, expectedvalue) (token && \ - !sstrcasecmp(((DavQLToken*)(token)->data)->value, S(expectedvalue))) + !cx_strcasecmp(token->value, cx_str(expectedvalue))) -typedef int(*exprparser_f)(DavQLStatement*,UcxList*,DavQLExpression*); +typedef int(*exprparser_f)(DavQLStatement*,DavQLToken*,DavQLExpression*); -static int dav_parse_binary_expr(DavQLStatement* stmt, UcxList* token, +static int dav_parse_binary_expr(DavQLStatement* stmt, DavQLToken* token, DavQLExpression* expr, exprparser_f parseL, char* opc, int* opv, exprparser_f parseR) { @@ -692,7 +705,7 @@ return 0; } total_consumed += consumed; - token = ucx_list_get(token, consumed); + token = cx_linked_list_at(token, 0, offsetof(DavQLToken, next), consumed); char *op; if (token_is(token, DAVQL_TOKEN_OPERATOR) && @@ -731,15 +744,20 @@ return total_consumed; } -static void dav_add_fmt_args(DavQLStatement *stmt, sstr_t str) { +static void fmt_args_add(DavQLStatement *stmt, void *data) { + if(!stmt->args) { + stmt->args = cxLinkedListCreateSimple(CX_STORE_POINTERS); + } + cxListAdd(stmt->args, data); +} + +static void dav_add_fmt_args(DavQLStatement *stmt, cxstring str) { int placeholder = 0; for (size_t i=0;iargs = ucx_list_append( - stmt->args, - (void*)(intptr_t)c); + fmt_args_add(stmt, (void*)(intptr_t)c); } placeholder = 0; } else if (c == '%') { @@ -748,7 +766,7 @@ } } -static int dav_parse_literal(DavQLStatement* stmt, UcxList* token, +static int dav_parse_literal(DavQLStatement* stmt, DavQLToken* token, DavQLExpression* expr) { expr->srctext = token_sstr(token); @@ -772,7 +790,7 @@ return 0; } // add fmtspec type to query arg list - stmt->args = ucx_list_append(stmt->args, (void*)(intptr_t)expr->srctext.ptr[1]); + fmt_args_add(stmt, (void*)(intptr_t)expr->srctext.ptr[1]); } else { return 0; } @@ -781,10 +799,10 @@ } // forward declaration -static int dav_parse_expression(DavQLStatement* stmt, UcxList* token, +static int dav_parse_expression(DavQLStatement* stmt, DavQLToken* token, DavQLExpression* expr); -static int dav_parse_arglist(DavQLStatement* stmt, UcxList* token, +static int dav_parse_arglist(DavQLStatement* stmt, DavQLToken* token, DavQLExpression* expr) { expr->srctext.ptr = token_sstr(token).ptr; @@ -796,7 +814,7 @@ // RULE: Expression, {",", Expression}; DavQLExpression *arglist = expr; DavQLExpression arg; - char *lastchar = expr->srctext.ptr; + const char *lastchar = expr->srctext.ptr; int consumed; do { memset(&arg, 0, sizeof(DavQLExpression)); @@ -804,7 +822,7 @@ if (consumed) { lastchar = arg.srctext.ptr + arg.srctext.length; total_consumed += consumed; - token = ucx_list_get(token, consumed); + token = cx_linked_list_at(token, 0, offsetof(DavQLToken, next), consumed); // look ahead for a comma if (token_is(token, DAVQL_TOKEN_COMMA)) { total_consumed++; @@ -837,7 +855,7 @@ return total_consumed; } -static int dav_parse_funccall(DavQLStatement* stmt, UcxList* token, +static int dav_parse_funccall(DavQLStatement* stmt, DavQLToken* token, DavQLExpression* expr) { // RULE: Identifier, "(", ArgumentList, ")"; @@ -861,7 +879,7 @@ return 2; } if (argtokens) { - token = ucx_list_get(token, argtokens); + token = cx_linked_list_at(token, 0, offsetof(DavQLToken, next), argtokens); dqlsec_malloc(stmt, expr->right, DavQLExpression); memcpy(expr->right, &arg, sizeof(DavQLExpression)); } else { @@ -881,10 +899,10 @@ } } -static int dav_parse_unary_expr(DavQLStatement* stmt, UcxList* token, +static int dav_parse_unary_expr(DavQLStatement* stmt, DavQLToken* token, DavQLExpression* expr) { - UcxList *firsttoken = token; // save for srctext recovery + DavQLToken *firsttoken = token; // save for srctext recovery DavQLExpression* atom = expr; int total_consumed = 0; @@ -923,7 +941,7 @@ _error_invalid_expr, stmt, token); return 0; } - token = ucx_list_get(token, consumed); + token = cx_linked_list_at(token, 0, offsetof(DavQLToken, next), consumed); total_consumed += consumed; if (token_is(token, DAVQL_TOKEN_CLOSEP)) { token = token->next; total_consumed++; @@ -951,8 +969,8 @@ // recover source text expr->srctext.ptr = token_sstr(firsttoken).ptr; if (total_consumed > 0) { - sstr_t lasttoken = - token_sstr(ucx_list_get(firsttoken, total_consumed-1)); + cxstring lasttoken = + token_sstr((DavQLToken*)cx_linked_list_at(token, 0, offsetof(DavQLToken, next), total_consumed-1)); expr->srctext.length = lasttoken.ptr - expr->srctext.ptr + lasttoken.length; } else { @@ -964,7 +982,7 @@ return total_consumed; } -static int dav_parse_bitexpr(DavQLStatement* stmt, UcxList* token, +static int dav_parse_bitexpr(DavQLStatement* stmt, DavQLToken* token, DavQLExpression* expr) { return dav_parse_binary_expr(stmt, token, expr, @@ -973,7 +991,7 @@ dav_parse_bitexpr); } -static int dav_parse_multexpr(DavQLStatement* stmt, UcxList* token, +static int dav_parse_multexpr(DavQLStatement* stmt, DavQLToken* token, DavQLExpression* expr) { return dav_parse_binary_expr(stmt, token, expr, @@ -982,7 +1000,7 @@ dav_parse_multexpr); } -static int dav_parse_expression(DavQLStatement* stmt, UcxList* token, +static int dav_parse_expression(DavQLStatement* stmt, DavQLToken* token, DavQLExpression* expr) { return dav_parse_binary_expr(stmt, token, expr, @@ -991,7 +1009,7 @@ dav_parse_expression); } -static int dav_parse_named_field(DavQLStatement *stmt, UcxList *token, +static int dav_parse_named_field(DavQLStatement *stmt, DavQLToken *token, DavQLField *field) { int total_consumed = 0, consumed; @@ -1010,7 +1028,7 @@ return 0; } - token = ucx_list_get(token, consumed); + token = cx_linked_list_at(token, 0, offsetof(DavQLToken, next), consumed); total_consumed += consumed; if (token_is(token, DAVQL_TOKEN_KEYWORD) && tokenvalue_is(token, "as")) { @@ -1034,13 +1052,16 @@ } } -static int dav_parse_fieldlist(DavQLStatement *stmt, UcxList *token) { +static int dav_parse_fieldlist(DavQLStatement *stmt, DavQLToken *token) { // RULE: "-" if (token_is(token, DAVQL_TOKEN_OPERATOR) && tokenvalue_is(token, "-")) { DavQLField *field; dqlsec_malloc(stmt, field, DavQLField); - dqlsec_list_append_or_free(stmt, stmt->fields, field); + if(dav_stmt_add_field(stmt, field)) { + free(field); + return 0; + } dqlsec_mallocz(stmt, field->expr, DavQLExpression); field->expr->type = DAVQL_IDENTIFIER; field->expr->srctext = field->name = token_sstr(token); @@ -1051,7 +1072,10 @@ if (token_is(token, DAVQL_TOKEN_OPERATOR) && tokenvalue_is(token, "*")) { DavQLField *field; dqlsec_malloc(stmt, field, DavQLField); - dqlsec_list_append_or_free(stmt, stmt->fields, field); + if(dav_stmt_add_field(stmt, field)) { + free(field); + return 0; + } dqlsec_mallocz(stmt, field->expr, DavQLExpression); field->expr->type = DAVQL_IDENTIFIER; field->expr->srctext = field->name = token_sstr(token); @@ -1060,7 +1084,7 @@ int consumed = 1; do { - token = ucx_list_get(token, consumed); + token = cx_linked_list_at(token, 0, offsetof(DavQLToken, next), consumed); total_consumed += consumed; if (token_is(token, DAVQL_TOKEN_COMMA)) { @@ -1071,7 +1095,10 @@ DavQLField *field; dqlsec_malloc(stmt, field, DavQLField); memcpy(field, &localfield, sizeof(DavQLField)); - dqlsec_list_append_or_free(stmt, stmt->fields, field); + if(dav_stmt_add_field(stmt, field)) { + free(field); + return 0; + } } } else { consumed = 0; @@ -1092,8 +1119,11 @@ DavQLField *field; dqlsec_malloc(stmt, field, DavQLField); memcpy(field, &localfield, sizeof(DavQLField)); - dqlsec_list_append_or_free(stmt, stmt->fields, field); - token = ucx_list_get(token, consumed); + if(dav_stmt_add_field(stmt, field)) { + free(field); + return 0; + } + token = cx_linked_list_at(token, 0, offsetof(DavQLToken, next), consumed); total_consumed += consumed; } else if (token_is(token, DAVQL_TOKEN_IDENTIFIER) // look ahead, if the field is JUST the identifier @@ -1105,7 +1135,10 @@ dqlsec_mallocz(stmt, field->expr, DavQLExpression); field->expr->type = DAVQL_IDENTIFIER; field->expr->srctext = field->name = token_sstr(token); - dqlsec_list_append_or_free(stmt, stmt->fields, field); + if(dav_stmt_add_field(stmt, field)) { + free(field); + return 0; + } consumed = 1; total_consumed++; @@ -1137,10 +1170,10 @@ } // forward declaration -static int dav_parse_logical_expr(DavQLStatement *stmt, UcxList *token, +static int dav_parse_logical_expr(DavQLStatement *stmt, DavQLToken *token, DavQLExpression *expr); -static int dav_parse_bool_prim(DavQLStatement *stmt, UcxList *token, +static int dav_parse_bool_prim(DavQLStatement *stmt, DavQLToken *token, DavQLExpression *expr) { expr->type = DAVQL_LOGICAL; @@ -1154,9 +1187,9 @@ if (!total_consumed || stmt->errorcode) { return 0; } - token = ucx_list_get(token, total_consumed); + token = cx_linked_list_at(token, 0, offsetof(DavQLToken, next), total_consumed); - UcxList* optok = token; + DavQLToken* optok = token; // RULE: Expression, (" like " | " unlike "), String if (token_is(optok, DAVQL_TOKEN_OPERATOR) && (tokenvalue_is(optok, "like") || tokenvalue_is(optok, "unlike"))) { @@ -1248,7 +1281,7 @@ } } -static int dav_parse_bool_expr(DavQLStatement *stmt, UcxList *token, +static int dav_parse_bool_expr(DavQLStatement *stmt, DavQLToken *token, DavQLExpression *expr) { // RULE: "not ", LogicalExpression @@ -1264,7 +1297,7 @@ return 0; } if (consumed) { - sstr_t lasttok = token_sstr(ucx_list_get(token, consumed-1)); + cxstring lasttok = token_sstr((DavQLToken*)cx_linked_list_at(token, 0, offsetof(DavQLToken, next), consumed-1)); expr->srctext.length = lasttok.ptr - expr->srctext.ptr + lasttok.length; return consumed + 1; @@ -1278,7 +1311,7 @@ else if (token_is(token, DAVQL_TOKEN_OPENP)) { int consumed = dav_parse_logical_expr(stmt, token->next, expr); if (consumed) { - token = ucx_list_get(token->next, consumed); + token = cx_linked_list_at(token, 0, offsetof(DavQLToken, next), consumed); if (token_is(token, DAVQL_TOKEN_CLOSEP)) { token = token->next; @@ -1301,10 +1334,10 @@ return dav_parse_bool_prim(stmt, token, expr); } -static int dav_parse_logical_expr(DavQLStatement *stmt, UcxList *token, +static int dav_parse_logical_expr(DavQLStatement *stmt, DavQLToken *token, DavQLExpression *expr) { - UcxList *firsttoken = token; + DavQLToken *firsttoken = token; int total_consumed = 0; // RULE: BooleanLiteral, [LogicalOperator, LogicalExpression]; @@ -1320,7 +1353,7 @@ return 0; } total_consumed += consumed; - token = ucx_list_get(token, consumed); + token = cx_linked_list_at(token, 0, offsetof(DavQLToken, next), consumed); if (token_is(token, DAVQL_TOKEN_OPERATOR)) { expr->type = DAVQL_LOGICAL; @@ -1354,7 +1387,7 @@ return 0; } total_consumed += consumed; - token = ucx_list_get(token, consumed); + token = cx_linked_list_at(token, 0, offsetof(DavQLToken, next), consumed); dqlsec_malloc(stmt, expr->left, DavQLExpression); memcpy(expr->left, &left, sizeof(DavQLExpression)); @@ -1368,20 +1401,20 @@ // set type and recover source text if (total_consumed > 0) { expr->srctext.ptr = token_sstr(firsttoken).ptr; - sstr_t lasttok = token_sstr(ucx_list_get(firsttoken, total_consumed-1)); + cxstring lasttok = token_sstr((DavQLToken*)cx_linked_list_at(firsttoken, 0, offsetof(DavQLToken, next), total_consumed-1)); expr->srctext.length = lasttok.ptr-expr->srctext.ptr+lasttok.length; } return total_consumed; } -static int dav_parse_where_clause(DavQLStatement *stmt, UcxList *token) { +static int dav_parse_where_clause(DavQLStatement *stmt, DavQLToken *token) { dqlsec_mallocz(stmt, stmt->where, DavQLExpression); return dav_parse_logical_expr(stmt, token, stmt->where); } -static int dav_parse_with_clause(DavQLStatement *stmt, UcxList *token) { +static int dav_parse_with_clause(DavQLStatement *stmt, DavQLToken *token) { int total_consumed = 0; @@ -1404,7 +1437,7 @@ if (depthexpr->srctext.ptr[0] == '%') { stmt->depth = DAV_DEPTH_PLACEHOLDER; } else { - sstr_t depthstr = depthexpr->srctext; + cxstring depthstr = depthexpr->srctext; char *conv = malloc(depthstr.length+1); if (!conv) { dav_free_expression(depthexpr); @@ -1436,7 +1469,7 @@ return total_consumed; } -static int dav_parse_order_crit(DavQLStatement *stmt, UcxList *token, +static int dav_parse_order_crit(DavQLStatement *stmt, DavQLToken *token, DavQLOrderCriterion *crit) { // RULE: (Identifier | Number), [" asc"|" desc"]; @@ -1456,7 +1489,7 @@ dqlsec_malloc(stmt, crit->column, DavQLExpression); memcpy(crit->column, &expr, sizeof(DavQLExpression)); - token = ucx_list_get(token, consumed); + token = cx_linked_list_at(token, 0, offsetof(DavQLToken, next), consumed); if (token_is(token, DAVQL_TOKEN_KEYWORD) && ( tokenvalue_is(token, "asc") || tokenvalue_is(token, "desc"))) { @@ -1469,12 +1502,19 @@ } } -static int dav_parse_orderby_clause(DavQLStatement *stmt, UcxList *token) { +static int dav_parse_orderby_clause(DavQLStatement *stmt, DavQLToken *token) { int total_consumed = 0, consumed; DavQLOrderCriterion crit; + if(!stmt->orderby) { + stmt->orderby = cxLinkedListCreateSimple(sizeof(DavQLOrderCriterion)); + if(!stmt->orderby) { + return 0; + } + } + // RULE: OrderByCriterion, {",", OrderByCriterion}; do { consumed = dav_parse_order_crit(stmt, token, &crit); @@ -1486,13 +1526,13 @@ stmt, token); return 0; } - token = ucx_list_get(token, consumed); + token = cx_linked_list_at(token, 0, offsetof(DavQLToken, next), consumed); total_consumed += consumed; - DavQLOrderCriterion *criterion; - dqlsec_malloc(stmt, criterion, DavQLOrderCriterion); - memcpy(criterion, &crit, sizeof(DavQLOrderCriterion)); - dqlsec_list_append_or_free(stmt, stmt->orderby, criterion); + if(cxListAdd(stmt->orderby, &crit)) { + stmt->errorcode = DAVQL_ERROR_OUT_OF_MEMORY; + return 0; + } if (token_is(token, DAVQL_TOKEN_COMMA)) { total_consumed++; @@ -1506,7 +1546,7 @@ } -static int dav_parse_assignments(DavQLStatement *stmt, UcxList *token) { +static int dav_parse_assignments(DavQLStatement *stmt, DavQLToken *token) { // RULE: Assignment, {",", Assignment} int total_consumed = 0, consumed; @@ -1540,11 +1580,14 @@ dav_free_field(field); return total_consumed; } - token = ucx_list_get(token, consumed); + token = cx_linked_list_at(token, 0, offsetof(DavQLToken, next), consumed); total_consumed += consumed; // Add assignment to list and check if there's another one - dqlsec_list_append_or_free(stmt, stmt->fields, field); + if(dav_stmt_add_field(stmt, field)) { + free(field); + return 0; + } consumed = token_is(token, DAVQL_TOKEN_COMMA) ? 1 : 0; if (consumed) { token = token->next; @@ -1560,7 +1603,7 @@ return total_consumed; } -static int dav_parse_path(DavQLStatement *stmt, UcxList *tokens) { +static int dav_parse_path(DavQLStatement *stmt, DavQLToken *tokens) { if (token_is(tokens, DAVQL_TOKEN_STRING)) { stmt->path = token_sstr(tokens); tokens = tokens->next; @@ -1572,7 +1615,7 @@ int consumed = 1; while (!token_is(tokens, DAVQL_TOKEN_KEYWORD) && !token_is(tokens, DAVQL_TOKEN_END)) { - sstr_t toksstr = token_sstr(tokens); + cxstring toksstr = token_sstr(tokens); stmt->path.length = toksstr.ptr-stmt->path.ptr+toksstr.length; tokens = tokens->next; consumed++; @@ -1582,7 +1625,7 @@ tokenvalue_is(tokens, "%s")) { stmt->path = token_sstr(tokens); tokens = tokens->next; - stmt->args = ucx_list_append(stmt->args, (void*)(intptr_t)'s'); + fmt_args_add(stmt, (void*)(intptr_t)'s'); return 1; } else { dav_error_in_context(DAVQL_ERROR_MISSING_TOKEN, @@ -1596,11 +1639,11 @@ * @param stmt the statement object that shall contain the syntax tree * @param tokens the token list */ -static void dav_parse_select_statement(DavQLStatement *stmt, UcxList *tokens) { +static void dav_parse_select_statement(DavQLStatement *stmt, DavQLToken *tokens) { stmt->type = DAVQL_SELECT; // Consume field list - tokens = ucx_list_get(tokens, dav_parse_fieldlist(stmt, tokens)); + tokens = cx_linked_list_at(tokens, 0, offsetof(DavQLToken, next), dav_parse_fieldlist(stmt, tokens)); if (stmt->errorcode) { return; } @@ -1616,7 +1659,7 @@ } // Consume path - tokens = ucx_list_get(tokens, dav_parse_path(stmt, tokens)); + tokens = cx_linked_list_at(tokens, 0, offsetof(DavQLToken, next), dav_parse_path(stmt, tokens)); if (stmt->errorcode) { return; } @@ -1626,7 +1669,7 @@ if (token_is(tokens, DAVQL_TOKEN_KEYWORD) && tokenvalue_is(tokens, "with")) { tokens = tokens->next; - tokens = ucx_list_get(tokens, + tokens = cx_linked_list_at(tokens, 0, offsetof(DavQLToken, next), dav_parse_with_clause(stmt, tokens)); } if (stmt->errorcode) { @@ -1637,7 +1680,7 @@ if (token_is(tokens, DAVQL_TOKEN_KEYWORD) && tokenvalue_is(tokens, "where")) { tokens = tokens->next; - tokens = ucx_list_get(tokens, + tokens = cx_linked_list_at(tokens, 0, offsetof(DavQLToken, next), dav_parse_where_clause(stmt, tokens)); } else if (token_is(tokens, DAVQL_TOKEN_KEYWORD) && tokenvalue_is(tokens, "anywhere")) { @@ -1656,7 +1699,7 @@ if (token_is(tokens, DAVQL_TOKEN_KEYWORD) && tokenvalue_is(tokens, "by")) { tokens = tokens->next; - tokens = ucx_list_get(tokens, + tokens = cx_linked_list_at(tokens, 0, offsetof(DavQLToken, next), dav_parse_orderby_clause(stmt, tokens)); } else { dav_error_in_context(DAVQL_ERROR_MISSING_TOKEN, @@ -1680,11 +1723,11 @@ } } -static void dav_parse_set_statement(DavQLStatement *stmt, UcxList *tokens) { +static void dav_parse_set_statement(DavQLStatement *stmt, DavQLToken *tokens) { stmt->type = DAVQL_SET; // Consume assignments - tokens = ucx_list_get(tokens, dav_parse_assignments(stmt, tokens)); + tokens = cx_linked_list_at(tokens, 0, offsetof(DavQLToken, next), dav_parse_assignments(stmt, tokens)); if (stmt->errorcode) { return; } @@ -1700,7 +1743,7 @@ } // Consume path - tokens = ucx_list_get(tokens, dav_parse_path(stmt, tokens)); + tokens = cx_linked_list_at(tokens, 0, offsetof(DavQLToken, next), dav_parse_path(stmt, tokens)); if (stmt->errorcode) { return; } @@ -1709,7 +1752,7 @@ if (token_is(tokens, DAVQL_TOKEN_KEYWORD) && tokenvalue_is(tokens, "with")) { tokens = tokens->next; - tokens = ucx_list_get(tokens, + tokens = cx_linked_list_at(tokens, 0, offsetof(DavQLToken, next), dav_parse_with_clause(stmt, tokens)); } if (stmt->errorcode) { @@ -1720,7 +1763,7 @@ if (token_is(tokens, DAVQL_TOKEN_KEYWORD) && tokenvalue_is(tokens, "where")) { tokens = tokens->next; - tokens = ucx_list_get(tokens, + tokens = cx_linked_list_at(tokens, 0, offsetof(DavQLToken, next), dav_parse_where_clause(stmt, tokens)); } else if (token_is(tokens, DAVQL_TOKEN_KEYWORD) && tokenvalue_is(tokens, "anywhere")) { @@ -1734,7 +1777,7 @@ } } -DavQLStatement* dav_parse_statement(sstr_t srctext) { +DavQLStatement* dav_parse_statement(cxstring srctext) { DavQLStatement *stmt = calloc(1, sizeof(DavQLStatement)); // if we can't even get enough memory for the statement object or an error @@ -1753,11 +1796,11 @@ stmt->depth = 1; // save trimmed source text - stmt->srctext = sstrtrim(srctext); + stmt->srctext = cx_strtrim(srctext); if (stmt->srctext.length) { // tokenization - UcxList* tokens = dav_parse_tokenize(stmt->srctext); + DavQLToken* tokens = dav_parse_tokenize(stmt->srctext); if (tokens) { // use first token to determine query type @@ -1773,10 +1816,7 @@ } // free token data - UCX_FOREACH(token, tokens) { - free(token->data); - } - ucx_list_free(tokens); + tokenlist_free(tokens); } else { stmt->errorcode = DAVQL_ERROR_OUT_OF_MEMORY; } @@ -1797,10 +1837,10 @@ } void dav_free_statement(DavQLStatement *stmt) { - UCX_FOREACH(expr, stmt->fields) { - dav_free_field(expr->data); + if(stmt->fields) { + stmt->fields->simple_destructor = (cx_destructor_func)dav_free_field; + cxListDestroy(stmt->fields); } - ucx_list_free(stmt->fields); if (stmt->where) { dav_free_expression(stmt->where); @@ -1808,10 +1848,13 @@ if (stmt->errormessage) { free(stmt->errormessage); } - UCX_FOREACH(crit, stmt->orderby) { - dav_free_order_criterion(crit->data); + + if(stmt->orderby) { + stmt->orderby->simple_destructor = (cx_destructor_func)dav_free_order_criterion; + cxListDestroy(stmt->orderby); } - ucx_list_free(stmt->orderby); - ucx_list_free(stmt->args); + if(stmt->args) { + cxListDestroy(stmt->args); + } free(stmt); } diff -r a569148841ff -r efbd59642577 libidav/davqlparser.h --- a/libidav/davqlparser.h Sun Apr 16 14:12:24 2023 +0200 +++ b/libidav/davqlparser.h Fri Apr 21 21:25:32 2023 +0200 @@ -34,8 +34,8 @@ #endif #include -#include "ucx/string.h" -#include "ucx/list.h" +#include +#include /** * Enumeration of possible statement types. @@ -74,10 +74,13 @@ DAVQL_LIKE, DAVQL_UNLIKE // comparisons } davqloperator_t; -typedef struct { +typedef struct DavQLToken DavQLToken; +struct DavQLToken { davqltokenclass_t tokenclass; - sstr_t value; -} DavQLToken; + cxstring value; + DavQLToken *prev; + DavQLToken *next; +}; /** * An expression within a DAVQL query. @@ -92,7 +95,7 @@ * The original expression text. * Contains the literal value, if type is LITERAL. */ - sstr_t srctext; + cxstring srctext; /** * The expression type. */ @@ -139,7 +142,7 @@ *
  • SET: the identifier
  • * */ - sstr_t name; + cxstring name; /** * The field expression. *
      @@ -242,7 +245,7 @@ /** * The original query text. */ - sstr_t srctext; + cxstring srctext; /** * The statement type. */ @@ -258,11 +261,11 @@ /** * The list of DavQLFields. */ - UcxList* fields; + CxList* fields; /** * A string that denotes the queried path. */ - sstr_t path; + cxstring path; /** * Logical expression for selection. * NULL, if there is no where clause. @@ -273,7 +276,7 @@ * This is NULL for SET queries and may be NULL * if the result doesn't need to be sorted. */ - UcxList* orderby; + CxList* orderby; /** * The recursion depth for the statement. * Defaults to 1. @@ -284,7 +287,7 @@ /** * A list of all required arguments */ - UcxList* args; + CxList* args; } DavQLStatement; /** Infinity recursion depth for a DavQLStatement. */ @@ -350,12 +353,12 @@ * @param stmt the sstr_t containing the statement * @return a DavQLStatement object */ -DavQLStatement* dav_parse_statement(sstr_t stmt); +DavQLStatement* dav_parse_statement(cxstring stmt); /** * Implicitly converts a cstr to a sstr_t and calls dav_parse_statement. */ -#define dav_parse_cstr_statement(stmt) dav_parse_statement(S(stmt)) +#define dav_parse_cstr_statement(stmt) dav_parse_statement(cx_str(stmt)) /** * Frees a DavQLStatement. diff -r a569148841ff -r efbd59642577 libidav/methods.c --- a/libidav/methods.c Sun Apr 16 14:12:24 2023 +0200 +++ b/libidav/methods.c Fri Apr 21 21:25:32 2023 +0200 @@ -36,21 +36,23 @@ #include "session.h" #include "xml.h" -#include +#include +#include +#include #define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b) -int dav_buffer_seek(UcxBuffer *b, curl_off_t offset, int origin) { - return ucx_buffer_seek(b, offset, origin) == 0 ? 0:CURL_SEEKFUNC_CANTSEEK; +int dav_buffer_seek(CxBuffer *b, curl_off_t offset, int origin) { + return cxBufferSeek(b, offset, origin) == 0 ? 0:CURL_SEEKFUNC_CANTSEEK; } /* ----------------------------- PROPFIND ----------------------------- */ CURLcode do_propfind_request( DavSession *sn, - UcxBuffer *request, - UcxBuffer *response) + CxBuffer *request, + CxBuffer *response) { CURL *handle = sn->handle; curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "PROPFIND"); @@ -64,14 +66,15 @@ CURLcode ret = 0; curl_easy_setopt(handle, CURLOPT_UPLOAD, 1); - curl_easy_setopt(handle, CURLOPT_READFUNCTION, ucx_buffer_read); - curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, dav_buffer_seek); + curl_easy_setopt(handle, CURLOPT_READFUNCTION, cxBufferRead); + curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, cxBufferSeek); curl_easy_setopt(handle, CURLOPT_READDATA, request); curl_easy_setopt(handle, CURLOPT_INFILESIZE, request->size); - curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, ucx_buffer_write); + curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, cxBufferWrite); curl_easy_setopt(handle, CURLOPT_WRITEDATA, response); - UcxMap *respheaders = ucx_map_new(32); + CxMap *respheaders = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 32); + respheaders->simple_destructor = free; util_capture_header(handle, respheaders); for(int i=0;i the server handled our request and we can stop requesting */ char *msdavexterror; - msdavexterror = ucx_map_cstr_get(respheaders, "x-msdavext_error"); + msdavexterror = cxMapGet(respheaders, cx_hash_key_str("x-msdavext_error")); int iishack = depth == 1 && msdavexterror && !strncmp(msdavexterror, "589831;", 7); @@ -113,71 +116,72 @@ // deactivate header capturing and free captured map util_capture_header(handle, NULL); - ucx_map_free_content(respheaders, free); - ucx_map_free(respheaders); + cxMapDestroy(respheaders); return ret; } -UcxBuffer* create_allprop_propfind_request(void) { - UcxBuffer *buf = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOFREE); - sstr_t s; +CxBuffer* create_allprop_propfind_request(void) { + CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); + cxstring s; - s = S("\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + s = CX_STR("\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); - s = S("\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + s = CX_STR("\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); - s = S("\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + s = CX_STR("\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); return buf; } -UcxBuffer* create_cryptoprop_propfind_request(void) { - UcxBuffer *buf = ucx_buffer_new(NULL, 256, UCX_BUFFER_AUTOFREE); - scstr_t s; +CxBuffer* create_cryptoprop_propfind_request(void) { + CxBuffer *buf = cxBufferCreate(NULL, 256, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); + cxstring s; - s = SC("\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + s = CX_STR("\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); - s = SC("\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + s = CX_STR("\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); - s = SC("\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + s = CX_STR("\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); return buf; } -UcxBuffer* create_propfind_request(DavSession *sn, UcxList *properties, char *rootelm, DavBool nocrypt) { - UcxBuffer *buf = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOEXTEND); - sstr_t s; +CxBuffer* create_propfind_request(DavSession *sn, CxList *properties, char *rootelm, DavBool nocrypt) { + CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); + cxstring s; int add_crypto_name = 1; int add_crypto_key = 1; int add_crypto_hash = 1; char *crypto_ns = "idav"; - UcxMap *namespaces = ucx_map_new(8); - UCX_FOREACH(elm, properties) { - DavProperty *p = elm->data; - if(strcmp(p->ns->name, "DAV:")) { - ucx_map_cstr_put(namespaces, p->ns->prefix, p->ns); - } - - // if the properties list contains the idav properties crypto-name - // and crypto-key, mark them as existent - if(!strcmp(p->ns->name, DAV_NS)) { - if(!strcmp(p->name, "crypto-name")) { - add_crypto_name = 0; - crypto_ns = p->ns->prefix; - } else if(!strcmp(p->name, "crypto-key")) { - add_crypto_key = 0; - crypto_ns = p->ns->prefix; - } else if(!strcmp(p->name, "crypto-hash")) { - add_crypto_hash = 0; - crypto_ns = p->ns->prefix; + CxMap *namespaces = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 8); + if(properties) { + CxIterator i = cxListIterator(properties); + cx_foreach(DavProperty*, p, i) { + if(strcmp(p->ns->name, "DAV:")) { + cxMapPut(namespaces, cx_hash_key_str(p->ns->prefix), p->ns); + } + + // if the properties list contains the idav properties crypto-name + // and crypto-key, mark them as existent + if(!strcmp(p->ns->name, DAV_NS)) { + if(!strcmp(p->name, "crypto-name")) { + add_crypto_name = 0; + crypto_ns = p->ns->prefix; + } else if(!strcmp(p->name, "crypto-key")) { + add_crypto_key = 0; + crypto_ns = p->ns->prefix; + } else if(!strcmp(p->name, "crypto-hash")) { + add_crypto_hash = 0; + crypto_ns = p->ns->prefix; + } } } } @@ -186,126 +190,126 @@ if(add_crypto_name && add_crypto_key && DAV_CRYPTO(sn) && !nocrypt) { idav_ns.prefix = "idav"; idav_ns.name = DAV_NS; - ucx_map_cstr_put(namespaces, "idav", &idav_ns); + cxMapPut(namespaces, cx_hash_key_str("idav"), &idav_ns); } - s = S("\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + s = CX_STR("\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); // write root element and namespaces - ucx_bprintf(buf, "prefix); - ucx_buffer_write(s.ptr, 1, s.length, buf); - s = S("=\""); - ucx_buffer_write(s.ptr, 1, s.length, buf); - s = sstr(ns->name); - ucx_buffer_write(s.ptr, 1, s.length, buf); - s = S("\""); - ucx_buffer_write(s.ptr, 1, s.length, buf); + CxIterator mapi = cxMapIteratorValues(namespaces); + cx_foreach(DavNamespace*, ns, mapi) { + s = CX_STR(" xmlns:"); + cxBufferWrite(s.ptr, 1, s.length, buf); + s = cx_str(ns->prefix); + cxBufferWrite(s.ptr, 1, s.length, buf); + s = CX_STR("=\""); + cxBufferWrite(s.ptr, 1, s.length, buf); + s = cx_str(ns->name); + cxBufferWrite(s.ptr, 1, s.length, buf); + s = CX_STR("\""); + cxBufferWrite(s.ptr, 1, s.length, buf); } - s = S(">\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + s = CX_STR(">\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); // default properties - s = S("\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + s = CX_STR("\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); - s = S("\n\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + s = CX_STR("\n\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); - s = S("\n\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + s = CX_STR("\n\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); - s = S("\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + s = CX_STR("\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); // crypto properties if(DAV_CRYPTO(sn) && !nocrypt) { if(add_crypto_name) { - ucx_buffer_putc(buf, '<'); - ucx_buffer_puts(buf, crypto_ns); - s = S(":crypto-name />\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + cxBufferPut(buf, '<'); + cxBufferPutString(buf, crypto_ns); + s = CX_STR(":crypto-name />\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); } if(add_crypto_key) { - ucx_buffer_putc(buf, '<'); - ucx_buffer_puts(buf, crypto_ns); - s = S(":crypto-key />\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + cxBufferPut(buf, '<'); + cxBufferPutString(buf, crypto_ns); + s = CX_STR(":crypto-key />\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); } if(add_crypto_hash) { - ucx_buffer_putc(buf, '<'); - ucx_buffer_puts(buf, crypto_ns); - s = S(":crypto-hash />\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + cxBufferPut(buf, '<'); + cxBufferPutString(buf, crypto_ns); + s = CX_STR(":crypto-hash />\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); } } // extra properties - UCX_FOREACH(elm, properties) { - DavProperty *prop = elm->data; - s = S("<"); - ucx_buffer_write(s.ptr, 1, s.length, buf); - s = sstr(prop->ns->prefix); - ucx_buffer_write(s.ptr, 1, s.length, buf); - s = S(":"); - ucx_buffer_write(s.ptr, 1, s.length, buf); - s = sstr(prop->name); - ucx_buffer_write(s.ptr, 1, s.length, buf); - s = S(" />\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + if(properties) { + CxIterator i = cxListIterator(properties); + cx_foreach(DavProperty*, prop, i) { + s = CX_STR("<"); + cxBufferWrite(s.ptr, 1, s.length, buf); + s = cx_str(prop->ns->prefix); + cxBufferWrite(s.ptr, 1, s.length, buf); + s = CX_STR(":"); + cxBufferWrite(s.ptr, 1, s.length, buf); + s = cx_str(prop->name); + cxBufferWrite(s.ptr, 1, s.length, buf); + s = CX_STR(" />\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); + } } // end - ucx_bprintf(buf, "\n\n", rootelm); + cx_bprintf(buf, "\n\n", rootelm); - ucx_map_free(namespaces); + cxMapDestroy(namespaces); return buf; } -UcxBuffer* create_basic_propfind_request(void) { - UcxBuffer *buf = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOEXTEND); - sstr_t s; +CxBuffer* create_basic_propfind_request(void) { + CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); + cxstring s; - s = S("\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + s = CX_STR("\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); - s = S("\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + s = CX_STR("\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); // properties - s = S("\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); - s = S("\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); - s = S("\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); - s = S("\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); - s = S("\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); - s = S("\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + s = CX_STR("\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); + s = CX_STR("\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); + s = CX_STR("\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); + s = CX_STR("\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); + s = CX_STR("\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); + s = CX_STR("\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); // end - s = S("\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + s = CX_STR("\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); return buf; } -PropfindParser* create_propfind_parser(UcxBuffer *response, char *url) { +PropfindParser* create_propfind_parser(CxBuffer *response, char *url) { PropfindParser *parser = malloc(sizeof(PropfindParser)); if(!parser) { return NULL; @@ -349,11 +353,10 @@ char *href = NULL; int iscollection = 0; - UcxList *properties = NULL; // xmlNode list char *crypto_name = NULL; // name set by crypto-name property char *crypto_key = NULL; - result->properties = NULL; + result->properties = cxLinkedListCreateSimple(CX_STORE_POINTERS); // xmlNode list xmlNode *node = parser->current->children; while(node) { @@ -380,13 +383,13 @@ // error return -1; } - sstr_t status_str = sstr((char*)status_node->content); + cxstring status_str = cx_str((char*)status_node->content); if(status_str.length < 13) { // error return -1; } - status_str = sstrsubsl(status_str, 9, 3); - if(!sstrcmp(status_str, S("200"))) { + status_str = cx_strsubsl(status_str, 9, 3); + if(!cx_strcmp(status_str, CX_STR("200"))) { ok = 1; } } @@ -398,7 +401,7 @@ n = prop_node->children; while(n) { if(n->type == XML_ELEMENT_NODE) { - properties = ucx_list_append(properties, n); + cxListAdd(result->properties, n); if(xstreq(n->name, "resourcetype")) { if(parse_resource_type(n)) { iscollection = TRUE; @@ -421,7 +424,6 @@ result->href = util_url_path(href); result->iscollection = iscollection; - result->properties = properties; result->crypto_name = crypto_name; result->crypto_key = crypto_key; @@ -442,27 +444,27 @@ void cleanup_response(ResponseTag *result) { if(result) { - ucx_list_free(result->properties); + cxListDestroy(result->properties); } } -int hrefeq(DavSession *sn, char *href1, char *href2) { - sstr_t href_s = sstr(util_url_decode(sn, href1)); - sstr_t href_r = sstr(util_url_decode(sn, href2)); +int hrefeq(DavSession *sn, const char *href1, const char *href2) { + cxmutstr href_s = cx_mutstr(util_url_decode(sn, href1)); + cxmutstr href_r = cx_mutstr(util_url_decode(sn, href2)); int ret = 0; - if(!sstrcmp(href_s, href_r)) { + if(!cx_strcmp(cx_strcast(href_s), cx_strcast(href_r))) { ret = 1; } else if(href_s.length == href_r.length + 1) { if(href_s.ptr[href_s.length-1] == '/') { href_s.length--; - if(!sstrcmp(href_s, href_r)) { + if(!cx_strcmp(cx_strcast(href_s), cx_strcast(href_r))) { ret = 1; } } } else if(href_r.length == href_s.length + 1) { if(href_r.ptr[href_r.length-1] == '/') { href_r.length--; - if(!sstrcmp(href_s, href_r)) { + if(!cx_strcmp(cx_strcast(href_s), cx_strcast(href_r))) { ret = 1; } } @@ -475,7 +477,7 @@ } -DavResource* parse_propfind_response(DavSession *sn, DavResource *root, UcxBuffer *response) { +DavResource* parse_propfind_response(DavSession *sn, DavResource *root, CxBuffer *response) { char *url = NULL; curl_easy_getinfo(sn->handle, CURLINFO_EFFECTIVE_URL, &url); if(!root) { @@ -523,7 +525,7 @@ return NULL; } } else { - sstr_t resname = sstr(util_resource_name(response->href)); + cxstring resname = cx_str(util_resource_name(response->href)); int nlen = 0; char *uname = curl_easy_unescape( sn->handle, @@ -555,19 +557,21 @@ char *crypto_key = NULL; // add properties - UCX_FOREACH(elm, response->properties) { - xmlNode *prop = elm->data; - resource_add_property(res, (char*)prop->ns->href, (char*)prop->name, prop->children); + if(response->properties) { + CxIterator i = cxListIterator(response->properties); + cx_foreach(xmlNode*, prop, i) { + resource_add_property(res, (char*)prop->ns->href, (char*)prop->name, prop->children); - if (decrypt_props && - prop->children && - prop->children->type == XML_TEXT_NODE && - xstreq(prop->ns->href, DAV_NS)) - { - if(xstreq(prop->name, "crypto-prop")) { - crypto_prop = prop; - } else if(xstreq(prop->name, "crypto-key")) { - crypto_key = util_xml_get_text(prop); + if (decrypt_props && + prop->children && + prop->children->type == XML_TEXT_NODE && + xstreq(prop->ns->href, DAV_NS)) + { + if(xstreq(prop->name, "crypto-prop")) { + crypto_prop = prop; + } else if(xstreq(prop->name, "crypto-key")) { + crypto_key = util_xml_get_text(prop); + } } } } @@ -576,7 +580,7 @@ char *crypto_prop_content = util_xml_get_text(crypto_prop); DavKey *key = dav_context_get_key(res->session->context, crypto_key); if(crypto_prop_content) { - UcxMap *cprops = parse_crypto_prop_str(res->session, key, crypto_prop_content); + CxMap *cprops = parse_crypto_prop_str(res->session, key, crypto_prop_content); resource_set_crypto_properties(res, cprops); } } @@ -589,8 +593,8 @@ //DavResource *res = resource; DavResource *res = NULL; - char *href = NULL; - UcxList *properties = NULL; // xmlNode list + const char *href = NULL; + CxList *properties = cxLinkedListCreateSimple(CX_STORE_POINTERS); // xmlNode list char *crypto_name = NULL; // name set by crypto-name property char *crypto_key = NULL; @@ -607,7 +611,7 @@ return 1; } //char *href = (char*)href_node->content; - href = util_url_path((char*)href_node->content); + href = util_url_path((const char*)href_node->content); char *href_s = util_url_decode(resource->session, href); char *href_r = util_url_decode(resource->session, resource->href); @@ -633,13 +637,13 @@ sn->error = DAV_ERROR; return 1; } - sstr_t status_str = sstr((char*)status_node->content); + cxstring status_str = cx_str((char*)status_node->content); if(status_str.length < 13) { sn->error = DAV_ERROR; return 1; } - status_str = sstrsubsl(status_str, 9, 3); - if(!sstrcmp(status_str, S("200"))) { + status_str = cx_strsubsl(status_str, 9, 3); + if(!cx_strcmp(status_str, CX_STR("200"))) { ok = 1; } } @@ -651,7 +655,7 @@ n = prop_node->children; while(n) { if(n->type == XML_ELEMENT_NODE) { - properties = ucx_list_append(properties, n); + cxListAdd(properties, n); if(xstreq(n->name, "resourcetype")) { if(parse_resource_type(n)) { iscollection = TRUE; @@ -689,7 +693,7 @@ return -1; } } else { - sstr_t resname = sstr(util_resource_name(href)); + cxstring resname = cx_str(util_resource_name(href)); int nlen = 0; char *uname = curl_easy_unescape( sn->handle, @@ -700,8 +704,8 @@ curl_free(uname); } - href = dav_session_strdup(sn, href); - res = dav_resource_new_full(sn, resource->path, name, href); + char *href_cp = dav_session_strdup(sn, href); + res = dav_resource_new_full(sn, resource->path, name, href_cp); dav_session_free(sn, name); } @@ -711,8 +715,8 @@ int decrypt_props = DAV_ENCRYPT_PROPERTIES(res->session); xmlNode *crypto_prop = NULL; - UCX_FOREACH(elm, properties) { - xmlNode *prop = elm->data; + CxIterator i = cxListIterator(properties); + cx_foreach(xmlNode*, prop, i) { if(!prop->ns) { continue; } @@ -728,13 +732,13 @@ } } } - ucx_list_free(properties); + cxListDestroy(properties); if(crypto_prop && crypto_key) { char *crypto_prop_content = util_xml_get_text(crypto_prop); DavKey *key = dav_context_get_key(res->session->context, crypto_key); if(crypto_prop_content && key) { - UcxMap *cprops = parse_crypto_prop_str(res->session, key, crypto_prop_content); + CxMap *cprops = parse_crypto_prop_str(res->session, key, crypto_prop_content); resource_set_crypto_properties(res, cprops); } } @@ -784,8 +788,8 @@ CURLcode do_proppatch_request( DavSession *sn, char *lock, - UcxBuffer *request, - UcxBuffer *response) + CxBuffer *request, + CxBuffer *response) { CURL *handle = sn->handle; curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "PROPPATCH"); @@ -795,7 +799,7 @@ if(lock) { char *url = NULL; curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &url); - char *ltheader = ucx_sprintf("If: <%s> (<%s>)", url, lock).ptr; + char *ltheader = cx_asprintf("If: <%s> (<%s>)", url, lock).ptr; headers = curl_slist_append(headers, ltheader); free(ltheader); curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); @@ -803,15 +807,15 @@ curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(handle, CURLOPT_UPLOAD, 1); - curl_easy_setopt(handle, CURLOPT_READFUNCTION, ucx_buffer_read); - curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, dav_buffer_seek); + curl_easy_setopt(handle, CURLOPT_READFUNCTION, cxBufferRead); + curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, cxBufferSeek); curl_easy_setopt(handle, CURLOPT_READDATA, request); curl_easy_setopt(handle, CURLOPT_INFILESIZE, request->size); - curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, ucx_buffer_write); + curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, cxBufferWrite); curl_easy_setopt(handle, CURLOPT_WRITEDATA, response); - ucx_buffer_seek(request, 0, SEEK_SET); + cxBufferSeek(request, 0, SEEK_SET); CURLcode ret = dav_session_curl_perform_buf(sn, request, response, NULL); curl_slist_free_all(headers); @@ -820,168 +824,171 @@ return ret; } -UcxBuffer* create_proppatch_request(DavResourceData *data) { - UcxBuffer *buf = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOEXTEND); - scstr_t s; +CxBuffer* create_proppatch_request(DavResourceData *data) { + CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); + cxstring s; - UcxMap *namespaces = ucx_map_new(8); + CxMap *namespaces = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 8); + namespaces->simple_destructor = free; + char prefix[8]; int pfxnum = 0; - UCX_FOREACH(elm, data->set) { - DavProperty *p = elm->data; - if(strcmp(p->ns->name, "DAV:")) { - snprintf(prefix, 8, "x%d", pfxnum++); - ucx_map_cstr_put(namespaces, p->ns->name, strdup(prefix)); + if(data->set) { + CxIterator i = cxListIterator(data->set); + cx_foreach(DavProperty*, p, i) { + if(strcmp(p->ns->name, "DAV:")) { + snprintf(prefix, 8, "x%d", pfxnum++); + cxMapPut(namespaces, cx_hash_key_str(p->ns->name), strdup(prefix)); + } } } - UCX_FOREACH(elm, data->remove) { - DavProperty *p = elm->data; - if(strcmp(p->ns->name, "DAV:")) { - snprintf(prefix, 8, "x%d", pfxnum++); - ucx_map_cstr_put(namespaces, p->ns->name, strdup(prefix)); + if(data->remove) { + CxIterator i = cxListIterator(data->remove); + cx_foreach(DavProperty*, p, i) { + if(strcmp(p->ns->name, "DAV:")) { + snprintf(prefix, 8, "x%d", pfxnum++); + cxMapPut(namespaces, cx_hash_key_str(p->ns->name), strdup(prefix)); + } } } - s = SC("\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + s = CX_STR("\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); // write root element and namespaces - s = SC("value); + cxBufferWrite(s.ptr, 1, s.length, buf); + s = CX_STR("=\""); + cxBufferWrite(s.ptr, 1, s.length, buf); + s = cx_strn(entry->key->data, entry->key->len); + cxBufferWrite(s.ptr, 1, s.length, buf); + s = CX_STR("\""); + cxBufferWrite(s.ptr, 1, s.length, buf); } - s = SC(">\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + s = CX_STR(">\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); if(data->set) { - s = SC("\n\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); - UCX_FOREACH(elm, data->set) { - DavProperty *property = elm->data; - char *prefix = ucx_map_cstr_get(namespaces, property->ns->name); + s = CX_STR("\n\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); + CxIterator i = cxListIterator(data->set); + cx_foreach(DavProperty*, property, i) { + char *prefix = cxMapGet(namespaces, cx_hash_key_str(property->ns->name)); if(!prefix) { prefix = "D"; } // begin tag - s = SC("<"); - ucx_buffer_write(s.ptr, 1, s.length, buf); - s = scstr(prefix); - ucx_buffer_write(s.ptr, 1, s.length, buf); - s = SC(":"); - ucx_buffer_write(s.ptr, 1, s.length, buf); - s = scstr(property->name); - ucx_buffer_write(s.ptr, 1, s.length, buf); - s = SC(">"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + s = CX_STR("<"); + cxBufferWrite(s.ptr, 1, s.length, buf); + s = cx_str(prefix); + cxBufferWrite(s.ptr, 1, s.length, buf); + s = CX_STR(":"); + cxBufferWrite(s.ptr, 1, s.length, buf); + s = cx_str(property->name); + cxBufferWrite(s.ptr, 1, s.length, buf); + s = CX_STR(">"); + cxBufferWrite(s.ptr, 1, s.length, buf); // content DavXmlNode *content = property->value; if(content->type == DAV_XML_TEXT && !content->next) { - ucx_buffer_write(content->content, 1, content->contentlength, buf); + cxBufferWrite(content->content, 1, content->contentlength, buf); } else { - dav_print_node(buf, (write_func)ucx_buffer_write, namespaces, content); + dav_print_node(buf, (cx_write_func)cxBufferWrite, namespaces, content); } // end tag - s = SC("name); - ucx_buffer_write(s.ptr, 1, s.length, buf); - s = SC(">\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + s = CX_STR("name); + cxBufferWrite(s.ptr, 1, s.length, buf); + s = CX_STR(">\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); } - s = SC("\n\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + s = CX_STR("\n\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); } if(data->remove) { - s = SC("\n\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); - UCX_FOREACH(elm, data->remove) { - DavProperty *property = elm->data; - char *prefix = ucx_map_cstr_get(namespaces, property->ns->name); + s = CX_STR("\n\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); + CxIterator i = cxListIterator(data->remove); + cx_foreach(DavProperty*, property, i) { + char *prefix = cxMapGet(namespaces, cx_hash_key_str(property->ns->name)); - s = SC("<"); - ucx_buffer_write(s.ptr, 1, s.length, buf); - s = scstr(prefix); - ucx_buffer_write(s.ptr, 1, s.length, buf); - s = SC(":"); - ucx_buffer_write(s.ptr, 1, s.length, buf); - s = scstr(property->name); - ucx_buffer_write(s.ptr, 1, s.length, buf); - s = SC(" />\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + s = CX_STR("<"); + cxBufferWrite(s.ptr, 1, s.length, buf); + s = cx_str(prefix); + cxBufferWrite(s.ptr, 1, s.length, buf); + s = CX_STR(":"); + cxBufferWrite(s.ptr, 1, s.length, buf); + s = cx_str(property->name); + cxBufferWrite(s.ptr, 1, s.length, buf); + s = CX_STR(" />\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); } - s = SC("\n\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + s = CX_STR("\n\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); } - s = SC("\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + s = CX_STR("\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); // cleanup namespace map - ucx_map_free_content(namespaces, free); - ucx_map_free(namespaces); + cxMapDestroy(namespaces); return buf; } -UcxBuffer* create_crypto_proppatch_request(DavSession *sn, DavKey *key, const char *name, const char *hash) { - UcxBuffer *buf = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOEXTEND); - sstr_t s; +CxBuffer* create_crypto_proppatch_request(DavSession *sn, DavKey *key, const char *name, const char *hash) { + CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); + cxstring s; - s = S("\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + s = CX_STR("\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); - s = S("\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + s = CX_STR("\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); - s = S("\n\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + s = CX_STR("\n\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); if(DAV_ENCRYPT_NAME(sn)) { - s = S(""); - ucx_buffer_write(s.ptr, 1, s.length, buf); + s = CX_STR(""); + cxBufferWrite(s.ptr, 1, s.length, buf); char *crname = aes_encrypt(name, strlen(name), key); - ucx_buffer_puts(buf, crname); + cxBufferPutString(buf, crname); free(crname); - s = S("\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + s = CX_STR("\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); } - s = S(""); - ucx_buffer_write(s.ptr, 1, s.length, buf); - ucx_buffer_puts(buf, key->name); - s = S("\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + s = CX_STR(""); + cxBufferWrite(s.ptr, 1, s.length, buf); + cxBufferPutString(buf, key->name); + s = CX_STR("\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); if(hash) { - s = S(""); - ucx_buffer_write(s.ptr, 1, s.length, buf); - ucx_buffer_puts(buf, hash); - s = S("\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + s = CX_STR(""); + cxBufferWrite(s.ptr, 1, s.length, buf); + cxBufferPutString(buf, hash); + s = CX_STR("\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); } - s = S("\n\n\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + s = CX_STR("\n\n\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); return buf; } @@ -1006,10 +1013,10 @@ char *ltheader = NULL; if(create) { url = util_parent_path(url); - ltheader = ucx_sprintf("If: <%s> (<%s>)", url, lock).ptr; + ltheader = cx_asprintf("If: <%s> (<%s>)", url, lock).ptr; free(url); } else { - ltheader = ucx_sprintf("If: <%s> (<%s>)", url, lock).ptr; + ltheader = cx_asprintf("If: <%s> (<%s>)", url, lock).ptr; } headers = curl_slist_append(headers, ltheader); free(ltheader); @@ -1017,12 +1024,12 @@ } curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); - UcxBuffer *buf = NULL; + CxBuffer *buf = NULL; if(!read_func) { - buf = ucx_buffer_new(data, length, 0); + buf = cxBufferCreate(data, length, cxDefaultAllocator, 0); buf->size = length; data = buf; - read_func = (dav_read_func)ucx_buffer_read; + read_func = (dav_read_func)cxBufferRead; curl_easy_setopt(handle, CURLOPT_INFILESIZE_LARGE, (curl_off_t)length); } else if(length == 0) { headers = curl_slist_append(headers, "Transfer-Encoding: chunked"); @@ -1042,19 +1049,19 @@ CURLcode ret = dav_session_curl_perform(sn, NULL); curl_slist_free_all(headers); if(buf) { - ucx_buffer_free(buf); + cxBufferFree(buf); } return ret; } -CURLcode do_delete_request(DavSession *sn, char *lock, UcxBuffer *response) { +CURLcode do_delete_request(DavSession *sn, char *lock, CxBuffer *response) { CURL *handle = sn->handle; struct curl_slist *headers = NULL; if(lock) { char *url = NULL; curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &url); - char *ltheader = ucx_sprintf("If: <%s> (<%s>)", url, lock).ptr; + char *ltheader = cx_asprintf("If: <%s> (<%s>)", url, lock).ptr; headers = curl_slist_append(headers, ltheader); free(ltheader); curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); @@ -1065,7 +1072,7 @@ curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "DELETE"); curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L); - curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, ucx_buffer_write); + curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, cxBufferWrite); curl_easy_setopt(handle, CURLOPT_WRITEDATA, response); CURLcode ret = dav_session_curl_perform(sn, NULL); @@ -1080,7 +1087,7 @@ char *url = NULL; curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &url); url = util_parent_path(url); - char *ltheader = ucx_sprintf("If: <%s> (<%s>)", url, lock).ptr; + char *ltheader = cx_asprintf("If: <%s> (<%s>)", url, lock).ptr; free(url); headers = curl_slist_append(headers, ltheader); free(ltheader); @@ -1138,12 +1145,12 @@ if(lock) { char *url = NULL; curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &url); - char *ltheader = ucx_sprintf("If: <%s> (<%s>)", url, lock).ptr; + char *ltheader = cx_asprintf("If: <%s> (<%s>)", url, lock).ptr; headers = curl_slist_append(headers, ltheader); free(ltheader); } - //sstr_t deststr = ucx_sprintf("Destination: %s", dest); - sstr_t deststr = sstrcat(2, S("Destination: "), sstr(dest)); + //cxstring deststr = ucx_sprintf("Destination: %s", dest); + cxmutstr deststr = cx_strcat(2, CX_STR("Destination: "), cx_str(dest)); headers = curl_slist_append(headers, deststr.ptr); if(override) { headers = curl_slist_append(headers, "Overwrite: T"); @@ -1161,26 +1168,26 @@ } -UcxBuffer* create_lock_request(void) { - UcxBuffer *buf = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOEXTEND); - sstr_t s; +CxBuffer* create_lock_request(void) { + CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); + cxstring s; - s = S("\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + s = CX_STR("\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); - s = S("\n" + s = CX_STR("\n" "\n" "\n" "http://davutils.org/libidav/\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + cxBufferWrite(s.ptr, 1, s.length, buf); - s = S("\n"); - ucx_buffer_write(s.ptr, 1, s.length, buf); + s = CX_STR("\n"); + cxBufferWrite(s.ptr, 1, s.length, buf); return buf; } -int parse_lock_response(DavSession *sn, UcxBuffer *response, LockDiscovery *lock) { +int parse_lock_response(DavSession *sn, CxBuffer *response, LockDiscovery *lock) { lock->locktoken = NULL; lock->timeout = NULL; @@ -1240,7 +1247,7 @@ return ret; } -CURLcode do_lock_request(DavSession *sn, UcxBuffer *request, UcxBuffer *response, time_t timeout) { +CURLcode do_lock_request(DavSession *sn, CxBuffer *request, CxBuffer *response, time_t timeout) { CURL *handle = sn->handle; curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "LOCK"); curl_easy_setopt(handle, CURLOPT_UPLOAD, 1L); @@ -1250,11 +1257,11 @@ struct curl_slist *headers = NULL; if(timeout != 0) { - sstr_t thdr; + cxmutstr thdr; if(timeout < 0) { - thdr = ucx_sprintf("%s", "Timeout: Infinite"); + thdr = cx_asprintf("%s", "Timeout: Infinite"); } else { - thdr = ucx_sprintf("Timeout: Second-%u", (unsigned int)timeout); + thdr = cx_asprintf("Timeout: Second-%u", (unsigned int)timeout); } headers = curl_slist_append(headers, thdr.ptr); free(thdr.ptr); @@ -1262,12 +1269,12 @@ curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(handle, CURLOPT_UPLOAD, 1); - curl_easy_setopt(handle, CURLOPT_READFUNCTION, ucx_buffer_read); - curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, dav_buffer_seek); + curl_easy_setopt(handle, CURLOPT_READFUNCTION, cxBufferRead); + curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, cxBufferSeek); curl_easy_setopt(handle, CURLOPT_READDATA, request); curl_easy_setopt(handle, CURLOPT_INFILESIZE, request->size); - curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, ucx_buffer_write); + curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, cxBufferWrite); curl_easy_setopt(handle, CURLOPT_WRITEDATA, response); CURLcode ret = dav_session_curl_perform_buf(sn, request, response, NULL); @@ -1288,7 +1295,7 @@ curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL); // set lock-token header - sstr_t ltheader = ucx_sprintf("Lock-Token: <%s>", locktoken); + cxmutstr ltheader = cx_asprintf("Lock-Token: <%s>", locktoken); struct curl_slist *headers = curl_slist_append(NULL, ltheader.ptr); curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); @@ -1308,10 +1315,10 @@ curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL); // set lock-token header - sstr_t ltheader; + cxmutstr ltheader; struct curl_slist *headers = NULL; if(locktoken) { - ltheader = ucx_sprintf("Lock-Token: <%s>", locktoken); + ltheader = cx_asprintf("Lock-Token: <%s>", locktoken); headers = curl_slist_append(NULL, ltheader.ptr); } curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); @@ -1326,17 +1333,17 @@ } -CURLcode do_report_request(DavSession *sn, UcxBuffer *request, UcxBuffer *response) { +CURLcode do_report_request(DavSession *sn, CxBuffer *request, CxBuffer *response) { CURL *handle = sn->handle; curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "REPORT"); curl_easy_setopt(handle, CURLOPT_UPLOAD, 1); - curl_easy_setopt(handle, CURLOPT_READFUNCTION, ucx_buffer_read); - curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, dav_buffer_seek); + curl_easy_setopt(handle, CURLOPT_READFUNCTION, cxBufferRead); + curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, cxBufferSeek); curl_easy_setopt(handle, CURLOPT_READDATA, request); curl_easy_setopt(handle, CURLOPT_INFILESIZE, request->size); - curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, ucx_buffer_write); + curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, cxBufferWrite); curl_easy_setopt(handle, CURLOPT_WRITEDATA, response); struct curl_slist *headers = NULL; diff -r a569148841ff -r efbd59642577 libidav/methods.h --- a/libidav/methods.h Sun Apr 16 14:12:24 2023 +0200 +++ b/libidav/methods.h Fri Apr 21 21:25:32 2023 +0200 @@ -32,7 +32,7 @@ #include "webdav.h" #include "resource.h" -#include +#include #ifdef __cplusplus extern "C" { @@ -48,11 +48,11 @@ }; struct ResponseTag { - char *href; - int iscollection; - UcxList *properties; - char *crypto_name; - char *crypto_key; + const char *href; + int iscollection; + CxList *properties; + const char *crypto_name; + const char *crypto_key; }; struct LockDiscovery { @@ -60,18 +60,18 @@ char *locktoken; }; -int dav_buffer_seek(UcxBuffer *b, curl_off_t offset, int origin); +int dav_buffer_seek(CxBuffer *b, curl_off_t offset, int origin); CURLcode do_propfind_request( DavSession *sn, - UcxBuffer *request, - UcxBuffer *response); + CxBuffer *request, + CxBuffer *response); CURLcode do_proppatch_request( DavSession *sn, char *lock, - UcxBuffer *request, - UcxBuffer *response); + CxBuffer *request, + CxBuffer *response); CURLcode do_put_request( DavSession *sn, @@ -82,21 +82,21 @@ dav_seek_func seek_func, size_t length); -UcxBuffer* create_allprop_propfind_request(void); -UcxBuffer* create_cryptoprop_propfind_request(void); -UcxBuffer* create_propfind_request(DavSession *sn, UcxList *properties, char *rootelm, DavBool nocrypt); -UcxBuffer* create_basic_propfind_request(void); +CxBuffer* create_allprop_propfind_request(void); +CxBuffer* create_cryptoprop_propfind_request(void); +CxBuffer* create_propfind_request(DavSession *sn, CxList *properties, char *rootelm, DavBool nocrypt); +CxBuffer* create_basic_propfind_request(void); -PropfindParser* create_propfind_parser(UcxBuffer *response, char *url); +PropfindParser* create_propfind_parser(CxBuffer *response, char *url); void destroy_propfind_parser(PropfindParser *parser); int get_propfind_response(PropfindParser *parser, ResponseTag *result); void cleanup_response(ResponseTag *result); -int hrefeq(DavSession *sn, char *href1, char *href2); +int hrefeq(DavSession *sn, const char *href1, const char *href2); DavResource* response2resource(DavSession *sn, ResponseTag *response, char *parent_path); void add_properties(DavResource *res, ResponseTag *response); -DavResource* parse_propfind_response(DavSession *sn, DavResource *root, UcxBuffer *response); +DavResource* parse_propfind_response(DavSession *sn, DavResource *root, CxBuffer *response); int parse_response_tag(DavResource *resource, xmlNode *node); void set_davprops(DavResource *res); @@ -106,10 +106,10 @@ */ int parse_resource_type(xmlNode *node); -UcxBuffer* create_proppatch_request(DavResourceData *data); -UcxBuffer* create_crypto_proppatch_request(DavSession *sn, DavKey *key, const char *name, const char *hash); +CxBuffer* create_proppatch_request(DavResourceData *data); +CxBuffer* create_crypto_proppatch_request(DavSession *sn, DavKey *key, const char *name, const char *hash); -CURLcode do_delete_request(DavSession *sn, char *lock, UcxBuffer *response); +CURLcode do_delete_request(DavSession *sn, char *lock, CxBuffer *response); CURLcode do_mkcol_request(DavSession *sn, char *lock); @@ -117,14 +117,14 @@ CURLcode do_copy_move_request(DavSession *sn, char *dest, char *lock, DavBool copy, DavBool override); -UcxBuffer* create_lock_request(void); -int parse_lock_response(DavSession *sn, UcxBuffer *response, LockDiscovery *lock); -CURLcode do_lock_request(DavSession *sn, UcxBuffer *request, UcxBuffer *response, time_t timeout); +CxBuffer* create_lock_request(void); +int parse_lock_response(DavSession *sn, CxBuffer *response, LockDiscovery *lock); +CURLcode do_lock_request(DavSession *sn, CxBuffer *request, CxBuffer *response, time_t timeout); CURLcode do_unlock_request(DavSession *sn, char *locktoken); CURLcode do_simple_request(DavSession *sn, char *method, char *locktoken); -CURLcode do_report_request(DavSession *sn, UcxBuffer *request, UcxBuffer *response); +CURLcode do_report_request(DavSession *sn, CxBuffer *request, CxBuffer *response); #ifdef __cplusplus } diff -r a569148841ff -r efbd59642577 libidav/resource.c --- a/libidav/resource.c Sun Apr 16 14:12:24 2023 +0200 +++ b/libidav/resource.c Fri Apr 21 21:25:32 2023 +0200 @@ -36,8 +36,12 @@ #include "session.h" #include "methods.h" #include "crypto.h" -#include "ucx/buffer.h" -#include "ucx/utils.h" +#include +#include +#include +#include +#include +#include #include "resource.h" #include "xml.h" @@ -45,11 +49,11 @@ #define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b) -DavResource* dav_resource_new(DavSession *sn, char *path) { +DavResource* dav_resource_new(DavSession *sn, const char *path) { //char *href = util_url_path(url); //DavResource *res = dav_resource_new_href(sn, href); char *parent = util_parent_path(path); - char *name = util_resource_name(path); + const char *name = util_resource_name(path); char *href = dav_session_create_plain_href(sn, path); DavResource *res = dav_resource_new_full(sn, parent, name, href); @@ -57,7 +61,7 @@ return res; } -DavResource* dav_resource_new_child(DavSession *sn, DavResource *parent, char *name) { +DavResource* dav_resource_new_child(DavSession *sn, DavResource *parent, const char *name) { char *path = util_concat_path(parent->path, name); char *href = dav_session_create_plain_href(sn, path); DavResource *res = dav_resource_new_full(sn, parent->path, name, href); @@ -66,8 +70,8 @@ } -DavResource* dav_resource_new_href(DavSession *sn, char *href) { - DavResource *res = ucx_mempool_calloc(sn->mp, 1, sizeof(DavResource)); +DavResource* dav_resource_new_href(DavSession *sn, const char *href) { + DavResource *res = cxCalloc(sn->mp->allocator, 1, sizeof(DavResource)); res->session = sn; // set name, path and href @@ -79,14 +83,14 @@ return res; } -DavResource* dav_resource_new_full(DavSession *sn, char *parent_path, char *name, char *href) { - sstr_t n = sstr(name); +DavResource* dav_resource_new_full(DavSession *sn, const char *parent_path, const char *name, char *href) { + cxstring n = cx_str(name); // the name must not contain path separators if(n.length > 0 && href) { for(int i=0;imp, 1, sizeof(DavResource)); + DavResource *res = cxCalloc(sn->mp->allocator, 1, sizeof(DavResource)); res->session = sn; // set name, path and href - res->name = sstrdup_a(sn->mp->allocator, n).ptr; + res->name = cx_strdup_a(sn->mp->allocator, n).ptr; char *path = util_concat_path(parent_path, name); res->path = dav_session_strdup(sn, path); @@ -112,23 +116,23 @@ // cache href/path if(href) { - dav_session_cache_path(sn, sstr(path), sstr(href)); + dav_session_cache_path(sn, cx_str(path), cx_str(href)); } free(path); return res; } -void resource_free_properties(DavSession *sn, UcxMap *properties) { +void resource_free_properties(DavSession *sn, CxMap *properties) { if(!properties) return; - UcxMapIterator i = ucx_map_iterator(properties); + CxIterator i = cxMapIteratorValues(properties); DavProperty *property; - UCX_MAP_FOREACH(key, property, i) { + cx_foreach(DavProperty*, property, i) { // TODO: free everything dav_session_free(sn, property); } - ucx_map_free(properties); + cxMapDestroy(properties); } void dav_resource_free(DavResource *res) { @@ -144,29 +148,62 @@ resource_free_properties(sn, data->properties); resource_free_properties(sn, data->crypto_properties); - UCX_FOREACH(elm, data->set) { - DavProperty *p = elm->data; - dav_session_free(sn, p->ns->name); - if(p->ns->prefix) { - dav_session_free(sn, p->ns->prefix); + if(data->set) { + CxIterator i = cxListIterator(data->set); + cx_foreach(DavProperty *, p, i) { + dav_session_free(sn, p->ns->name); + if(p->ns->prefix) { + dav_session_free(sn, p->ns->prefix); + } + dav_session_free(sn, p->ns); + + dav_session_free(sn, p->name); + dav_free_xml_node_sn(sn, p->value); + dav_session_free(sn, p); } - dav_session_free(sn, p->ns); - - dav_session_free(sn, p->name); - dav_free_xml_node_sn(sn, p->value); - dav_session_free(sn, p); } - UCX_FOREACH(elm, data->remove) { - DavProperty *p = elm->data; - dav_session_free(sn, p->ns->name); - if(p->ns->prefix) { - dav_session_free(sn, p->ns->prefix); + if(data->remove) { + CxIterator i = cxListIterator(data->remove); + cx_foreach(DavProperty *, p, i) { + dav_session_free(sn, p->ns->name); + if(p->ns->prefix) { + dav_session_free(sn, p->ns->prefix); + } + dav_session_free(sn, p->ns); + + dav_session_free(sn, p->name); + dav_session_free(sn, p); } - dav_session_free(sn, p->ns); - - dav_session_free(sn, p->name); - dav_session_free(sn, p); + } + + if(data->crypto_set) { + CxIterator i = cxListIterator(data->crypto_set); + cx_foreach(DavProperty *, p, i) { + dav_session_free(sn, p->ns->name); + if(p->ns->prefix) { + dav_session_free(sn, p->ns->prefix); + } + dav_session_free(sn, p->ns); + + dav_session_free(sn, p->name); + dav_free_xml_node_sn(sn, p->value); + dav_session_free(sn, p); + } + } + + if(data->crypto_remove) { + CxIterator i = cxListIterator(data->crypto_remove); + cx_foreach(DavProperty *, p, i) { + dav_session_free(sn, p->ns->name); + if(p->ns->prefix) { + dav_session_free(sn, p->ns->prefix); + } + dav_session_free(sn, p->ns); + + dav_session_free(sn, p->name); + dav_session_free(sn, p); + } } if(!data->read && data->content) { @@ -187,20 +224,20 @@ } } -void resource_set_href(DavResource *res, sstr_t href) { - res->href = sstrdup_a(res->session->mp->allocator, href).ptr; +void resource_set_href(DavResource *res, cxstring href) { + res->href = cx_strdup_a(res->session->mp->allocator, href).ptr; } -void resource_set_info(DavResource *res, char *href_str) { +void resource_set_info(DavResource *res, const char *href_str) { char *url_str = NULL; curl_easy_getinfo(res->session->handle, CURLINFO_EFFECTIVE_URL, &url_str); - sstr_t name = sstr(util_resource_name(href_str)); - sstr_t href = sstr(href_str); + cxstring name = cx_str(util_resource_name(href_str)); + cxstring href = cx_str(href_str); - sstr_t base_href = sstr(util_url_path(res->session->base_url)); - sstr_t path = sstrsubs(href, base_href.length - 1); + cxstring base_href = cx_str(util_url_path(res->session->base_url)); + cxstring path = cx_strsubs(href, base_href.length - 1); - UcxAllocator *a = res->session->mp->allocator; + const CxAllocator *a = res->session->mp->allocator; CURL *handle = res->session->handle; int nlen = 0; @@ -208,22 +245,22 @@ int plen = 0; char *upath = curl_easy_unescape(handle, path.ptr, path.length, &plen); - res->name = sstrdup_a(a, sstrn(uname, nlen)).ptr; - res->href = sstrdup_a(a, href).ptr; - res->path = sstrdup_a(a, sstrn(upath, plen)).ptr; + res->name = cx_strdup_a(a, cx_strn(uname, nlen)).ptr; + res->href = cx_strdup_a(a, href).ptr; + res->path = cx_strdup_a(a, cx_strn(upath, plen)).ptr; curl_free(uname); curl_free(upath); } DavResourceData* resource_data_new(DavSession *sn) { - DavResourceData *data = ucx_mempool_malloc( - sn->mp, + DavResourceData *data = cxMalloc( + sn->mp->allocator, sizeof(DavResourceData)); if(!data) { return NULL; } - data->properties = ucx_map_new_a(sn->mp->allocator, 32); + data->properties = cxHashMapCreate(sn->mp->allocator, CX_STORE_POINTERS, 32); data->crypto_properties = NULL; data->set = NULL; data->remove = NULL; @@ -257,9 +294,10 @@ prop->ns = namespace; prop->value = val; - sstr_t key = dav_property_key(ns, name); - ucx_map_sstr_put(((DavResourceData*)res->data)->properties, key, prop); - free(key.ptr); + cxmutstr keystr = dav_property_key(ns, name); + CxHashKey key = cx_hash_key(keystr.ptr, keystr.length); + cxMapPut(((DavResourceData*)res->data)->properties, key, prop); + free(keystr.ptr); } void resource_add_property(DavResource *res, const char *ns, const char *name, xmlNode *val) { @@ -278,15 +316,15 @@ resource_add_prop(res, ns, name, dav_text_node(res->session, val)); } -void resource_set_crypto_properties(DavResource *res, UcxMap *cprops) { +void resource_set_crypto_properties(DavResource *res, CxMap *cprops) { DavResourceData *data = res->data; resource_free_properties(res->session, data->crypto_properties); data->crypto_properties = cprops; } DavXmlNode* resource_get_property(DavResource *res, const char *ns, const char *name) { - sstr_t keystr = dav_property_key(ns, name); - UcxKey key = ucx_key(keystr.ptr, keystr.length); + cxmutstr keystr = dav_property_key(ns, name); + CxHashKey key = cx_hash_key(keystr.ptr, keystr.length); DavXmlNode *ret = resource_get_property_k(res, key); free(keystr.ptr); @@ -294,37 +332,37 @@ } DavXmlNode* resource_get_encrypted_property(DavResource *res, const char *ns, const char *name) { - sstr_t keystr = dav_property_key(ns, name); - UcxKey key = ucx_key(keystr.ptr, keystr.length); + cxmutstr keystr = dav_property_key(ns, name); + CxHashKey key = cx_hash_key(keystr.ptr, keystr.length); DavXmlNode *ret = resource_get_encrypted_property_k(res, key); free(keystr.ptr); return ret; } -DavXmlNode* resource_get_property_k(DavResource *res, UcxKey key) { +DavXmlNode* resource_get_property_k(DavResource *res, CxHashKey key) { DavResourceData *data = (DavResourceData*)res->data; - DavProperty *property = ucx_map_get(data->properties, key); + DavProperty *property = cxMapGet(data->properties, key); return property ? property->value : NULL; } -DavXmlNode* resource_get_encrypted_property_k(DavResource *res, UcxKey key) { +DavXmlNode* resource_get_encrypted_property_k(DavResource *res, CxHashKey key) { DavResourceData *data = (DavResourceData*)res->data; - DavProperty *property = ucx_map_get(data->crypto_properties, key); + DavProperty *property = cxMapGet(data->crypto_properties, key); return property ? property->value : NULL; } -sstr_t dav_property_key(const char *ns, const char *name) { - return dav_property_key_a(ucx_default_allocator(), ns, name); +cxmutstr dav_property_key(const char *ns, const char *name) { + return dav_property_key_a(cxDefaultAllocator, ns, name); } -sstr_t dav_property_key_a(UcxAllocator *a, const char *ns, const char *name) { - scstr_t ns_str = scstr(ns); - scstr_t name_str = scstr(name); +cxmutstr dav_property_key_a(const CxAllocator *a, const char *ns, const char *name) { + cxstring ns_str = cx_str(ns); + cxstring name_str = cx_str(name); - return sstrcat_a(a, 4, ns_str, S("\0"), name_str, S("\0")); + return cx_strcat_a(a, 4, ns_str, CX_STR("\0"), name_str, CX_STR("\0")); } @@ -411,7 +449,7 @@ return cr->descending ? -ret : ret; } -void resource_add_ordered_child(DavResource *parent, DavResource *child, UcxList *ordercr) { +void resource_add_ordered_child(DavResource *parent, DavResource *child, CxList *ordercr) { if(!ordercr) { resource_add_child(parent, child); return; @@ -427,8 +465,8 @@ DavResource *resource = parent->children; while(resource) { int r = 0; - UCX_FOREACH(elm, ordercr) { - DavOrderCriterion *cr = elm->data; + CxIterator i = cxListIterator(ordercr); + cx_foreach(DavOrderCriterion*, cr, i) { r = resource_cmp(child, resource, cr); if(r != 0) { break; @@ -495,8 +533,8 @@ DavResourceData *data = res->data; DavXmlNode *property = NULL; - UcxList *remove_list = NULL; - UcxList *set_list = NULL; + CxList *remove_list = NULL; + CxList *set_list = NULL; if(encrypted) { // check if crypto_properties because it will only be created @@ -515,23 +553,27 @@ // resource_get_property only returns persistent properties // check the remove and set list - if(property) { + if(property && remove_list) { // if the property is in the remove list, we return NULL - UCX_FOREACH(elm, remove_list) { - DavProperty *p = elm->data; + CxIterator i = cxListIterator(remove_list); + cx_foreach(DavProperty*, p, i) { if(!strcmp(p->name, name) && !strcmp(p->ns->name, ns)) { return NULL; } } } + // the set list contains property updates // we return an updated property if possible - UCX_FOREACH(elm, set_list) { - DavProperty *p = elm->data; - if(!strcmp(p->name, name) && !strcmp(p->ns->name, ns)) { - return p->value; // TODO: fix + if(set_list) { + CxIterator i = cxListIterator(set_list); + cx_foreach(DavProperty*, p, i) { + if(!strcmp(p->name, name) && !strcmp(p->ns->name, ns)) { + return p->value; // TODO: fix + } } } + // no property update return property; @@ -572,18 +614,30 @@ dav_set_string_property_ns(res, pns, pname, value); } +static int add2propertylist(const CxAllocator *a, CxList **list, DavProperty *property) { + if(!*list) { + CxList *newlist = cxLinkedListCreate(a, NULL, CX_STORE_POINTERS); + if(!newlist) { + return 1; + } + *list = newlist; + } + cxListAdd(*list, property); + return 0; +} + void dav_set_string_property_ns(DavResource *res, char *ns, char *name, char *value) { DavSession *sn = res->session; - UcxAllocator *a = res->session->mp->allocator; + const CxAllocator *a = res->session->mp->allocator; DavResourceData *data = res->data; DavProperty *property = createprop(res->session, ns, name); property->value = dav_text_node(res->session, value); if(DAV_ENCRYPT_PROPERTIES(sn) && dav_namespace_is_encrypted(sn->context, ns)) { - data->crypto_set = ucx_list_append_a(a, data->crypto_set, property); + add2propertylist(a, &data->crypto_set, property); } else { - data->set = ucx_list_append_a(a, data->set, property); + add2propertylist(a, &data->set, property); } } @@ -596,7 +650,7 @@ void dav_set_property_ns(DavResource *res, char *ns, char *name, DavXmlNode *value) { DavSession *sn = res->session; - UcxAllocator *a = sn->mp->allocator; + const CxAllocator *a = sn->mp->allocator; DavResourceData *data = res->data; DavProperty *property = createprop(sn, ns, name); @@ -605,9 +659,9 @@ property->value = value; if(DAV_ENCRYPT_PROPERTIES(sn) && dav_namespace_is_encrypted(sn->context, ns)) { - data->crypto_set = ucx_list_append_a(a, data->crypto_set, property); + add2propertylist(a, &data->crypto_set, property); } else { - data->set = ucx_list_append_a(a, data->set, property); + add2propertylist(a, &data->set, property); } } @@ -621,44 +675,44 @@ void dav_remove_property_ns(DavResource *res, char *ns, char *name) { DavSession *sn = res->session; DavResourceData *data = res->data; - UcxAllocator *a = res->session->mp->allocator; + const CxAllocator *a = res->session->mp->allocator; DavProperty *property = createprop(res->session, ns, name); if(DAV_ENCRYPT_PROPERTIES(sn) && dav_namespace_is_encrypted(sn->context, ns)) { - data->crypto_remove = ucx_list_append_a(a, data->crypto_remove, property); + add2propertylist(a, &data->crypto_remove, property); } else { - data->remove = ucx_list_append_a(a, data->remove, property); + add2propertylist(a, &data->remove, property); } } void dav_set_encrypted_property_ns(DavResource *res, char *ns, char *name, DavXmlNode *value) { - UcxAllocator *a = res->session->mp->allocator; + const CxAllocator *a = res->session->mp->allocator; DavResourceData *data = res->data; DavProperty *property = createprop(res->session, ns, name); property->value = value; // TODO: copy node? - data->crypto_set = ucx_list_append_a(a, data->crypto_set, property); + add2propertylist(a, &data->crypto_set, property); } void dav_set_encrypted_string_property_ns(DavResource *res, char *ns, char *name, char *value) { - UcxAllocator *a = res->session->mp->allocator; + const CxAllocator *a = res->session->mp->allocator; DavResourceData *data = res->data; DavProperty *property = createprop(res->session, ns, name); property->value = dav_text_node(res->session, value); - data->crypto_set = ucx_list_append_a(a, data->crypto_set, property); + add2propertylist(a, &data->crypto_set, property); } void dav_remove_encrypted_property_ns(DavResource *res, char *ns, char *name) { DavResourceData *data = res->data; - UcxAllocator *a = res->session->mp->allocator; + const CxAllocator *a = res->session->mp->allocator; DavProperty *property = createprop(res->session, ns, name); - data->crypto_remove = ucx_list_append_a(a, data->crypto_remove, property); + add2propertylist(a, &data->crypto_remove, property); } static int compare_propname(const void *a, const void *b) { @@ -676,17 +730,17 @@ DavPropName* dav_get_property_names(DavResource *res, size_t *count) { DavResourceData *data = res->data; - *count = data->properties->count; + *count = data->properties->size; DavPropName *names = dav_session_calloc( res->session, *count, sizeof(DavPropName)); - UcxMapIterator i = ucx_map_iterator(data->properties); + CxIterator i = cxMapIteratorValues(data->properties); DavProperty *value; int j = 0; - UCX_MAP_FOREACH(key, value, i) { + cx_foreach(DavProperty*, value, i) { DavPropName *name = &names[j]; name->ns = value->ns->name; @@ -726,34 +780,35 @@ int dav_load(DavResource *res) { - UcxBuffer *rqbuf = create_allprop_propfind_request(); + CxBuffer *rqbuf = create_allprop_propfind_request(); int ret = dav_propfind(res->session, res, rqbuf); - ucx_buffer_free(rqbuf); + cxBufferFree(rqbuf); return ret; } int dav_load_prop(DavResource *res, DavPropName *properties, size_t numprop) { - UcxMempool *mp = ucx_mempool_new(64); + CxMempool *mp = cxBasicMempoolCreate(64); + const CxAllocator *a = mp->allocator; - UcxList *proplist = NULL; + CxList *proplist = cxArrayListCreate(a, NULL, sizeof(DavProperty), numprop); for(size_t i=0;iname = properties[i].name; - p->ns = ucx_mempool_malloc(mp, sizeof(DavNamespace)); - p->ns->name = properties[i].ns; + DavProperty p; + p.name = properties[i].name; + p.ns = cxMalloc(a, sizeof(DavNamespace)); + p.ns->name = properties[i].ns; if(!strcmp(properties[i].ns, "DAV:")) { - p->ns->prefix = "D"; + p.ns->prefix = "D"; } else { - p->ns->prefix = ucx_asprintf(mp->allocator, "x%d", i).ptr; + p.ns->prefix = cx_asprintf_a(a, "x%d", (int)i).ptr; } - p->value = NULL; - proplist = ucx_list_append_a(mp->allocator, proplist, p); + p.value = NULL; + cxListAdd(proplist, &p); } - UcxBuffer *rqbuf = create_propfind_request(res->session, proplist, "propfind", 0); + CxBuffer *rqbuf = create_propfind_request(res->session, proplist, "propfind", 0); int ret = dav_propfind(res->session, res, rqbuf); - ucx_buffer_free(rqbuf); - ucx_mempool_destroy(mp); + cxBufferFree(rqbuf); + cxMempoolDestroy(mp); return ret; } @@ -805,7 +860,7 @@ CURLcode ret; if(encryption) { AESEncrypter *enc = NULL; - UcxBuffer *buf = NULL; + CxBuffer *buf = NULL; if(data->read) { enc = aes_encrypter_new( sn->key, @@ -813,13 +868,13 @@ data->read, data->seek); } else { - buf = ucx_buffer_new(data->content, data->length, 0); + buf = cxBufferCreate(data->content, data->length, cxDefaultAllocator, 0); buf->size = data->length; enc = aes_encrypter_new( sn->key, buf, - (dav_read_func)ucx_buffer_read, - (dav_seek_func)dav_buffer_seek); + (dav_read_func)cxBufferRead, + (dav_seek_func)cxBufferSeek); } // put resource @@ -838,7 +893,7 @@ aes_encrypter_close(enc); if(buf) { - ucx_buffer_free(buf); + cxBufferFree(buf); } // add crypto properties @@ -851,15 +906,15 @@ free(enc_hash); } else if((sn->flags & DAV_SESSION_STORE_HASH) == DAV_SESSION_STORE_HASH) { HashStream hstr; - UcxBuffer *iobuf = NULL; + CxBuffer *iobuf = NULL; if(!data->read) { - iobuf = ucx_buffer_new(data->content, data->length, 0); + iobuf = cxBufferCreate(data->content, data->length, cxDefaultAllocator, 0); iobuf->size = data->length; init_hash_stream( &hstr, iobuf, - (dav_read_func)ucx_buffer_read, - (dav_seek_func)ucx_buffer_seek); + (dav_read_func)cxBufferRead, + (dav_seek_func)cxBufferSeek); } else { init_hash_stream( &hstr, @@ -900,7 +955,7 @@ res->session->error = 0; // cleanup node data if(!data->read) { - ucx_mempool_free(sn->mp, data->content); + cxFree(sn->mp->allocator, data->content); } data->content = NULL; data->read = NULL; @@ -917,51 +972,56 @@ int ret = 1; if(crypto_res) { - UcxBuffer *rqbuf = create_cryptoprop_propfind_request(); + CxBuffer *rqbuf = create_cryptoprop_propfind_request(); ret = dav_propfind(res->session, res, rqbuf); - ucx_buffer_free(rqbuf); + cxBufferFree(rqbuf); } if(!ret) { DavXmlNode *crypto_prop_node = dav_get_property_ns(crypto_res, DAV_NS, "crypto-prop"); - UcxMap *crypto_props = parse_crypto_prop(sn, sn->key, crypto_prop_node); + CxMap *crypto_props = parse_crypto_prop(sn, sn->key, crypto_prop_node); if(!crypto_props) { // resource hasn't encrypted properties yet - crypto_props = ucx_map_new(32); // create new map + crypto_props = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 32); // create new map } // remove all properties - UCX_FOREACH(elm, data->crypto_remove) { - if(crypto_props->count == 0) { - break; // map already empty, can't remove any more + if(data->crypto_remove) { + CxIterator i = cxListIterator(data->crypto_remove); + cx_foreach(DavProperty *, property, i) { + if(crypto_props->size == 0) { + break; // map already empty, can't remove any more + } + + cxmutstr key = dav_property_key(property->ns->name, property->name); + DavProperty *existing_prop = cxMapGet(crypto_props, cx_hash_key(key.ptr, key.length)); + if(existing_prop) { + // TODO: free existing_prop + } + free(key.ptr); } - - DavProperty *property = elm->data; - sstr_t key = dav_property_key(property->ns->name, property->name); - DavProperty *existing_prop = ucx_map_sstr_remove(crypto_props, key); - if(existing_prop) { - // TODO: free existing_prop - } - free(key.ptr); } // set properties - UCX_FOREACH(elm, data->crypto_set) { - DavProperty *property = elm->data; - sstr_t key = dav_property_key(property->ns->name, property->name); - DavProperty *existing_prop = ucx_map_sstr_remove(crypto_props, key); - ucx_map_sstr_put(crypto_props, key, property); - if(existing_prop) { - // TODO: free existing_prop - } - free(key.ptr); + if(data->crypto_set) { + CxIterator i = cxListIterator(data->crypto_set); + cx_foreach(DavProperty *, property, i) { + cxmutstr keystr = dav_property_key(property->ns->name, property->name); + CxHashKey key = cx_hash_key(keystr.ptr, keystr.length); + DavProperty *existing_prop = cxMapRemoveAndGet(crypto_props, key); + cxMapPut(crypto_props, key, property); + if(existing_prop) { + // TODO: free existing_prop + } + free(keystr.ptr); + } } DavXmlNode *crypto_prop_value = create_crypto_prop(sn, crypto_props); if(crypto_prop_value) { DavProperty *new_crypto_prop = createprop(sn, DAV_NS, "crypto-prop"); new_crypto_prop->value = crypto_prop_value; - data->set = ucx_list_prepend_a(sn->mp->allocator, data->set, new_crypto_prop); + add2propertylist(sn->mp->allocator, &data->set, new_crypto_prop); } dav_resource_free(crypto_res); @@ -976,8 +1036,8 @@ int r = 0; sn->error = DAV_OK; if(data->set || data->remove) { - UcxBuffer *request = create_proppatch_request(data); - UcxBuffer *response = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND); + CxBuffer *request = create_proppatch_request(data); + CxBuffer *response = cxBufferCreate(NULL, 1024, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); //printf("request:\n%.*s\n\n", request->pos, request->space); CURLcode ret = do_proppatch_request(sn, locktoken, request, response); @@ -994,8 +1054,8 @@ r = -1; } - ucx_buffer_free(request); - ucx_buffer_free(response); + cxBufferFree(request); + cxBufferFree(response); } return r; @@ -1129,7 +1189,7 @@ DavLock *lock = dav_get_lock(res->session, res->path); char *locktoken = lock ? lock->token : NULL; - UcxBuffer *response = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND); + CxBuffer *response = cxBufferCreate(NULL, 4096, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); CURLcode ret = do_delete_request(res->session, locktoken, response); long status = 0; curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); @@ -1145,7 +1205,7 @@ r = 1; } - ucx_buffer_free(response); + cxBufferFree(response); return r; } @@ -1172,7 +1232,7 @@ curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &status); if(status == 201) { // resource successfully created - char *name = util_resource_name(p); + char *name = (char*)util_resource_name(p); int len = strlen(name); if(name[len - 1] == '/') { name[len - 1] = '\0'; @@ -1225,9 +1285,9 @@ // if the session has encrypted file names, add crypto infos if(!resource_add_crypto_info(sn, res->href, res->name, NULL)) { // do a minimal propfind request - UcxBuffer *rqbuf = create_propfind_request(sn, NULL, "propfind", 0); + CxBuffer *rqbuf = create_propfind_request(sn, NULL, "propfind", 0); int ret = dav_propfind(sn, res, rqbuf); - ucx_buffer_free(rqbuf); + cxBufferFree(rqbuf); return ret; } else { return 1; @@ -1331,26 +1391,26 @@ CURL *handle = sn->handle; util_set_url(sn, dav_resource_get_href(res)); - UcxBuffer *request = create_lock_request(); - UcxBuffer *response = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOEXTEND); + CxBuffer *request = create_lock_request(); + CxBuffer *response = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); CURLcode ret = do_lock_request(sn, request, response, timeout); //printf("\nlock\n"); //printf("%.*s\n\n", request->size, request->space); //printf("%.*s\n\n", response->size, response->space); - ucx_buffer_free(request); + cxBufferFree(request); long status = 0; curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); if(ret == CURLE_OK && (status >= 200 && status < 300)) { LockDiscovery lock; - if(parse_lock_response(sn, response, &lock)) { + int parse_error = parse_lock_response(sn, response, &lock); + cxBufferFree(response); + if(parse_error) { sn->error = DAV_ERROR; - ucx_buffer_free(response); return -1; } - ucx_buffer_free(response); DavLock *l = dav_create_lock(sn, lock.locktoken, lock.timeout); free(lock.locktoken); @@ -1373,7 +1433,7 @@ } } else { dav_session_set_error(sn, ret, status); - ucx_buffer_free(response); + cxBufferFree(response); return -1; } } @@ -1408,46 +1468,47 @@ return 0; } - UcxBuffer *request = create_crypto_proppatch_request(sn, sn->key, name, hash); - UcxBuffer *response = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND); + CxBuffer *request = create_crypto_proppatch_request(sn, sn->key, name, hash); + CxBuffer *response = cxBufferCreate(NULL, 1024, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); util_set_url(sn, href); // TODO: lock CURLcode ret = do_proppatch_request(sn, NULL, request, response); - ucx_buffer_free(request); + cxBufferFree(request); long status = 0; curl_easy_getinfo (sn->handle, CURLINFO_RESPONSE_CODE, &status); if(ret == CURLE_OK && status == 207) { // TODO: parse response sn->error = DAV_OK; - ucx_buffer_free(response); + cxBufferFree(response); return 0; } else { dav_session_set_error(sn, ret, status); - ucx_buffer_free(response); + cxBufferFree(response); return 1; } } /* ----------------------------- crypto-prop ----------------------------- */ -DavXmlNode* create_crypto_prop(DavSession *sn, UcxMap *properties) { +DavXmlNode* create_crypto_prop(DavSession *sn, CxMap *properties) { if(!sn->key) { return NULL; } - UcxBuffer *content = ucx_buffer_new(NULL, 2048, UCX_BUFFER_AUTOEXTEND); + CxBuffer *content = cxBufferCreate(NULL, 2048, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); // create an xml document containing all properties - UcxMap *nsmap = ucx_map_new(8); - ucx_map_cstr_put(nsmap, "DAV:", strdup("D")); + CxMap *nsmap = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 8); + nsmap->simple_destructor = free; + cxMapPut(nsmap, cx_hash_key_str("DAV:"), strdup("D")); - ucx_buffer_puts(content, "\n"); - ucx_buffer_puts(content, "\n"); + cxBufferPutString(content, "\n"); + cxBufferPutString(content, "\n"); - UcxMapIterator i = ucx_map_iterator(properties); + CxIterator i = cxMapIteratorValues(properties); DavProperty *prop; - UCX_MAP_FOREACH(key, prop, i) { + cx_foreach(DavProperty*, prop, i) { DavXmlNode pnode; pnode.type = DAV_XML_ELEMENT; pnode.namespace = prop->ns->name; @@ -1460,18 +1521,17 @@ pnode.content = NULL; pnode.contentlength = 0; - dav_print_node(content, (write_func)ucx_buffer_write, nsmap, &pnode); - ucx_buffer_putc(content, '\n'); + dav_print_node(content, (cx_write_func)cxBufferWrite, nsmap, &pnode); + cxBufferPut(content, '\n'); } - ucx_buffer_puts(content, ""); + cxBufferPutString(content, ""); - ucx_map_free_content(nsmap, (ucx_destructor)free); - ucx_map_free(nsmap); + cxMapDestroy(nsmap); // encrypt xml document char *crypto_prop_content = aes_encrypt(content->space, content->size, sn->key); - ucx_buffer_free(content); + cxBufferDestroy(content); DavXmlNode *ret = NULL; if(crypto_prop_content) { @@ -1481,7 +1541,7 @@ return ret; } -UcxMap* parse_crypto_prop(DavSession *sn, DavKey *key, DavXmlNode *node) { +CxMap* parse_crypto_prop(DavSession *sn, DavKey *key, DavXmlNode *node) { if(!node || node->type != DAV_XML_TEXT || node->contentlength == 0) { return NULL; } @@ -1489,7 +1549,7 @@ return parse_crypto_prop_str(sn, key, node->content); } -UcxMap* parse_crypto_prop_str(DavSession *sn, DavKey *key, const char *content) { +CxMap* parse_crypto_prop_str(DavSession *sn, DavKey *key, const char *content) { size_t len = 0; char *dec_str = aes_decrypt(content, &len, key); @@ -1519,7 +1579,7 @@ } // ready to get the properties - UcxMap *map = ucx_map_new(32); + CxMap *map = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 32); xmlNode *n = xml_root->children; while(n) { if(n->type == XML_ELEMENT_NODE && n->ns && n->ns->href) { @@ -1531,16 +1591,16 @@ dav_session_strdup(sn, (const char*)n->ns->prefix) : NULL; property->value = n->children ? dav_convert_xml(sn, n->children) : NULL; - sstr_t key = dav_property_key(property->ns->name, property->name); - ucx_map_sstr_put(map, key, property); + cxmutstr key = dav_property_key(property->ns->name, property->name); + cxMapPut(map, cx_hash_key(key.ptr, key.length), property); free(key.ptr); } n = n->next; } xmlFreeDoc(doc); - if(map->count == 0) { - ucx_map_free(map); + if(map->size == 0) { + cxMapDestroy(map); return NULL; } return map; diff -r a569148841ff -r efbd59642577 libidav/resource.h --- a/libidav/resource.h Sun Apr 16 14:12:24 2023 +0200 +++ b/libidav/resource.h Fri Apr 21 21:25:32 2023 +0200 @@ -31,7 +31,8 @@ #include "webdav.h" #include "crypto.h" -#include +#include +#include #ifdef __cplusplus extern "C" { @@ -40,16 +41,16 @@ typedef struct DavResourceData DavResourceData; struct DavResourceData { - UcxMap *properties; - UcxList *set; - UcxList *remove; - UcxList *crypto_set; - UcxList *crypto_remove; + CxMap *properties; + CxList *set; + CxList *remove; + CxList *crypto_set; + CxList *crypto_remove; /* * properties encapsulated in a crypto-prop property or NULL */ - UcxMap *crypto_properties; + CxMap *crypto_properties; /* * char* or stream @@ -58,7 +59,7 @@ /* * if NULL, content is a char* */ - read_func read; + dav_read_func read; /* * curl seek func */ @@ -109,29 +110,29 @@ int eof; }; -DavResource* dav_resource_new_full(DavSession *sn, char *parent_path, char *name, char *href); +DavResource* dav_resource_new_full(DavSession *sn, const char *parent_path, const char *name, char *href); -void resource_free_properties(DavSession *sn, UcxMap *properties); +void resource_free_properties(DavSession *sn, CxMap *properties); -void resource_set_href(DavResource *res, sstr_t href); +void resource_set_href(DavResource *res, cxstring href); -void resource_set_info(DavResource *res, char *href_str); +void resource_set_info(DavResource *res, const char *href_str); DavResourceData* resource_data_new(DavSession *sn); void resource_add_property(DavResource *res, const char *ns, const char *name, xmlNode *val); -void resource_set_crypto_properties(DavResource *res, UcxMap *cprops); +void resource_set_crypto_properties(DavResource *res, CxMap *cprops); DavXmlNode* resource_get_property(DavResource *res, const char *ns, const char *name); DavXmlNode* resource_get_encrypted_property(DavResource *res, const char *ns, const char *name); -DavXmlNode* resource_get_property_k(DavResource *res, UcxKey key); -DavXmlNode* resource_get_encrypted_property_k(DavResource *res, UcxKey key); +DavXmlNode* resource_get_property_k(DavResource *res, CxHashKey key); +DavXmlNode* resource_get_encrypted_property_k(DavResource *res, CxHashKey key); void resource_add_child(DavResource *parent, DavResource *child); -void resource_add_ordered_child(DavResource *parent, DavResource *child, UcxList *ordercr); +void resource_add_ordered_child(DavResource *parent, DavResource *child, CxList *ordercr); int resource_add_crypto_info(DavSession *sn, const char *href, const char *name, const char *hash); -sstr_t dav_property_key_a(UcxAllocator *a, const char *ns, const char *name); +cxmutstr dav_property_key_a(const CxAllocator *a, const char *ns, const char *name); -DavXmlNode* create_crypto_prop(DavSession *sn, UcxMap *properties); -UcxMap* parse_crypto_prop(DavSession *sn, DavKey *key, DavXmlNode *node); -UcxMap* parse_crypto_prop_str(DavSession *sn, DavKey *key, const char *content); +DavXmlNode* create_crypto_prop(DavSession *sn, CxMap *properties); +CxMap* parse_crypto_prop(DavSession *sn, DavKey *key, DavXmlNode *node); +CxMap* parse_crypto_prop_str(DavSession *sn, DavKey *key, const char *content); #ifdef __cplusplus } diff -r a569148841ff -r efbd59642577 libidav/session.c --- a/libidav/session.c Sun Apr 16 14:12:24 2023 +0200 +++ b/libidav/session.c Fri Apr 21 21:25:32 2023 +0200 @@ -30,8 +30,10 @@ #include #include -#include -#include +#include +#include +#include +#include #include "utils.h" #include "session.h" @@ -42,14 +44,14 @@ if(!base_url) { return NULL; } - sstr_t url = sstr(base_url); + cxstring url = cx_str(base_url); if(url.length == 0) { return NULL; } DavSession *sn = malloc(sizeof(DavSession)); memset(sn, 0, sizeof(DavSession)); - sn->mp = ucx_mempool_new(DAV_SESSION_MEMPOOL_SIZE); - sn->pathcache = ucx_map_new_a(sn->mp->allocator, DAV_PATH_CACHE_SIZE); + sn->mp = cxBasicMempoolCreate(DAV_SESSION_MEMPOOL_SIZE); + sn->pathcache = cxHashMapCreate(sn->mp->allocator, CX_STORE_POINTERS, DAV_PATH_CACHE_SIZE); sn->key = NULL; sn->errorstr = NULL; sn->error = DAV_OK; @@ -60,14 +62,11 @@ sn->handle = curl_easy_init(); curl_easy_setopt(sn->handle, CURLOPT_FOLLOWLOCATION, 1L); - // create lock manager - DavLockManager *locks = ucx_mempool_malloc(sn->mp, sizeof(DavLockManager)); - locks->resource_locks = ucx_map_new_a(sn->mp->allocator, 16); - locks->collection_locks = NULL; - sn->locks = locks; + // lock manager is created on-demand + sn->locks = NULL; // set proxy - DavProxy *proxy = sstrprefix(url, S("https")) ? context->https_proxy + DavProxy *proxy = cx_strprefix(url, CX_STR("https")) ? context->https_proxy : context->http_proxy; if (proxy->url) { @@ -95,7 +94,7 @@ curl_easy_setopt(sn->handle, CURLOPT_URL, base_url); // add to context - context->sessions = ucx_list_append(context->sessions, sn); + cxListAdd(context->sessions, sn); sn->context = context; return sn; @@ -128,16 +127,17 @@ } void dav_session_set_baseurl(DavSession *sn, char *base_url) { + const CxAllocator *a = sn->mp->allocator; if(sn->base_url) { - ucx_mempool_free(sn->mp, sn->base_url); + cxFree(a, sn->base_url); } - sstr_t url = sstr(base_url); + cxstring url = cx_str(base_url); if(url.ptr[url.length - 1] == '/') { - sstr_t url = sstrdup_a(sn->mp->allocator, sstr(base_url)); - sn->base_url = url.ptr; + cxmutstr url_m = cx_strdup_a(a, cx_str(base_url)); + sn->base_url = url_m.ptr; } else { - char *url_str = ucx_mempool_malloc(sn->mp, url.length + 2); + char *url_str = cxMalloc(a, url.length + 2); memcpy(url_str, base_url, url.length); url_str[url.length] = '/'; url_str[url.length + 1] = '\0'; @@ -170,17 +170,17 @@ return dav_session_curl_perform_buf(sn, NULL, NULL, status); } -CURLcode dav_session_curl_perform_buf(DavSession *sn, UcxBuffer *request, UcxBuffer *response, long *status) { +CURLcode dav_session_curl_perform_buf(DavSession *sn, CxBuffer *request, CxBuffer *response, long *status) { CURLcode ret = curl_easy_perform(sn->handle); long http_status; curl_easy_getinfo(sn->handle, CURLINFO_RESPONSE_CODE, &http_status); if(ret == CURLE_OK && http_status == 401 && sn->auth_prompt) { if(!sn->auth_prompt(sn, sn->authprompt_userdata)) { if(request) { - ucx_buffer_seek(request, 0, SEEK_SET); + cxBufferSeek(request, 0, SEEK_SET); } if(response) { - ucx_buffer_seek(response, 0, SEEK_SET); + cxBufferSeek(response, 0, SEEK_SET); } ret = curl_easy_perform(sn->handle); curl_easy_getinfo(sn->handle, CURLINFO_RESPONSE_CODE, &http_status); @@ -274,43 +274,40 @@ void dav_session_destroy(DavSession *sn) { // remove session from context - UcxList *sessions = sn->context->sessions; - ssize_t i = ucx_list_find(sessions, sn, ucx_cmp_ptr, NULL); - if(i >= 0) { - UcxList *elm = ucx_list_get(sessions, i); - if(elm) { - sn->context->sessions = ucx_list_remove(sessions, elm); - } + CxList *sessions = sn->context->sessions; + ssize_t i = cxListFind(sessions, sn); + if(i >= 0) { + cxListRemove(sessions, i); } - ucx_mempool_destroy(sn->mp); + cxMempoolDestroy(sn->mp); curl_easy_cleanup(sn->handle); free(sn); } void* dav_session_malloc(DavSession *sn, size_t size) { - return ucx_mempool_malloc(sn->mp, size); + return cxMalloc(sn->mp->allocator, size); } void* dav_session_calloc(DavSession *sn, size_t nelm, size_t size) { - return ucx_mempool_calloc(sn->mp, nelm, size); + return cxCalloc(sn->mp->allocator, nelm, size); } void* dav_session_realloc(DavSession *sn, void *ptr, size_t size) { - return ucx_mempool_realloc(sn->mp, ptr, size); + return cxRealloc(sn->mp->allocator, ptr, size); } void dav_session_free(DavSession *sn, void *ptr) { - ucx_mempool_free(sn->mp, ptr); + cxFree(sn->mp->allocator, ptr); } char* dav_session_strdup(DavSession *sn, const char *str) { - return sstrdup_a(sn->mp->allocator, sstr((char*)str)).ptr; + return cx_strdup_a(sn->mp->allocator, cx_str((char*)str)).ptr; } -char* dav_session_create_plain_href(DavSession *sn, char *path) { +char* dav_session_create_plain_href(DavSession *sn, const char *path) { if(!DAV_ENCRYPT_NAME(sn) && !DAV_DECRYPT_NAME(sn)) { // non encrypted file names char *url = util_path_to_url(sn, path); @@ -322,11 +319,14 @@ } } -char* dav_session_get_href(DavSession *sn, char *path) { +char* dav_session_get_href(DavSession *sn, const char *path) { if(DAV_DECRYPT_NAME(sn) || DAV_ENCRYPT_NAME(sn)) { - sstr_t p = sstr(path); - UcxBuffer *href = ucx_buffer_new(NULL, 256, UCX_BUFFER_AUTOEXTEND); - UcxBuffer *pbuf = ucx_buffer_new(NULL, 256, UCX_BUFFER_AUTOEXTEND); + cxstring p = cx_str(path); + CxBuffer href; + CxBuffer pbuf; + cxBufferInit(&href, NULL, 256, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); + cxBufferInit(&pbuf, NULL, 256, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); + int start = 0; int begin = 0; @@ -334,11 +334,11 @@ char *cp = strdup(path); //printf("cp: %s\n", cp); while(strlen(cp) > 1) { - char *cached = ucx_map_cstr_get(sn->pathcache, cp); + char *cached = cxMapGet(sn->pathcache, cx_hash_key_str(cp)); if(cached) { start = strlen(cp); begin = start; - ucx_buffer_puts(href, cached); + cxBufferPutString(&href, cached); break; } else { // check, if the parent path is cached @@ -348,101 +348,96 @@ } } free(cp); - if(href->pos == 0) { + if(href.pos == 0) { // if there are no cached elements we have to add the base url path // to the href buffer - ucx_buffer_puts(href, util_url_path(sn->base_url)); + cxBufferPutString(&href, util_url_path(sn->base_url)); } // create resource for name lookup - sstr_t rp = sstrdup(sstrn(path, start)); + cxmutstr rp = cx_strdup(cx_strn(path, start)); DavResource *root = dav_resource_new(sn, rp.ptr); free(rp.ptr); - resource_set_href(root, sstrn(href->space, href->pos)); + resource_set_href(root, cx_strn(href.space, href.pos)); // create request buffer for propfind requests - UcxBuffer *rqbuf = create_basic_propfind_request(); + CxBuffer *rqbuf = create_basic_propfind_request(); - sstr_t remaining = sstrsubs(p, start); - ssize_t nelm = 0; - sstr_t *elms = sstrsplit(remaining, S("/"), &nelm); + cxstring remaining = cx_strsubs(p, start); + CxStrtokCtx elms = cx_strtok(remaining, CX_STR("/"), INT_MAX); DavResource *res = root; - ucx_buffer_puts(pbuf, res->path); + cxBufferPutString(&pbuf, res->path); // iterate over all remaining path elements - for(int i=0;i 0) { //printf("elm: %.*s\n", elm.length, elm.ptr); DavResource *child = dav_find_child(sn, res, rqbuf, elm.ptr); // if necessary add a path separator - if(pbuf->space[pbuf->pos-1] != '/') { - if(href->space[href->pos-1] != '/') { - ucx_buffer_putc(href, '/'); + if(pbuf.space[pbuf.pos-1] != '/') { + if(href.space[href.pos-1] != '/') { + cxBufferPut(&href, '/'); } - ucx_buffer_putc(pbuf, '/'); + cxBufferPut(&pbuf, '/'); } // add last path/href to the cache - sstr_t pp = sstrn(pbuf->space, pbuf->size); - sstr_t hh = sstrn(href->space, href->size); + cxstring pp = cx_strn(pbuf.space, pbuf.size); + cxstring hh = cx_strn(href.space, href.size); dav_session_cache_path(sn, pp, hh); - ucx_buffer_write(elm.ptr, 1, elm.length, pbuf); + cxBufferWrite(elm.ptr, 1, elm.length, &pbuf); if(child) { // href is already URL encoded, so don't encode again - ucx_buffer_puts(href, util_resource_name(child->href)); + cxBufferPutString(&href, util_resource_name(child->href)); res = child; } else if(DAV_ENCRYPT_NAME(sn)) { char *random_name = util_random_str(); - ucx_buffer_puts(href, random_name); + cxBufferPutString(&href, random_name); free(random_name); } else { // path is not URL encoded, so we have to do this here - scstr_t resname = scstr(util_resource_name(path)); + cxstring resname = cx_str(util_resource_name((const char*)path)); // the name of collections ends with // a trailing slash, which MUST NOT be encoded if(resname.ptr[resname.length-1] == '/') { char *esc = curl_easy_escape(sn->handle, resname.ptr, resname.length-1); - ucx_buffer_write(esc, 1, strlen(esc), href); - ucx_buffer_putc(href, '/'); + cxBufferWrite(esc, 1, strlen(esc), &href); + cxBufferPut(&href, '/'); curl_free(esc); } else { char *esc = curl_easy_escape(sn->handle, resname.ptr, resname.length); - ucx_buffer_write(esc, 1, strlen(esc), href); + cxBufferWrite(esc, 1, strlen(esc), &href); curl_free(esc); } } } - - // cleanup - free(elm.ptr); } - free(elms); // if necessary add a path separator if(p.ptr[p.length-1] == '/') { - if(href->space[href->pos-1] != '/') { - ucx_buffer_putc(href, '/'); + if(href.space[href.pos-1] != '/') { + cxBufferPut(&href, '/'); } - ucx_buffer_putc(pbuf, '/'); + cxBufferPut(&pbuf, '/'); } // add the final path to the cache - sstr_t pp = sstrn(pbuf->space, pbuf->size); - sstr_t hh = sstrn(href->space, href->size); + cxstring pp = cx_strn(pbuf.space, pbuf.size); + cxstring hh = cx_strn(href.space, href.size); dav_session_cache_path(sn, pp, hh); - sstr_t href_str = sstrdup_a( + cxmutstr href_str = cx_strdup_a( sn->mp->allocator, - sstrn(href->space, - href->size)); + cx_strn(href.space, href.size)); // cleanup dav_resource_free_all(root); - ucx_buffer_free(rqbuf); - ucx_buffer_free(pbuf); - ucx_buffer_free(href); + cxBufferFree(rqbuf); + + cxBufferDestroy(&pbuf); + cxBufferDestroy(&href); return href_str.ptr; } else { @@ -450,7 +445,7 @@ } } -DavResource* dav_find_child(DavSession *sn, DavResource *res, UcxBuffer *rqbuf, char *name) { +DavResource* dav_find_child(DavSession *sn, DavResource *res, CxBuffer *rqbuf, const char *name) { if(res && !dav_propfind(sn, res, rqbuf)) { DavResource *child = res->children; while(child) { @@ -463,16 +458,17 @@ return NULL; } -void dav_session_cache_path(DavSession *sn, sstr_t path, sstr_t href) { - char *elm = ucx_map_sstr_get(sn->pathcache, path); +void dav_session_cache_path(DavSession *sn, cxstring path, cxstring href) { + CxHashKey path_key = cx_hash_key(path.ptr, path.length); + char *elm = cxMapGet(sn->pathcache, path_key); if(!elm) { - href = sstrdup_a(sn->mp->allocator, href); - ucx_map_sstr_put(sn->pathcache, path, href.ptr); + cxmutstr href_s = cx_strdup_a(sn->mp->allocator, href); + cxMapPut(sn->pathcache, path_key, href_s.ptr); } } -DavLock* dav_create_lock(DavSession *sn, char *token, char *timeout) { +DavLock* dav_create_lock(DavSession *sn, const char *token, char *timeout) { DavLock *lock = dav_session_malloc(sn, sizeof(DavLock)); lock->path = NULL; lock->token = dav_session_strdup(sn, token); @@ -490,77 +486,79 @@ dav_session_free(sn, lock); } -int dav_add_resource_lock(DavSession *sn, char *path, DavLock *lock) { - DavLockManager *locks = sn->locks; - if(ucx_map_cstr_get(locks->resource_locks, path)) { - return -1; - } - - ucx_map_cstr_put(locks->resource_locks, path, lock); + +static int dav_lock_cmp(void const *left, void const *right) { + const DavLock *l = left; + const DavLock *r = right; + return strcmp(l->path, r->path); +} + +static int create_lock_manager(DavSession *sn) { + // create lock manager + DavLockManager *locks = cxMalloc(sn->mp->allocator, sizeof(DavLockManager)); + locks->resource_locks = cxHashMapCreate(sn->mp->allocator, CX_STORE_POINTERS, 16); + locks->collection_locks = cxLinkedListCreate(sn->mp->allocator, dav_lock_cmp, CX_STORE_POINTERS); + sn->locks = locks; return 0; } -static void insert_lock(DavSession *sn, UcxList *elm, UcxList *newelm) { - UcxList *next = elm->next; - if(next) { - next->prev = newelm; - newelm->next = next; +static DavLockManager* get_lock_manager(DavSession *sn) { + DavLockManager *locks = sn->locks; + if(!locks) { + if(create_lock_manager(sn)) { + return NULL; + } + locks = sn->locks; } - newelm->prev = elm; - elm->next = newelm; + return locks; } -int dav_add_collection_lock(DavSession *sn, char *path, DavLock *lock) { - DavLockManager *locks = sn->locks; - if(!locks->collection_locks) { - locks->collection_locks = ucx_list_append_a( - sn->mp->allocator, - NULL, - lock); - lock->path = dav_session_strdup(sn, path); - return 0; +int dav_add_resource_lock(DavSession *sn, const char *path, DavLock *lock) { + DavLockManager *locks = get_lock_manager(sn); + if(!locks) { + return -1; } - UcxList *elm = locks->collection_locks; - for(;;) { - DavLock *l = elm->data; - int cmp = strcmp(path, l->path); - if(cmp > 0) { - UcxList *newelm = ucx_list_append_a(sn->mp->allocator, NULL, lock); - lock->path = dav_session_strdup(sn, path); - insert_lock(sn, elm, newelm); - } else if(cmp == 0) { - return -1; - } - - if(elm->next) { - elm = elm->next; - } else { - UcxList *newelm = ucx_list_append_a(sn->mp->allocator, NULL, lock); - lock->path = dav_session_strdup(sn, path); - ucx_list_concat(elm, newelm); - break; - } + CxHashKey path_key = cx_hash_key_str(path); + if(cxMapGet(locks->resource_locks, path_key)) { + return -1; } + cxMapPut(locks->resource_locks, path_key, lock); + return 0; +} + +int dav_add_collection_lock(DavSession *sn, const char *path, DavLock *lock) { + DavLockManager *locks = get_lock_manager(sn); + if(!locks) { + return -1; + } + + cxListAdd(locks->collection_locks, lock); + cxListSort(locks->collection_locks); + return 0; } -DavLock* dav_get_lock(DavSession *sn, char *path) { - DavLockManager *locks = sn->locks; +DavLock* dav_get_lock(DavSession *sn, const char *path) { + DavLockManager *locks = get_lock_manager(sn); + if(!locks) { + return NULL; + } - DavLock *lock = ucx_map_cstr_get(locks->resource_locks, path); + cxstring p = cx_str(path); + + DavLock *lock = cxMapGet(locks->resource_locks, cx_hash_key(p.ptr, p.length)); if(lock) { return lock; } - sstr_t p = sstr(path); - UCX_FOREACH(elm, locks->collection_locks) { - DavLock *cl = elm->data; + CxIterator i = cxListIterator(locks->collection_locks); + cx_foreach(DavLock*, cl, i) { int cmd = strcmp(path, cl->path); if(cmd == 0) { return cl; - } else if(sstrprefix(p, sstr(cl->path))) { + } else if(cx_strprefix(p, cx_str(cl->path))) { return cl; } else if(cmd > 0) { break; @@ -570,26 +568,25 @@ return NULL; } -void dav_remove_lock(DavSession *sn, char *path, DavLock *lock) { - DavLockManager *locks = sn->locks; +void dav_remove_lock(DavSession *sn, const char *path, DavLock *lock) { + DavLockManager *locks = get_lock_manager(sn); + if(!locks) { + return; + } - if(ucx_map_cstr_remove(locks->resource_locks, path)) { + if(cxMapRemoveAndGet(locks->resource_locks, cx_hash_key_str(path))) { return; } - UcxList *rm = NULL; - UCX_FOREACH(elm, locks->collection_locks) { - DavLock *cl = elm->data; + CxMutIterator i = cxListMutIterator(locks->collection_locks); + int rm = 0; + cx_foreach(DavLock* , cl, i) { + if(rm) { + break; + } if(cl == lock) { - rm = elm; - break; + cxIteratorFlagRemoval(i); + rm = 1; } } - - if(rm) { - locks->collection_locks = ucx_list_remove_a( - sn->mp->allocator, - locks->collection_locks, - rm); - } } diff -r a569148841ff -r efbd59642577 libidav/session.h --- a/libidav/session.h Sun Apr 16 14:12:24 2023 +0200 +++ b/libidav/session.h Fri Apr 21 21:25:32 2023 +0200 @@ -29,7 +29,7 @@ #ifndef DAV_SESSION_H #define DAV_SESSION_H -#include +#include #include "webdav.h" #ifdef __cplusplus @@ -74,19 +74,19 @@ } DavPathCacheElement; */ -typedef struct DavLock { +typedef struct DavLock DavLock; +struct DavLock { char *path; char *token; - -} DavLock; +}; typedef struct DavLockManager { - UcxMap *resource_locks; - UcxList *collection_locks; + CxMap *resource_locks; + CxList *collection_locks; } DavLockManager; CURLcode dav_session_curl_perform(DavSession *sn, long *status); -CURLcode dav_session_curl_perform_buf(DavSession *sn, UcxBuffer *request, UcxBuffer *response, long *status); +CURLcode dav_session_curl_perform_buf(DavSession *sn, CxBuffer *request, CxBuffer *response, long *status); int dav_session_get_progress(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow); int dav_session_put_progress(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow); @@ -94,23 +94,23 @@ void dav_session_set_error(DavSession *sn, CURLcode c, int status); void dav_session_set_errstr(DavSession *sn, const char *str); -char* dav_session_create_plain_href(DavSession *sn, char *path); +char* dav_session_create_plain_href(DavSession *sn, const char *path); -char* dav_session_get_href(DavSession *sn, char *path); +char* dav_session_get_href(DavSession *sn, const char *path); -DavResource* dav_find_child(DavSession *sn, DavResource *res, UcxBuffer *rqbuf, char *name); +DavResource* dav_find_child(DavSession *sn, DavResource *res, CxBuffer *rqbuf, const char *name); -void dav_session_cache_path(DavSession *sn, sstr_t path, sstr_t href); +void dav_session_cache_path(DavSession *sn, cxstring path, cxstring href); -DavLock* dav_create_lock(DavSession *sn, char *token, char *timeout); +DavLock* dav_create_lock(DavSession *sn, const char *token, char *timeout); void dav_destroy_lock(DavSession *sn, DavLock *lock); -int dav_add_resource_lock(DavSession *sn, char *path, DavLock *lock); -int dav_add_collection_lock(DavSession *sn, char *path, DavLock *lock); +int dav_add_resource_lock(DavSession *sn, const char *path, DavLock *lock); +int dav_add_collection_lock(DavSession *sn, const char *path, DavLock *lock); -DavLock* dav_get_lock(DavSession *sn, char *path); -void dav_remove_lock(DavSession *sn, char *path, DavLock *lock); +DavLock* dav_get_lock(DavSession *sn, const char *path); +void dav_remove_lock(DavSession *sn, const char *path, DavLock *lock); #ifdef __cplusplus } diff -r a569148841ff -r efbd59642577 libidav/utils.c --- a/libidav/utils.c Sun Apr 16 14:12:24 2023 +0200 +++ b/libidav/utils.c Fri Apr 21 21:25:32 2023 +0200 @@ -32,9 +32,10 @@ #include #include #include -#include -#include -#include +#include +#include +#include +#include #include #include @@ -66,7 +67,7 @@ #include */ -static size_t extractval(sstr_t str, char *result, char delim) { +static size_t extractval(cxstring str, char *result, char delim) { size_t n = 0; for(size_t i = 0; i < str.length ; i++) { if(isdigit(str.ptr[i])) { @@ -93,19 +94,19 @@ char conv[16]; // work on the trimmed string - sstr_t date = sstrtrim(sstr(iso8601str)); + cxstring date = cx_strtrim(cx_str(iso8601str)); - sstr_t time = sstrchr(date, 'T'); + cxstring time = cx_strchr(date, 'T'); if(time.length == 0) { return 0; } date.length = time.ptr - date.ptr; time.ptr++; time.length--; - sstr_t tzinfo; - if((tzinfo = sstrchr(time, 'Z')).length > 0 || - (tzinfo = sstrchr(time, '+')).length > 0 || - (tzinfo = sstrchr(time, '-')).length > 0) { + cxstring tzinfo; + if((tzinfo = cx_strchr(time, 'Z')).length > 0 || + (tzinfo = cx_strchr(time, '+')).length > 0 || + (tzinfo = cx_strchr(time, '-')).length > 0) { time.length = tzinfo.ptr - time.ptr; } @@ -124,9 +125,9 @@ tparts.tm_year = val / 10000 - 1900; // parse time and skip possible fractional seconds - sstr_t frac; - if((frac = sstrchr(time, '.')).length > 0 || - (frac = sstrchr(time, ',')).length > 0) { + cxstring frac; + if((frac = cx_strchr(time, '.')).length > 0 || + (frac = cx_strchr(time, ',')).length > 0) { time.length = frac.ptr - time.ptr; } if((time.length != 6 && time.length != 8) @@ -144,7 +145,7 @@ // local time tparts.tm_isdst = -1; return mktime(&tparts); - } else if(!sstrcmp(tzinfo, S("Z"))) { + } else if(!cx_strcmp(tzinfo, cx_str("Z"))) { #ifdef __FreeBSD__ return timegm(&tparts); #else @@ -276,13 +277,13 @@ } } -char* util_url_base_s(sstr_t url) { +char* util_url_base_s(cxstring url) { size_t i = 0; if(url.length > 0) { int slmax; - if(sstrprefix(url, SC("http://"))) { + if(cx_strprefix(url, cx_str("http://"))) { slmax = 3; - } else if(sstrprefix(url, SC("https://"))) { + } else if(cx_strprefix(url, cx_str("https://"))) { slmax = 3; } else { slmax = 1; @@ -298,16 +299,16 @@ } } } - sstr_t server = sstrsubsl(url, 0, i); - return sstrdup(server).ptr; + cxstring server = cx_strsubsl(url, 0, i); + return cx_strdup(server).ptr; } char* util_url_base(char *url) { - return util_url_base_s(sstr(url)); + return util_url_base_s(cx_str(url)); } -char* util_url_path(char *url) { - char *path = NULL; +const char* util_url_path(const char *url) { + const char *path = NULL; size_t len = strlen(url); int slashcount = 0; int slmax; @@ -335,7 +336,7 @@ return path; } -char* util_url_decode(DavSession *sn, char *url) { +char* util_url_decode(DavSession *sn, const char *url) { char *unesc = curl_easy_unescape(sn->handle, url, strlen(url), NULL); char *ret = strdup(unesc); curl_free(unesc); @@ -345,24 +346,24 @@ static size_t util_header_callback(char *buffer, size_t size, size_t nitems, void *data) { - sstr_t sbuffer = sstrn(buffer, size*nitems); + cxstring sbuffer = cx_strn(buffer, size*nitems); - UcxMap *map = (UcxMap*) data; + CxMap *map = (CxMap*) data; // if we get a status line, clear the map and exit - if(sstrprefix(sbuffer, S("HTTP/"))) { - ucx_map_free_content(map, free); - ucx_map_clear(map); + if(cx_strprefix(sbuffer, cx_str("HTTP/"))) { + // TODO: use new map destructor ucx_map_free_content(map, free); + cxMapClear(map); return size*nitems; } // if we get the terminating CRLF, just exit - if(!sstrcmp(sbuffer, S("\r\n"))) { + if(!cx_strcmp(sbuffer, cx_str("\r\n"))) { return 2; } - sstr_t key = sbuffer; - sstr_t value = sstrchr(sbuffer, ':'); + cxstring key = sbuffer; + cxstring value = cx_strchr(sbuffer, ':'); if(value.length == 0) { return 0; // invalid header line @@ -371,19 +372,20 @@ key.length = value.ptr - key.ptr; value.ptr++; value.length--; - key = sstrlower(sstrtrim(key)); - value = sstrdup(sstrtrim(value)); + cxmutstr key_cp = cx_strdup(cx_strtrim(key)); + cx_strlower(key_cp); + cxmutstr value_cp = cx_strdup(cx_strtrim(value)); - ucx_map_sstr_put(map, key, value.ptr); + cxMapPut(map, cx_hash_key(key_cp.ptr, key_cp.length), value_cp.ptr); - free(key.ptr); + free(key_cp.ptr); return sbuffer.length; } int util_path_isrelated(const char *path1, const char *path2) { - scstr_t p1 = scstr(path1); - scstr_t p2 = scstr(path2); + cxstring p1 = cx_str(path1); + cxstring p2 = cx_str(path2); if(IS_PATH_SEPARATOR(p1.ptr[p1.length-1])) { p1.length--; @@ -396,11 +398,11 @@ return 0; } - if(!sstrcmp(p1, p2)) { + if(!cx_strcmp(p1, p2)) { return 1; } - if(sstrprefix(p2, p1)) { + if(cx_strprefix(p2, p1)) { if(IS_PATH_SEPARATOR(p2.ptr[p1.length])) { return 1; } @@ -434,10 +436,11 @@ char* util_path_normalize(const char *path) { size_t len = strlen(path); - UcxBuffer *buf = ucx_buffer_new(NULL, len+1, UCX_BUFFER_AUTOEXTEND); + CxBuffer buf; + cxBufferInit(&buf, NULL, len+1, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); if(path[0] == '/') { - ucx_buffer_putc(buf, '/'); + cxBufferPut(&buf, '/'); } int add_separator = 0; @@ -453,25 +456,25 @@ } if(seg_len > 0) { - scstr_t seg = scstrn(seg_ptr, seg_len); - if(!sstrcmp(seg, SC(".."))) { - for(int j=buf->pos;j>=0;j--) { - char t = buf->space[j]; + cxstring seg = cx_strn(seg_ptr, seg_len); + if(!cx_strcmp(seg, CX_STR(".."))) { + for(int j=buf.pos;j>=0;j--) { + char t = buf.space[j]; if(IS_PATH_SEPARATOR(t) || j == 0) { - buf->pos = j; - buf->size = j; - buf->space[j] = 0; + buf.pos = j; + buf.size = j; + buf.space[j] = 0; add_separator = IS_PATH_SEPARATOR(t) ? 1 : 0; break; } } - } else if(!sstrcmp(seg, SC("."))) { + } else if(!cx_strcmp(seg, CX_STR("."))) { // ignore } else { if(add_separator) { - ucx_buffer_putc(buf, PATH_SEPARATOR); + cxBufferPut(&buf, PATH_SEPARATOR); } - ucx_buffer_write(seg_ptr, 1, seg_len, buf); + cxBufferWrite(seg_ptr, 1, seg_len, &buf); add_separator = 1; } } @@ -480,13 +483,9 @@ } } - ucx_buffer_putc(buf, 0); - + cxBufferPut(&buf, 0); - char *space = buf->space; - buf->flags = 0; // disable autofree - ucx_buffer_free(buf); - return space; + return buf.space; } static char* create_relative_path(const char *abspath, const char *base) { @@ -523,7 +522,7 @@ } char *ret = NULL; - UcxBuffer *out = NULL; + CxBuffer out; if(last_dir+1 < base_len) { // base is deeper than the link root, we have to go backwards int dircount = 0; @@ -533,22 +532,19 @@ } } - out = ucx_buffer_new(NULL, dircount*3+path_len-last_dir, UCX_BUFFER_AUTOEXTEND); + cxBufferInit(&out, NULL, dircount*3+path_len-last_dir, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); for(int i=0;iflags = 0; - ret = out->space; - ucx_buffer_free(out); + cxBufferPutString(&out, abspath + last_dir + 1); + cxBufferPut(&out, 0); - return ret; + return out.space; } #ifdef _WIN32 @@ -581,7 +577,7 @@ #endif -void util_capture_header(CURL *handle, UcxMap* map) { +void util_capture_header(CURL *handle, CxMap* map) { if(map) { curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, util_header_callback); curl_easy_setopt(handle, CURLOPT_HEADERDATA, map); @@ -591,12 +587,12 @@ } } -char* util_resource_name(char *url) { - sstr_t urlstr = sstr(url); +const char* util_resource_name(const char *url) { + cxstring urlstr = cx_str(url); if(urlstr.ptr[urlstr.length-1] == '/') { urlstr.length--; } - sstr_t resname = sstrrchr(urlstr, '/'); + cxstring resname = cx_strrchr(urlstr, '/'); if(resname.length > 1) { return resname.ptr+1; } else { @@ -613,12 +609,12 @@ } char* util_concat_path(const char *url_base, const char *p) { - sstr_t base = sstr((char*)url_base); - sstr_t path; + cxstring base = cx_str((char*)url_base); + cxstring path; if(p) { - path = sstr((char*)p); + path = cx_str((char*)p); } else { - path = sstrn("", 0); + path = CX_STR(""); } int add_separator = 0; @@ -632,24 +628,24 @@ } } - sstr_t url; + cxmutstr url; if(add_separator) { - url = sstrcat(3, base, sstr("/"), path); + url = cx_strcat(3, base, CX_STR("/"), path); } else { - url = sstrcat(2, base, path); + url = cx_strcat(2, base, path); } return url.ptr; } char* util_get_url(DavSession *sn, const char *href) { - scstr_t base = scstr(sn->base_url); - scstr_t href_str = scstr(href); + cxstring base = cx_str(sn->base_url); + cxstring href_str = cx_str(href); - char *base_path = util_url_path(sn->base_url); + const char *base_path = util_url_path(sn->base_url); base.length -= strlen(base_path); - sstr_t url = sstrcat(2, base, href_str); + cxmutstr url = cx_strcat(2, base, href_str); return url.ptr; } @@ -659,43 +655,38 @@ free(url); } -char* util_path_to_url(DavSession *sn, char *path) { - char *space = malloc(256); - UcxBuffer *url = ucx_buffer_new(space, 256, UCX_BUFFER_AUTOEXTEND); +char* util_path_to_url(DavSession *sn, const char *path) { + CxBuffer url; + cxBufferInit(&url, NULL, 256, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); // add base url - ucx_buffer_write(sn->base_url, 1, strlen(sn->base_url), url); + cxBufferWrite(sn->base_url, 1, strlen(sn->base_url), &url); // remove trailing slash - ucx_buffer_seek(url, -1, SEEK_CUR); + cxBufferSeek(&url, -1, SEEK_CUR); - sstr_t p = sstr(path); - ssize_t ntk = 0; - sstr_t *tks = sstrsplit(p, S("/"), &ntk); + cxstring p = cx_str(path); - for(int i=0;i 0) { char *esc = curl_easy_escape(sn->handle, node.ptr, node.length); - ucx_buffer_putc(url, '/'); - ucx_buffer_write(esc, 1, strlen(esc), url); + cxBufferPut(&url, '/'); + cxBufferWrite(esc, 1, strlen(esc), &url); curl_free(esc); } - free(node.ptr); - } - free(tks); - if(path[p.length-1] == '/') { - ucx_buffer_putc(url, '/'); } - ucx_buffer_putc(url, 0); - space = url->space; - ucx_buffer_free(url); + if(path[p.length-1] == '/') { + cxBufferPut(&url, '/'); + } + cxBufferPut(&url, 0); - return space; + return url.space; } char* util_parent_path(const char *path) { - char *name = util_resource_name((char*)path); + const char *name = util_resource_name(path); size_t namelen = strlen(name); size_t pathlen = strlen(path); size_t parentlen = pathlen - namelen; @@ -952,11 +943,11 @@ return out; } -char* util_encrypt_str(DavSession *sn, char *str, char *key) { +char* util_encrypt_str(DavSession *sn, const char *str, const char *key) { DavKey *k = dav_context_get_key(sn->context, key); if(!k) { sn->error = DAV_ERROR; - sstr_t err = ucx_sprintf("Key %s not found", key); + cxmutstr err = cx_asprintf("Key %s not found", key); dav_session_set_errstr(sn, err.ptr); free(err.ptr); return NULL; @@ -965,18 +956,18 @@ return util_encrypt_str_k(sn, str, k); } -char* util_encrypt_str_k(DavSession *sn, char *str, DavKey *key) { +char* util_encrypt_str_k(DavSession *sn, const char *str, DavKey *key) { char *enc_str = aes_encrypt(str, strlen(str), key); char *ret_str = dav_session_strdup(sn, enc_str); free(enc_str); return ret_str; } -char* util_decrypt_str(DavSession *sn, char *str, char *key) { +char* util_decrypt_str(DavSession *sn, const char *str, const char *key) { DavKey *k = dav_context_get_key(sn->context, key); if(!k) { sn->error = DAV_ERROR; - sstr_t err = ucx_sprintf("Key %s not found", key); + cxmutstr err = cx_asprintf("Key %s not found", key); dav_session_set_errstr(sn, err.ptr); free(err.ptr); return NULL; @@ -985,7 +976,7 @@ return util_decrypt_str_k(sn, str, k); } -char* util_decrypt_str_k(DavSession *sn, char *str, DavKey *key) { +char* util_decrypt_str_k(DavSession *sn, const char *str, DavKey *key) { size_t len = 0; char *dec_str = aes_decrypt(str, &len, key); char *ret_str = dav_session_strdup(sn, dec_str); @@ -997,7 +988,7 @@ unsigned char *str = malloc(25); str[24] = '\0'; - sstr_t t = S( + cxstring t = CX_STR( "01234567890" "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); @@ -1021,6 +1012,8 @@ * tokens are separated by space * sets sub to the substring and returns the remaining string */ +// TODO: remove if it isn't used +/* sstr_t util_getsubstr_until_token(sstr_t str, sstr_t token, sstr_t *sub) { int i; int token_start = -1; @@ -1060,20 +1053,22 @@ return str; } } +*/ -sstr_t util_readline(FILE *stream) { - UcxBuffer *buf = ucx_buffer_new(NULL, 128, UCX_BUFFER_AUTOEXTEND); +cxmutstr util_readline(FILE *stream) { + CxBuffer buf; + cxBufferInit(&buf, NULL, 128, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); int c; while((c = fgetc(stream)) != EOF) { if(c == '\n') { break; } - ucx_buffer_putc(buf, c); + cxBufferPut(&buf, c); } - sstr_t str = sstrdup(sstrtrim(sstrn(buf->space, buf->size))); - ucx_buffer_free(buf); + cxmutstr str = cx_strdup(cx_strtrim(cx_strn(buf.space, buf.size))); + cxBufferDestroy(&buf); return str; } @@ -1097,15 +1092,16 @@ #endif // read password input - UcxBuffer *buf = ucx_buffer_new(NULL, 128, UCX_BUFFER_AUTOEXTEND); + CxBuffer buf; + cxBufferInit(&buf, NULL, 128, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); int c = 0; while((c = getpasswordchar()) != EOF) { if(c == '\n' || c == '\r') { break; } - ucx_buffer_putc(buf, c); + cxBufferPut(&buf, c); } - ucx_buffer_putc(buf, 0); + cxBufferPut(&buf, 0); fflush(stdin); #ifndef _WIN32 @@ -1115,12 +1111,10 @@ } #endif - char *str = buf->space; - free(buf); // only free the UcxBuffer struct - return str; + return buf.space; } -int util_exec_command(char *command, UcxBuffer *outbuf) { +int util_exec_command(char *command, CxBuffer *outbuf) { #ifdef _WIN32 fprintf(stderr, "util_exec_command unsupported\n"); return 1; @@ -1156,7 +1150,7 @@ ssize_t r; char buf[1024]; while((r = read(pout[0], buf, 1024)) > 0) { - ucx_buffer_write(buf, 1, r, outbuf); + cxBufferWrite(buf, 1, r, outbuf); } } @@ -1173,14 +1167,13 @@ char* util_hexstr(const unsigned char *data, size_t len) { size_t buflen = 2*len + 4; - UcxBuffer *buf = ucx_buffer_new(malloc(buflen), buflen + 1, 0); + CxBuffer buf; + cxBufferInit(&buf, NULL, buflen + 1, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); for(int i=0;ispace; - ucx_buffer_free(buf); - return str; + cxBufferPut(&buf, 0); + return buf.space; } void util_remove_trailing_pathseparator(char *path) { @@ -1215,3 +1208,25 @@ return util_hexstr(hash, DAV_SHA256_DIGEST_LENGTH); } + +struct regdestructor { + cx_destructor_func destructor; + void *data; +}; + +static void call_destructor(struct regdestructor *d) { + d->destructor(d->data); +} + +int util_regdestr(CxMempool *mp, void *data, cx_destructor_func destructor) { + // the ucx maintainer doesn't like me anymore and forces me to + // implement basic stuff by myself + struct regdestructor *reg = cxMalloc(mp->allocator, sizeof(struct regdestructor)); + if(!reg) { + return 1; + } + reg->destructor = destructor; + reg->data = data; + cxMempoolSetDestructor(mp, reg, (cx_destructor_func)call_destructor); + return 0; +} diff -r a569148841ff -r efbd59642577 libidav/utils.h --- a/libidav/utils.h Sun Apr 16 14:12:24 2023 +0200 +++ b/libidav/utils.h Fri Apr 21 21:25:32 2023 +0200 @@ -36,8 +36,8 @@ #include #include -#include -#include +#include +#include #include #include @@ -66,10 +66,10 @@ int util_mkdir(char *path, mode_t mode); char* util_url_base(char *url); -char* util_url_base_s(sstr_t url); -char* util_url_path(char *url); -char* util_url_decode(DavSession *sn, char *url); -char* util_resource_name(char *url); +char* util_url_base_s(cxstring url); +const char* util_url_path(const char *url); +char* util_url_decode(DavSession *sn, const char *url); +const char* util_resource_name(const char *url); char* util_concat_path(const char *url_base, const char *path); char* util_get_url(DavSession *sn, const char *href); void util_set_url(DavSession *sn, const char *href); @@ -84,9 +84,9 @@ char* util_path_normalize(const char *path); char* util_create_relative_path(const char *abspath, const char *base); -void util_capture_header(CURL *handle, UcxMap* map); +void util_capture_header(CURL *handle, CxMap* map); -char* util_path_to_url(DavSession *sn, char *path); +char* util_path_to_url(DavSession *sn, const char *path); char* util_parent_path(const char *path); char* util_size_str(DavBool iscollection, uint64_t contentlength); @@ -105,19 +105,19 @@ char* util_base64decode_len(const char *in, int *outlen); char* util_base64encode(const char *in, size_t len); -char* util_encrypt_str(DavSession *sn, char *str, char *key); -char* util_encrypt_str_k(DavSession *sn, char *str, DavKey *key); -char* util_decrypt_str(DavSession *sn, char *str, char *key); -char* util_decrypt_str_k(DavSession *sn, char *str, DavKey *key); +char* util_encrypt_str(DavSession *sn, const char *str, const char *key); +char* util_encrypt_str_k(DavSession *sn, const char *str, DavKey *key); +char* util_decrypt_str(DavSession *sn, const char *str, const char *key); +char* util_decrypt_str_k(DavSession *sn, const char *str, DavKey *key); char* util_random_str(); -sstr_t util_getsubstr_until_token(sstr_t str, sstr_t token, sstr_t *sub); +//sstr_t util_getsubstr_until_token(sstr_t str, sstr_t token, sstr_t *sub); -sstr_t util_readline(FILE *stream); +cxmutstr util_readline(FILE *stream); char* util_password_input(char *prompt); -int util_exec_command(char *command, UcxBuffer *outbuf); +int util_exec_command(char *command, CxBuffer *outbuf); char* util_hexstr(const unsigned char *data, size_t len); @@ -125,6 +125,8 @@ char* util_file_hash(const char *path); +int util_regdestr(CxMempool *mp, void *data, cx_destructor_func destructor); + #ifdef __cplusplus } #endif diff -r a569148841ff -r efbd59642577 libidav/versioning.c --- a/libidav/versioning.c Sun Apr 16 14:12:24 2023 +0200 +++ b/libidav/versioning.c Fri Apr 21 21:25:32 2023 +0200 @@ -74,32 +74,34 @@ DavSession *sn = res->session; util_set_url(sn, dav_resource_get_href(res)); - UcxList *proplist = NULL; + CxList *proplist = NULL; if(properties) { - proplist = parse_properties_string(sn->context, sstr(properties)); + proplist = parse_properties_string(sn->context, cx_str(properties)); + + // check if the list already contains a D:version-name property + int add_vname = 1; + CxIterator i = cxListIterator(proplist); + cx_foreach(DavProperty *, p, i) { + if(!strcmp(p->ns->name, "DAV:") && !strcmp(p->name, "version-name")) { + add_vname = 0; + break; + } + } + if(add_vname) { + // we need at least the D:version-name prop + DavProperty p; + p.ns = dav_get_namespace(sn->context, "D"); + p.name = strdup("version-name"); + p.value = NULL; + cxListInsert(proplist, 0, &p); + } } - // check if the list already contains a D:version-name property - int add_vname = 1; - UCX_FOREACH(elm, proplist) { - DavProperty *p = elm->data; - if(!strcmp(p->ns->name, "DAV:") && !strcmp(p->name, "version-name")) { - add_vname = 0; - break; - } - } - if(add_vname) { - // we need at least the D:version-name prop - DavProperty *p = malloc(sizeof(DavProperty)); - p->ns = dav_get_namespace(sn->context, "D"); - p->name = strdup("version-name"); - p->value = NULL; - proplist = ucx_list_prepend(proplist, p); - } + // create a version-tree request, which is almost the same as propfind - UcxBuffer *rqbuf = create_propfind_request(sn, proplist, "version-tree", 1); - UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND); + CxBuffer *rqbuf = create_propfind_request(sn, proplist, "version-tree", 1); + CxBuffer *rpbuf = cxBufferCreate(NULL, 4096, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); // do the request CURLcode ret = do_report_request(sn, rqbuf, rpbuf); @@ -151,14 +153,14 @@ } // cleanup - while(proplist) { - DavProperty *p = proplist->data; - free(p->name); - free(p); - UcxList *next = proplist->next; - free(proplist); - proplist = next; + if(proplist) { + CxIterator i = cxListIterator(proplist); + cx_foreach(DavProperty*, p, i) { + free(p->name); + } + cxListDestroy(proplist); } + if(error && versions) { DavResource *cur = versions; while(cur) { diff -r a569148841ff -r efbd59642577 libidav/webdav.c --- a/libidav/webdav.c Sun Apr 16 14:12:24 2023 +0200 +++ b/libidav/webdav.c Fri Apr 21 21:25:32 2023 +0200 @@ -35,8 +35,11 @@ #include "webdav.h" #include "session.h" #include "methods.h" -#include "ucx/buffer.h" -#include "ucx/utils.h" +#include +#include +#include +#include +#include #include "davqlparser.h" #include "davqlexec.h" @@ -47,7 +50,7 @@ if(!context) { return NULL; } - context->sessions = NULL; + context->sessions = cxLinkedListCreate(cxDefaultAllocator, cx_cmp_intptr, CX_STORE_POINTERS); context->http_proxy = calloc(1, sizeof(DavProxy)); if(!context->http_proxy) { dav_context_destroy(context); @@ -58,16 +61,16 @@ dav_context_destroy(context); return NULL; } - context->namespaces = ucx_map_new(16); + context->namespaces = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); if(!context->namespaces) { dav_context_destroy(context); return NULL; } - context->namespaceinfo = ucx_map_new(16); + context->namespaceinfo = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); if(!context->namespaceinfo) { dav_context_destroy(context); } - context->keys = ucx_map_new(16); + context->keys = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); if(!context->keys) { dav_context_destroy(context); return NULL; @@ -97,12 +100,11 @@ void dav_context_destroy(DavContext *ctx) { // destroy all sessions assoziated with this context - UcxList *elm = ctx->sessions; - while(elm) { - DavSession *sn = elm->data; - elm = elm->next; + CxIterator i = cxListIterator(ctx->sessions); + cx_foreach(DavSession*, sn, i) { dav_session_destroy(sn); } + cxListDestroy(ctx->sessions); if(ctx->http_proxy) { free(ctx->http_proxy); } @@ -111,10 +113,8 @@ } if(ctx->namespaces) { - UcxMapIterator i = ucx_map_iterator(ctx->namespaces); - UcxKey k; - DavNamespace *ns; - UCX_MAP_FOREACH(k, ns, i) { + i = cxMapIteratorValues(ctx->namespaces); + cx_foreach(DavNamespace*, ns, i) { if(!ns) continue; if(ns->prefix) { free(ns->prefix); @@ -124,16 +124,14 @@ } free(ns); } - ucx_map_free(ctx->namespaces); + cxMapDestroy(ctx->namespaces); } if(ctx->namespaceinfo) { // TODO: implement } if(ctx->keys) { - UcxMapIterator i = ucx_map_iterator(ctx->keys); - UcxKey k; - DavKey *key; - UCX_MAP_FOREACH(k, key, i) { + i = cxMapIteratorValues(ctx->keys); + cx_foreach(DavKey*, key, i) { if(!key) continue; if(key->name) { free(key->name); @@ -143,19 +141,19 @@ } free(key); } - ucx_map_free(ctx->keys); + cxMapDestroy(ctx->keys); } free(ctx); } void dav_context_add_key(DavContext *context, DavKey *key) { - ucx_map_cstr_put(context->keys, key->name, key); + cxMapPut(context->keys, cx_hash_key_str(key->name), key); } -DavKey* dav_context_get_key(DavContext *context, char *name) { +DavKey* dav_context_get_key(DavContext *context, const char *name) { if(name) { - return ucx_map_cstr_get(context->keys, name); + return cxMapGet(context->keys, cx_hash_key_str(name)); } return NULL; } @@ -173,7 +171,7 @@ if(p && n) { namespace->prefix = p; namespace->name = n; - err = ucx_map_cstr_put(context->namespaces, prefix, namespace); + err = cxMapPut(context->namespaces, cx_hash_key_str(prefix), namespace); } if(err) { @@ -186,19 +184,20 @@ } DavNamespace* dav_get_namespace(DavContext *context, const char *prefix) { - return ucx_map_cstr_get(context->namespaces, prefix); + return cxMapGet(context->namespaces, cx_hash_key_str(prefix)); } -DavNamespace* dav_get_namespace_s(DavContext *context, sstr_t prefix) { - return ucx_map_sstr_get(context->namespaces, prefix); +DavNamespace* dav_get_namespace_s(DavContext *context, cxstring prefix) { + return cxMapGet(context->namespaces, cx_hash_key(prefix.ptr, prefix.length)); } int dav_enable_namespace_encryption(DavContext *context, const char *ns, DavBool encrypt) { - DavNSInfo *info = ucx_map_cstr_get(context->namespaceinfo, ns); + CxHashKey hkey = cx_hash_key_str(ns); + DavNSInfo *info = cxMapGet(context->namespaceinfo, hkey); if(!info) { info = calloc(1, sizeof(DavNSInfo)); info->encrypt = encrypt; - ucx_map_cstr_put(context->namespaceinfo, ns, info); + cxMapPut(context->namespaceinfo, hkey, info); } else { info->encrypt = encrypt; } @@ -206,7 +205,7 @@ } int dav_namespace_is_encrypted(DavContext *context, const char *ns) { - DavNSInfo *info = ucx_map_cstr_get(context->namespaceinfo, ns); + DavNSInfo *info = cxMapGet(context->namespaceinfo, cx_hash_key_str(ns)); if(info) { return info->encrypt; } @@ -226,7 +225,7 @@ if(pname) { DavNamespace *ns = dav_get_namespace_s( ctx, - sstrn(prefixed_name, pname-prefixed_name)); + cx_strn(prefixed_name, pname-prefixed_name)); if(ns) { pns = ns->name; pname++; @@ -250,7 +249,7 @@ if(pname) { DavNamespace *ns = dav_get_namespace_s( ctx, - sstrn(prefixed_name, pname-prefixed_name)); + cx_strn(prefixed_name, pname-prefixed_name)); if(ns) { *name = pname +1; return ns; @@ -260,7 +259,7 @@ } } else { *name = prefixed_name; - return dav_get_namespace_s(ctx, S("D")); + return dav_get_namespace_s(ctx, cx_str("D")); } } @@ -270,7 +269,7 @@ char *eff_url; curl_easy_getinfo(sn->handle, CURLINFO_EFFECTIVE_URL, &eff_url); if(eff_url) { - char *href = util_url_path(eff_url); + const char *href = util_url_path(eff_url); if(strcmp(href, resource->href)) { dav_session_free(sn, resource->href); resource->href = dav_session_strdup(sn, href); @@ -278,17 +277,17 @@ } } -DavResource* dav_get(DavSession *sn, char *path, char *properties) { +DavResource* dav_get(DavSession *sn, char *path, const char *properties) { CURL *handle = sn->handle; DavResource *resource = dav_resource_new(sn, path); util_set_url(sn, dav_resource_get_href(resource)); - UcxList *proplist = NULL; + CxList *proplist = NULL; if(properties) { - proplist = parse_properties_string(sn->context, sstr(properties)); + proplist = parse_properties_string(sn->context, cx_str(properties)); } - UcxBuffer *rqbuf = create_propfind_request(sn, proplist, "propfind", 0); - UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND); + CxBuffer *rqbuf = create_propfind_request(sn, proplist, "propfind", 0); + CxBuffer *rpbuf = cxBufferCreate(NULL, 4096, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); //fwrite(rqbuf->space, 1, rqbuf->size, stdout); //printf("\n"); @@ -310,30 +309,30 @@ resource = NULL; } - ucx_buffer_free(rqbuf); - ucx_buffer_free(rpbuf); - while(proplist) { - DavProperty *p = proplist->data; - free(p->name); - free(p); - UcxList *next = proplist->next; - free(proplist); - proplist = next; + cxBufferFree(rqbuf); + cxBufferFree(rpbuf); + + if(proplist) { + CxIterator i = cxListIterator(proplist); + cx_foreach(DavProperty*, p, i) { + free(p->name); + } + cxListDestroy(proplist); } return resource; } -int dav_propfind(DavSession *sn, DavResource *root, UcxBuffer *rqbuf) { +int dav_propfind(DavSession *sn, DavResource *root, CxBuffer *rqbuf) { // clean resource properties DavResourceData *data = root->data; - ucx_map_clear(data->properties); // TODO: free existing content + cxMapClear(data->properties); // TODO: free existing content CURL *handle = sn->handle; util_set_url(sn, dav_resource_get_href(root)); - UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND); + CxBuffer *rpbuf = cxBufferCreate(NULL, 4096, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); DavResource *resource = root; CURLcode ret = do_propfind_request(sn, rqbuf, rpbuf); long status = 0; @@ -350,41 +349,40 @@ dav_session_set_error(sn, ret, status); error = 1; } - ucx_buffer_free(rpbuf); + cxBufferFree(rpbuf); return error; } -UcxList* parse_properties_string(DavContext *context, sstr_t str) { - UcxList *proplist = NULL; - ssize_t nprops = 0; - sstr_t *props = sstrsplit(str, S(","), &nprops); - for(int i=0;i 0) { - sstr_t nspre = sstrsubsl(s, 0, nsname.ptr - s.ptr); + cxstring nspre = cx_strsubsl(s, 0, nsname.ptr - s.ptr); nsname.ptr++; nsname.length--; - DavProperty *dp = malloc(sizeof(DavProperty)); - sstr_t pre = sstrtrim(nspre); - dp->ns = dav_get_namespace_s(context, pre); - dp->name = sstrdup(nsname).ptr; - if(dp->ns && dp->name) { - proplist = ucx_list_append(proplist, dp); + DavProperty dp; + cxstring pre = cx_strtrim(nspre); + dp.ns = dav_get_namespace_s(context, pre); + dp.name = cx_strdup(nsname).ptr; + dp.value = NULL; + if(dp.ns && dp.name) { + cxListAdd(proplist, &dp); } else { - free(dp->name); - free(dp); + free(dp.name); } } - free(s.ptr); } - free(props); + return proplist; } DavResource* dav_query(DavSession *sn, char *query, ...) { - DavQLStatement *stmt = dav_parse_statement(sstr(query)); + DavQLStatement *stmt = dav_parse_statement(cx_str(query)); if(!stmt) { sn->error = DAV_ERROR; return NULL; diff -r a569148841ff -r efbd59642577 libidav/webdav.h --- a/libidav/webdav.h Sun Apr 16 14:12:24 2023 +0200 +++ b/libidav/webdav.h Fri Apr 21 21:25:32 2023 +0200 @@ -30,10 +30,11 @@ #define WEBDAV_H #include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include #include @@ -148,8 +149,8 @@ DavContext *context; CURL *handle; char *base_url; - UcxMempool *mp; - UcxMap *pathcache; + CxMempool *mp; + CxMap *pathcache; DavKey *key; void *locks; uint32_t flags; @@ -165,10 +166,10 @@ }; struct DavContext { - UcxMap *namespaces; - UcxMap *namespaceinfo; - UcxMap *keys; - UcxList *sessions; + CxMap *namespaces; + CxMap *namespaceinfo; + CxMap *keys; + CxList *sessions; DavProxy *http_proxy; DavProxy *https_proxy; }; @@ -238,10 +239,11 @@ void dav_context_destroy(DavContext *ctx); void dav_context_add_key(DavContext *context, DavKey *key); -DavKey* dav_context_get_key(DavContext *context, char *name); +DavKey* dav_context_get_key(DavContext *context, const char *name); int dav_add_namespace(DavContext *context, const char *prefix, const char *ns); DavNamespace* dav_get_namespace(DavContext *context, const char *prefix); +DavNamespace* dav_get_namespace_s(DavContext *context, cxstring prefix); int dav_enable_namespace_encryption(DavContext *context, const char *ns, DavBool encrypt); int dav_namespace_is_encrypted(DavContext *context, const char *ns); @@ -268,13 +270,13 @@ char* dav_session_strdup(DavSession *sn, const char *str); void dav_set_effective_href(DavSession *sn, DavResource *resource); -DavResource* dav_get(DavSession *sn, char *path, char *properties); +DavResource* dav_get(DavSession *sn, char *path, const char *properties); -UcxList* parse_properties_string(DavContext *context, sstr_t str); +CxList* parse_properties_string(DavContext *context, cxstring str); DavResource* dav_query(DavSession *sn, char *query, ...); -sstr_t dav_property_key(const char *ns, const char *name); +cxmutstr dav_property_key(const char *ns, const char *name); void dav_get_property_namespace_str( DavContext *ctx, char *prefixed_name, @@ -287,9 +289,9 @@ /* ------------------------ resource functions ------------------------ */ -DavResource* dav_resource_new(DavSession *sn, char *path); -DavResource* dav_resource_new_child(DavSession *sn, DavResource *parent, char *name); -DavResource* dav_resource_new_href(DavSession *sn, char *href); +DavResource* dav_resource_new(DavSession *sn, const char *path); +DavResource* dav_resource_new_child(DavSession *sn, DavResource *parent, const char *name); +DavResource* dav_resource_new_href(DavSession *sn, const char *href); void dav_resource_free(DavResource *res); void dav_resource_free_all(DavResource *res); @@ -347,7 +349,7 @@ int dav_outputstream_close(DavOutputStream *out); // private -int dav_propfind(DavSession *sn, DavResource *root, UcxBuffer *rqbuf); +int dav_propfind(DavSession *sn, DavResource *root, CxBuffer *rqbuf); /* --------------------------- DeltaV ---------------------------- */ diff -r a569148841ff -r efbd59642577 libidav/xml.c --- a/libidav/xml.c Sun Apr 16 14:12:24 2023 +0200 +++ b/libidav/xml.c Fri Apr 21 21:25:32 2023 +0200 @@ -30,7 +30,8 @@ #include #include -#include +#include +#include #include "xml.h" @@ -58,23 +59,27 @@ return NULL; } - UcxMempool *mp = sn->mp; + const CxAllocator *a = sn->mp->allocator; - ConvXmlElm *ce = malloc(sizeof(ConvXmlElm)); - ce->node = node; - ce->parent = NULL; - UcxList *stack = ucx_list_prepend(NULL, ce); + ConvXmlElm ce; + ce.node = node; + ce.parent = NULL; + CxList *stack = cxLinkedListCreate(cxDefaultAllocator, NULL, sizeof(ConvXmlElm)); + if(!stack) { + return NULL; + } + cxListInsert(stack, 0, &ce); DavXmlNode *ret = NULL; - while(stack) { - ConvXmlElm *c = stack->data; - stack = ucx_list_remove(stack, stack); + while(stack->size > 0) { + ConvXmlElm *c = cxListAt(stack, 0); + cxListRemove(stack, 0); xmlNode *n = c->node; DavXmlNode *prev = NULL; while(n) { - DavXmlNode *newxn = ucx_mempool_calloc(mp, 1, sizeof(DavXmlNode)); + DavXmlNode *newxn = cxCalloc(a, 1, sizeof(DavXmlNode)); if(!ret) { ret = newxn; } @@ -98,7 +103,7 @@ DavXmlAttr *newattr = NULL; DavXmlAttr *newattr_last = NULL; while(attr) { - DavXmlAttr *na = ucx_mempool_calloc(mp, 1, sizeof(DavXmlAttr)); + DavXmlAttr *na = cxCalloc(a, 1, sizeof(DavXmlAttr)); na->name = dav_session_strdup(sn, (char*)attr->name); if(attr->children && attr->children->type == XML_TEXT_NODE) { na->value = dav_session_strdup(sn, (char*)attr->children->content); @@ -115,13 +120,13 @@ newxn->attributes = newattr; if(n->children) { - ConvXmlElm *convc = malloc(sizeof(ConvXmlElm)); - convc->node = n->children; - convc->parent = newxn; - stack = ucx_list_prepend(stack, convc); + ConvXmlElm convc; + convc.node = n->children; + convc.parent = newxn; + cxListInsert(stack, 0, &convc); } } else if(newxn->type == DAV_XML_TEXT) { - sstr_t content = sstrdup_a(mp->allocator, sstr((char*)n->content)); + cxmutstr content = cx_strdup_a(a, cx_str((char*)n->content)); newxn->content = content.ptr; newxn->contentlength = content.length; } @@ -129,8 +134,6 @@ prev = newxn; n = n->next; } - - free(c); } return ret; @@ -161,19 +164,21 @@ } } -void dav_print_node(void *stream, write_func writef, UcxMap *nsmap, DavXmlNode *node) { +void dav_print_node(void *stream, cx_write_func writef, CxMap *nsmap, DavXmlNode *node) { while(node) { if(node->type == DAV_XML_ELEMENT) { char *tagend = node->children ? ">" : " />"; char *prefix = NULL; + char *prefix_fr = NULL; if(node->namespace) { - prefix = ucx_map_cstr_get(nsmap, node->namespace); + prefix = cxMapGet(nsmap, cx_hash_key_str(node->namespace)); if(!prefix) { - sstr_t newpre = ucx_sprintf("x%d", (int)nsmap->count+1); + cxmutstr newpre = cx_asprintf("x%d", (int)nsmap->size+1); // TODO: fix namespace declaration //ucx_map_cstr_put(nsmap, node->namespace, newpre.ptr); prefix = newpre.ptr; - ucx_fprintf( + prefix_fr = prefix; + cx_fprintf( stream, writef, "<%s:%s xmlns:%s=\"%s\"", @@ -182,15 +187,15 @@ prefix, node->namespace); } else { - ucx_fprintf(stream, writef, "<%s:%s", prefix, node->name); + cx_fprintf(stream, writef, "<%s:%s", prefix, node->name); } } else { - ucx_fprintf(stream, writef, "<%s", node->name); + cx_fprintf(stream, writef, "<%s", node->name); } DavXmlAttr *attr = node->attributes; while(attr) { - ucx_fprintf(stream, writef, " %s=\"%s\"", attr->name, attr->value); + cx_fprintf(stream, writef, " %s=\"%s\"", attr->name, attr->value); attr = attr->next; } writef(tagend, 1, strlen(tagend), stream); // end xml tag @@ -198,11 +203,15 @@ if(node->children) { dav_print_node(stream, writef, nsmap, node->children); if(prefix) { - ucx_fprintf(stream, writef, "", prefix, node->name); + cx_fprintf(stream, writef, "", prefix, node->name); } else { - ucx_fprintf(stream, writef, "", node->name); + cx_fprintf(stream, writef, "", node->name); } } + + if(prefix_fr) { + free(prefix_fr); + } } else if(node->type == DAV_XML_TEXT) { writef(node->content, 1, node->contentlength, stream); } @@ -241,33 +250,33 @@ } DavXmlNode* dav_text_node(DavSession *sn, const char *text) { - UcxMempool *mp = sn->mp; - DavXmlNode *newxn = ucx_mempool_calloc(mp, 1, sizeof(DavXmlNode)); + const CxAllocator *a = sn->mp->allocator; + DavXmlNode *newxn = cxCalloc(a, 1, sizeof(DavXmlNode)); newxn->type = DAV_XML_TEXT; - sstr_t content = scstrdup_a(mp->allocator, scstr(text)); + cxmutstr content = cx_strdup_a(a, cx_str(text)); newxn->content = content.ptr; newxn->contentlength = content.length; return newxn; } DavXmlNode* dav_text_element(DavSession *sn, const char *ns, const char *name, const char *text) { - UcxMempool *mp = sn->mp; - DavXmlNode *newelm = ucx_mempool_calloc(mp, 1, sizeof(DavXmlNode)); + const CxAllocator *a = sn->mp->allocator; + DavXmlNode *newelm = cxCalloc(a, 1, sizeof(DavXmlNode)); newelm->type = DAV_XML_ELEMENT; - newelm->namespace = scstrdup_a(mp->allocator, scstr(ns)).ptr; - newelm->name = scstrdup_a(mp->allocator, scstr(name)).ptr; + newelm->namespace = cx_strdup_a(a, cx_str(ns)).ptr; + newelm->name = cx_strdup_a(a, cx_str(name)).ptr; newelm->children = dav_text_node(sn, text); return newelm; } -static void dav_free_xml_node_a(UcxAllocator *a, DavXmlNode *node) { - if(node->name) alfree(a, node->name); - if(node->namespace) alfree(a, node->namespace); - if(node->content) alfree(a, node->content); +static void dav_free_xml_node_a(const CxAllocator *a, DavXmlNode *node) { + if(node->name) cxFree(a, node->name); + if(node->namespace) cxFree(a, node->namespace); + if(node->content) cxFree(a, node->content); DavXmlAttr *attr = node->attributes; while(attr) { - if(attr->name) alfree(a, attr->name); - if(attr->value) alfree(a, attr->value); + if(attr->name) cxFree(a, attr->name); + if(attr->value) cxFree(a, attr->value); attr = attr->next; } DavXmlNode *children = node->children; @@ -276,7 +285,7 @@ dav_free_xml_node_a(a, children); children = next_ch; } - alfree(a, node); + cxFree(a, node); } void dav_free_xml_node_sn(DavSession *sn, DavXmlNode *node) { @@ -284,7 +293,7 @@ } void dav_free_xml_node(DavXmlNode *node) { - dav_free_xml_node_a(ucx_default_allocator(), node); + dav_free_xml_node_a(cxDefaultAllocator, node); } DavXmlAttr* dav_copy_xml_attr(DavXmlAttr *attr) { @@ -362,7 +371,7 @@ DavXmlNode* dav_xml_createtextnode(const char *text) { DavXmlNode *node = calloc(1, sizeof(DavXmlNode)); node->type = DAV_XML_TEXT; - sstr_t content = sstrdup(sstr((char*)text)); + cxmutstr content = cx_strdup(cx_str((char*)text)); node->content = content.ptr; node->contentlength = content.length; return node; diff -r a569148841ff -r efbd59642577 libidav/xml.h --- a/libidav/xml.h Sun Apr 16 14:12:24 2023 +0200 +++ b/libidav/xml.h Fri Apr 21 21:25:32 2023 +0200 @@ -38,7 +38,7 @@ void dav_print_xml(DavXmlNode *node); -void dav_print_node(void *stream, write_func writef, UcxMap *nsmap, DavXmlNode *node); +void dav_print_node(void *stream, cx_write_func writef, CxMap *nsmap, DavXmlNode *node); #ifdef __cplusplus diff -r a569148841ff -r efbd59642577 test/Makefile --- a/test/Makefile Sun Apr 16 14:12:24 2023 +0200 +++ b/test/Makefile Fri Apr 21 21:25:32 2023 +0200 @@ -31,6 +31,7 @@ include ../config.mk TEST_SRC = main.c +TEST_SRC += test.c TEST_SRC += base64.c TEST_SRC += crypto.c @@ -41,9 +42,9 @@ all: $(TEST_TARGET) -$(TEST_TARGET): $(TEST_OBJ) ../build/libidav$(LIB_EXT) +$(TEST_TARGET): $(TEST_OBJ) ../build/lib/libidav$(LIB_EXT) $(LD) -o $(TEST_TARGET) $(TEST_OBJ) \ - ../build/libidav$(LIB_EXT) ../build/libucx$(LIB_EXT) \ + ../build/lib/libidav$(LIB_EXT) ../build/lib/libucx$(LIB_EXT) \ $(LDFLAGS) $(DAV_LDFLAGS) ../build/test/%$(OBJ_EXT): %.c diff -r a569148841ff -r efbd59642577 test/base64.c --- a/test/base64.c Sun Apr 16 14:12:24 2023 +0200 +++ b/test/base64.c Fri Apr 21 21:25:32 2023 +0200 @@ -32,7 +32,7 @@ #include "base64.h" -#include +#include #include UCX_TEST(test_util_base64decode) { diff -r a569148841ff -r efbd59642577 test/base64.h --- a/test/base64.h Sun Apr 16 14:12:24 2023 +0200 +++ b/test/base64.h Fri Apr 21 21:25:32 2023 +0200 @@ -29,7 +29,7 @@ #ifndef BASE64_H #define BASE64_H -#include +#include "test.h" #ifdef __cplusplus extern "C" { diff -r a569148841ff -r efbd59642577 test/crypto.c --- a/test/crypto.c Sun Apr 16 14:12:24 2023 +0200 +++ b/test/crypto.c Fri Apr 21 21:25:32 2023 +0200 @@ -33,9 +33,9 @@ #include "crypto.h" -#include -#include -#include +#include +#include +#include #include #include @@ -254,11 +254,11 @@ DavKey *key = i < 16 ? &keys256[i] : &keys128[i%16]; for(int j=0;j<20;j++) { - UcxBuffer *content = ucx_buffer_new(NULL, 256, UCX_BUFFER_AUTOEXTEND); - ucx_buffer_puts(content, strings[j]); + CxBuffer *content = cxBufferCreate(NULL, 256, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); + cxBufferPutString(content, strings[j]); content->pos = 0; - UcxBuffer *enc = aes_encrypt_buffer(content, key); + CxBuffer *enc = aes_encrypt_buffer(content, key); UCX_TEST_ASSERT(enc->size >= content->size + 16, "aes_encrypt_buffer failed"); char *base64 = util_base64encode(enc->space, enc->size); @@ -269,14 +269,14 @@ UCX_TEST_ASSERT(plainlen == content->size, "aes_decrypt: wrong length"); UCX_TEST_ASSERT(!memcmp(plain, content->space, plainlen), "aes_decrypt: wrong content"); - UcxBuffer *dec = aes_decrypt_buffer(enc, key); + CxBuffer *dec = aes_decrypt_buffer(enc, key); UCX_TEST_ASSERT(dec->size == content->size, "aes_decrypt_buffer failed"); UCX_TEST_ASSERT(!memcmp(content->space, dec->space, dec->size), "decrypted buffer has wrong content"); - ucx_buffer_free(content); - ucx_buffer_free(enc); - ucx_buffer_free(dec); + cxBufferFree(content); + cxBufferFree(enc); + cxBufferFree(dec); free(base64); free(plain); } @@ -286,9 +286,9 @@ } UCX_TEST(test_crypto_stream) { - UcxBuffer *data = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND); - UcxBuffer *cbuf = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND); - UcxBuffer *pbuf = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND); + CxBuffer *data = cxBufferCreate(NULL, 1024, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); + CxBuffer *cbuf = cxBufferCreate(NULL, 1024, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); + CxBuffer *pbuf = cxBufferCreate(NULL, 1024, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); UCX_TEST_BEGIN; @@ -298,23 +298,23 @@ data->pos = 0; data->size = 0; size_t slen = strlen(strings[j]); - ucx_buffer_write(strings[j], 1, slen, data); - ucx_buffer_seek(data, 0, SEEK_SET); + cxBufferWrite(strings[j], 1, slen, data); + cxBufferSeek(data, 0, SEEK_SET); cbuf->pos = 0; cbuf->size = 0; pbuf->pos = 0; pbuf->size = 0; - AESEncrypter *enc = aes_encrypter_new(key, data, (dav_read_func)ucx_buffer_read, NULL); + AESEncrypter *enc = aes_encrypter_new(key, data, (dav_read_func)cxBufferRead, NULL); char buf[1024]; size_t r = 0; while((r = aes_read(buf, 1, 1024, enc)) != 0) { - ucx_buffer_write(buf, 1, r, cbuf); + cxBufferWrite(buf, 1, r, cbuf); } aes_encrypter_close(enc); - AESDecrypter *dec = aes_decrypter_new(key, pbuf, (dav_write_func)ucx_buffer_write); + AESDecrypter *dec = aes_decrypter_new(key, pbuf, (dav_write_func)cxBufferWrite); aes_write(cbuf->space, 1, cbuf->pos, dec); aes_decrypter_shutdown(dec); aes_decrypter_close(dec); @@ -323,8 +323,8 @@ UCX_TEST_ASSERT(!memcmp(strings[j], pbuf->space, slen), "wrong content after enc-dec"); data->pos = 0; - UcxBuffer *enc2 = aes_encrypt_buffer(data, key); - UcxBuffer *dec2 = aes_decrypt_buffer(enc2, key); + CxBuffer *enc2 = aes_encrypt_buffer(data, key); + CxBuffer *dec2 = aes_decrypt_buffer(enc2, key); UCX_TEST_ASSERT(dec2->size == data->size, "dec2 has wrong size"); UCX_TEST_ASSERT(!memcmp(strings[j], dec2->space, dec2->size), "dec2 has wrong content"); diff -r a569148841ff -r efbd59642577 test/crypto.h --- a/test/crypto.h Sun Apr 16 14:12:24 2023 +0200 +++ b/test/crypto.h Fri Apr 21 21:25:32 2023 +0200 @@ -29,7 +29,7 @@ #ifndef CRYPTO_H #define CRYPTO_H -#include +#include "test.h" #ifdef __cplusplus extern "C" { diff -r a569148841ff -r efbd59642577 test/main.c --- a/test/main.c Sun Apr 16 14:12:24 2023 +0200 +++ b/test/main.c Fri Apr 21 21:25:32 2023 +0200 @@ -29,7 +29,7 @@ #include #include -#include +#include "test.h" #include "base64.h" #include "crypto.h" diff -r a569148841ff -r efbd59642577 test/test.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/test.c Fri Apr 21 21:25:32 2023 +0200 @@ -0,0 +1,91 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 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 "test.h" + +UcxTestSuite* ucx_test_suite_new() { + UcxTestSuite* suite = (UcxTestSuite*) malloc(sizeof(UcxTestSuite)); + if (suite != NULL) { + suite->success = 0; + suite->failure = 0; + suite->tests = NULL; + } + + return suite; +} + +void ucx_test_suite_free(UcxTestSuite* suite) { + UcxTestList *l = suite->tests; + while (l != NULL) { + UcxTestList *e = l; + l = l->next; + free(e); + } + free(suite); +} + +int ucx_test_register(UcxTestSuite* suite, UcxTest test) { + if (suite->tests) { + UcxTestList *newelem = (UcxTestList*) malloc(sizeof(UcxTestList)); + if (newelem) { + newelem->test = test; + newelem->next = NULL; + + UcxTestList *last = suite->tests; + while (last->next) { + last = last->next; + } + last->next = newelem; + + return EXIT_SUCCESS; + } else { + return EXIT_FAILURE; + } + } else { + suite->tests = (UcxTestList*) malloc(sizeof(UcxTestList)); + if (suite->tests) { + suite->tests->test = test; + suite->tests->next = NULL; + + return EXIT_SUCCESS; + } else { + return EXIT_FAILURE; + } + } +} + +void ucx_test_run(UcxTestSuite* suite, FILE* output) { + suite->success = 0; + suite->failure = 0; + for (UcxTestList* elem = suite->tests ; elem ; elem = elem->next) { + elem->test(suite, output); + } + fwrite("\nAll test completed.\n", 1, 21, output); + fprintf(output, " Total: %u\n Success: %u\n Failure: %u\n", + suite->success+suite->failure, suite->success, suite->failure); +} diff -r a569148841ff -r efbd59642577 test/test.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/test.h Fri Apr 21 21:25:32 2023 +0200 @@ -0,0 +1,241 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 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. + */ + +/** + * @file: test.h + * + * UCX Test Framework. + * + * Usage of this test framework: + * + * **** IN HEADER FILE: **** + * + *
      + * UCX_TEST(function_name);
      + * UCX_TEST_SUBROUTINE(subroutine_name, paramlist); // optional
      + * 
      + * + * **** IN SOURCE FILE: **** + *
      + * UCX_TEST_SUBROUTINE(subroutine_name, paramlist) {
      + *   // tests with UCX_TEST_ASSERT()
      + * }
      + * 
      + * UCX_TEST(function_name) {
      + *   // memory allocation and other stuff here
      + *   #UCX_TEST_BEGIN
      + *   // tests with UCX_TEST_ASSERT() and/or
      + *   // calls with UCX_TEST_CALL_SUBROUTINE() here
      + *   #UCX_TEST_END
      + *   // cleanup of memory here
      + * }
      + * 
      + * + * Note: if a test fails, a longjump is performed + * back to the #UCX_TEST_BEGIN macro! + * + * Attention: Do not call own functions within a test, that use + * UCX_TEST_ASSERT() macros and are not defined by using UCX_TEST_SUBROUTINE(). + * + * + * @author Mike Becker + * @author Olaf Wintermann + * + */ + +#ifndef UCX_TEST_H +#define UCX_TEST_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef __FUNCTION__ + +/** + * Alias for the __func__ preprocessor macro. + * Some compilers use __func__ and others use __FUNCTION__. + * We use __FUNCTION__ so we define it for those compilers which use + * __func__. + */ +#define __FUNCTION__ __func__ +#endif + +/** Type for the UcxTestSuite. */ +typedef struct UcxTestSuite UcxTestSuite; + +/** Pointer to a test function. */ +typedef void(*UcxTest)(UcxTestSuite*,FILE*); + +/** Type for the internal list of test cases. */ +typedef struct UcxTestList UcxTestList; + +/** Structure for the internal list of test cases. */ +struct UcxTestList { + + /** Test case. */ + UcxTest test; + + /** Pointer to the next list element. */ + UcxTestList *next; +}; + +/** + * A test suite containing multiple test cases. + */ +struct UcxTestSuite { + + /** The number of successful tests after the suite has been run. */ + unsigned int success; + + /** The number of failed tests after the suite has been run. */ + unsigned int failure; + + /** + * Internal list of test cases. + * Use ucx_test_register() to add tests to this list. + */ + UcxTestList *tests; +}; + +/** + * Creates a new test suite. + * @return a new test suite + */ +UcxTestSuite* ucx_test_suite_new(); + +/** + * Destroys a test suite. + * @param suite the test suite to destroy + */ +void ucx_test_suite_free(UcxTestSuite* suite); + +/** + * Registers a test function with the specified test suite. + * + * @param suite the suite, the test function shall be added to + * @param test the test function to register + * @return EXIT_SUCCESS on success or + * EXIT_FAILURE on failure + */ +int ucx_test_register(UcxTestSuite* suite, UcxTest test); + +/** + * Runs a test suite and writes the test log to the specified stream. + * @param suite the test suite to run + * @param outstream the stream the log shall be written to + */ +void ucx_test_run(UcxTestSuite* suite, FILE* outstream); + +/** + * Macro for a #UcxTest function header. + * + * Use this macro to declare and/or define a #UcxTest function. + * + * @param name the name of the test function + */ +#define UCX_TEST(name) void name(UcxTestSuite* _suite_,FILE *_output_) + +/** + * Marks the begin of a test. + * Note: Any UCX_TEST_ASSERT() calls must be performed after + * #UCX_TEST_BEGIN. + * + * @see #UCX_TEST_END + */ +#define UCX_TEST_BEGIN fwrite("Running ", 1, 8, _output_);\ + fwrite(__FUNCTION__, 1, strlen(__FUNCTION__), _output_);\ + fwrite("... ", 1, 4, _output_);\ + jmp_buf _env_; \ + if (!setjmp(_env_)) { + +/** + * Checks a test assertion. + * If the assertion is correct, the test carries on. If the assertion is not + * correct, the specified message (terminated by a dot and a line break) is + * written to the test suites output stream. + * @param condition the condition to check + * @param message the message that shall be printed out on failure + */ +#define UCX_TEST_ASSERT(condition,message) if (!(condition)) { \ + fwrite(message".\n", 1, 2+strlen(message), _output_); \ + _suite_->failure++; \ + longjmp(_env_, 1);\ + } + +/** + * Macro for a test subroutine function header. + * + * Use this to declare and/or define a subroutine that can be called by using + * UCX_TEST_CALL_SUBROUTINE(). + * + * @param name the name of the subroutine + * @param ... the parameter list + * + * @see UCX_TEST_CALL_SUBROUTINE() + */ +#define UCX_TEST_SUBROUTINE(name,...) void name(UcxTestSuite* _suite_,\ + FILE *_output_, jmp_buf _env_, __VA_ARGS__) + +/** + * Macro for calling a test subroutine. + * + * Subroutines declared with UCX_TEST_SUBROUTINE() can be called by using this + * macro. + * + * Note: You may only call subroutines within a #UCX_TEST_BEGIN- + * #UCX_TEST_END-block. + * + * @param name the name of the subroutine + * @param ... the argument list + * + * @see UCX_TEST_SUBROUTINE() + */ +#define UCX_TEST_CALL_SUBROUTINE(name,...) \ + name(_suite_,_output_,_env_,__VA_ARGS__); + +/** + * Marks the end of a test. + * Note: Any UCX_TEST_ASSERT() calls must be performed before + * #UCX_TEST_END. + * + * @see #UCX_TEST_BEGIN + */ +#define UCX_TEST_END fwrite("success.\n", 1, 9, _output_); _suite_->success++;} + +#ifdef __cplusplus +} +#endif + +#endif /* UCX_TEST_H */ + diff -r a569148841ff -r efbd59642577 ucx/Makefile --- a/ucx/Makefile Sun Apr 16 14:12:24 2023 +0200 +++ b/ucx/Makefile Fri Apr 21 21:25:32 2023 +0200 @@ -1,7 +1,7 @@ # # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. # -# Copyright 2016 Olaf Wintermann. All rights reserved. +# Copyright 2013 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: @@ -26,34 +26,35 @@ # POSSIBILITY OF SUCH DAMAGE. # -BUILD_ROOT = .. - +BUILD_ROOT = ../ include ../config.mk # list of source files -SRC = utils.c +SRC = allocator.c +SRC += array_list.c +SRC += basic_mempool.c +SRC += buffer.c +SRC += compare.c +SRC += hash_key.c +SRC += hash_map.c +SRC += linked_list.c SRC += list.c -SRC += map.c -SRC += avl.c -SRC += properties.c -SRC += mempool.c +SRC += printf.c SRC += string.c -SRC += test.c -SRC += allocator.c -SRC += logging.c -SRC += buffer.c -SRC += stack.c -SRC += ucx.c +SRC += utils.c + +OBJ = $(SRC:%.c=../build/ucx/%$(OBJ_EXT)) + +UCX_LIB = ../build/lib/libucx$(LIB_EXT) -OBJ = $(SRC:%.c=../build/ucx/%$(OBJ_EXT)) +all: ../build/ucx $(UCX_LIB) -all: ../build/ucx ../build/libucx$(LIB_EXT) +$(UCX_LIB): $(OBJ) + $(AR) $(ARFLAGS) $(UCX_LIB) $(OBJ) -../build/libucx$(LIB_EXT): $(OBJ) - $(AR) $(ARFLAGS) $(AOFLAGS)../build/libucx$(LIB_EXT) $(OBJ) +../build/ucx: + mkdir -p ../build/ucx ../build/ucx/%$(OBJ_EXT): %.c - $(CC) $(CFLAGS) -c -o $@ $< + $(CC) $(CFLAGS) -o $@ -c $< -../build/ucx: - $(MKDIR) $(MKDIRFLAGS) ../build/ucx diff -r a569148841ff -r efbd59642577 ucx/README --- a/ucx/README Sun Apr 16 14:12:24 2023 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -UCX is a library for common data structures, algorithms and string functions. - -More informations at: https://develop.uap-core.de/ucx/ - diff -r a569148841ff -r efbd59642577 ucx/allocator.c --- a/ucx/allocator.c Sun Apr 16 14:12:24 2023 +0200 +++ b/ucx/allocator.c Fri Apr 21 21:25:32 2023 +0200 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. + * 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: @@ -26,35 +26,97 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include "ucx/allocator.h" - -#include +#include "cx/allocator.h" -static UcxAllocator default_allocator = { - NULL, - ucx_default_malloc, - ucx_default_calloc, - ucx_default_realloc, - ucx_default_free -}; - -UcxAllocator *ucx_default_allocator() { - UcxAllocator *allocator = &default_allocator; - return allocator; -} - -void *ucx_default_malloc(void *ignore, size_t n) { +__attribute__((__malloc__, __alloc_size__(2))) +static void *cx_malloc_stdlib( + __attribute__((__unused__)) void *d, + size_t n +) { return malloc(n); } -void *ucx_default_calloc(void *ignore, size_t n, size_t size) { - return calloc(n, size); +__attribute__((__warn_unused_result__, __alloc_size__(3))) +static void *cx_realloc_stdlib( + __attribute__((__unused__)) void *d, + void *mem, + size_t n +) { + return realloc(mem, n); +} + +__attribute__((__malloc__, __alloc_size__(2, 3))) +static void *cx_calloc_stdlib( + __attribute__((__unused__)) void *d, + size_t nelem, + size_t n +) { + return calloc(nelem, n); +} + +__attribute__((__nonnull__)) +static void cx_free_stdlib( + __attribute__((__unused__)) void *d, + void *mem +) { + free(mem); } -void *ucx_default_realloc(void *ignore, void *data, size_t n) { - return realloc(data, n); +static cx_allocator_class cx_default_allocator_class = { + cx_malloc_stdlib, + cx_realloc_stdlib, + cx_calloc_stdlib, + cx_free_stdlib +}; + +struct cx_allocator_s cx_default_allocator = { + &cx_default_allocator_class, + NULL +}; +CxAllocator *cxDefaultAllocator = &cx_default_allocator; + +// IMPLEMENTATION OF HIGH LEVEL API + +void *cxMalloc( + CxAllocator const *allocator, + size_t n +) { + return allocator->cl->malloc(allocator->data, n); } -void ucx_default_free(void *ignore, void *data) { - free(data); +void *cxRealloc( + CxAllocator const *allocator, + void *mem, + size_t n +) { + return allocator->cl->realloc(allocator->data, mem, n); } + +int cxReallocate( + CxAllocator const *allocator, + void **mem, + size_t n +) { + void *nmem = allocator->cl->realloc(allocator->data, *mem, n); + if (nmem == NULL) { + return 1; + } else { + *mem = nmem; + return 0; + } +} + +void *cxCalloc( + CxAllocator const *allocator, + size_t nelem, + size_t n +) { + return allocator->cl->calloc(allocator->data, nelem, n); +} + +void cxFree( + CxAllocator const *allocator, + void *mem +) { + allocator->cl->free(allocator->data, mem); +} diff -r a569148841ff -r efbd59642577 ucx/array_list.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/array_list.c Fri Apr 21 21:25:32 2023 +0200 @@ -0,0 +1,529 @@ +/* + * 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/array_list.h" +#include +#include + +// LOW LEVEL ARRAY LIST FUNCTIONS + +enum cx_array_copy_result cx_array_copy( + void **target, + size_t *size, + size_t *capacity, + size_t index, + void const *src, + size_t elem_size, + size_t elem_count, + struct cx_array_reallocator_s *reallocator +) { + // assert pointers + assert(target != NULL); + assert(size != NULL); + assert(src != NULL); + + // determine capacity + size_t cap = capacity == NULL ? *size : *capacity; + + // check if resize is required + size_t minsize = index + elem_count; + size_t newsize = *size < minsize ? minsize : *size; + bool needrealloc = newsize > cap; + + // reallocate if possible + if (needrealloc) { + // a reallocator and a capacity variable must be available + if (reallocator == NULL || capacity == NULL) { + return CX_ARRAY_COPY_REALLOC_NOT_SUPPORTED; + } + + // check, if we need to repair the src pointer + uintptr_t targetaddr = (uintptr_t) *target; + uintptr_t srcaddr = (uintptr_t) src; + bool repairsrc = targetaddr <= srcaddr + && srcaddr < targetaddr + cap * elem_size; + + // calculate new capacity (next number divisible by 16) + cap = newsize - (newsize % 16) + 16; + assert(cap > newsize); + + // perform reallocation + void *newmem = reallocator->realloc( + *target, cap, elem_size, reallocator + ); + if (newmem == NULL) { + return CX_ARRAY_COPY_REALLOC_FAILED; + } + + // repair src pointer, if necessary + if (repairsrc) { + src = ((char *) newmem) + (srcaddr - targetaddr); + } + + // store new pointer and capacity + *target = newmem; + *capacity = cap; + } + + // determine target pointer + char *start = *target; + start += index * elem_size; + + // copy elements and set new size + memmove(start, src, elem_count * elem_size); + *size = newsize; + + // return successfully + return CX_ARRAY_COPY_SUCCESS; +} + +#ifndef CX_ARRAY_SWAP_SBO_SIZE +#define CX_ARRAY_SWAP_SBO_SIZE 512 +#endif + +void cx_array_swap( + void *arr, + size_t elem_size, + size_t idx1, + size_t idx2 +) { + assert(arr != NULL); + + // short circuit + if (idx1 == idx2) return; + + char sbo_mem[CX_ARRAY_SWAP_SBO_SIZE]; + void *tmp; + + // decide if we can use the local buffer + if (elem_size > CX_ARRAY_SWAP_SBO_SIZE) { + tmp = malloc(elem_size); + // we don't want to enforce error handling + if (tmp == NULL) abort(); + } else { + tmp = sbo_mem; + } + + // calculate memory locations + char *left = arr, *right = arr; + left += idx1 * elem_size; + right += idx2 * elem_size; + + // three-way swap + memcpy(tmp, left, elem_size); + memcpy(left, right, elem_size); + memcpy(right, tmp, elem_size); + + // free dynamic memory, if it was needed + if (tmp != sbo_mem) { + free(tmp); + } +} + +// HIGH LEVEL ARRAY LIST FUNCTIONS + +typedef struct { + struct cx_list_s base; + void *data; + size_t capacity; + struct cx_array_reallocator_s reallocator; +} cx_array_list; + +static void *cx_arl_realloc( + void *array, + size_t capacity, + size_t elem_size, + struct cx_array_reallocator_s *alloc +) { + // retrieve the pointer to the list allocator + CxAllocator const *al = alloc->ptr1; + + // use the list allocator to reallocate the memory + return cxRealloc(al, array, capacity * elem_size); +} + +static void cx_arl_destructor(struct cx_list_s *list) { + cx_array_list *arl = (cx_array_list *) list; + cxFree(list->allocator, arl->data); +} + +static size_t cx_arl_insert_array( + struct cx_list_s *list, + size_t index, + void const *array, + size_t n +) { + // out of bounds and special case check + if (index > list->size || n == 0) return 0; + + // get a correctly typed pointer to the list + cx_array_list *arl = (cx_array_list *) list; + + // do we need to move some elements? + if (index < list->size) { + char const *first_to_move = (char const *) arl->data; + first_to_move += index * list->item_size; + size_t elems_to_move = list->size - index; + size_t start_of_moved = index + n; + + if (CX_ARRAY_COPY_SUCCESS != cx_array_copy( + &arl->data, + &list->size, + &arl->capacity, + start_of_moved, + first_to_move, + list->item_size, + elems_to_move, + &arl->reallocator + )) { + // if moving existing elems is unsuccessful, abort + return 0; + } + } + + // note that if we had to move the elements, the following operation + // is guaranteed to succeed, because we have the memory already allocated + // therefore, it is impossible to leave this function with an invalid array + + // place the new elements + if (CX_ARRAY_COPY_SUCCESS == cx_array_copy( + &arl->data, + &list->size, + &arl->capacity, + index, + array, + list->item_size, + n, + &arl->reallocator + )) { + return n; + } else { + // array list implementation is "all or nothing" + return 0; + } +} + +static int cx_arl_insert_element( + struct cx_list_s *list, + size_t index, + void const *element +) { + return 1 != cx_arl_insert_array(list, index, element, 1); +} + +static int cx_arl_insert_iter( + struct cx_mut_iterator_s *iter, + void const *elem, + int prepend +) { + struct cx_list_s *list = iter->src_handle; + if (iter->index < list->size) { + int result = cx_arl_insert_element( + list, + iter->index + 1 - prepend, + elem + ); + if (result == 0 && prepend != 0) { + iter->index++; + iter->elem_handle = ((char *) iter->elem_handle) + list->item_size; + } + return result; + } else { + int result = cx_arl_insert_element(list, list->size, elem); + iter->index = list->size; + return result; + } +} + +static int cx_arl_remove( + struct cx_list_s *list, + size_t index +) { + cx_array_list *arl = (cx_array_list *) list; + + // out-of-bounds check + if (index >= list->size) { + return 1; + } + + // content destruction + cx_invoke_destructor(list, ((char *) arl->data) + index * list->item_size); + + // short-circuit removal of last element + if (index == list->size - 1) { + list->size--; + return 0; + } + + // just move the elements starting at index to the left + int result = cx_array_copy( + &arl->data, + &list->size, + &arl->capacity, + index, + ((char *) arl->data) + (index + 1) * list->item_size, + list->item_size, + list->size - index - 1, + &arl->reallocator + ); + if (result == 0) { + // decrease the size + list->size--; + } + return result; +} + +static void cx_arl_clear(struct cx_list_s *list) { + if (list->size == 0) return; + + cx_array_list *arl = (cx_array_list *) list; + char *ptr = arl->data; + + if (list->simple_destructor) { + for (size_t i = 0; i < list->size; i++) { + cx_invoke_simple_destructor(list, ptr); + ptr += list->item_size; + } + } + if (list->advanced_destructor) { + for (size_t i = 0; i < list->size; i++) { + cx_invoke_advanced_destructor(list, ptr); + ptr += list->item_size; + } + } + + memset(arl->data, 0, list->size * list->item_size); + list->size = 0; +} + +static int cx_arl_swap( + struct cx_list_s *list, + size_t i, + size_t j +) { + if (i >= list->size || j >= list->size) return 1; + cx_array_list *arl = (cx_array_list *) list; + cx_array_swap(arl->data, list->item_size, i, j); + return 0; +} + +static void *cx_arl_at( + struct cx_list_s const *list, + size_t index +) { + if (index < list->size) { + cx_array_list const *arl = (cx_array_list const *) list; + char *space = arl->data; + return space + index * list->item_size; + } else { + return NULL; + } +} + +static size_t cx_arl_find( + struct cx_list_s const *list, + void const *elem +) { + assert(list->cmpfunc != NULL); + char *cur = ((cx_array_list const *) list)->data; + + for (size_t i = 0; i < list->size; i++) { + if (0 == list->cmpfunc(elem, cur)) { + return i; + } + cur += list->item_size; + } + + return list->size; +} + +static void cx_arl_sort(struct cx_list_s *list) { + assert(list->cmpfunc != NULL); + qsort(((cx_array_list *) list)->data, + list->size, + list->item_size, + list->cmpfunc + ); +} + +static int cx_arl_compare( + struct cx_list_s const *list, + struct cx_list_s const *other +) { + assert(list->cmpfunc != NULL); + if (list->size == other->size) { + char const *left = ((cx_array_list const *) list)->data; + char const *right = ((cx_array_list const *) other)->data; + for (size_t i = 0; i < list->size; i++) { + int d = list->cmpfunc(left, right); + if (d != 0) { + return d; + } + left += list->item_size; + right += other->item_size; + } + return 0; + } else { + return list->size < other->size ? -1 : 1; + } +} + +static void cx_arl_reverse(struct cx_list_s *list) { + if (list->size < 2) return; + void *data = ((cx_array_list const *) list)->data; + size_t half = list->size / 2; + for (size_t i = 0; i < half; i++) { + cx_array_swap(data, list->item_size, i, list->size - 1 - i); + } +} + +static bool cx_arl_iter_valid(void const *it) { + struct cx_iterator_s const *iter = it; + struct cx_list_s const *list = iter->src_handle; + return iter->index < list->size; +} + +static void *cx_arl_iter_current(void const *it) { + struct cx_iterator_s const *iter = it; + return iter->elem_handle; +} + +static void cx_arl_iter_next(void *it) { + struct cx_iterator_base_s *itbase = it; + if (itbase->remove) { + struct cx_mut_iterator_s *iter = it; + itbase->remove = false; + cx_arl_remove(iter->src_handle, iter->index); + } else { + struct cx_iterator_s *iter = it; + iter->index++; + iter->elem_handle = + ((char *) iter->elem_handle) + + ((struct cx_list_s const *) iter->src_handle)->item_size; + } +} + +static void cx_arl_iter_prev(void *it) { + struct cx_iterator_base_s *itbase = it; + struct cx_mut_iterator_s *iter = it; + cx_array_list *const list = iter->src_handle; + if (itbase->remove) { + itbase->remove = false; + cx_arl_remove(iter->src_handle, iter->index); + } + iter->index--; + if (iter->index < list->base.size) { + iter->elem_handle = ((char *) list->data) + + iter->index * list->base.item_size; + } +} + +static bool cx_arl_iter_flag_rm(void *it) { + struct cx_iterator_base_s *iter = it; + if (iter->mutating) { + iter->remove = true; + return true; + } else { + return false; + } +} + +static struct cx_iterator_s cx_arl_iterator( + struct cx_list_s const *list, + size_t index, + bool backwards +) { + struct cx_iterator_s iter; + + iter.index = index; + iter.src_handle = list; + iter.elem_handle = cx_arl_at(list, index); + iter.base.valid = cx_arl_iter_valid; + iter.base.current = cx_arl_iter_current; + iter.base.next = backwards ? cx_arl_iter_prev : cx_arl_iter_next; + iter.base.flag_removal = cx_arl_iter_flag_rm; + iter.base.remove = false; + iter.base.mutating = false; + + return iter; +} + +static cx_list_class cx_array_list_class = { + cx_arl_destructor, + cx_arl_insert_element, + cx_arl_insert_array, + cx_arl_insert_iter, + cx_arl_remove, + cx_arl_clear, + cx_arl_swap, + cx_arl_at, + cx_arl_find, + cx_arl_sort, + cx_arl_compare, + cx_arl_reverse, + cx_arl_iterator, +}; + +CxList *cxArrayListCreate( + CxAllocator const *allocator, + cx_compare_func comparator, + size_t item_size, + size_t initial_capacity +) { + if (allocator == NULL) { + allocator = cxDefaultAllocator; + } + + 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.allocator = allocator; + list->base.cmpfunc = comparator; + list->capacity = initial_capacity; + + if (item_size > 0) { + list->base.item_size = item_size; + } else { + item_size = sizeof(void *); + cxListStorePointers((CxList *) list); + } + + // allocate the array after the real item_size is known + list->data = cxCalloc(allocator, initial_capacity, item_size); + if (list->data == NULL) { + cxFree(allocator, list); + return NULL; + } + + // configure the reallocator + list->reallocator.realloc = cx_arl_realloc; + list->reallocator.ptr1 = (void *) allocator; + + return (CxList *) list; +} diff -r a569148841ff -r efbd59642577 ucx/avl.c --- a/ucx/avl.c Sun Apr 16 14:12:24 2023 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,373 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 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 "ucx/avl.h" - -#include - -#define ptrcast(ptr) ((void*)(ptr)) -#define alloc_tree(al) (UcxAVLTree*) almalloc((al), sizeof(UcxAVLTree)) -#define alloc_node(al) (UcxAVLNode*) almalloc((al), sizeof(UcxAVLNode)) - -static void ucx_avl_connect(UcxAVLTree *tree, - UcxAVLNode *node, UcxAVLNode *child, intptr_t nullkey) { - if (child) { - child->parent = node; - } - // if child is NULL, nullkey decides if left or right pointer is cleared - if (tree->cmpfunc( - ptrcast(child ? child->key : nullkey), - ptrcast(node->key), tree->userdata) > 0) { - node->right = child; - } else { - node->left = child; - } - size_t lh = node->left ? node->left->height : 0; - size_t rh = node->right ? node->right->height : 0; - node->height = 1 + (lh > rh ? lh : rh); -} - -#define avlheight(node) ((node) ? (node)->height : 0) - -static UcxAVLNode* avl_rotright(UcxAVLTree *tree, UcxAVLNode *l0) { - UcxAVLNode *p = l0->parent; - UcxAVLNode *l1 = l0->left; - if (p) { - ucx_avl_connect(tree, p, l1, 0); - } else { - l1->parent = NULL; - } - ucx_avl_connect(tree, l0, l1->right, l1->key); - ucx_avl_connect(tree, l1, l0, 0); - return l1; -} - -static UcxAVLNode* avl_rotleft(UcxAVLTree *tree, UcxAVLNode *l0) { - UcxAVLNode *p = l0->parent; - UcxAVLNode *l1 = l0->right; - if (p) { - ucx_avl_connect(tree, p, l1, 0); - } else { - l1->parent = NULL; - } - ucx_avl_connect(tree, l0, l1->left, l1->key); - ucx_avl_connect(tree, l1, l0, 0); - return l1; -} - -static void ucx_avl_balance(UcxAVLTree *tree, UcxAVLNode *n) { - int lh = avlheight(n->left); - int rh = avlheight(n->right); - n->height = 1 + (lh > rh ? lh : rh); - - if (lh - rh == 2) { - UcxAVLNode *c = n->left; - if (avlheight(c->right) - avlheight(c->left) == 1) { - avl_rotleft(tree, c); - } - n = avl_rotright(tree, n); - } else if (rh - lh == 2) { - UcxAVLNode *c = n->right; - if (avlheight(c->left) - avlheight(c->right) == 1) { - avl_rotright(tree, c); - } - n = avl_rotleft(tree, n); - } - - if (n->parent) { - ucx_avl_balance(tree, n->parent); - } else { - tree->root = n; - } -} - -UcxAVLTree *ucx_avl_new(cmp_func cmpfunc) { - return ucx_avl_new_a(cmpfunc, ucx_default_allocator()); -} - -UcxAVLTree *ucx_avl_new_a(cmp_func cmpfunc, UcxAllocator *allocator) { - UcxAVLTree* tree = alloc_tree(allocator); - if (tree) { - tree->allocator = allocator; - tree->cmpfunc = cmpfunc; - tree->root = NULL; - tree->userdata = NULL; - } - - return tree; -} - -static void ucx_avl_free_node(UcxAllocator *al, UcxAVLNode *node) { - if (node) { - ucx_avl_free_node(al, node->left); - ucx_avl_free_node(al, node->right); - alfree(al, node); - } -} - -void ucx_avl_free(UcxAVLTree *tree) { - UcxAllocator *al = tree->allocator; - ucx_avl_free_node(al, tree->root); - alfree(al, tree); -} - -static void ucx_avl_free_content_node(UcxAllocator *al, UcxAVLNode *node, - ucx_destructor destr) { - if (node) { - ucx_avl_free_content_node(al, node->left, destr); - ucx_avl_free_content_node(al, node->right, destr); - if (destr) { - destr(node->value); - } else { - alfree(al, node->value); - } - } -} - -void ucx_avl_free_content(UcxAVLTree *tree, ucx_destructor destr) { - ucx_avl_free_content_node(tree->allocator, tree->root, destr); -} - -UcxAVLNode *ucx_avl_get_node(UcxAVLTree *tree, intptr_t key) { - UcxAVLNode *n = tree->root; - int cmpresult; - while (n && (cmpresult = tree->cmpfunc( - ptrcast(key), ptrcast(n->key), tree->userdata))) { - n = cmpresult > 0 ? n->right : n->left; - } - return n; -} - -void *ucx_avl_get(UcxAVLTree *tree, intptr_t key) { - UcxAVLNode *n = ucx_avl_get_node(tree, key); - return n ? n->value : NULL; -} - -UcxAVLNode *ucx_avl_find_node(UcxAVLTree *tree, intptr_t key, - distance_func dfnc, int mode) { - UcxAVLNode *n = tree->root; - UcxAVLNode *closest = NULL; - - intmax_t cmpresult; - intmax_t closest_dist; - closest_dist = mode == UCX_AVL_FIND_LOWER_BOUNDED ? INTMAX_MIN : INTMAX_MAX; - - while (n && (cmpresult = dfnc( - ptrcast(key), ptrcast(n->key), tree->userdata))) { - if (mode == UCX_AVL_FIND_CLOSEST) { - intmax_t dist = cmpresult; - if (dist < 0) dist *= -1; - if (dist < closest_dist) { - closest_dist = dist; - closest = n; - } - } else if (mode == UCX_AVL_FIND_LOWER_BOUNDED && cmpresult <= 0) { - if (cmpresult > closest_dist) { - closest_dist = cmpresult; - closest = n; - } - } else if (mode == UCX_AVL_FIND_UPPER_BOUNDED && cmpresult >= 0) { - if (cmpresult < closest_dist) { - closest_dist = cmpresult; - closest = n; - } - } - n = cmpresult > 0 ? n->right : n->left; - } - return n ? n : closest; -} - -void *ucx_avl_find(UcxAVLTree *tree, intptr_t key, - distance_func dfnc, int mode) { - UcxAVLNode *n = ucx_avl_find_node(tree, key, dfnc, mode); - return n ? n->value : NULL; -} - -int ucx_avl_put(UcxAVLTree *tree, intptr_t key, void *value) { - return ucx_avl_put_s(tree, key, value, NULL); -} - -int ucx_avl_put_s(UcxAVLTree *tree, intptr_t key, void *value, - void **oldvalue) { - if (tree->root) { - UcxAVLNode *n = tree->root; - int cmpresult; - while ((cmpresult = tree->cmpfunc( - ptrcast(key), ptrcast(n->key), tree->userdata))) { - UcxAVLNode *m = cmpresult > 0 ? n->right : n->left; - if (m) { - n = m; - } else { - break; - } - } - - if (cmpresult) { - UcxAVLNode* e = alloc_node(tree->allocator); - if (e) { - e->key = key; e->value = value; e->height = 1; - e->parent = e->left = e->right = NULL; - ucx_avl_connect(tree, n, e, 0); - ucx_avl_balance(tree, n); - return 0; - } else { - return 1; - } - } else { - if (oldvalue) { - *oldvalue = n->value; - } - n->value = value; - return 0; - } - } else { - tree->root = alloc_node(tree->allocator); - if (tree->root) { - tree->root->key = key; tree->root->value = value; - tree->root->height = 1; - tree->root->parent = tree->root->left = tree->root->right = NULL; - - if (oldvalue) { - *oldvalue = NULL; - } - - return 0; - } else { - return 1; - } - } -} - -int ucx_avl_remove(UcxAVLTree *tree, intptr_t key) { - return ucx_avl_remove_s(tree, key, NULL, NULL); -} - -int ucx_avl_remove_node(UcxAVLTree *tree, UcxAVLNode *node) { - return ucx_avl_remove_s(tree, node->key, NULL, NULL); -} - -int ucx_avl_remove_s(UcxAVLTree *tree, intptr_t key, - intptr_t *oldkey, void **oldvalue) { - - UcxAVLNode *n = tree->root; - int cmpresult; - while (n && (cmpresult = tree->cmpfunc( - ptrcast(key), ptrcast(n->key), tree->userdata))) { - n = cmpresult > 0 ? n->right : n->left; - } - if (n) { - if (oldkey) { - *oldkey = n->key; - } - if (oldvalue) { - *oldvalue = n->value; - } - - UcxAVLNode *p = n->parent; - if (n->left && n->right) { - UcxAVLNode *s = n->right; - while (s->left) { - s = s->left; - } - ucx_avl_connect(tree, s->parent, s->right, s->key); - n->key = s->key; n->value = s->value; - p = s->parent; - alfree(tree->allocator, s); - } else { - if (p) { - ucx_avl_connect(tree, p, n->right ? n->right:n->left, n->key); - } else { - tree->root = n->right ? n->right : n->left; - if (tree->root) { - tree->root->parent = NULL; - } - } - alfree(tree->allocator, n); - } - - if (p) { - ucx_avl_balance(tree, p); - } - - return 0; - } else { - return 1; - } -} - -static size_t ucx_avl_countn(UcxAVLNode *node) { - if (node) { - return 1 + ucx_avl_countn(node->left) + ucx_avl_countn(node->right); - } else { - return 0; - } -} - -size_t ucx_avl_count(UcxAVLTree *tree) { - return ucx_avl_countn(tree->root); -} - -UcxAVLNode* ucx_avl_pred(UcxAVLNode* node) { - if (node->left) { - UcxAVLNode* n = node->left; - while (n->right) { - n = n->right; - } - return n; - } else { - UcxAVLNode* n = node; - while (n->parent) { - if (n->parent->right == n) { - return n->parent; - } else { - n = n->parent; - } - } - return NULL; - } -} - -UcxAVLNode* ucx_avl_succ(UcxAVLNode* node) { - if (node->right) { - UcxAVLNode* n = node->right; - while (n->left) { - n = n->left; - } - return n; - } else { - UcxAVLNode* n = node; - while (n->parent) { - if (n->parent->left == n) { - return n->parent; - } else { - n = n->parent; - } - } - return NULL; - } -} diff -r a569148841ff -r efbd59642577 ucx/basic_mempool.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/basic_mempool.c Fri Apr 21 21:25:32 2023 +0200 @@ -0,0 +1,235 @@ +/* + * 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/basic_mempool.h" +#include "cx/utils.h" +#include + +#define of_chk_(n) if (SIZE_MAX - sizeof(cx_destructor_func) < (n)) return NULL + +/** Internal structure for denoting pooled memory. */ +typedef struct { + /** The destructor. */ + cx_destructor_func destructor; + /** + * Access to the first byte of the polled memory. + */ + char c; +} cx_basic_mempool_memory; + +static int cx_basic_mempool_chcap( + struct cx_basic_mempool_s *pool, + size_t newcap +) { + if (newcap < pool->ndata) { + return 1; + } + + size_t newcapsz; + if (cx_szmul(newcap, sizeof(void *), &newcapsz)) { + return 1; + } + + void **data = realloc(pool->data, newcapsz); + if (data) { + pool->data = data; + pool->size = newcap; + return 0; + } else { + return 1; + } +} + +void *cx_malloc_basic_mempool( + void *data, + size_t n +) { + of_chk_(n); + struct cx_basic_mempool_s *pool = data; + + if (pool->ndata >= pool->size) { + size_t newcap = pool->size * 2; + if (newcap < pool->size || cx_basic_mempool_chcap(pool, newcap)) { + return NULL; + } + } + + cx_basic_mempool_memory *mem = malloc(sizeof(cx_destructor_func) + n); + if (mem == NULL) { + return NULL; + } + + mem->destructor = NULL; + pool->data[pool->ndata] = mem; + pool->ndata++; + + return &(mem->c); +} + +void *cx_calloc_basic_mempool( + void *data, + size_t nelem, + size_t elsize +) { + size_t msz; + if (cx_szmul(nelem, elsize, &msz)) { + return NULL; + } + void *ptr = cx_malloc_basic_mempool(data, msz); + if (ptr == NULL) { + return NULL; + } + memset(ptr, 0, nelem * elsize); + return ptr; +} + +void *cx_realloc_basic_mempool( + void *data, + void *ptr, + size_t n +) { + of_chk_(n); + struct cx_basic_mempool_s *pool = data; + + char *mem = ((char *) ptr) - sizeof(cx_destructor_func); + char *newm = (char *) realloc(mem, n + sizeof(cx_destructor_func)); + if (newm == NULL) { + return NULL; + } + if (mem != newm) { + cx_for_n(i, pool->ndata) { + if (pool->data[i] == mem) { + pool->data[i] = newm; + return newm + sizeof(cx_destructor_func); + } + } + abort(); + } else { + return newm + sizeof(cx_destructor_func); + } +} + +void cx_free_basic_mempool( + void *data, + void *ptr +) { + struct cx_basic_mempool_s *pool = data; + + cx_basic_mempool_memory *mem = (cx_basic_mempool_memory *) + ((char *) ptr - sizeof(cx_destructor_func)); + cx_for_n(i, pool->ndata) { + if (mem == pool->data[i]) { + if (mem->destructor != NULL) { + mem->destructor(&(mem->c)); + } + free(mem); + size_t last_index = pool->ndata - 1; + if (i != last_index) { + pool->data[i] = pool->data[last_index]; + pool->data[last_index] = NULL; + } + pool->ndata--; + return; + } + } + abort(); +} + +void cx_basic_mempool_destroy(CxMempool *p) { + struct cx_basic_mempool_s *pool = (struct cx_basic_mempool_s *) p; + cx_basic_mempool_memory *mem; + cx_for_n(i, pool->ndata) { + mem = (cx_basic_mempool_memory *) pool->data[i]; + if (mem) { + if (mem->destructor) { + mem->destructor(&(mem->c)); + } + free(mem); + } + } + free(pool->data); + free((void *) p->allocator); + free(pool); +} + +void cx_basic_mempool_set_destr( + __attribute__((__unused__)) CxMempool *pool, + void *ptr, + cx_destructor_func func +) { + *(cx_destructor_func *) ((char *) ptr - sizeof(cx_destructor_func)) = func; +} + +static cx_allocator_class cx_basic_mempool_allocator_class = { + cx_malloc_basic_mempool, + cx_realloc_basic_mempool, + cx_calloc_basic_mempool, + cx_free_basic_mempool +}; + +static cx_mempool_class cx_basic_mempool_class = { + cx_basic_mempool_destroy, + cx_basic_mempool_set_destr, +}; + +CxMempool *cxBasicMempoolCreate(size_t capacity) { + size_t poolsize; + if (cx_szmul(capacity, sizeof(void *), &poolsize)) { + return NULL; + } + + struct cx_basic_mempool_s *pool = + malloc(sizeof(struct cx_basic_mempool_s)); + if (pool == NULL) { + return NULL; + } + + + CxAllocator *provided_allocator = malloc(sizeof(CxAllocator)); + if (!provided_allocator) { + free(pool); + return NULL; + } + provided_allocator->cl = &cx_basic_mempool_allocator_class; + provided_allocator->data = pool; + + pool->base.cl = &cx_basic_mempool_class; + pool->base.allocator = provided_allocator; + + pool->data = malloc(poolsize); + if (pool->data == NULL) { + free(provided_allocator); + free(pool); + return NULL; + } + + pool->ndata = 0; + pool->size = capacity; + + return (CxMempool *) pool; +} diff -r a569148841ff -r efbd59642577 ucx/buffer.c --- a/ucx/buffer.c Sun Apr 16 14:12:24 2023 +0200 +++ b/ucx/buffer.c Fri Apr 21 21:25:32 2023 +0200 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. + * 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: @@ -26,90 +26,100 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include "ucx/buffer.h" +#include "cx/buffer.h" +#include "cx/utils.h" -#include -#include +#include #include -UcxBuffer *ucx_buffer_new(void *space, size_t capacity, int flags) { - UcxBuffer *buffer = (UcxBuffer*) malloc(sizeof(UcxBuffer)); - if (buffer) { - buffer->flags = flags; - if (!space) { - buffer->space = (char*)malloc(capacity); - if (!buffer->space) { - free(buffer); - return NULL; - } - memset(buffer->space, 0, capacity); - buffer->flags |= UCX_BUFFER_AUTOFREE; - } else { - buffer->space = (char*)space; +int cxBufferInit( + CxBuffer *buffer, + void *space, + size_t capacity, + CxAllocator const *allocator, + int flags +) { + if (allocator == NULL) allocator = cxDefaultAllocator; + buffer->allocator = allocator; + buffer->flags = flags; + if (!space) { + buffer->bytes = cxMalloc(allocator, capacity); + if (buffer->bytes == NULL) { + return 1; } - buffer->capacity = capacity; - buffer->size = 0; - - buffer->pos = 0; + buffer->flags |= CX_BUFFER_FREE_CONTENTS; + } else { + buffer->bytes = space; } + buffer->capacity = capacity; + buffer->size = 0; + buffer->pos = 0; - return buffer; -} + buffer->flush_func = NULL; + buffer->flush_target = NULL; + buffer->flush_blkmax = 0; + buffer->flush_blksize = 4096; + buffer->flush_threshold = SIZE_MAX; -void ucx_buffer_free(UcxBuffer *buffer) { - if ((buffer->flags & UCX_BUFFER_AUTOFREE) == UCX_BUFFER_AUTOFREE) { - free(buffer->space); - } - free(buffer); + return 0; } -UcxBuffer* ucx_buffer_extract( - UcxBuffer *src, size_t start, size_t length, int flags) { - if (src->size == 0 || length == 0 || - ((size_t)-1) - start < length || start+length > src->capacity) - { +void cxBufferDestroy(CxBuffer *buffer) { + if ((buffer->flags & CX_BUFFER_FREE_CONTENTS) == CX_BUFFER_FREE_CONTENTS) { + cxFree(buffer->allocator, buffer->bytes); + } +} + +CxBuffer *cxBufferCreate( + void *space, + size_t capacity, + CxAllocator const *allocator, + int flags +) { + CxBuffer *buf = cxMalloc(allocator, sizeof(CxBuffer)); + if (buf == NULL) return NULL; + if (0 == cxBufferInit(buf, space, capacity, allocator, flags)) { + return buf; + } else { + cxFree(allocator, buf); return NULL; } +} - UcxBuffer *dst = (UcxBuffer*) malloc(sizeof(UcxBuffer)); - if (dst) { - dst->space = (char*)malloc(length); - if (!dst->space) { - free(dst); - return NULL; - } - dst->capacity = length; - dst->size = length; - dst->flags = flags | UCX_BUFFER_AUTOFREE; - dst->pos = 0; - memcpy(dst->space, src->space+start, length); +void cxBufferFree(CxBuffer *buffer) { + if ((buffer->flags & CX_BUFFER_FREE_CONTENTS) == CX_BUFFER_FREE_CONTENTS) { + cxFree(buffer->allocator, buffer->bytes); } - return dst; + cxFree(buffer->allocator, buffer); } -int ucx_buffer_seek(UcxBuffer *buffer, off_t offset, int whence) { +int cxBufferSeek( + CxBuffer *buffer, + off_t offset, + int whence +) { size_t npos; switch (whence) { - case SEEK_CUR: - npos = buffer->pos; - break; - case SEEK_END: - npos = buffer->size; - break; - case SEEK_SET: - npos = 0; - break; - default: - return -1; + case SEEK_CUR: + npos = buffer->pos; + break; + case SEEK_END: + npos = buffer->size; + break; + case SEEK_SET: + npos = 0; + break; + default: + return -1; } size_t opos = npos; npos += offset; - + if ((offset > 0 && npos < opos) || (offset < 0 && npos > opos)) { return -1; } - + if (npos >= buffer->size) { return -1; } else { @@ -119,135 +129,237 @@ } -int ucx_buffer_eof(UcxBuffer *buffer) { +void cxBufferClear(CxBuffer *buffer) { + memset(buffer->bytes, 0, buffer->size); + buffer->size = 0; + buffer->pos = 0; +} + +int cxBufferEof(CxBuffer const *buffer) { return buffer->pos >= buffer->size; } -int ucx_buffer_extend(UcxBuffer *buffer, size_t len) { - size_t newcap = buffer->capacity; - - if (buffer->capacity + len < buffer->capacity) { - return -1; +int cxBufferMinimumCapacity( + CxBuffer *buffer, + size_t newcap +) { + if (newcap <= buffer->capacity) { + return 0; } - - while (buffer->capacity + len > newcap) { - newcap <<= 1; - if (newcap < buffer->capacity) { - return -1; - } - } - - char *newspace = (char*)realloc(buffer->space, newcap); - if (newspace) { - memset(newspace+buffer->size, 0, newcap-buffer->size); - buffer->space = newspace; + + if (cxReallocate(buffer->allocator, + (void **) &buffer->bytes, newcap) == 0) { buffer->capacity = newcap; + return 0; } else { return -1; } - - return 0; } -size_t ucx_buffer_write(const void *ptr, size_t size, size_t nitems, - UcxBuffer *buffer) { +/** + * Helps flushing data to the flush target of a buffer. + * + * @param buffer the buffer containing the config + * @param space the data to flush + * @param size the element size + * @param nitems the number of items + * @return the number of items flushed + */ +static size_t cx_buffer_write_flush_helper( + CxBuffer *buffer, + unsigned char const *space, + size_t size, + size_t nitems +) { + size_t pos = 0; + size_t remaining = nitems; + size_t max_items = buffer->flush_blksize / size; + while (remaining > 0) { + size_t items = remaining > max_items ? max_items : remaining; + size_t flushed = buffer->flush_func( + space + pos, + size, items, + buffer->flush_target); + if (flushed > 0) { + pos += (flushed * size); + remaining -= flushed; + } else { + // if no bytes can be flushed out anymore, we give up + break; + } + } + return nitems - remaining; +} + +size_t cxBufferWrite( + void const *ptr, + size_t size, + size_t nitems, + CxBuffer *buffer +) { + // optimize for easy case + if (size == 1 && (buffer->capacity - buffer->pos) >= nitems) { + memcpy(buffer->bytes + buffer->pos, ptr, nitems); + buffer->pos += nitems; + if (buffer->pos > buffer->size) { + buffer->size = buffer->pos; + } + return nitems; + } + size_t len; - if(ucx_szmul(size, nitems, &len)) { + size_t nitems_out = nitems; + if (cx_szmul(size, nitems, &len)) { return 0; } size_t required = buffer->pos + len; if (buffer->pos > required) { return 0; } - + + bool perform_flush = false; if (required > buffer->capacity) { - if ((buffer->flags & UCX_BUFFER_AUTOEXTEND) == UCX_BUFFER_AUTOEXTEND) { - if (ucx_buffer_extend(buffer, required - buffer->capacity)) { - return 0; + if ((buffer->flags & CX_BUFFER_AUTO_EXTEND) == CX_BUFFER_AUTO_EXTEND && required) { + if (buffer->flush_blkmax > 0 && required > buffer->flush_threshold) { + perform_flush = true; + } else { + if (cxBufferMinimumCapacity(buffer, required)) { + return 0; + } } } else { - len = buffer->capacity - buffer->pos; - if (size > 1) { - len -= len%size; + if (buffer->flush_blkmax > 0) { + perform_flush = true; + } else { + // truncate data to be written, if we can neither extend nor flush + len = buffer->capacity - buffer->pos; + if (size > 1) { + len -= len % size; + } + nitems_out = len / size; } } } - + if (len == 0) { return len; } - - memcpy(buffer->space + buffer->pos, ptr, len); - buffer->pos += len; - if(buffer->pos > buffer->size) { - buffer->size = buffer->pos; + + if (perform_flush) { + size_t flush_max; + if (cx_szmul(buffer->flush_blkmax, buffer->flush_blksize, &flush_max)) { + return 0; + } + size_t flush_pos = buffer->flush_func == NULL || buffer->flush_target == NULL + ? buffer->pos + : cx_buffer_write_flush_helper(buffer, buffer->bytes, 1, buffer->pos); + if (flush_pos == buffer->pos) { + // entire buffer has been flushed, we can reset + buffer->size = buffer->pos = 0; + + size_t items_flush; // how many items can also be directly flushed + size_t items_keep; // how many items have to be written to the buffer + + items_flush = flush_max >= required ? nitems : (flush_max - flush_pos) / size; + if (items_flush > 0) { + items_flush = cx_buffer_write_flush_helper(buffer, ptr, size, items_flush / size); + // in case we could not flush everything, keep the rest + } + items_keep = nitems - items_flush; + if (items_keep > 0) { + // try again with the remaining stuff + unsigned char const *new_ptr = ptr; + new_ptr += items_flush * size; + // report the directly flushed items as written plus the remaining stuff + return items_flush + cxBufferWrite(new_ptr, size, items_keep, buffer); + } else { + // all items have been flushed - report them as written + return nitems; + } + } else if (flush_pos == 0) { + // nothing could be flushed at all, we immediately give up without writing any data + return 0; + } else { + // we were partially successful, we shift left and try again + cxBufferShiftLeft(buffer, flush_pos); + return cxBufferWrite(ptr, size, nitems, buffer); + } + } else { + memcpy(buffer->bytes + buffer->pos, ptr, len); + buffer->pos += len; + if (buffer->pos > buffer->size) { + buffer->size = buffer->pos; + } + return nitems_out; } - - return len / size; + } -size_t ucx_buffer_read(void *ptr, size_t size, size_t nitems, - UcxBuffer *buffer) { +int cxBufferPut( + CxBuffer *buffer, + int c +) { + c &= 0xFF; + unsigned char const ch = c; + if (cxBufferWrite(&ch, 1, 1, buffer) == 1) { + return c; + } else { + return EOF; + } +} + +size_t cxBufferPutString( + CxBuffer *buffer, + const char *str +) { + return cxBufferWrite(str, 1, strlen(str), buffer); +} + +size_t cxBufferRead( + void *ptr, + size_t size, + size_t nitems, + CxBuffer *buffer +) { size_t len; - if(ucx_szmul(size, nitems, &len)) { + if (cx_szmul(size, nitems, &len)) { return 0; } if (buffer->pos + len > buffer->size) { len = buffer->size - buffer->pos; - if (size > 1) len -= len%size; + if (size > 1) len -= len % size; } - + if (len <= 0) { return len; } - - memcpy(ptr, buffer->space + buffer->pos, len); + + memcpy(ptr, buffer->bytes + buffer->pos, len); buffer->pos += len; - + return len / size; } -int ucx_buffer_putc(UcxBuffer *buffer, int c) { - if(buffer->pos >= buffer->capacity) { - if ((buffer->flags & UCX_BUFFER_AUTOEXTEND) == UCX_BUFFER_AUTOEXTEND) { - if(ucx_buffer_extend(buffer, 1)) { - return EOF; - } - } else { - return EOF; - } - } - - c &= 0xFF; - buffer->space[buffer->pos] = (char) c; - buffer->pos++; - if(buffer->pos > buffer->size) { - buffer->size = buffer->pos; - } - return c; -} - -int ucx_buffer_getc(UcxBuffer *buffer) { - if (ucx_buffer_eof(buffer)) { +int cxBufferGet(CxBuffer *buffer) { + if (cxBufferEof(buffer)) { return EOF; } else { - int c = ((unsigned char*)buffer->space)[buffer->pos]; + int c = buffer->bytes[buffer->pos]; buffer->pos++; return c; } } -size_t ucx_buffer_puts(UcxBuffer *buffer, const char *str) { - return ucx_buffer_write((const void*)str, 1, strlen(str), buffer); -} - -int ucx_buffer_shift_left(UcxBuffer* buffer, size_t shift) { +int cxBufferShiftLeft( + CxBuffer *buffer, + size_t shift +) { if (shift >= buffer->size) { buffer->pos = buffer->size = 0; } else { - memmove(buffer->space, buffer->space + shift, buffer->size - shift); + memmove(buffer->bytes, buffer->bytes + shift, buffer->size - shift); buffer->size -= shift; - + if (buffer->pos >= shift) { buffer->pos -= shift; } else { @@ -257,14 +369,17 @@ return 0; } -int ucx_buffer_shift_right(UcxBuffer* buffer, size_t shift) { +int cxBufferShiftRight( + CxBuffer *buffer, + size_t shift +) { size_t req_capacity = buffer->size + shift; size_t movebytes; - + // auto extend buffer, if required and enabled if (buffer->capacity < req_capacity) { - if ((buffer->flags & UCX_BUFFER_AUTOEXTEND) == UCX_BUFFER_AUTOEXTEND) { - if (ucx_buffer_extend(buffer, req_capacity - buffer->capacity)) { + if ((buffer->flags & CX_BUFFER_AUTO_EXTEND) == CX_BUFFER_AUTO_EXTEND) { + if (cxBufferMinimumCapacity(buffer, req_capacity)) { return 1; } movebytes = buffer->size; @@ -274,23 +389,26 @@ } else { movebytes = buffer->size; } - - memmove(buffer->space + shift, buffer->space, movebytes); - buffer->size = shift+movebytes; - + + memmove(buffer->bytes + shift, buffer->bytes, movebytes); + buffer->size = shift + movebytes; + buffer->pos += shift; if (buffer->pos > buffer->size) { buffer->pos = buffer->size; } - + return 0; } -int ucx_buffer_shift(UcxBuffer* buffer, off_t shift) { +int cxBufferShift( + CxBuffer *buffer, + off_t shift +) { if (shift < 0) { - return ucx_buffer_shift_left(buffer, (size_t) (-shift)); + return cxBufferShiftLeft(buffer, (size_t) (-shift)); } else if (shift > 0) { - return ucx_buffer_shift_right(buffer, (size_t) shift); + return cxBufferShiftRight(buffer, (size_t) shift); } else { return 0; } diff -r a569148841ff -r efbd59642577 ucx/compare.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/compare.c Fri Apr 21 21:25:32 2023 +0200 @@ -0,0 +1,201 @@ +/* + * 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/compare.h" + +#include + +int cx_cmp_int(void const *i1, void const *i2) { + int a = *((const int *) i1); + int b = *((const int *) i2); + if (a == b) { + return 0; + } else { + return a < b ? -1 : 1; + } +} + +int cx_cmp_longint(void const *i1, void const *i2) { + long int a = *((const long int *) i1); + long int b = *((const long int *) i2); + if (a == b) { + return 0; + } else { + return a < b ? -1 : 1; + } +} + +int cx_cmp_longlong(void const *i1, void const *i2) { + long long a = *((const long long *) i1); + long long b = *((const long long *) i2); + if (a == b) { + return 0; + } else { + return a < b ? -1 : 1; + } +} + +int cx_cmp_int16(void const *i1, void const *i2) { + int16_t a = *((const int16_t *) i1); + int16_t b = *((const int16_t *) i2); + if (a == b) { + return 0; + } else { + return a < b ? -1 : 1; + } +} + +int cx_cmp_int32(void const *i1, void const *i2) { + int32_t a = *((const int32_t *) i1); + int32_t b = *((const int32_t *) i2); + if (a == b) { + return 0; + } else { + return a < b ? -1 : 1; + } +} + +int cx_cmp_int64(void const *i1, void const *i2) { + int64_t a = *((const int64_t *) i1); + int64_t b = *((const int64_t *) i2); + if (a == b) { + return 0; + } else { + return a < b ? -1 : 1; + } +} + +int cx_cmp_uint(void const *i1, void const *i2) { + unsigned int a = *((const unsigned int *) i1); + unsigned int b = *((const unsigned int *) i2); + if (a == b) { + return 0; + } else { + return a < b ? -1 : 1; + } +} + +int cx_cmp_ulongint(void const *i1, void const *i2) { + unsigned long int a = *((const unsigned long int *) i1); + unsigned long int b = *((const unsigned long int *) i2); + if (a == b) { + return 0; + } else { + return a < b ? -1 : 1; + } +} + +int cx_cmp_ulonglong(void const *i1, void const *i2) { + unsigned long long a = *((const unsigned long long *) i1); + unsigned long long b = *((const unsigned long long *) i2); + if (a == b) { + return 0; + } else { + return a < b ? -1 : 1; + } +} + +int cx_cmp_uint16(void const *i1, void const *i2) { + uint16_t a = *((const uint16_t *) i1); + uint16_t b = *((const uint16_t *) i2); + if (a == b) { + return 0; + } else { + return a < b ? -1 : 1; + } +} + +int cx_cmp_uint32(void const *i1, void const *i2) { + uint32_t a = *((const uint32_t *) i1); + uint32_t b = *((const uint32_t *) i2); + if (a == b) { + return 0; + } else { + return a < b ? -1 : 1; + } +} + +int cx_cmp_uint64(void const *i1, void const *i2) { + uint64_t a = *((const uint64_t *) i1); + uint64_t b = *((const uint64_t *) i2); + if (a == b) { + return 0; + } else { + return a < b ? -1 : 1; + } +} + +int cx_cmp_float(void const *f1, void const *f2) { + float a = *((const float *) f1); + float b = *((const float *) f2); + if (fabsf(a - b) < 1e-6f) { + return 0; + } else { + return a < b ? -1 : 1; + } +} + +int cx_cmp_double( + void const *d1, + void const *d2 +) { + double a = *((const double *) d1); + double b = *((const double *) d2); + if (fabs(a - b) < 1e-14) { + return 0; + } else { + return a < b ? -1 : 1; + } +} + +int cx_cmp_intptr( + void const *ptr1, + void const *ptr2 +) { + intptr_t p1 = *(const intptr_t *) ptr1; + intptr_t p2 = *(const intptr_t *) ptr2; + if (p1 == p2) { + return 0; + } else { + return p1 < p2 ? -1 : 1; + } +} + +int cx_cmp_uintptr( + void const *ptr1, + void const *ptr2 +) { + uintptr_t p1 = *(const uintptr_t *) ptr1; + uintptr_t p2 = *(const uintptr_t *) ptr2; + if (p1 == p2) { + return 0; + } else { + return p1 < p2 ? -1 : 1; + } +} + diff -r a569148841ff -r efbd59642577 ucx/cx/allocator.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/allocator.h Fri Apr 21 21:25:32 2023 +0200 @@ -0,0 +1,225 @@ +/* + * 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. + */ +/** + * \file allocator.h + * Interface for custom allocators. + */ + +#ifndef UCX_ALLOCATOR_H +#define UCX_ALLOCATOR_H + +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The class definition for an allocator. + */ +typedef struct { + /** + * The allocator's malloc() implementation. + */ + void *(*malloc)( + void *data, + size_t n + ); + + /** + * The allocator's realloc() implementation. + */ + void *(*realloc)( + void *data, + void *mem, + size_t n + ) + __attribute__((__warn_unused_result__)); + + /** + * The allocator's calloc() implementation. + */ + void *(*calloc)( + void *data, + size_t nelem, + size_t n + ); + + /** + * The allocator's free() implementation. + */ + void (*free)( + void *data, + void *mem + ) + __attribute__((__nonnull__)); +} cx_allocator_class; + +/** + * Structure holding the data for an allocator. + */ +struct cx_allocator_s { + /** + * A pointer to the instance of the allocator class. + */ + cx_allocator_class *cl; + /** + * A pointer to the data this allocator uses. + */ + void *data; +}; + +/** + * High-Level type alias for the allocator type. + */ +typedef struct cx_allocator_s CxAllocator; + +/** + * A default allocator using standard library malloc() etc. + */ +extern CxAllocator *cxDefaultAllocator; + +/** + * Function pointer type for destructor functions. + * + * A destructor function deallocates possible contents and MAY free the memory + * pointed to by \p memory. Read the documentation of the respective function + * pointer to learn if a destructor SHALL, MAY, or MUST NOT free the memory in that + * particular implementation. + * + * @param memory a pointer to the object to destruct + */ +typedef void (*cx_destructor_func)(void *memory) __attribute__((__nonnull__)); + +/** + * Function pointer type for destructor functions. + * + * A destructor function deallocates possible contents and MAY free the memory + * pointed to by \p memory. Read the documentation of the respective function + * pointer to learn if a destructor SHALL, MAY, or MUST NOT free the memory in that + * particular implementation. + * + * @param data an optional pointer to custom data + * @param memory a pointer to the object to destruct + */ +typedef void (*cx_destructor_func2)( + void *data, + void *memory +) __attribute__((__nonnull__(2))); + +/** + * Allocate \p n bytes of memory. + * + * @param allocator the allocator + * @param n the number of bytes + * @return a pointer to the allocated memory + */ +void *cxMalloc( + CxAllocator const *allocator, + size_t n +) +__attribute__((__malloc__)) +__attribute__((__alloc_size__(2))); + +/** + * Re-allocate 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. + * + * \note Re-allocating a block allocated by a different allocator is undefined. + * + * @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 + */ +void *cxRealloc( + CxAllocator const *allocator, + void *mem, + size_t n +) +__attribute__((__warn_unused_result__)) +__attribute__((__alloc_size__(3))); + +/** + * Re-allocate 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. + * On success, the pointer is changed to the new location (in case the + * + * \note Re-allocating a block allocated by a different allocator is undefined. + * + * \par Error handling + * \c errno will be set, if the underlying realloc function does so. + * + * @param allocator the allocator + * @param mem pointer to the pointer to allocated block + * @param n the new size in bytes + * @return zero on success, non-zero on failure + */ +int cxReallocate( + CxAllocator const *allocator, + void **mem, + size_t n +) +__attribute__((__nonnull__)); + +/** + * Allocate \p nelem 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 + * @return a pointer to the allocated memory + */ +void *cxCalloc( + CxAllocator const *allocator, + size_t nelem, + size_t n +) +__attribute__((__malloc__)) +__attribute__((__alloc_size__(2, 3))); + +/** + * Free a block allocated by this allocator. + * + * \note Freeing a block of a different allocator is undefined. + * + * @param allocator the allocator + * @param mem a pointer to the block to free + */ +void cxFree( + CxAllocator const *allocator, + void *mem +) +__attribute__((__nonnull__)); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // UCX_ALLOCATOR_H diff -r a569148841ff -r efbd59642577 ucx/cx/array_list.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/array_list.h Fri Apr 21 21:25:32 2023 +0200 @@ -0,0 +1,194 @@ +/* + * 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. + */ +/** + * \file array_list.h + * \brief Array list implementation. + * \details Also provides several low-level functions for custom array list implementations. + * \author Mike Becker + * \author Olaf Wintermann + * \version 3.0 + * \copyright 2-Clause BSD License + */ + + +#ifndef UCX_ARRAY_LIST_H +#define UCX_ARRAY_LIST_H + +#include "list.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Defines a reallocation mechanism for arrays. + */ +struct cx_array_reallocator_s { + /** + * Re-allocates space for the given array. + * + * Implementations are not required to free the original array. + * This allows re-allocation of static memory by allocating heap memory + * and copying the array contents. The information in \p data can keep + * track of the state of the memory or other additional allocator info. + * + * @param array the array to reallocate + * @param capacity the new capacity (number of elements) + * @param elem_size the size of each element + * @param alloc a reference to this allocator + * @return a pointer to the reallocated memory or \c NULL on failure + */ + void *(*realloc)( + void *array, + size_t capacity, + size_t elem_size, + struct cx_array_reallocator_s *alloc + ); + + /** + * Custom data pointer. + */ + void *ptr1; + /** + * Custom data pointer. + */ + void *ptr2; + /** + * Custom data integer. + */ + size_t int1; + /** + * Custom data integer. + */ + size_t int2; +}; + +/** + * Return codes for cx_array_copy(). + */ +enum cx_array_copy_result { + CX_ARRAY_COPY_SUCCESS, + CX_ARRAY_COPY_REALLOC_NOT_SUPPORTED, + CX_ARRAY_COPY_REALLOC_FAILED, +}; + +/** + * Copies elements from one array to another. + * + * The elements are copied to the \p target array at the specified \p index, + * overwriting possible elements. The \p index does not need to be in range of + * the current array \p size. If the new index plus the number of elements added + * would extend the array's size, and \p capacity is not \c NULL, the remaining + * capacity is used. + * + * If the capacity is insufficient to hold the new data, a reallocation + * attempt is made, unless the allocator is set to \c NULL, in which case + * this function ultimately returns a failure. + * + * @param target the target array + * @param size a pointer to the size of the target array + * @param capacity a pointer to the target array's capacity - + * \c NULL if only the size shall be used to bound the array + * @param index the index where the copied elements shall be placed + * @param src the source array + * @param elem_size the size of one element + * @param elem_count the number of elements to copy + * @param reallocator the array re-allocator to use, or \c NULL + * if re-allocation shall not happen + * @return zero on success, non-zero error code on failure + */ +enum cx_array_copy_result cx_array_copy( + void **target, + size_t *size, + size_t *capacity, + size_t index, + void const *src, + size_t elem_size, + size_t elem_count, + struct cx_array_reallocator_s *reallocator +) __attribute__((__nonnull__(1, 2, 5))); + + +/** + * Swaps two array elements. + * + * @param arr the array + * @param elem_size the element size + * @param idx1 index of first element + * @param idx2 index of second element + */ +void cx_array_swap( + void *arr, + size_t elem_size, + size_t idx1, + size_t idx2 +) __attribute__((__nonnull__)); + +/** + * Allocates an array list for storing elements with \p item_size bytes each. + * + * If \p item_size is CX_STORE_POINTERS, the created list will be created as if + * cxListStorePointers() was called immediately after creation. + * + * @param allocator the allocator for allocating the list memory + * (if \c NULL the cxDefaultAllocator will be used) + * @param comparator the comparator for the elements + * (if \c NULL sort and find functions will not work) + * @param item_size the size of each element in bytes + * @param initial_capacity the initial number of elements the array can store + * @return the created list + */ +CxList *cxArrayListCreate( + CxAllocator const *allocator, + cx_compare_func comparator, + size_t item_size, + size_t initial_capacity +); + +/** + * Allocates an array list for storing elements with \p item_size bytes each. + * + * The list will use the cxDefaultAllocator and \em NO compare function. + * If you want to call functions that need a compare function, you have to + * set it immediately after creation or use cxArrayListCreate(). + * + * If \p item_size is CX_STORE_POINTERS, the created list will be created as if + * cxListStorePointers() was called immediately after creation. + * + * @param item_size the size of each element in bytes + * @param initial_capacity the initial number of elements the array can store + * @return the created list + */ +#define cxArrayListCreateSimple(item_size, initial_capacity) \ + cxArrayListCreate(NULL, NULL, item_size, initial_capacity) + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // UCX_ARRAY_LIST_H diff -r a569148841ff -r efbd59642577 ucx/cx/basic_mempool.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/basic_mempool.h Fri Apr 21 21:25:32 2023 +0200 @@ -0,0 +1,76 @@ +/* + * 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. + */ +/** + * \file basic_mempool.h + * \brief Implementation of a basic memory pool. + * \author Mike Becker + * \author Olaf Wintermann + * \version 3.0 + * \copyright 2-Clause BSD License + */ + +#ifndef UCX_BASIC_MEMPOOL_H +#define UCX_BASIC_MEMPOOL_H + +#include "mempool.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Basic array-based memory pool. + */ +struct cx_basic_mempool_s { + /** Inherit base structure members. */ + CxMempool base; + + /** List of pointers to pooled memory. */ + void **data; + + /** Number of pooled memory items. */ + size_t ndata; + + /** Memory pool size. */ + size_t size; +}; + +/** + * Creates a basic array-based memory pool. + * + * @param capacity the initial capacity of the pool + * @return the created memory pool or \c NULL if allocation failed + */ +__attribute__((__warn_unused_result__)) +CxMempool *cxBasicMempoolCreate(size_t capacity); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // UCX_BASIC_MEMPOOL_H diff -r a569148841ff -r efbd59642577 ucx/cx/buffer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/buffer.h Fri Apr 21 21:25:32 2023 +0200 @@ -0,0 +1,451 @@ +/* + * 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. + */ + +/** + * \file buffer.h + * + * \brief Advanced buffer implementation. + * + * Instances of CxBuffer can be used to read from or to write to like one + * would do with a stream. + * + * Some features for convenient use of the buffer + * can be enabled. See the documentation of the macro constants for more + * information. + * + * \author Mike Becker + * \author Olaf Wintermann + * \version 3.0 + * \copyright 2-Clause BSD License + */ + +#ifndef UCX_BUFFER_H +#define UCX_BUFFER_H + +#include "common.h" +#include "allocator.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * No buffer features enabled (all flags cleared). + */ +#define CX_BUFFER_DEFAULT 0x00 + +/** + * If this flag is enabled, the buffer will automatically free its contents when destroyed. + */ +#define CX_BUFFER_FREE_CONTENTS 0x01 + +/** + * If this flag is enabled, the buffer will automatically extends its capacity. + */ +#define CX_BUFFER_AUTO_EXTEND 0x02 + +/** Structure for the UCX buffer data. */ +typedef struct { + /** A pointer to the buffer contents. */ + union { + /** + * Data is interpreted as text. + */ + char *space; + /** + * Data is interpreted as binary. + */ + unsigned char *bytes; + }; + /** The allocator to use for automatic memory management. */ + CxAllocator const *allocator; + /** Current position of the buffer. */ + size_t pos; + /** Current capacity (i.e. maximum size) of the buffer. */ + size_t capacity; + /** Current size of the buffer content. */ + size_t size; + /** + * The buffer may not extend beyond this threshold before starting to flush. + * Default is \c SIZE_MAX (flushing disabled when auto extension is enabled). + */ + size_t flush_threshold; + /** + * The block size for the elements to flush. + * Default is 4096 bytes. + */ + size_t flush_blksize; + /** + * The maximum number of blocks to flush in one cycle. + * Zero disables flushing entirely (this is the default). + * Set this to \c SIZE_MAX to flush the entire buffer. + * + * @attention if the maximum number of blocks multiplied with the block size + * is smaller than the expected contents written to this buffer within one write + * operation, multiple flush cycles are performed after that write. + * That means the total number of blocks flushed after one write to this buffer may + * be larger than \c flush_blkmax. + */ + size_t flush_blkmax; + + /** + * The write function used for flushing. + * If NULL, the flushed content gets discarded. + */ + cx_write_func flush_func; + + /** + * The target for \c flush_func. + */ + void *flush_target; + + /** + * Flag register for buffer features. + * @see #CX_BUFFER_DEFAULT + * @see #CX_BUFFER_FREE_CONTENTS + * @see #CX_BUFFER_AUTO_EXTEND + */ + int flags; +} cx_buffer_s; + +/** + * UCX buffer. + */ +typedef cx_buffer_s CxBuffer; + +/** + * Initializes a fresh buffer. + * + * \note You may provide \c NULL as argument for \p space. + * Then this function will allocate the space and enforce + * the #CX_BUFFER_FREE_CONTENTS flag. + * + * @param buffer the buffer to initialize + * @param space pointer to the memory area, or \c NULL to allocate + * new memory + * @param capacity the capacity of the buffer + * @param allocator the allocator this buffer shall use for automatic + * memory management. If \c NULL, the default heap allocator will be used. + * @param flags buffer features (see cx_buffer_s.flags) + * @return zero on success, non-zero if a required allocation failed + */ +__attribute__((__nonnull__(1))) +int cxBufferInit( + CxBuffer *buffer, + void *space, + size_t capacity, + CxAllocator const *allocator, + int flags +); + +/** + * Allocates and initializes a fresh buffer. + * + * \note You may provide \c NULL as argument for \p space. + * Then this function will allocate the space and enforce + * the #CX_BUFFER_FREE_CONTENTS flag. + * + * @param space pointer to the memory area, or \c NULL to allocate + * new memory + * @param capacity the capacity of the buffer + * @param allocator the allocator to use for allocating the structure and the automatic + * memory management within the buffer. If \c NULL, the default heap allocator will be used. + * @param flags buffer features (see cx_buffer_s.flags) + * @return a pointer to the buffer on success, \c NULL if a required allocation failed + */ +CxBuffer *cxBufferCreate( + void *space, + size_t capacity, + CxAllocator const *allocator, + int flags +); + +/** + * Destroys the buffer contents. + * + * Has no effect if the #CX_BUFFER_FREE_CONTENTS feature is not enabled. + * If you want to free the memory of the entire buffer, use cxBufferFree(). + * + * @param buffer the buffer which contents shall be destroyed + * @see cxBufferInit() + */ +__attribute__((__nonnull__)) +void cxBufferDestroy(CxBuffer *buffer); + +/** + * Deallocates the buffer. + * + * If the #CX_BUFFER_FREE_CONTENTS feature is enabled, this function also destroys + * the contents. If you \em only want to destroy the contents, use cxBufferDestroy(). + * + * @param buffer the buffer to deallocate + * @see cxBufferCreate() + */ +__attribute__((__nonnull__)) +void cxBufferFree(CxBuffer *buffer); + +/** + * Shifts the contents of the buffer by the given offset. + * + * If the offset is positive, the contents are shifted to the right. + * If auto extension is enabled, the buffer grows, if necessary. + * In case the auto extension fails, this function returns a non-zero value and + * no contents are changed. + * If auto extension is disabled, the contents that do not fit into the buffer + * are discarded. + * + * If the offset is negative, the contents are shifted to the left where the + * first \p shift bytes are discarded. + * The new size of the buffer is the old size minus the absolute shift value. + * If this value is larger than the buffer size, the buffer is emptied (but + * not cleared, see the security note below). + * + * The buffer position gets shifted alongside with the content but is kept + * within the boundaries of the buffer. + * + * \note For situations where \c off_t is not large enough, there are specialized cxBufferShiftLeft() and + * cxBufferShiftRight() functions using a \c size_t as parameter type. + * + * \attention + * Security Note: The shifting operation does \em not erase the previously occupied memory cells. + * But you can easily do that manually, e.g. by calling + * memset(buffer->bytes, 0, shift) for a right shift or + * memset(buffer->bytes + buffer->size, 0, buffer->capacity - buffer->size) + * for a left shift. + * + * @param buffer the buffer + * @param shift the shift offset (negative means left shift) + * @return 0 on success, non-zero if a required auto-extension fails + */ +__attribute__((__nonnull__)) +int cxBufferShift( + CxBuffer *buffer, + off_t shift +); + +/** + * Shifts the buffer to the right. + * See cxBufferShift() for details. + * + * @param buffer the buffer + * @param shift the shift offset + * @return 0 on success, non-zero if a required auto-extension fails + * @see cxBufferShift() + */ +__attribute__((__nonnull__)) +int cxBufferShiftRight( + CxBuffer *buffer, + size_t shift +); + +/** + * Shifts the buffer to the left. + * See cxBufferShift() for details. + * + * \note Since a left shift cannot fail due to memory allocation problems, this + * function always returns zero. + * + * @param buffer the buffer + * @param shift the positive shift offset + * @return always zero + * @see cxBufferShift() + */ +__attribute__((__nonnull__)) +int cxBufferShiftLeft( + CxBuffer *buffer, + size_t shift +); + + +/** + * Moves the position of the buffer. + * + * The new position is relative to the \p whence argument. + * + * \li \c SEEK_SET marks the start of the buffer. + * \li \c SEEK_CUR marks the current position. + * \li \c SEEK_END marks the end of the buffer. + * + * With an offset of zero, this function sets the buffer position to zero + * (\c SEEK_SET), the buffer size (\c SEEK_END) or leaves the buffer position + * unchanged (\c SEEK_CUR). + * + * @param buffer the buffer + * @param offset position offset relative to \p whence + * @param whence one of \c SEEK_SET, \c SEEK_CUR or \c SEEK_END + * @return 0 on success, non-zero if the position is invalid + * + */ +__attribute__((__nonnull__)) +int cxBufferSeek( + CxBuffer *buffer, + off_t offset, + int whence +); + +/** + * Clears the buffer by resetting the position and deleting the data. + * + * The data is deleted by zeroing it with a call to memset(). + * + * @param buffer the buffer to be cleared + */ +__attribute__((__nonnull__)) +void cxBufferClear(CxBuffer *buffer); + +/** + * Tests, if the buffer position has exceeded the buffer capacity. + * + * @param buffer the buffer to test + * @return non-zero, if the current buffer position has exceeded the last + * available byte of the buffer. + */ +__attribute__((__nonnull__)) +int cxBufferEof(CxBuffer const *buffer); + + +/** + * Ensures that the buffer has a minimum capacity. + * + * If the current capacity is not sufficient, the buffer will be extended. + * + * @param buffer the buffer + * @param capacity the minimum required capacity for this buffer + * @return 0 on success or a non-zero value on failure + */ +__attribute__((__nonnull__)) +int cxBufferMinimumCapacity( + CxBuffer *buffer, + size_t capacity +); + +/** + * Writes data to a CxBuffer. + * + * If flushing is enabled and the buffer needs to flush, the data is flushed to + * the target until the target signals that it cannot take more data by + * returning zero via the respective write function. In that case, the remaining + * data in this buffer is shifted to the beginning of this buffer so that the + * newly available space can be used to append as much data as possible. This + * function only stops writing more elements, when the flush target and this + * buffer are both incapable of taking more data or all data has been written. + * The number returned by this function is the total number of elements that + * could be written during the process. It does not necessarily mean that those + * elements are still in this buffer, because some of them could have also be + * flushed already. + * + * If automatic flushing is not enabled, the position of the buffer is increased + * by the number of bytes written. + * + * \note The signature is compatible with the fwrite() family of functions. + * + * @param ptr a pointer to the memory area containing the bytes to be written + * @param size the length of one element + * @param nitems the element count + * @param buffer the CxBuffer to write to + * @return the total count of elements written + */ +__attribute__((__nonnull__)) +size_t cxBufferWrite( + void const *ptr, + size_t size, + size_t nitems, + CxBuffer *buffer +); + +/** + * Reads data from a CxBuffer. + * + * The position of the buffer is increased by the number of bytes read. + * + * \note The signature is compatible with the fread() family of functions. + * + * @param ptr a pointer to the memory area where to store the read data + * @param size the length of one element + * @param nitems the element count + * @param buffer the CxBuffer to read from + * @return the total number of elements read + */ +__attribute__((__nonnull__)) +size_t cxBufferRead( + void *ptr, + size_t size, + size_t nitems, + CxBuffer *buffer +); + +/** + * Writes a character to a buffer. + * + * The least significant byte of the argument is written to the buffer. If the + * end of the buffer is reached and #CX_BUFFER_AUTO_EXTEND feature is enabled, + * the buffer capacity is extended by cxBufferMinimumCapacity(). If the feature is + * disabled or buffer extension fails, \c EOF is returned. + * + * On successful write, the position of the buffer is increased. + * + * @param buffer the buffer to write to + * @param c the character to write + * @return the byte that has bean written or \c EOF when the end of the stream is + * reached and automatic extension is not enabled or not possible + */ +__attribute__((__nonnull__)) +int cxBufferPut( + CxBuffer *buffer, + int c +); + +/** + * Writes a string to a buffer. + * + * @param buffer the buffer + * @param str the zero-terminated string + * @return the number of bytes written + */ +__attribute__((__nonnull__)) +size_t cxBufferPutString( + CxBuffer *buffer, + const char *str +); + +/** + * Gets a character from a buffer. + * + * The current position of the buffer is increased after a successful read. + * + * @param buffer the buffer to read from + * @return the character or \c EOF, if the end of the buffer is reached + */ +__attribute__((__nonnull__)) +int cxBufferGet(CxBuffer *buffer); + +#ifdef __cplusplus +} +#endif + +#endif // UCX_BUFFER_H diff -r a569148841ff -r efbd59642577 ucx/cx/collection.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/collection.h Fri Apr 21 21:25:32 2023 +0200 @@ -0,0 +1,138 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 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. + */ +/** + * \file collection.h + * \brief Common definitions for various collection implementations. + * \author Mike Becker + * \author Olaf Wintermann + * \version 3.0 + * \copyright 2-Clause BSD License + */ + +#ifndef UCX_COLLECTION_H +#define UCX_COLLECTION_H + +#include "allocator.h" +#include "iterator.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Special constant used for creating collections that are storing pointers. + */ +#define CX_STORE_POINTERS 0 + +/** + * A comparator function comparing two collection elements. + */ +typedef int(*cx_compare_func)( + void const *left, + void const *right +); + +/** + * Use this macro to declare common members for a collection structure. + */ +#define CX_COLLECTION_MEMBERS \ + /** \ + * The allocator to use. \ + */ \ + CxAllocator const *allocator; \ + /** \ + * The comparator function for the elements. \ + */ \ + cx_compare_func cmpfunc; \ + /** \ + * The size of each element. \ + */ \ + size_t item_size; \ + /** \ + * The number of currently stored elements. \ + */ \ + size_t size; \ + /** \ + * An optional simple destructor for the collection's elements. \ + * \ + * @attention Read the documentation of the particular collection implementation \ + * whether this destructor shall only destroy the contents or also free the memory. \ + */ \ + cx_destructor_func simple_destructor; \ + /** \ + * An optional advanced destructor for the collection's elements. \ + * \ + * @attention Read the documentation of the particular collection implementation \ + * whether this destructor shall only destroy the contents or also free the memory. \ + */ \ + cx_destructor_func2 advanced_destructor; \ + /** \ + * The pointer to additional data that is passed to the advanced destructor. \ + */ \ + void *destructor_data; \ + /** \ + * Indicates if this instance of a collection is supposed to store pointers \ + * instead of copies of the actual objects. \ + */ \ + bool store_pointer; + +/** + * Invokes the simple destructor function for a specific element. + * + * Usually only used by collection implementations. There should be no need + * to invoke this macro manually. + * + * @param c the collection + * @param e the element + */ +#define cx_invoke_simple_destructor(c, e) \ + (c)->simple_destructor((c)->store_pointer ? (*((void **) (e))) : (e)) + +/** + * Invokes the advanced destructor function for a specific element. + * + * Usually only used by collection implementations. There should be no need + * to invoke this macro manually. + * + * @param c the collection + * @param e the element + */ +#define cx_invoke_advanced_destructor(c, e) \ + (c)->advanced_destructor((c)->destructor_data, \ + (c)->store_pointer ? (*((void **) (e))) : (e)) + + +#define cx_invoke_destructor(c, e) \ + if ((c)->simple_destructor) cx_invoke_simple_destructor(c,e); \ + if ((c)->advanced_destructor) cx_invoke_advanced_destructor(c,e) + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // UCX_COLLECTION_H diff -r a569148841ff -r efbd59642577 ucx/cx/common.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/common.h Fri Apr 21 21:25:32 2023 +0200 @@ -0,0 +1,141 @@ +/* + * 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. + */ + +/** + * \file common.h + * + * \brief Common definitions and feature checks. + * + * \author Mike Becker + * \author Olaf Wintermann + * \version 3.0 + * \copyright 2-Clause BSD License + * + * \mainpage UAP Common Extensions + * Library with common and useful functions, macros and data structures. + *

      + * Latest available source:
      + * https://sourceforge.net/projects/ucx/files/ + *

      + * + *

      + * Repositories:
      + * https://sourceforge.net/p/ucx/code + * - or - + * https://develop.uap-core.de/hg/ucx + *

      + * + *

      LICENCE

      + * + * 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. + */ + +#ifndef UCX_COMMON_H +#define UCX_COMMON_H + +/** Major UCX version as integer constant. */ +#define UCX_VERSION_MAJOR 3 + +/** Minor UCX version as integer constant. */ +#define UCX_VERSION_MINOR 0 + +/** Version constant which ensures to increase monotonically. */ +#define UCX_VERSION (((UCX_VERSION_MAJOR)<<16)|UCX_VERSION_MINOR) + +// Common Includes + +#include +#include +#include +#include +#include + +/** + * Function pointer compatible with fwrite-like functions. + */ +typedef size_t (*cx_write_func)( + void const *, + size_t, + size_t, + void * +); + +/** + * Function pointer compatible with fread-like functions. + */ +typedef size_t (*cx_read_func)( + void *, + size_t, + size_t, + void * +); + + +// Compiler specific stuff + +#ifndef __GNUC__ +/** + * Removes GNU C attributes where they are not supported. + */ +#define __attribute__(x) +#endif + +#ifdef _MSC_VER + +// fix missing ssize_t definition +#include +typedef SSIZE_T ssize_t; + +// fix missing _Thread_local support +#define _Thread_local __declspec(thread) + +#endif + +#endif // UCX_COMMON_H diff -r a569148841ff -r efbd59642577 ucx/cx/compare.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/compare.h Fri Apr 21 21:25:32 2023 +0200 @@ -0,0 +1,220 @@ +/* + * 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. + */ +/** + * \file compare.h + * \brief A collection of simple compare functions. + * \author Mike Becker + * \author Olaf Wintermann + * \version 3.0 + * \copyright 2-Clause BSD License + */ + +#ifndef UCX_COMPARE_H +#define UCX_COMPARE_H + +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Compares two integers of type int. + * + * @param i1 pointer to integer one + * @param i2 pointer to integer two + * @return -1, if *i1 is less than *i2, 0 if both are equal, + * 1 if *i1 is greater than *i2 + */ +int cx_cmp_int(void const *i1, void const *i2); + +/** + * Compares two integers of type long int. + * + * @param i1 pointer to long integer one + * @param i2 pointer to long integer two + * @return -1, if *i1 is less than *i2, 0 if both are equal, + * 1 if *i1 is greater than *i2 + */ +int cx_cmp_longint(void const *i1, void const *i2); + +/** + * Compares two integers of type long long. + * + * @param i1 pointer to long long one + * @param i2 pointer to long long two + * @return -1, if *i1 is less than *i2, 0 if both are equal, + * 1 if *i1 is greater than *i2 + */ +int cx_cmp_longlong(void const *i1, void const *i2); + +/** + * Compares two integers of type int16_t. + * + * @param i1 pointer to int16_t one + * @param i2 pointer to int16_t two + * @return -1, if *i1 is less than *i2, 0 if both are equal, + * 1 if *i1 is greater than *i2 + */ +int cx_cmp_int16(void const *i1, void const *i2); + +/** + * Compares two integers of type int32_t. + * + * @param i1 pointer to int32_t one + * @param i2 pointer to int32_t two + * @return -1, if *i1 is less than *i2, 0 if both are equal, + * 1 if *i1 is greater than *i2 + */ +int cx_cmp_int32(void const *i1, void const *i2); + +/** + * Compares two integers of type int64_t. + * + * @param i1 pointer to int64_t one + * @param i2 pointer to int64_t two + * @return -1, if *i1 is less than *i2, 0 if both are equal, + * 1 if *i1 is greater than *i2 + */ +int cx_cmp_int64(void const *i1, void const *i2); + +/** + * Compares two integers of type unsigned int. + * + * @param i1 pointer to unsigned integer one + * @param i2 pointer to unsigned integer two + * @return -1, if *i1 is less than *i2, 0 if both are equal, + * 1 if *i1 is greater than *i2 + */ +int cx_cmp_uint(void const *i1, void const *i2); + +/** + * Compares two integers of type unsigned long int. + * + * @param i1 pointer to unsigned long integer one + * @param i2 pointer to unsigned long integer two + * @return -1, if *i1 is less than *i2, 0 if both are equal, + * 1 if *i1 is greater than *i2 + */ +int cx_cmp_ulongint(void const *i1, void const *i2); + +/** + * Compares two integers of type unsigned long long. + * + * @param i1 pointer to unsigned long long one + * @param i2 pointer to unsigned long long two + * @return -1, if *i1 is less than *i2, 0 if both are equal, + * 1 if *i1 is greater than *i2 + */ +int cx_cmp_ulonglong(void const *i1, void const *i2); + +/** + * Compares two integers of type uint16_t. + * + * @param i1 pointer to uint16_t one + * @param i2 pointer to uint16_t two + * @return -1, if *i1 is less than *i2, 0 if both are equal, + * 1 if *i1 is greater than *i2 + */ +int cx_cmp_uint16(void const *i1, void const *i2); + +/** + * Compares two integers of type uint32_t. + * + * @param i1 pointer to uint32_t one + * @param i2 pointer to uint32_t two + * @return -1, if *i1 is less than *i2, 0 if both are equal, + * 1 if *i1 is greater than *i2 + */ +int cx_cmp_uint32(void const *i1, void const *i2); + +/** + * Compares two integers of type uint64_t. + * + * @param i1 pointer to uint64_t one + * @param i2 pointer to uint64_t two + * @return -1, if *i1 is less than *i2, 0 if both are equal, + * 1 if *i1 is greater than *i2 + */ +int cx_cmp_uint64(void const *i1, void const *i2); + +/** + * Compares two real numbers of type float with precision 1e-6f. + * + * @param f1 pointer to float one + * @param f2 pointer to float two + * @return -1, if *f1 is less than *f2, 0 if both are equal, + * 1 if *f1 is greater than *f2 + */ + +int cx_cmp_float(void const *f1, void const *f2); + +/** + * Compares two real numbers of type double with precision 1e-14. + * + * @param d1 pointer to double one + * @param d2 pointer to double two + * @return -1, if *d1 is less than *d2, 0 if both are equal, + * 1 if *d1 is greater than *d2 + */ +int cx_cmp_double( + void const *d1, + void const *d2 +); + +/** + * Compares the integer representation of two pointers. + * + * @param ptr1 pointer to pointer one (intptr_t const*) + * @param ptr2 pointer to pointer two (intptr_t const*) + * @return -1 if *ptr1 is less than *ptr2, 0 if both are equal, + * 1 if *ptr1 is greater than *ptr2 + */ +int cx_cmp_intptr( + void const *ptr1, + void const *ptr2 +); + +/** + * Compares the unsigned integer representation of two pointers. + * + * @param ptr1 pointer to pointer one (uintptr_t const*) + * @param ptr2 pointer to pointer two (uintptr_t const*) + * @return -1 if *ptr1 is less than *ptr2, 0 if both are equal, + * 1 if *ptr1 is greater than *ptr2 + */ +int cx_cmp_uintptr( + void const *ptr1, + void const *ptr2 +); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif //UCX_COMPARE_H diff -r a569148841ff -r efbd59642577 ucx/cx/hash_key.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/hash_key.h Fri Apr 21 21:25:32 2023 +0200 @@ -0,0 +1,129 @@ +/* + * 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. + */ +/** + * \file hash_key.h + * \brief Interface for map implementations. + * \author Mike Becker + * \author Olaf Wintermann + * \version 3.0 + * \copyright 2-Clause BSD License + */ + + +#ifndef UCX_HASH_KEY_H +#define UCX_HASH_KEY_H + +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Internal structure for a key within a hash map. */ +struct cx_hash_key_s { + /** The key data. */ + void const *data; + /** + * The key data length. + */ + size_t len; + /** The hash value of the key data. */ + unsigned hash; +}; + +/** + * Type for a hash key. + */ +typedef struct cx_hash_key_s CxHashKey; + +/** + * Computes a murmur2 32 bit hash. + * + * You need to initialize \c data and \c len in the key struct. + * The hash is then directly written to that struct. + * + * \note If \c data is \c NULL, the hash is defined as 1574210520. + * + * @param key the key, the hash shall be computed for + */ +void cx_hash_murmur(CxHashKey *key); + +/** + * Computes a hash key from a string. + * + * The string needs to be zero-terminated. + * + * @param str the string + * @return the hash key + */ +__attribute__((__warn_unused_result__)) +CxHashKey cx_hash_key_str(char const *str); + +/** + * Computes a hash key from a byte array. + * + * @param bytes the array + * @param len the length + * @return the hash key + */ +__attribute__((__warn_unused_result__)) +CxHashKey cx_hash_key_bytes( + unsigned char const *bytes, + size_t len +); + +/** + * Computes a hash key for an arbitrary object. + * + * The computation uses the in-memory representation that might not be + * the same on different platforms. Therefore, this hash should not be + * used for data exchange with different machines. + * + * @param obj a pointer to an arbitrary object + * @param len the length of object in memory + * @return the hash key + */ +__attribute__((__warn_unused_result__)) +CxHashKey cx_hash_key( + void const *obj, + size_t len +); + +/** + * Computes a hash key from a UCX string. + * + * @param str the string + * @return the hash key + */ +#define cx_hash_key_cxstr(str) cx_hash_key((void*)(str).ptr, (str).length) + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // UCX_HASH_KEY_H diff -r a569148841ff -r efbd59642577 ucx/cx/hash_map.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/hash_map.h Fri Apr 21 21:25:32 2023 +0200 @@ -0,0 +1,118 @@ +/* + * 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. + */ +/** + * \file hash_map.h + * \brief Hash map implementation. + * \author Mike Becker + * \author Olaf Wintermann + * \version 3.0 + * \copyright 2-Clause BSD License + */ + +#ifndef UCX_HASH_MAP_H +#define UCX_HASH_MAP_H + +#include "map.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Internal structure for an element of a hash map. */ +struct cx_hash_map_element_s; + +/** + * Internal structure for a hash map. + */ +struct cx_hash_map_s { + /** + * Base structure for maps. + */ + struct cx_map_s base; + /** + * The buckets of this map, each containing a linked list of elements. + */ + struct cx_hash_map_element_s **buckets; + /** + * The number of buckets. + */ + size_t bucket_count; +}; + + +/** + * Creates a new hash map with the specified number of buckets. + * + * If \p buckets is zero, an implementation defined default will be used. + * + * If \p item_size is CX_STORE_POINTERS, the created map will be created as if + * cxMapStorePointers() was called immediately after creation. + * + * @note Iterators provided by this hash map implementation provide the remove operation. + * The index value of an iterator is the incremented when the iterator advanced without removal. + * In other words, when the iterator is finished, \c index==size . + * + * @param allocator the allocator to use + * @param itemsize the size of one element + * @param buckets the initial number of buckets in this hash map + * @return a pointer to the new hash map + */ +__attribute__((__nonnull__, __warn_unused_result__)) +CxMap *cxHashMapCreate( + CxAllocator const *allocator, + size_t itemsize, + size_t buckets +); + +/** + * Increases the number of buckets, if necessary. + * + * The load threshold is \c 0.75*buckets. If the element count exceeds the load + * threshold, the map will be rehashed. Otherwise, no action is performed and + * this function simply returns 0. + * + * The rehashing process ensures, that the number of buckets is at least + * 2.5 times the element count. So there is enough room for additional + * elements without the need of another soon rehashing. + * + * You can use this function after filling a map to increase access performance. + * + * @note If the specified map is not a hash map, the behavior is undefined. + * + * @param map the map to rehash + * @return zero on success, non-zero if a memory allocation error occurred + */ +__attribute__((__nonnull__)) +int cxMapRehash(CxMap *map); + + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // UCX_HASH_MAP_H diff -r a569148841ff -r efbd59642577 ucx/cx/iterator.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/iterator.h Fri Apr 21 21:25:32 2023 +0200 @@ -0,0 +1,257 @@ +/* + * 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. + */ +/** + * \file iterator.h + * \brief Interface for iterator implementations. + * \author Mike Becker + * \author Olaf Wintermann + * \version 3.0 + * \copyright 2-Clause BSD License + */ + +#ifndef UCX_ITERATOR_H +#define UCX_ITERATOR_H + +#include "common.h" + +/** + * The base of mutating and non-mutating iterators. + */ +struct cx_iterator_base_s { + /** + * True iff the iterator points to valid data. + */ + __attribute__ ((__nonnull__)) + bool (*valid)(void const *); + + /** + * Returns a pointer to the current element. + */ + __attribute__ ((__nonnull__)) + void *(*current)(void const *); + + /** + * Original implementation in case the function needs to be wrapped. + */ + __attribute__ ((__nonnull__)) + void *(*current_impl)(void const *); + + /** + * Advances the iterator. + */ + __attribute__ ((__nonnull__)) + void (*next)(void *); + + /** + * Flag current element for removal, if possible. + */ + __attribute__ ((__nonnull__)) + bool (*flag_removal)(void *); + + /** + * Indicates whether this iterator is muting. + */ + bool mutating; + + /** + * Internal flag for removing the current element when advancing. + */ + bool remove; +}; + +/** + * Internal iterator struct - use CxMutIterator. + */ +struct cx_mut_iterator_s { + + /** + * The base properties of this iterator. + */ + struct cx_iterator_base_s base; + + /** + * Handle for the current element, if required. + */ + void *elem_handle; + + /** + * Handle for the source collection, if any. + */ + void *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. + */ + void const *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. + */ + size_t index; +}; + +/** + * Mutating iterator value type. + * + * An iterator points to a certain element in an (possibly unbounded) chain of elements. + * Iterators that are based on collections (which have a defined "first" element), are supposed + * to be "position-aware", which means that they keep track of the current index within the collection. + * + * @note Objects that are pointed to by an iterator are mutable through that iterator. However, if the + * iterator is based on a collection and the underlying collection is mutated by other means than this iterator + * (e.g. elements added or removed), the iterator becomes invalid (regardless of what cxIteratorValid() returns) + * and MUST be re-obtained from the collection. + * + * @see CxIterator + */ +typedef struct cx_mut_iterator_s CxMutIterator; + +/** + * Internal iterator struct - use CxIterator. + */ +struct cx_iterator_s { + + /** + * The base properties of this iterator. + */ + struct cx_iterator_base_s base; + + /** + * Handle for the current element, if required. + */ + void *elem_handle; + + /** + * Handle for the source collection, if any. + */ + void const *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. + */ + void const *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. + */ + size_t index; +}; + +/** + * Iterator value type. + * An iterator points to a certain element in an (possibly unbounded) chain of elements. + * Iterators that are based on collections (which have a defined "first" element), are supposed + * to be "position-aware", which means that they keep track of the current index within the collection. + * + * @note Objects that are pointed to by an iterator are always mutable through that iterator. However, + * this iterator cannot mutate the collection itself (add or remove elements) and any mutation of the + * collection by other means make this iterator invalid (regardless of what cxIteratorValid() returns). + * + * @see CxMutIterator + */ +typedef struct cx_iterator_s CxIterator; + +/** + * Checks if the iterator points to valid data. + * + * This is especially false for past-the-end iterators. + * + * @param iter the iterator + * @return true iff the iterator points to valid data + */ +#define cxIteratorValid(iter) (iter).base.valid(&(iter)) + +/** + * Returns a pointer to the current element. + * + * The behavior is undefined if this iterator is invalid. + * + * @param iter the iterator + * @return a pointer to the current element + */ +#define cxIteratorCurrent(iter) (iter).base.current(&iter) + +/** + * Advances the iterator to the next element. + * + * @param iter the iterator + */ +#define cxIteratorNext(iter) (iter).base.next(&iter) + +/** + * Flags the current element for removal. + * + * @param iter the iterator + * @return false if this iterator cannot remove the element + */ +#define cxIteratorFlagRemoval(iter) (iter).base.flag_removal(&iter) + +/** + * Loops over an iterator. + * @param type the type of the elements + * @param elem the name of the iteration variable + * @param iter the iterator + */ +#define cx_foreach(type, elem, iter) \ +for (type elem; cxIteratorValid(iter) && (elem = (type)cxIteratorCurrent(iter)) != NULL ; cxIteratorNext(iter)) + +#endif // UCX_ITERATOR_H diff -r a569148841ff -r efbd59642577 ucx/cx/linked_list.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/linked_list.h Fri Apr 21 21:25:32 2023 +0200 @@ -0,0 +1,415 @@ +/* + * 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. + */ +/** + * \file linked_list.h + * \brief Linked list implementation. + * \details Also provides several low-level functions for custom linked list implementations. + * \author Mike Becker + * \author Olaf Wintermann + * \version 3.0 + * \copyright 2-Clause BSD License + */ + +#ifndef UCX_LINKED_LIST_H +#define UCX_LINKED_LIST_H + +#include "common.h" +#include "list.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Set this flag to true, if you want to disable the use of SBO for + * linked list swap operations. + */ +extern bool CX_DISABLE_LINKED_LIST_SWAP_SBO; + +/** + * Allocates a linked list for storing elements with \p item_size bytes each. + * + * If \p item_size is CX_STORE_POINTERS, the created list will be created as if + * cxListStorePointers() was called immediately after creation. + * + * @param allocator the allocator for allocating the list nodes + * (if \c NULL the cxDefaultAllocator will be used) + * @param comparator the comparator for the elements + * (if \c NULL sort and find functions will not work) + * @param item_size the size of each element in bytes + * @return the created list + */ +CxList *cxLinkedListCreate( + CxAllocator const *allocator, + cx_compare_func comparator, + size_t item_size +); + +/** + * Allocates a linked list for storing elements with \p item_size bytes each. + * + * The list will use cxDefaultAllocator and no comparator function. If you want + * to call functions that need a comparator, you must either set one immediately + * after list creation or use cxLinkedListCreate(). + * + * If \p item_size is CX_STORE_POINTERS, the created list will be created as if + * cxListStorePointers() was called immediately after creation. + * + * @param item_size the size of each element in bytes + * @return the created list + */ +#define cxLinkedListCreateSimple(item_size) \ + cxLinkedListCreate(NULL, NULL, item_size) + +/** + * Finds the node at a certain index. + * + * This function can be used to start at an arbitrary position within the list. + * If the search index is large than the start index, \p loc_advance must denote + * the location of some sort of \c next pointer (i.e. a pointer to the next node). + * But it is also possible that the search index is smaller than the start index + * (e.g. in cases where traversing a list backwards is faster) in which case + * \p loc_advance must denote the location of some sort of \c prev pointer + * (i.e. a pointer to the previous node). + * + * @param start a pointer to the start node + * @param start_index the start index + * @param loc_advance the location of the pointer to advance + * @param index the search index + * @return the node found at the specified index + */ +void *cx_linked_list_at( + void const *start, + size_t start_index, + ptrdiff_t loc_advance, + size_t index +) __attribute__((__nonnull__)); + +/** + * 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 past-one index if the element could not be found + */ +size_t cx_linked_list_find( + void const *start, + ptrdiff_t loc_advance, + ptrdiff_t loc_data, + cx_compare_func cmp_func, + void const *elem +) __attribute__((__nonnull__)); + +/** + * Finds the first node in a linked list. + * + * The function starts with the pointer denoted by \p node and traverses the list + * along a prev pointer whose location within the node struct is + * denoted by \p loc_prev. + * + * @param node a pointer to a node in the list + * @param loc_prev the location of the \c prev pointer + * @return a pointer to the first node + */ +void *cx_linked_list_first( + void const *node, + ptrdiff_t loc_prev +) __attribute__((__nonnull__)); + +/** + * Finds the last node in a linked list. + * + * The function starts with the pointer denoted by \p node and traverses the list + * along a next pointer whose location within the node struct is + * denoted by \p loc_next. + * + * @param node a pointer to a node in the list + * @param loc_next the location of the \c next pointer + * @return a pointer to the last node + */ +void *cx_linked_list_last( + void const *node, + ptrdiff_t loc_next +) __attribute__((__nonnull__)); + +/** + * Finds the predecessor of a node in case it is not linked. + * + * \remark If \p node is not contained in the list starting with \p begin, the behavior is undefined. + * + * @param begin the node where to start the search + * @param loc_next the location of the \c next pointer + * @param node the successor of the node to find + * @return the node or \c NULL if \p node has no predecessor + */ +void *cx_linked_list_prev( + void const *begin, + ptrdiff_t loc_next, + void const *node +) __attribute__((__nonnull__)); + +/** + * Adds a new node to a linked list. + * The node must not be part of any list already. + * + * \remark One of the pointers \p begin or \p end may be \c NULL, but not both. + * + * @param begin a pointer to the begin node pointer (if your list has one) + * @param end a pointer to the end node pointer (if your list has one) + * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one) + * @param loc_next the location of a \c next pointer within your node struct (required) + * @param new_node a pointer to the node that shall be appended + */ +void cx_linked_list_add( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *new_node +) __attribute__((__nonnull__(5))); + +/** + * Prepends a new node to a linked list. + * The node must not be part of any list already. + * + * \remark One of the pointers \p begin or \p end may be \c NULL, but not both. + * + * @param begin a pointer to the begin node pointer (if your list has one) + * @param end a pointer to the end node pointer (if your list has one) + * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one) + * @param loc_next the location of a \c next pointer within your node struct (required) + * @param new_node a pointer to the node that shall be prepended + */ +void cx_linked_list_prepend( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *new_node +) __attribute__((__nonnull__(5))); + +/** + * Links two nodes. + * + * @param left the new predecessor of \p right + * @param right the new successor of \p left + * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one) + * @param loc_next the location of a \c next pointer within your node struct (required) + */ +void cx_linked_list_link( + void *left, + void *right, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +) __attribute__((__nonnull__)); + +/** + * Unlinks two nodes. + * + * If right is not the successor of left, the behavior is undefined. + * + * @param left the predecessor of \p right + * @param right the successor of \p left + * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one) + * @param loc_next the location of a \c next pointer within your node struct (required) + */ +void cx_linked_list_unlink( + void *left, + void *right, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +) __attribute__((__nonnull__)); + +/** + * Inserts a new node after a given node of a linked list. + * The new node must not be part of any list already. + * + * \note If you specify \c NULL as the \p node to insert after, this function needs either the \p begin or + * the \p end pointer to determine the start of the list. Then the new node will be prepended to the list. + * + * @param begin a pointer to the begin node pointer (if your list has one) + * @param end a pointer to the end node pointer (if your list has one) + * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one) + * @param loc_next the location of a \c next pointer within your node struct (required) + * @param node the node after which to insert (\c NULL if you want to prepend the node to the list) + * @param new_node a pointer to the node that shall be prepended + */ +void cx_linked_list_insert( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *node, + void *new_node +) __attribute__((__nonnull__(6))); + +/** + * Inserts a chain of nodes after a given node of a linked list. + * The chain must not be part of any list already. + * + * If you do not explicitly specify the end of the chain, it will be determined by traversing + * the \c next pointer. + * + * \note If you specify \c NULL as the \p node to insert after, this function needs either the \p begin or + * the \p end pointer to determine the start of the list. If only the \p end pointer is specified, you also need + * to provide a valid \p loc_prev location. + * Then the chain will be prepended to the list. + * + * @param begin a pointer to the begin node pointer (if your list has one) + * @param end a pointer to the end node pointer (if your list has one) + * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one) + * @param loc_next the location of a \c next pointer within your node struct (required) + * @param node the node after which to insert (\c NULL to prepend the chain to the list) + * @param insert_begin a pointer to the first node of the chain that shall be inserted + * @param insert_end a pointer to the last node of the chain (or NULL if the last node shall be determined) + */ +void cx_linked_list_insert_chain( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *node, + void *insert_begin, + void *insert_end +) __attribute__((__nonnull__(6))); + +/** + * Removes a node from the linked list. + * + * If the node to remove is the begin (resp. end) node of the list and if \p begin (resp. \p end) + * addresses are provided, the pointers are adjusted accordingly. + * + * The following combinations of arguments are valid (more arguments are optional): + * \li \p loc_next and \p loc_prev (ancestor node is determined by using the prev pointer, overall O(1) performance) + * \li \p loc_next and \p begin (ancestor node is determined by list traversal, overall O(n) performance) + * + * \remark The \c next and \c prev pointers of the removed node are not cleared by this function and may still be used + * to traverse to a former adjacent node in the list. + * + * @param begin a pointer to the begin node pointer (optional) + * @param end a pointer to the end node pointer (optional) + * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one) + * @param loc_next the location of a \c next pointer within your node struct (required) + * @param node the node to remove + */ +void cx_linked_list_remove( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *node +) __attribute__((__nonnull__(5))); + + +/** + * Determines the size of a linked list starting with \p node. + * @param node the first node + * @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 + */ +size_t cx_linked_list_size( + void const *node, + ptrdiff_t loc_next +); + +/** + * Sorts a linked list based on a comparison function. + * + * This function can work with linked lists of the following structure: + * \code + * typedef struct node node; + * struct node { + * node* prev; + * node* next; + * my_payload data; + * } + * \endcode + * + * @note This is a recursive function with at most logarithmic recursion depth. + * + * @param begin a pointer to the begin node pointer (required) + * @param end a pointer to the end node pointer (optional) + * @param loc_prev the location of a \c prev pointer within your node struct (negative if not present) + * @param loc_next the location of a \c next pointer within your node struct (required) + * @param loc_data the location of the \c data pointer within your node struct + * @param cmp_func the compare function defining the sort order + */ +void cx_linked_list_sort( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + ptrdiff_t loc_data, + cx_compare_func cmp_func +) __attribute__((__nonnull__(1, 6))); + + +/** + * Compares two lists element wise. + * + * \note Both list must have the same structure. + * + * @param begin_left the begin of the left list (\c NULL denotes an empty list) + * @param begin_right the begin of the right list (\c NULL denotes an empty list) + * @param loc_advance the location of the pointer to advance + * @param loc_data the location of the \c data pointer within your node struct + * @param cmp_func the function to compare the elements + * @return the first non-zero result of invoking \p cmp_func or: negative if the left list is smaller than the + * right list, positive if the left list is larger than the right list, zero if both lists are equal. + */ +int cx_linked_list_compare( + void const *begin_left, + void const *begin_right, + ptrdiff_t loc_advance, + ptrdiff_t loc_data, + cx_compare_func cmp_func +) __attribute__((__nonnull__(5))); + +/** + * Reverses the order of the nodes in a linked list. + * + * @param begin a pointer to the begin node pointer (required) + * @param end a pointer to the end node pointer (optional) + * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one) + * @param loc_next the location of a \c next pointer within your node struct (required) + */ +void cx_linked_list_reverse( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +) __attribute__((__nonnull__(1))); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // UCX_LINKED_LIST_H diff -r a569148841ff -r efbd59642577 ucx/cx/list.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/list.h Fri Apr 21 21:25:32 2023 +0200 @@ -0,0 +1,638 @@ +/* + * 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. + */ +/** + * \file list.h + * \brief Interface for list implementations. + * \author Mike Becker + * \author Olaf Wintermann + * \version 3.0 + * \copyright 2-Clause BSD License + */ + +#ifndef UCX_LIST_H +#define UCX_LIST_H + +#include "common.h" +#include "collection.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * List class type. + */ +typedef struct cx_list_class_s cx_list_class; + +/** + * Structure for holding the base data of a list. + */ +struct cx_list_s { + CX_COLLECTION_MEMBERS + /** + * The list class definition. + */ + cx_list_class const *cl; + /** + * The actual implementation in case the list class is delegating. + */ + cx_list_class const *climpl; +}; + +/** + * The class definition for arbitrary lists. + */ +struct cx_list_class_s { + /** + * Destructor function. + */ + void (*destructor)(struct cx_list_s *list); + + /** + * Member function for inserting a single elements. + * Implementors SHOULD see to performant implementations for corner cases. + */ + int (*insert_element)( + struct cx_list_s *list, + size_t index, + void const *data + ); + + /** + * Member function for inserting multiple elements. + * Implementors SHOULD see to performant implementations for corner cases. + */ + size_t (*insert_array)( + struct cx_list_s *list, + size_t index, + void const *data, + size_t n + ); + + /** + * Member function for inserting an element relative to an iterator position. + */ + int (*insert_iter)( + struct cx_mut_iterator_s *iter, + void const *elem, + int prepend + ); + + /** + * Member function for removing an element. + */ + int (*remove)( + struct cx_list_s *list, + size_t index + ); + + /** + * Member function for removing all elements. + */ + void (*clear)(struct cx_list_s *list); + + /** + * Member function for swapping two elements. + */ + int (*swap)( + struct cx_list_s *list, + size_t i, + size_t j + ); + + /** + * Member function for element lookup. + */ + void *(*at)( + struct cx_list_s const *list, + size_t index + ); + + /** + * Member function for finding an element. + */ + size_t (*find)( + struct cx_list_s const *list, + void const *elem + ); + + /** + * Member function for sorting the list in place. + */ + void (*sort)(struct cx_list_s *list); + + /** + * Member function for comparing this list to another list of the same type. + */ + int (*compare)( + struct cx_list_s const *list, + struct cx_list_s const *other + ); + + /** + * Member function for reversing the order of the items. + */ + void (*reverse)(struct cx_list_s *list); + + /** + * Member function for returning an iterator pointing to the specified index. + */ + struct cx_iterator_s (*iterator)( + struct cx_list_s const *list, + size_t index, + bool backward + ); +}; + +/** + * 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() + */ +__attribute__((__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() + */ +__attribute__((__nonnull__)) +void cxListStorePointers(CxList *list); + +/** + * 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() + */ +__attribute__((__nonnull__)) +static inline bool cxListIsStoringPointers(CxList const *list) { + return list->store_pointer; +} + +/** + * Returns the number of elements currently stored in the list. + * + * @param list the list + * @return the number of currently stored elements + */ +__attribute__((__nonnull__)) +static inline size_t cxListSize(CxList const *list) { + return list->size; +} + +/** + * Adds an item to the end of the list. + * + * @param list the list + * @param elem a pointer to the element to add + * @return zero on success, non-zero on memory allocation failure + * @see cxListAddArray() + */ +__attribute__((__nonnull__)) +static inline int cxListAdd( + CxList *list, + void const *elem +) { + return list->cl->insert_element(list, list->size, elem); +} + +/** + * Adds multiple items to the end of the list. + * + * This method is more efficient than invoking cxListAdd() multiple times. + * + * If there is not enough memory to add all elements, the returned value is + * less than \p n. + * + * If this list is storing pointers instead of objects \p array is expected to + * be an array of pointers. + * + * @param list the list + * @param array a pointer to the elements to add + * @param n the number of elements to add + * @return the number of added elements + */ +__attribute__((__nonnull__)) +static inline size_t cxListAddArray( + CxList *list, + void const *array, + size_t n +) { + return list->cl->insert_array(list, list->size, array, n); +} + +/** + * Inserts an item at the specified index. + * + * If \p index equals the list \c size, this is effectively cxListAdd(). + * + * @param list the list + * @param index the index the element shall have + * @param elem a pointer to the element to add + * @return zero on success, non-zero on memory allocation failure + * or when the index is out of bounds + * @see cxListInsertAfter() + * @see cxListInsertBefore() + */ +__attribute__((__nonnull__)) +static inline int cxListInsert( + CxList *list, + size_t index, + void const *elem +) { + return list->cl->insert_element(list, index, elem); +} + +/** + * Inserts multiple items to the list at the specified index. + * If \p index equals the list size, this is effectively cxListAddArray(). + * + * This method is usually more efficient than invoking cxListInsert() + * multiple times. + * + * If there is not enough memory to add all elements, the returned value is + * less than \p n. + * + * If this list is storing pointers instead of objects \p array is expected to + * be an array of pointers. + * + * @param list the list + * @param index the index where to add the elements + * @param array a pointer to the elements to add + * @param n the number of elements to add + * @return the number of added elements + */ +__attribute__((__nonnull__)) +static inline size_t cxListInsertArray( + CxList *list, + size_t index, + void const *array, + size_t n +) { + return list->cl->insert_array(list, index, array, n); +} + +/** + * Inserts an element after the current location of the specified iterator. + * + * The used iterator remains operational, but all other active iterators should + * be considered invalidated. + * + * If \p iter is not a list iterator, the behavior is undefined. + * If \p iter is a past-the-end iterator, the new element gets appended to the list. + * + * @param iter an iterator + * @param elem the element to insert + * @return zero on success, non-zero on memory allocation failure + * @see cxListInsert() + * @see cxListInsertBefore() + */ +__attribute__((__nonnull__)) +static inline int cxListInsertAfter( + CxMutIterator *iter, + void const *elem +) { + return ((struct cx_list_s *) iter->src_handle)->cl->insert_iter(iter, elem, 0); +} + +/** + * Inserts an element before the current location of the specified iterator. + * + * The used iterator remains operational, but all other active iterators should + * be considered invalidated. + * + * If \p iter is not a list iterator, the behavior is undefined. + * If \p iter is a past-the-end iterator, the new element gets appended to the list. + * + * @param iter an iterator + * @param elem the element to insert + * @return zero on success, non-zero on memory allocation failure + * @see cxListInsert() + * @see cxListInsertAfter() + */ +__attribute__((__nonnull__)) +static inline int cxListInsertBefore( + CxMutIterator *iter, + void const *elem +) { + return ((struct cx_list_s *) iter->src_handle)->cl->insert_iter(iter, elem, 1); +} + +/** + * Removes the element at the specified index. + * + * If an element destructor function is specified, it is called before + * removing the element. + * + * @param list the list + * @param index the index of the element + * @return zero on success, non-zero if the index is out of bounds + */ +__attribute__((__nonnull__)) +static inline int cxListRemove( + CxList *list, + size_t index +) { + return list->cl->remove(list, index); +} + +/** + * Removes all elements from this list. + * + * If an element destructor function is specified, it is called for each + * element before removing them. + * + * @param list the list + */ +__attribute__((__nonnull__)) +static inline void cxListClear(CxList *list) { + list->cl->clear(list); +} + +/** + * Swaps two items in the list. + * + * Implementations should only allocate temporary memory for the swap, if + * it is necessary. + * + * @param list the list + * @param i the index of the first element + * @param j the index of the second element + * @return zero on success, non-zero if one of the indices is out of bounds + */ +__attribute__((__nonnull__)) +static inline int cxListSwap( + CxList *list, + size_t i, + size_t j +) { + return list->cl->swap(list, i, j); +} + +/** + * Returns a pointer to the element at the specified index. + * + * @param list the list + * @param index the index of the element + * @return a pointer to the element or \c NULL if the index is out of bounds + */ +__attribute__((__nonnull__)) +static inline void *cxListAt( + CxList *list, + size_t index +) { + return list->cl->at(list, index); +} + +/** + * Returns an iterator pointing to the item at the specified index. + * + * The returned iterator is position-aware. + * + * If the index is out of range, a past-the-end iterator will be returned. + * + * @param list the list + * @param index the index where the iterator shall point at + * @return a new iterator + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline CxIterator cxListIteratorAt( + CxList const *list, + size_t index +) { + return list->cl->iterator(list, index, false); +} + +/** + * Returns a backwards iterator pointing to the item at the specified index. + * + * The returned iterator is position-aware. + * + * If the index is out of range, a past-the-end iterator will be returned. + * + * @param list the list + * @param index the index where the iterator shall point at + * @return a new iterator + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline CxIterator cxListBackwardsIteratorAt( + CxList const *list, + size_t index +) { + return list->cl->iterator(list, index, true); +} + +/** + * Returns a mutating iterator pointing to the item at the specified index. + * + * The returned iterator is position-aware. + * + * If the index is out of range, a past-the-end iterator will be returned. + * + * @param list the list + * @param index the index where the iterator shall point at + * @return a new iterator + */ +__attribute__((__nonnull__, __warn_unused_result__)) +CxMutIterator cxListMutIteratorAt( + CxList *list, + size_t index +); + +/** + * Returns a mutating backwards iterator pointing to the item at the + * specified index. + * + * The returned iterator is position-aware. + * + * If the index is out of range, a past-the-end iterator will be returned. + * + * @param list the list + * @param index the index where the iterator shall point at + * @return a new iterator + */ +__attribute__((__nonnull__, __warn_unused_result__)) +CxMutIterator cxListMutBackwardsIteratorAt( + CxList *list, + size_t index +); + +/** + * Returns an iterator pointing to the first item of the list. + * + * The returned iterator is position-aware. + * + * If the list is empty, a past-the-end iterator will be returned. + * + * @param list the list + * @return a new iterator + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline CxIterator cxListIterator(CxList const *list) { + return list->cl->iterator(list, 0, false); +} + +/** + * Returns a mutating iterator pointing to the first item of the list. + * + * The returned iterator is position-aware. + * + * If the list is empty, a past-the-end iterator will be returned. + * + * @param list the list + * @return a new iterator + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline CxMutIterator cxListMutIterator(CxList *list) { + return cxListMutIteratorAt(list, 0); +} + + +/** + * Returns a backwards iterator pointing to the last item of the list. + * + * The returned iterator is position-aware. + * + * If the list is empty, a past-the-end iterator will be returned. + * + * @param list the list + * @return a new iterator + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline CxIterator cxListBackwardsIterator(CxList const *list) { + return list->cl->iterator(list, list->size - 1, true); +} + +/** + * Returns a mutating backwards iterator pointing to the last item of the list. + * + * The returned iterator is position-aware. + * + * If the list is empty, a past-the-end iterator will be returned. + * + * @param list the list + * @return a new iterator + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline CxMutIterator cxListMutBackwardsIterator(CxList *list) { + return cxListMutBackwardsIteratorAt(list, list->size - 1); +} + +/** + * Returns the index of the first element that equals \p elem. + * + * Determining equality is performed by the list's comparator function. + * + * @param list the list + * @param elem the element to find + * @return the index of the element or \c (size+1) if the element is not found + */ +__attribute__((__nonnull__)) +static inline size_t cxListFind( + CxList const *list, + void const *elem +) { + return list->cl->find(list, elem); +} + +/** + * Sorts the list in place. + * + * \remark The underlying sort algorithm is implementation defined. + * + * @param list the list + */ +__attribute__((__nonnull__)) +static inline void cxListSort(CxList *list) { + list->cl->sort(list); +} + +/** + * Reverses the order of the items. + * + * @param list the list + */ +__attribute__((__nonnull__)) +static inline void cxListReverse(CxList *list) { + list->cl->reverse(list); +} + +/** + * Compares a list to another list of the same type. + * + * First, the list sizes are compared. + * If they match, the lists are compared element-wise. + * + * @param list the list + * @param other the list to compare to + * @return zero, if both lists are equal element wise, + * negative if the first list is smaller, positive if the first list is larger + */ +__attribute__((__nonnull__)) +int cxListCompare( + CxList const *list, + CxList const *other +); + +/** + * Deallocates the memory of the specified list structure. + * + * Also calls content a destructor function, depending on the configuration + * in CxList.content_destructor_type. + * + * This function itself is a destructor function for the CxList. + * + * @param list the list which shall be destroyed + */ +__attribute__((__nonnull__)) +void cxListDestroy(CxList *list); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // UCX_LIST_H diff -r a569148841ff -r efbd59642577 ucx/cx/map.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/map.h Fri Apr 21 21:25:32 2023 +0200 @@ -0,0 +1,1145 @@ +/* + * 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. + */ +/** + * \file map.h + * \brief Interface for map implementations. + * \author Mike Becker + * \author Olaf Wintermann + * \version 3.0 + * \copyright 2-Clause BSD License + */ + +#ifndef UCX_MAP_H +#define UCX_MAP_H + +#include "common.h" +#include "collection.h" +#include "string.h" +#include "hash_key.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Type for the UCX map. */ +typedef struct cx_map_s CxMap; + +/** Type for a map entry. */ +typedef struct cx_map_entry_s CxMapEntry; + +/** Type for map class definitions. */ +typedef struct cx_map_class_s cx_map_class; + +/** Structure for the UCX map. */ +struct cx_map_s { + CX_COLLECTION_MEMBERS + /** The map class definition. */ + cx_map_class *cl; +}; + +/** + * The class definition for arbitrary maps. + */ +struct cx_map_class_s { + /** + * Deallocates the entire memory. + */ + __attribute__((__nonnull__)) + void (*destructor)(struct cx_map_s *map); + + /** + * Removes all elements. + */ + __attribute__((__nonnull__)) + void (*clear)(struct cx_map_s *map); + + /** + * Add or overwrite an element. + */ + __attribute__((__nonnull__)) + int (*put)( + CxMap *map, + CxHashKey key, + void *value + ); + + /** + * Returns an element. + */ + __attribute__((__nonnull__, __warn_unused_result__)) + void *(*get)( + CxMap const *map, + CxHashKey key + ); + + /** + * Removes an element. + */ + __attribute__((__nonnull__)) + void *(*remove)( + CxMap *map, + CxHashKey key, + bool destroy + ); + + /** + * Iterator over the key/value pairs. + */ + __attribute__((__nonnull__, __warn_unused_result__)) + CxIterator (*iterator)(CxMap const *map); + + /** + * Iterator over the keys. + */ + __attribute__((__nonnull__, __warn_unused_result__)) + CxIterator (*iterator_keys)(CxMap const *map); + + /** + * Iterator over the values. + */ + __attribute__((__nonnull__, __warn_unused_result__)) + CxIterator (*iterator_values)(CxMap const *map); + + /** + * Mutating iterator over the key/value pairs. + */ + __attribute__((__nonnull__, __warn_unused_result__)) + CxMutIterator (*mut_iterator)(CxMap *map); + + /** + * Mutating iterator over the keys. + */ + __attribute__((__nonnull__, __warn_unused_result__)) + CxMutIterator (*mut_iterator_keys)(CxMap *map); + + /** + * Mutating iterator over the values. + */ + __attribute__((__nonnull__, __warn_unused_result__)) + CxMutIterator (*mut_iterator_values)(CxMap *map); +}; + +/** + * A map entry. + */ +struct cx_map_entry_s { + /** + * A pointer to the key. + */ + CxHashKey const *key; + /** + * A pointer to the value. + */ + void *value; +}; + +/** + * 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() + */ +__attribute__((__nonnull__)) +static inline void cxMapStoreObjects(CxMap *map) { + map->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() + */ +__attribute__((__nonnull__)) +static inline void cxMapStorePointers(CxMap *map) { + map->store_pointer = true; + map->item_size = sizeof(void *); +} + + +/** + * Deallocates the memory of the specified map. + * + * @param map the map to be destroyed + */ +__attribute__((__nonnull__)) +static inline void cxMapDestroy(CxMap *map) { + map->cl->destructor(map); +} + + +/** + * Clears a map by removing all elements. + * + * @param map the map to be cleared + */ +__attribute__((__nonnull__)) +static inline void cxMapClear(CxMap *map) { + map->cl->clear(map); +} + + +// TODO: set-like map operations (union, intersect, difference) + +/** + * Creates a value iterator for a map. + * + * \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. + * + * @param map the map to create the iterator for + * @return an iterator for the currently stored values + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline CxIterator cxMapIteratorValues(CxMap *map) { + return map->cl->iterator_values(map); +} + +/** + * Creates a key iterator for a map. + * + * The elements of the iterator are keys of type 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. + * + * @param map the map to create the iterator for + * @return an iterator for the currently stored keys + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline CxIterator cxMapIteratorKeys(CxMap *map) { + return map->cl->iterator_keys(map); +} + +/** + * Creates an iterator for a map. + * + * The elements of the iterator are key/value pairs of type 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. + * + * @param map the map to create the iterator for + * @return an iterator for the currently stored entries + * @see cxMapIteratorKeys() + * @see cxMapIteratorValues() + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline CxIterator cxMapIterator(CxMap *map) { + return map->cl->iterator(map); +} + + +/** + * Creates a mutating iterator over the values of a map. + * + * \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. + * + * @param map the map to create the iterator for + * @return an iterator for the currently stored values + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline CxMutIterator cxMapMutIteratorValues(CxMap *map) { + return map->cl->mut_iterator_values(map); +} + +/** + * Creates a mutating iterator over the keys of a map. + * + * The elements of the iterator are keys of type 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. + * + * @param map the map to create the iterator for + * @return an iterator for the currently stored keys + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline CxMutIterator cxMapMutIteratorKeys(CxMap *map) { + return map->cl->mut_iterator_keys(map); +} + +/** + * Creates a mutating iterator for a map. + * + * The elements of the iterator are key/value pairs of type 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. + * + * @param map the map to create the iterator for + * @return an iterator for the currently stored entries + * @see cxMapMutIteratorKeys() + * @see cxMapMutIteratorValues() + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline CxMutIterator cxMapMutIterator(CxMap *map) { + return map->cl->mut_iterator(map); +} + +#ifdef __cplusplus +} // end the extern "C" block here, because we want to start overloading + +/** + * Puts a key/value-pair into the map. + * + * @param map the map + * @param key the key + * @param value the value + * @return 0 on success, non-zero value on failure + */ +__attribute__((__nonnull__)) +static inline int cxMapPut( + CxMap *map, + CxHashKey const &key, + void *value +) { + return map->cl->put(map, key, value); +} + + +/** + * Puts a key/value-pair into the map. + * + * @param map the map + * @param key the key + * @param value the value + * @return 0 on success, non-zero value on failure + */ +__attribute__((__nonnull__)) +static inline int cxMapPut( + CxMap *map, + cxstring const &key, + void *value +) { + return map->cl->put(map, cx_hash_key_cxstr(key), value); +} + +/** + * Puts a key/value-pair into the map. + * + * @param map the map + * @param key the key + * @param value the value + * @return 0 on success, non-zero value on failure + */ +__attribute__((__nonnull__)) +static inline int cxMapPut( + CxMap *map, + cxmutstr const &key, + void *value +) { + return map->cl->put(map, cx_hash_key_cxstr(key), value); +} + +/** + * Puts a key/value-pair into the map. + * + * @param map the map + * @param key the key + * @param value the value + * @return 0 on success, non-zero value on failure + */ +__attribute__((__nonnull__)) +static inline int cxMapPut( + CxMap *map, + char const *key, + void *value +) { + return map->cl->put(map, cx_hash_key_str(key), value); +} + +/** + * Retrieves a value by using a key. + * + * @param map the map + * @param key the key + * @return the value + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cxMapGet( + CxMap const *map, + CxHashKey const &key +) { + return map->cl->get(map, key); +} + +/** + * Retrieves a value by using a key. + * + * @param map the map + * @param key the key + * @return the value + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cxMapGet( + CxMap const *map, + cxstring const &key +) { + return map->cl->get(map, cx_hash_key_cxstr(key)); +} + +/** + * Retrieves a value by using a key. + * + * @param map the map + * @param key the key + * @return the value + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cxMapGet( + CxMap const *map, + cxmutstr const &key +) { + return map->cl->get(map, cx_hash_key_cxstr(key)); +} + +/** + * Retrieves a value by using a key. + * + * @param map the map + * @param key the key + * @return the value + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cxMapGet( + CxMap const *map, + char const *key +) { + return map->cl->get(map, cx_hash_key_str(key)); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * Always invokes the destructor function, if any, on the removed element. + * If this map is storing pointers and you just want to retrieve the pointer + * without invoking the destructor, use cxMapRemoveAndGet(). + * If you just want to detach the element from the map without invoking the + * destructor or returning the element, use cxMapDetach(). + * + * @param map the map + * @param key the key + * @see cxMapRemoveAndGet() + * @see cxMapDetach() + */ +__attribute__((__nonnull__)) +static inline void cxMapRemove( + CxMap *map, + CxHashKey const &key +) { + (void) map->cl->remove(map, key, true); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * Always invokes the destructor function, if any, on the removed element. + * If this map is storing pointers and you just want to retrieve the pointer + * without invoking the destructor, use cxMapRemoveAndGet(). + * If you just want to detach the element from the map without invoking the + * destructor or returning the element, use cxMapDetach(). + * + * @param map the map + * @param key the key + * @see cxMapRemoveAndGet() + * @see cxMapDetach() + */ +__attribute__((__nonnull__)) +static inline void cxMapRemove( + CxMap *map, + cxstring const &key +) { + (void) map->cl->remove(map, cx_hash_key_cxstr(key), true); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * Always invokes the destructor function, if any, on the removed element. + * If this map is storing pointers and you just want to retrieve the pointer + * without invoking the destructor, use cxMapRemoveAndGet(). + * If you just want to detach the element from the map without invoking the + * destructor or returning the element, use cxMapDetach(). + * + * @param map the map + * @param key the key + * @see cxMapRemoveAndGet() + * @see cxMapDetach() + */ +__attribute__((__nonnull__)) +static inline void cxMapRemove( + CxMap *map, + cxmutstr const &key +) { + (void) map->cl->remove(map, cx_hash_key_cxstr(key), true); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * Always invokes the destructor function, if any, on the removed element. + * If this map is storing pointers and you just want to retrieve the pointer + * without invoking the destructor, use cxMapRemoveAndGet(). + * If you just want to detach the element from the map without invoking the + * destructor or returning the element, use cxMapDetach(). + * + * @param map the map + * @param key the key + * @see cxMapRemoveAndGet() + * @see cxMapDetach() + */ +__attribute__((__nonnull__)) +static inline void cxMapRemove( + CxMap *map, + char const *key +) { + (void) map->cl->remove(map, cx_hash_key_str(key), true); +} + +/** + * Detaches a key/value-pair from the map by using the key + * without invoking the destructor. + * + * In general, you should only use this function if the map does not own + * the data and there is a valid reference to the data somewhere else + * in the program. In all other cases it is preferable to use + * cxMapRemove() or cxMapRemoveAndGet(). + * + * @param map the map + * @param key the key + * @see cxMapRemove() + * @see cxMapRemoveAndGet() + */ +__attribute__((__nonnull__)) +static inline void cxMapDetach( + CxMap *map, + CxHashKey const &key +) { + (void) map->cl->remove(map, key, false); +} + +/** + * Detaches a key/value-pair from the map by using the key + * without invoking the destructor. + * + * In general, you should only use this function if the map does not own + * the data and there is a valid reference to the data somewhere else + * in the program. In all other cases it is preferable to use + * cxMapRemove() or cxMapRemoveAndGet(). + * + * @param map the map + * @param key the key + * @see cxMapRemove() + * @see cxMapRemoveAndGet() + */ +__attribute__((__nonnull__)) +static inline void cxMapDetach( + CxMap *map, + cxstring const &key +) { + (void) map->cl->remove(map, cx_hash_key_cxstr(key), false); +} + +/** + * Detaches a key/value-pair from the map by using the key + * without invoking the destructor. + * + * In general, you should only use this function if the map does not own + * the data and there is a valid reference to the data somewhere else + * in the program. In all other cases it is preferable to use + * cxMapRemove() or cxMapRemoveAndGet(). + * + * @param map the map + * @param key the key + * @see cxMapRemove() + * @see cxMapRemoveAndGet() + */ +__attribute__((__nonnull__)) +static inline void cxMapDetach( + CxMap *map, + cxmutstr const &key +) { + (void) map->cl->remove(map, cx_hash_key_cxstr(key), false); +} + +/** + * Detaches a key/value-pair from the map by using the key + * without invoking the destructor. + * + * In general, you should only use this function if the map does not own + * the data and there is a valid reference to the data somewhere else + * in the program. In all other cases it is preferable to use + * cxMapRemove() or cxMapRemoveAndGet(). + * + * @param map the map + * @param key the key + * @see cxMapRemove() + * @see cxMapRemoveAndGet() + */ +__attribute__((__nonnull__)) +static inline void cxMapDetach( + CxMap *map, + char const *key +) { + (void) map->cl->remove(map, cx_hash_key_str(key), false); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * This function can be used when the map is storing pointers, + * in order to retrieve the pointer from the map without invoking + * any destructor function. Sometimes you do not want the pointer + * to be returned - in that case (instead of suppressing the "unused + * result" warning) you can use cxMapDetach(). + * + * If this map is not storing pointers, this function behaves like + * cxMapRemove() and returns \c NULL. + * + * @param map the map + * @param key the key + * @return the stored pointer or \c NULL if either the key is not present + * in the map or the map is not storing pointers + * @see cxMapStorePointers() + * @see cxMapDetach() + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cxMapRemoveAndGet( + CxMap *map, + CxHashKey key +) { + return map->cl->remove(map, key, !map->store_pointer); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * This function can be used when the map is storing pointers, + * in order to retrieve the pointer from the map without invoking + * any destructor function. Sometimes you do not want the pointer + * to be returned - in that case (instead of suppressing the "unused + * result" warning) you can use cxMapDetach(). + * + * If this map is not storing pointers, this function behaves like + * cxMapRemove() and returns \c NULL. + * + * @param map the map + * @param key the key + * @return the stored pointer or \c NULL if either the key is not present + * in the map or the map is not storing pointers + * @see cxMapStorePointers() + * @see cxMapDetach() + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cxMapRemoveAndGet( + CxMap *map, + cxstring key +) { + return map->cl->remove(map, cx_hash_key_cxstr(key), !map->store_pointer); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * This function can be used when the map is storing pointers, + * in order to retrieve the pointer from the map without invoking + * any destructor function. Sometimes you do not want the pointer + * to be returned - in that case (instead of suppressing the "unused + * result" warning) you can use cxMapDetach(). + * + * If this map is not storing pointers, this function behaves like + * cxMapRemove() and returns \c NULL. + * + * @param map the map + * @param key the key + * @return the stored pointer or \c NULL if either the key is not present + * in the map or the map is not storing pointers + * @see cxMapStorePointers() + * @see cxMapDetach() + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cxMapRemoveAndGet( + CxMap *map, + cxmutstr key +) { + return map->cl->remove(map, cx_hash_key_cxstr(key), !map->store_pointer); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * This function can be used when the map is storing pointers, + * in order to retrieve the pointer from the map without invoking + * any destructor function. Sometimes you do not want the pointer + * to be returned - in that case (instead of suppressing the "unused + * result" warning) you can use cxMapDetach(). + * + * If this map is not storing pointers, this function behaves like + * cxMapRemove() and returns \c NULL. + * + * @param map the map + * @param key the key + * @return the stored pointer or \c NULL if either the key is not present + * in the map or the map is not storing pointers + * @see cxMapStorePointers() + * @see cxMapDetach() + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cxMapRemoveAndGet( + CxMap *map, + char const *key +) { + return map->cl->remove(map, cx_hash_key_str(key), !map->store_pointer); +} + +#else // __cplusplus + +/** + * Puts a key/value-pair into the map. + * + * @param map the map + * @param key the key + * @param value the value + * @return 0 on success, non-zero value on failure + */ +__attribute__((__nonnull__)) +static inline int cx_map_put( + CxMap *map, + CxHashKey key, + void *value +) { + return map->cl->put(map, key, value); +} + +/** + * Puts a key/value-pair into the map. + * + * @param map the map + * @param key the key + * @param value the value + * @return 0 on success, non-zero value on failure + */ +__attribute__((__nonnull__)) +static inline int cx_map_put_cxstr( + CxMap *map, + cxstring key, + void *value +) { + return map->cl->put(map, cx_hash_key_cxstr(key), value); +} + +/** + * Puts a key/value-pair into the map. + * + * @param map the map + * @param key the key + * @param value the value + * @return 0 on success, non-zero value on failure + */ +__attribute__((__nonnull__)) +static inline int cx_map_put_mustr( + CxMap *map, + cxmutstr key, + void *value +) { + return map->cl->put(map, cx_hash_key_cxstr(key), value); +} + +/** + * Puts a key/value-pair into the map. + * + * @param map the map + * @param key the key + * @param value the value + * @return 0 on success, non-zero value on failure + */ +__attribute__((__nonnull__)) +static inline int cx_map_put_str( + CxMap *map, + char const *key, + void *value +) { + return map->cl->put(map, cx_hash_key_str(key), value); +} + +/** + * Puts a key/value-pair into the map. + * + * @param map the map + * @param key the key + * @param value the value + * @return 0 on success, non-zero value on failure + */ +#define cxMapPut(map, key, value) _Generic((key), \ + CxHashKey: cx_map_put, \ + cxstring: cx_map_put_cxstr, \ + cxmutstr: cx_map_put_mustr, \ + char*: cx_map_put_str, \ + char const*: cx_map_put_str) \ + (map, key, value) + +/** + * Retrieves a value by using a key. + * + * @param map the map + * @param key the key + * @return the value + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cx_map_get( + CxMap const *map, + CxHashKey key +) { + return map->cl->get(map, key); +} + +/** + * Retrieves a value by using a key. + * + * @param map the map + * @param key the key + * @return the value + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cx_map_get_cxstr( + CxMap const *map, + cxstring key +) { + return map->cl->get(map, cx_hash_key_cxstr(key)); +} + +/** + * Retrieves a value by using a key. + * + * @param map the map + * @param key the key + * @return the value + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cx_map_get_mustr( + CxMap const *map, + cxmutstr key +) { + return map->cl->get(map, cx_hash_key_cxstr(key)); +} + +/** + * Retrieves a value by using a key. + * + * @param map the map + * @param key the key + * @return the value + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cx_map_get_str( + CxMap const *map, + char const *key +) { + return map->cl->get(map, cx_hash_key_str(key)); +} + +/** + * Retrieves a value by using a key. + * + * @param map the map + * @param key the key + * @return the value + */ +#define cxMapGet(map, key) _Generic((key), \ + CxHashKey: cx_map_get, \ + cxstring: cx_map_get_cxstr, \ + cxmutstr: cx_map_get_mustr, \ + char*: cx_map_get_str, \ + char const*: cx_map_get_str) \ + (map, key) + +/** + * Removes a key/value-pair from the map by using the key. + * + * @param map the map + * @param key the key + */ +__attribute__((__nonnull__)) +static inline void cx_map_remove( + CxMap *map, + CxHashKey key +) { + (void) map->cl->remove(map, key, true); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * @param map the map + * @param key the key + */ +__attribute__((__nonnull__)) +static inline void cx_map_remove_cxstr( + CxMap *map, + cxstring key +) { + (void) map->cl->remove(map, cx_hash_key_cxstr(key), true); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * @param map the map + * @param key the key + */ +__attribute__((__nonnull__)) +static inline void cx_map_remove_mustr( + CxMap *map, + cxmutstr key +) { + (void) map->cl->remove(map, cx_hash_key_cxstr(key), true); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * @param map the map + * @param key the key + */ +__attribute__((__nonnull__)) +static inline void cx_map_remove_str( + CxMap *map, + char const *key +) { + (void) map->cl->remove(map, cx_hash_key_str(key), true); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * Always invokes the destructor function, if any, on the removed element. + * If this map is storing pointers and you just want to retrieve the pointer + * without invoking the destructor, use cxMapRemoveAndGet(). + * If you just want to detach the element from the map without invoking the + * destructor or returning the element, use cxMapDetach(). + * + * @param map the map + * @param key the key + * @see cxMapRemoveAndGet() + * @see cxMapDetach() + */ +#define cxMapRemove(map, key) _Generic((key), \ + CxHashKey: cx_map_remove, \ + cxstring: cx_map_remove_cxstr, \ + cxmutstr: cx_map_remove_mustr, \ + char*: cx_map_remove_str, \ + char const*: cx_map_remove_str) \ + (map, key) + +/** + * Detaches a key/value-pair from the map by using the key + * without invoking the destructor. + * + * @param map the map + * @param key the key + */ +__attribute__((__nonnull__)) +static inline void cx_map_detach( + CxMap *map, + CxHashKey key +) { + (void) map->cl->remove(map, key, false); +} + +/** + * Detaches a key/value-pair from the map by using the key + * without invoking the destructor. + * + * @param map the map + * @param key the key + */ +__attribute__((__nonnull__)) +static inline void cx_map_detach_cxstr( + CxMap *map, + cxstring key +) { + (void) map->cl->remove(map, cx_hash_key_cxstr(key), false); +} + +/** + * Detaches a key/value-pair from the map by using the key + * without invoking the destructor. + * + * @param map the map + * @param key the key + */ +__attribute__((__nonnull__)) +static inline void cx_map_detach_mustr( + CxMap *map, + cxmutstr key +) { + (void) map->cl->remove(map, cx_hash_key_cxstr(key), false); +} + +/** + * Detaches a key/value-pair from the map by using the key + * without invoking the destructor. + * + * @param map the map + * @param key the key + */ +__attribute__((__nonnull__)) +static inline void cx_map_detach_str( + CxMap *map, + char const *key +) { + (void) map->cl->remove(map, cx_hash_key_str(key), false); +} + +/** + * Detaches a key/value-pair from the map by using the key + * without invoking the destructor. + * + * In general, you should only use this function if the map does not own + * the data and there is a valid reference to the data somewhere else + * in the program. In all other cases it is preferable to use + * cxMapRemove() or cxMapRemoveAndGet(). + * + * @param map the map + * @param key the key + * @see cxMapRemove() + * @see cxMapRemoveAndGet() + */ +#define cxMapDetach(map, key) _Generic((key), \ + CxHashKey: cx_map_detach, \ + cxstring: cx_map_detach_cxstr, \ + cxmutstr: cx_map_detach_mustr, \ + char*: cx_map_detach_str, \ + char const*: cx_map_detach_str) \ + (map, key) + +/** + * Removes a key/value-pair from the map by using the key. + * + * @param map the map + * @param key the key + * @return the stored pointer or \c NULL if either the key is not present + * in the map or the map is not storing pointers + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cx_map_remove_and_get( + CxMap *map, + CxHashKey key +) { + return map->cl->remove(map, key, !map->store_pointer); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * @param map the map + * @param key the key + * @return the stored pointer or \c NULL if either the key is not present + * in the map or the map is not storing pointers + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cx_map_remove_and_get_cxstr( + CxMap *map, + cxstring key +) { + return map->cl->remove(map, cx_hash_key_cxstr(key), !map->store_pointer); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * @param map the map + * @param key the key + * @return the stored pointer or \c NULL if either the key is not present + * in the map or the map is not storing pointers + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cx_map_remove_and_get_mustr( + CxMap *map, + cxmutstr key +) { + return map->cl->remove(map, cx_hash_key_cxstr(key), !map->store_pointer); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * @param map the map + * @param key the key + * @return the stored pointer or \c NULL if either the key is not present + * in the map or the map is not storing pointers + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cx_map_remove_and_get_str( + CxMap *map, + char const *key +) { + return map->cl->remove(map, cx_hash_key_str(key), !map->store_pointer); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * This function can be used when the map is storing pointers, + * in order to retrieve the pointer from the map without invoking + * any destructor function. Sometimes you do not want the pointer + * to be returned - in that case (instead of suppressing the "unused + * result" warning) you can use cxMapDetach(). + * + * If this map is not storing pointers, this function behaves like + * cxMapRemove() and returns \c NULL. + * + * @param map the map + * @param key the key + * @return the stored pointer or \c NULL if either the key is not present + * in the map or the map is not storing pointers + * @see cxMapStorePointers() + * @see cxMapDetach() + */ +#define cxMapRemoveAndGet(map, key) _Generic((key), \ + CxHashKey: cx_map_remove_and_get, \ + cxstring: cx_map_remove_and_get_cxstr, \ + cxmutstr: cx_map_remove_and_get_mustr, \ + char*: cx_map_remove_and_get_str, \ + char const*: cx_map_remove_and_get_str) \ + (map, key) + +#endif // __cplusplus + +#endif // UCX_MAP_H diff -r a569148841ff -r efbd59642577 ucx/cx/mempool.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/mempool.h Fri Apr 21 21:25:32 2023 +0200 @@ -0,0 +1,122 @@ +/* + * 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. + */ +/** + * \file mempool.h + * \brief Interface for memory pool implementations. + * \author Mike Becker + * \author Olaf Wintermann + * \version 3.0 + * \copyright 2-Clause BSD License + */ + +#ifndef UCX_MEMPOOL_H +#define UCX_MEMPOOL_H + +#include "common.h" +#include "allocator.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Memory pool class type. + */ +typedef struct cx_mempool_class_s cx_mempool_class; + +/** + * The basic structure of a memory pool. + * Should be the first member of an actual memory pool implementation. + */ +struct cx_mempool_s { + /** + * The pool class definition. + */ + cx_mempool_class *cl; + /** + * The provided allocator. + */ + CxAllocator const *allocator; +}; + +/** + * Common type for all memory pool implementations. + */ +typedef struct cx_mempool_s CxMempool; + +/** + * The class definition for a memory pool. + */ +struct cx_mempool_class_s { + /** Member function for destroying the pool. */ + __attribute__((__nonnull__)) + void (*destroy)(CxMempool *pool); + + /** Member function for setting a destructor. */ + __attribute__((__nonnull__)) + void (*set_destructor)( + CxMempool *pool, + void *memory, + cx_destructor_func fnc + ); +}; + + +/** + * Destroys a memory pool including their contents. + * + * @param pool the memory pool to destroy + */ +__attribute__((__nonnull__)) +static inline void cxMempoolDestroy(CxMempool *pool) { + pool->cl->destroy(pool); +} + +/** + * Sets a destructor function for an allocated memory object. + * + * If the memory is not managed by the pool, the behavior is undefined. + * + * @param pool the pool + * @param memory the objected allocated in the pool + * @param fnc the destructor function + */ +__attribute__((__nonnull__)) +static inline void cxMempoolSetDestructor( + CxMempool *pool, + void *memory, + cx_destructor_func fnc +) { + pool->cl->set_destructor(pool, memory, fnc); +} + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // UCX_MEMPOOL_H diff -r a569148841ff -r efbd59642577 ucx/cx/printf.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/printf.h Fri Apr 21 21:25:32 2023 +0200 @@ -0,0 +1,166 @@ +/* + * 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. + */ +/** + * \file printf.h + * \brief Wrapper for write functions with a printf-like interface. + * \author Mike Becker + * \author Olaf Wintermann + * \version 3.0 + * \copyright 2-Clause BSD License + */ + +#ifndef UCX_PRINTF_H +#define UCX_PRINTF_H + +#include "common.h" +#include "string.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * A \c fprintf like function which writes the output to a stream by + * using a write_func. + * + * @param stream the stream the data is written to + * @param wfc the write function + * @param fmt format string + * @param ... additional arguments + * @return the total number of bytes written + */ +__attribute__((__nonnull__(1, 2, 3), __format__(printf, 3, 4))) +int cx_fprintf( + void *stream, + cx_write_func wfc, + char const *fmt, + ... +); + +/** + * A \c vfprintf like function which writes the output to a stream by + * using a write_func. + * + * @param stream the stream the data is written to + * @param wfc the write function + * @param fmt format string + * @param ap argument list + * @return the total number of bytes written + * @see cx_fprintf() + */ +__attribute__((__nonnull__)) +int cx_vfprintf( + void *stream, + cx_write_func wfc, + char const *fmt, + va_list ap +); + +/** + * A \c asprintf like function which allocates space for a string + * the result is written to. + * + * \note The resulting string is guaranteed to be zero-terminated. + * + * @param allocator the CxAllocator used for allocating the string + * @param fmt format string + * @param ... additional arguments + * @return the formatted string + * @see cx_strfree_a() + */ +__attribute__((__nonnull__(1, 2), __format__(printf, 2, 3))) +cxmutstr cx_asprintf_a( + CxAllocator const *allocator, + char const *fmt, + ... +); + +/** + * A \c asprintf like function which allocates space for a string + * the result is written to. + * + * \note The resulting string is guaranteed to be zero-terminated. + * + * @param fmt format string + * @param ... additional arguments + * @return the formatted string + * @see cx_strfree() + */ +#define cx_asprintf(fmt, ...) \ + cx_asprintf_a(cxDefaultAllocator, fmt, __VA_ARGS__) + +/** +* A \c vasprintf like function which allocates space for a string + * the result is written to. + * + * \note The resulting string is guaranteed to be zero-terminated. + * + * @param allocator the CxAllocator used for allocating the string + * @param fmt format string + * @param ap argument list + * @return the formatted string + * @see cx_asprintf_a() + */ +__attribute__((__nonnull__)) +cxmutstr cx_vasprintf_a( + CxAllocator const *allocator, + char const *fmt, + va_list ap +); + +/** +* A \c vasprintf like function which allocates space for a string + * the result is written to. + * + * \note The resulting string is guaranteed to be zero-terminated. + * + * @param fmt format string + * @param ap argument list + * @return the formatted string + * @see cx_asprintf() + */ +#define cx_vasprintf(fmt, ap) cx_vasprintf_a(cxDefaultAllocator, fmt, ap) + +/** + * A \c printf like function which writes the output to a CxBuffer. + * + * @param buffer a pointer to the buffer the data is written to + * @param fmt the format string + * @param ... additional arguments + * @return the total number of bytes written + * @see ucx_fprintf() + */ +#define cx_bprintf(buffer, fmt, ...) cx_fprintf((CxBuffer*)buffer, \ + (cx_write_func) cxBufferWrite, fmt, __VA_ARGS__) + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif //UCX_PRINTF_H diff -r a569148841ff -r efbd59642577 ucx/cx/string.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/string.h Fri Apr 21 21:25:32 2023 +0200 @@ -0,0 +1,1007 @@ +/* + * 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. + */ +/** + * \file string.h + * \brief Strings that know their length. + * \author Mike Becker + * \author Olaf Wintermann + * \version 3.0 + * \copyright 2-Clause BSD License + */ + +#ifndef UCX_STRING_H +#define UCX_STRING_H + +#include "common.h" +#include "allocator.h" + +/** + * The UCX string structure. + */ +struct cx_mutstr_s { + /** + * 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 */ + size_t length; +}; + +/** + * A mutable string. + */ +typedef struct cx_mutstr_s cxmutstr; + +/** + * The UCX string structure for immutable (constant) strings. + */ +struct cx_string_s { + /** + * A pointer to the immutable string. + * \note The string is not necessarily \c NULL terminated. + * Always use the length. + */ + char const *ptr; + /** The length of the string */ + size_t length; +}; + +/** + * An immutable string. + */ +typedef struct cx_string_s cxstring; + +/** + * Context for string tokenizing. + */ +struct cx_strtok_ctx_s { + /** + * The string to tokenize. + */ + cxstring str; + /** + * The primary delimiter. + */ + cxstring delim; + /** + * Optional array of more delimiters. + */ + cxstring const *delim_more; + /** + * Length of the array containing more delimiters. + */ + size_t delim_more_count; + /** + * Position of the currently active token in the source string. + */ + size_t pos; + /** + * Position of next delimiter in the source string. + * + * If the tokenizer has not yet returned a token, the content of this field + * is undefined. If the tokenizer reached the end of the string, this field + * contains the length of the source string. + */ + size_t delim_pos; + /** + * The position of the next token in the source string. + */ + size_t next_pos; + /** + * The number of already found tokens. + */ + size_t found; + /** + * The maximum number of tokens that shall be returned. + */ + size_t limit; +}; + +/** + * A string tokenizing context. + */ +typedef struct cx_strtok_ctx_s CxStrtokCtx; + +#ifdef __cplusplus +extern "C" { + +/** + * A literal initializer for an UCX string structure. + * + * @param literal the string literal + */ +#define CX_STR(literal) cxstring{literal, sizeof(literal) - 1} + +#else // __cplusplus + +/** + * A literal initializer for an UCX string structure. + * + * The argument MUST be a string (const char*) \em literal. + * + * @param literal the string literal + */ +#define CX_STR(literal) (cxstring){literal, sizeof(literal) - 1} + +#endif + + +/** + * Wraps a mutable string that must be zero-terminated. + * + * The length is implicitly inferred by using a call to \c strlen(). + * + * \note the wrapped string will share the specified pointer to the string. + * If you do want a copy, use cx_strdup() on the return value of this function. + * + * If you need to wrap a constant string, use cx_str(). + * + * @param cstring the string to wrap, must be zero-terminated + * @return the wrapped string + * + * @see cx_mutstrn() + */ +__attribute__((__warn_unused_result__, __nonnull__)) +cxmutstr cx_mutstr(char *cstring); + +/** + * Wraps a string that does not need to be zero-terminated. + * + * The argument may be \c NULL if the length is zero. + * + * \note the wrapped string will share the specified pointer to the string. + * If you do want a copy, use cx_strdup() on the return value of this function. + * + * If you need to wrap a constant string, use cx_strn(). + * + * @param cstring the string to wrap (or \c NULL, only if the length is zero) + * @param length the length of the string + * @return the wrapped string + * + * @see cx_mutstr() + */ +__attribute__((__warn_unused_result__)) +cxmutstr cx_mutstrn( + char *cstring, + size_t length +); + +/** + * Wraps a string that must be zero-terminated. + * + * The length is implicitly inferred by using a call to \c strlen(). + * + * \note the wrapped string will share the specified pointer to the string. + * If you do want a copy, use cx_strdup() on the return value of this function. + * + * If you need to wrap a non-constant string, use cx_mutstr(). + * + * @param cstring the string to wrap, must be zero-terminated + * @return the wrapped string + * + * @see cx_strn() + */ +__attribute__((__warn_unused_result__, __nonnull__)) +cxstring cx_str(char const *cstring); + + +/** + * Wraps a string that does not need to be zero-terminated. + * + * The argument may be \c NULL if the length is zero. + * + * \note the wrapped string will share the specified pointer to the string. + * If you do want a copy, use cx_strdup() on the return value of this function. + * + * If you need to wrap a non-constant string, use cx_mutstrn(). + * + * @param cstring the string to wrap (or \c NULL, only if the length is zero) + * @param length the length of the string + * @return the wrapped string + * + * @see cx_str() + */ +__attribute__((__warn_unused_result__)) +cxstring cx_strn( + char const *cstring, + size_t length +); + +/** +* Casts a mutable string to an immutable string. +* +* \note This is not seriously a cast. Instead you get a copy +* of the struct with the desired pointer type. Both structs still +* point to the same location, though! +* +* @param str the mutable string to cast +* @return an immutable copy of the string pointer +*/ +__attribute__((__warn_unused_result__)) +cxstring cx_strcast(cxmutstr str); + +/** + * 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. + * + * \note There is no implementation for cxstring, because it is unlikely that + * you ever have a \c char \c const* you are really supposed to free. If you + * encounter such situation, you should double-check your code. + * + * @param str the string to free + */ +__attribute__((__nonnull__)) +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. + * + * \note There is no implementation for cxstring, because it is unlikely that + * you ever have a \c char \c const* you are really supposed to free. If you + * encounter such situation, you should double-check your code. + * + * @param alloc the allocator + * @param str the string to free + */ +__attribute__((__nonnull__)) +void cx_strfree_a( + CxAllocator const *alloc, + cxmutstr *str +); + +/** + * Returns the accumulated length of all specified strings. + * + * \attention if the count argument is larger than the number of the + * specified strings, the behavior is undefined. + * + * @param count the total number of specified strings + * @param ... all strings + * @return the accumulated length of all strings + */ +__attribute__((__warn_unused_result__)) +size_t cx_strlen( + size_t count, + ... +); + +/** + * Concatenates two or more strings. + * + * The resulting string will be allocated by the specified allocator. + * So developers \em must pass the return value to cx_strfree() eventually. + * + * \note It is guaranteed that there is only one allocation. + * It is also guaranteed that the returned string is zero-terminated. + * + * @param alloc the allocator to use + * @param count the total number of strings to concatenate + * @param ... all strings + * @return the concatenated string + */ +__attribute__((__warn_unused_result__, __nonnull__)) +cxmutstr cx_strcat_a( + CxAllocator const *alloc, + size_t count, + ... +); + +/** + * Concatenates two or more strings. + * + * The resulting string will be allocated by standard \c malloc(). + * So developers \em must pass the return value to cx_strfree() eventually. + * + * \note It is guaranteed that there is only one allocation. + * It is also guaranteed that the returned string is zero-terminated. + * + * @param count the total number of strings to concatenate + * @param ... all strings + * @return the concatenated string + */ +#define cx_strcat(count, ...) \ +cx_strcat_a(cxDefaultAllocator, count, __VA_ARGS__) + +/** + * Returns a substring starting at the specified location. + * + * \attention the new string references the same memory area as the + * input string and is usually \em not zero-terminated. + * Use cx_strdup() to get a copy. + * + * @param string input string + * @param start start location of the substring + * @return a substring of \p string starting at \p start + * + * @see cx_strsubsl() + * @see cx_strsubs_m() + * @see cx_strsubsl_m() + */ +__attribute__((__warn_unused_result__)) +cxstring cx_strsubs( + cxstring string, + size_t start +); + +/** + * Returns a substring starting at the specified location. + * + * The returned string will be limited to \p length bytes or the number + * of bytes available in \p string, whichever is smaller. + * + * \attention the new string references the same memory area as the + * input string and is usually \em not zero-terminated. + * Use cx_strdup() to get a copy. + * + * @param string input string + * @param start start location of the substring + * @param length the maximum length of the returned string + * @return a substring of \p string starting at \p start + * + * @see cx_strsubs() + * @see cx_strsubs_m() + * @see cx_strsubsl_m() + */ +__attribute__((__warn_unused_result__)) +cxstring cx_strsubsl( + cxstring string, + size_t start, + size_t length +); + +/** + * Returns a substring starting at the specified location. + * + * \attention the new string references the same memory area as the + * input string and is usually \em not zero-terminated. + * Use cx_strdup() to get a copy. + * + * @param string input string + * @param start start location of the substring + * @return a substring of \p string starting at \p start + * + * @see cx_strsubsl_m() + * @see cx_strsubs() + * @see cx_strsubsl() + */ +__attribute__((__warn_unused_result__)) +cxmutstr cx_strsubs_m( + cxmutstr string, + size_t start +); + +/** + * Returns a substring starting at the specified location. + * + * The returned string will be limited to \p length bytes or the number + * of bytes available in \p string, whichever is smaller. + * + * \attention the new string references the same memory area as the + * input string and is usually \em not zero-terminated. + * Use cx_strdup() to get a copy. + * + * @param string input string + * @param start start location of the substring + * @param length the maximum length of the returned string + * @return a substring of \p string starting at \p start + * + * @see cx_strsubs_m() + * @see cx_strsubs() + * @see cx_strsubsl() + */ +__attribute__((__warn_unused_result__)) +cxmutstr cx_strsubsl_m( + cxmutstr string, + size_t start, + size_t length +); + +/** + * Returns a substring starting at the location of the first occurrence of the + * specified character. + * + * If the string does not contain the character, an empty string is returned. + * + * @param string the string where to locate the character + * @param chr the character to locate + * @return a substring starting at the first location of \p chr + * + * @see cx_strchr_m() + */ +__attribute__((__warn_unused_result__)) +cxstring cx_strchr( + cxstring string, + int chr +); + +/** + * Returns a substring starting at the location of the first occurrence of the + * specified character. + * + * If the string does not contain the character, an empty string is returned. + * + * @param string the string where to locate the character + * @param chr the character to locate + * @return a substring starting at the first location of \p chr + * + * @see cx_strchr() + */ +__attribute__((__warn_unused_result__)) +cxmutstr cx_strchr_m( + cxmutstr string, + int chr +); + +/** + * Returns a substring starting at the location of the last occurrence of the + * specified character. + * + * If the string does not contain the character, an empty string is returned. + * + * @param string the string where to locate the character + * @param chr the character to locate + * @return a substring starting at the last location of \p chr + * + * @see cx_strrchr_m() + */ +__attribute__((__warn_unused_result__)) +cxstring cx_strrchr( + cxstring string, + int chr +); + +/** + * Returns a substring starting at the location of the last occurrence of the + * specified character. + * + * If the string does not contain the character, an empty string is returned. + * + * @param string the string where to locate the character + * @param chr the character to locate + * @return a substring starting at the last location of \p chr + * + * @see cx_strrchr() + */ +__attribute__((__warn_unused_result__)) +cxmutstr cx_strrchr_m( + cxmutstr string, + int chr +); + +/** + * Returns a substring starting at the location of the first occurrence of the + * specified string. + * + * If \p haystack does not contain \p needle, an empty string is returned. + * + * If \p needle is an empty string, the complete \p haystack is + * returned. + * + * @param haystack the string to be scanned + * @param needle string containing the sequence of characters to match + * @return a substring starting at the first occurrence of + * \p needle, or an empty string, if the sequence is not + * contained + * @see cx_strstr_m() + */ +__attribute__((__warn_unused_result__)) +cxstring cx_strstr( + cxstring haystack, + cxstring needle +); + +/** + * Returns a substring starting at the location of the first occurrence of the + * specified string. + * + * If \p haystack does not contain \p needle, an empty string is returned. + * + * If \p needle is an empty string, the complete \p haystack is + * returned. + * + * @param haystack the string to be scanned + * @param needle string containing the sequence of characters to match + * @return a substring starting at the first occurrence of + * \p needle, or an empty string, if the sequence is not + * contained + * @see cx_strstr() + */ +__attribute__((__warn_unused_result__)) +cxmutstr cx_strstr_m( + cxmutstr haystack, + cxstring needle +); + +/** + * Splits a given string using a delimiter string. + * + * \note The resulting array contains strings that point to the source + * \p string. Use cx_strdup() to get copies. + * + * @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 + * @return the actual number of split items + */ +__attribute__((__warn_unused_result__, __nonnull__)) +size_t cx_strsplit( + cxstring string, + cxstring delim, + size_t limit, + cxstring *output +); + +/** + * Splits a given string using a delimiter string. + * + * The array pointed to by \p output will be allocated by \p allocator. + * + * \note The resulting array contains strings that point to the source + * \p string. Use cx_strdup() to get copies. + * + * \attention If allocation fails, the \c NULL pointer will be written to + * \p output and the number returned will be zero. + * + * @param allocator the allocator to use for allocating the resulting array + * @param string the string to split + * @param delim the delimiter + * @param limit the maximum number of split items + * @param output a pointer where the address of the allocated array shall be + * written to + * @return the actual number of split items + */ +__attribute__((__warn_unused_result__, __nonnull__)) +size_t cx_strsplit_a( + CxAllocator const *allocator, + cxstring string, + cxstring delim, + size_t limit, + cxstring **output +); + + +/** + * Splits a given string using a delimiter string. + * + * \note The resulting array contains strings that point to the source + * \p string. Use cx_strdup() to get copies. + * + * @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 + * @return the actual number of split items + */ +__attribute__((__warn_unused_result__, __nonnull__)) +size_t cx_strsplit_m( + cxmutstr string, + cxstring delim, + size_t limit, + cxmutstr *output +); + +/** + * Splits a given string using a delimiter string. + * + * The array pointed to by \p output will be allocated by \p allocator. + * + * \note The resulting array contains strings that point to the source + * \p string. Use cx_strdup() to get copies. + * + * \attention If allocation fails, the \c NULL pointer will be written to + * \p output and the number returned will be zero. + * + * @param allocator the allocator to use for allocating the resulting array + * @param string the string to split + * @param delim the delimiter + * @param limit the maximum number of split items + * @param output a pointer where the address of the allocated array shall be + * written to + * @return the actual number of split items + */ +__attribute__((__warn_unused_result__, __nonnull__)) +size_t cx_strsplit_ma( + CxAllocator const *allocator, + cxmutstr string, + cxstring delim, + size_t limit, + cxmutstr **output +); + +/** + * Compares two strings. + * + * @param s1 the first string + * @param s2 the second string + * @return negative if \p s1 is smaller than \p s2, positive if \p s1 is larger + * than \p s2, zero if both strings equal + */ +__attribute__((__warn_unused_result__)) +int cx_strcmp( + cxstring s1, + cxstring s2 +); + +/** + * Compares two strings ignoring case. + * + * @param s1 the first string + * @param s2 the second string + * @return negative if \p s1 is smaller than \p s2, positive if \p s1 is larger + * than \p s2, zero if both strings equal ignoring case + */ +__attribute__((__warn_unused_result__)) +int cx_strcasecmp( + cxstring s1, + cxstring s2 +); + +/** + * Compares two strings. + * + * This function has a compatible signature for the use as a cx_compare_func. + * + * @param s1 the first string + * @param s2 the second string + * @return negative if \p s1 is smaller than \p s2, positive if \p s1 is larger + * than \p s2, zero if both strings equal + */ +__attribute__((__warn_unused_result__, __nonnull__)) +int cx_strcmp_p( + void const *s1, + void const *s2 +); + +/** + * Compares two strings ignoring case. + * + * This function has a compatible signature for the use as a cx_compare_func. + * + * @param s1 the first string + * @param s2 the second string + * @return negative if \p s1 is smaller than \p s2, positive if \p s1 is larger + * than \p s2, zero if both strings equal ignoring case + */ +__attribute__((__warn_unused_result__, __nonnull__)) +int cx_strcasecmp_p( + void const *s1, + void const *s2 +); + + +/** + * Creates a duplicate of the specified string. + * + * The new string will contain a copy allocated by \p allocator. + * + * \note The returned string is guaranteed to be zero-terminated. + * + * @param allocator the allocator to use + * @param string the string to duplicate + * @return a duplicate of the string + * @see cx_strdup() + */ +__attribute__((__warn_unused_result__, __nonnull__)) +cxmutstr cx_strdup_a( + CxAllocator const *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 the string to duplicate + * @return a duplicate of the string + * @see cx_strdup_a() + */ +#define cx_strdup(string) cx_strdup_a(cxDefaultAllocator, string) + +/** + * Omits leading and trailing spaces. + * + * \note the returned string references the same memory, thus you + * must \em not free the returned memory. + * + * @param string the string that shall be trimmed + * @return the trimmed string + */ +__attribute__((__warn_unused_result__)) +cxstring cx_strtrim(cxstring string); + +/** + * Omits leading and trailing spaces. + * + * \note the returned string references the same memory, thus you + * must \em not free the returned memory. + * + * @param string the string that shall be trimmed + * @return the trimmed string + */ +__attribute__((__warn_unused_result__)) +cxmutstr cx_strtrim_m(cxmutstr string); + +/** + * Checks, if a string has a specific prefix. + * + * @param string the string to check + * @param prefix the prefix the string should have + * @return \c true, if and only if the string has the specified prefix, + * \c false otherwise + */ +__attribute__((__warn_unused_result__)) +bool cx_strprefix( + cxstring string, + cxstring prefix +); + +/** + * Checks, if a string has a specific suffix. + * + * @param string the string to check + * @param suffix the suffix the string should have + * @return \c true, if and only if the string has the specified suffix, + * \c false otherwise + */ +__attribute__((__warn_unused_result__)) +bool cx_strsuffix( + cxstring string, + cxstring suffix +); + +/** + * Checks, if a string has a specific prefix, ignoring the case. + * + * @param string the string to check + * @param prefix the prefix the string should have + * @return \c true, if and only if the string has the specified prefix, + * \c false otherwise + */ +__attribute__((__warn_unused_result__)) +bool cx_strcaseprefix( + cxstring string, + cxstring prefix +); + +/** + * Checks, if a string has a specific suffix, ignoring the case. + * + * @param string the string to check + * @param suffix the suffix the string should have + * @return \c true, if and only if the string has the specified suffix, + * \c false otherwise + */ +__attribute__((__warn_unused_result__)) +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. + * + * 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 + * to be zero-terminated. + * + * If allocation fails, or the input string is empty, + * the returned string will be empty. + * + * @param allocator the allocator to use + * @param str the string where replacements should be applied + * @param pattern the pattern to search for + * @param replacement the replacement string + * @param replmax maximum number of replacements + * @return the resulting string after applying the replacements + */ +__attribute__((__warn_unused_result__, __nonnull__)) +cxmutstr cx_strreplacen_a( + CxAllocator const *allocator, + cxstring str, + cxstring pattern, + cxstring replacement, + size_t replmax +); + +/** + * 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. + * + * The returned string will be allocated by \c malloc() and is guaranteed + * to be zero-terminated. + * + * If allocation fails, or the input string is empty, + * the returned string will be empty. + * + * @param str the string where replacements should be applied + * @param pattern the pattern to search for + * @param replacement the replacement string + * @param replmax maximum number of replacements + * @return the resulting string after applying the replacements + */ +#define cx_strreplacen(str, pattern, replacement, replmax) \ +cx_strreplacen_a(cxDefaultAllocator, str, pattern, replacement, replmax) + +/** + * Replaces a pattern in a string with another string. + * + * The pattern is taken literally and is no regular expression. + * + * The returned string will be allocated by \p allocator and is guaranteed + * to be zero-terminated. + * + * If allocation fails, or the input string is empty, + * the returned string will be empty. + * + * @param allocator the allocator to use + * @param str the string where replacements should be applied + * @param pattern the pattern to search for + * @param replacement the replacement string + * @return the resulting string after applying the replacements + */ +#define cx_strreplace_a(allocator, str, pattern, replacement) \ +cx_strreplacen_a(allocator, str, pattern, 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. + * + * The returned string will be allocated by \c malloc() and is guaranteed + * to be zero-terminated. + * + * If allocation fails, or the input string is empty, + * the returned string will be empty. + * + * @param str the string where replacements should be applied + * @param pattern the pattern to search for + * @param replacement the replacement string + * @return the resulting string after applying the replacements + */ +#define cx_strreplace(str, pattern, replacement) \ +cx_strreplacen_a(cxDefaultAllocator, str, pattern, replacement, SIZE_MAX) + +/** + * Creates a string tokenization context. + * + * @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 + */ +__attribute__((__warn_unused_result__)) +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 +*/ +__attribute__((__warn_unused_result__)) +CxStrtokCtx cx_strtok_m( + cxmutstr str, + cxstring delim, + size_t limit +); + +/** + * Returns the next token. + * + * The token will point to the source string. + * + * @param ctx the tokenization context + * @param token a pointer to memory where the next token shall be stored + * @return true if successful, false if the limit or the end of the string + * has been reached + */ +__attribute__((__warn_unused_result__, __nonnull__)) +bool cx_strtok_next( + CxStrtokCtx *ctx, + cxstring *token +); + +/** + * Returns the next token of a mutable string. + * + * The token will point to the source string. + * If the context was not initialized over a mutable string, modifying + * the data of the returned token is undefined behavior. + * + * @param ctx the tokenization context + * @param token a pointer to memory where the next token shall be stored + * @return true if successful, false if the limit or the end of the string + * has been reached + */ +__attribute__((__warn_unused_result__, __nonnull__)) +bool cx_strtok_next_m( + CxStrtokCtx *ctx, + cxmutstr *token +); + +/** + * Defines an array of more delimiters for the specified tokenization context. + * + * @param ctx the tokenization context + * @param delim array of more delimiters + * @param count number of elements in the array + */ +__attribute__((__nonnull__)) +void cx_strtok_delim( + CxStrtokCtx *ctx, + cxstring const *delim, + size_t count +); + + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif //UCX_STRING_H diff -r a569148841ff -r efbd59642577 ucx/cx/tree.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/tree.h Fri Apr 21 21:25:32 2023 +0200 @@ -0,0 +1,136 @@ +/* + * 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. + */ +/** + * \file tree.h + * \brief Interface for tree implementations. + * \author Olaf Wintermann + * \author Mike Becker + * \version 3.0 + * \copyright 2-Clause BSD License + */ + +#ifndef UCX_TREE_H +#define UCX_TREE_H + +#include "common.h" +#include "allocator.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Adds a sibling to the current tree node. + * + * In case your struct does not have a \p prev or a \p parent pointer, + * specify a negative location. The location of the \p next pointer is + * mandatory. + * + * \attention Do not use this function to add siblings in a tree where the + * nodes store a pointer to the last sibling because that would not be modified by this function. + * + * \remark If yo do not provide a location to the parent pointer, a call to this function is + * effectively the same as a call to cx_linked_list_add(). + * + * @param node a pointer to the node + * @param loc_prev the location of a \c prev pointer within your node struct + * @param loc_next the location of a \c next pointer within your node struct + * @param loc_parent the location of a \c parent pointer within your node struct + * @param new_node the new node that shall be added as a sibling + */ +void cx_tree_add_sibling(void *node, + ptrdiff_t loc_prev, ptrdiff_t loc_next, + ptrdiff_t loc_parent, + void *new_node) +__attribute__((__nonnull__)); + +/** + * Adds a node to the list of children. + * + * \par Example with a full structure + * A full tree node structure may look like this: + * \code + * typedef struct MyTreeNode MyTreeNode; + * struct MyTreeNode { + * MyTreeNode* parent; + * MyTreeNode* first_child; + * MyTreeNode* last_child; + * MyTreeNode* prev_sibling; + * MyTreeNode* next_sibling; + * // ...contents... + * } + * \endcode + * Adding a new child to a node with the above structure can be performed with the following call: + * \code + * MyTreeNode *node, *child; // given + * cx_tree_add_child(&node->first_child, &node->last_child, + * offsetof(MyTreeNode, prev_sibling), offsetof(MyTreeNode, next_sibling), + * child, offsetof(MyTreeNode, parent), node); + * \endcode + * + * \par Example with a reduced structure + * The minimal reasonable structure with parent pointer looks like this: + * \code + * typedef struct MyTreeNode MyTreeNode; + * struct MyTreeNode { + * MyTreeNode* parent; + * MyTreeNode* children; + * MyTreeNode* next_sibling; + * // ...contents... + * } + * \endcode + * This simplifies the function call to: + * \code + * MyTreeNode *node, *child; // given + * cx_tree_add_child(&node->children, NULL, -1, offsetof(MyTreeNode, next_sibling), + * child, offsetof(MyTreeNode, parent), node); + * \endcode + * + * \remark If your tree structure does not possess a parent pointer, a call to this function is + * effectively the same as a call to cx_linked_list_add(). + * + * @param children_begin a pointer to the begin node pointer (if your list has one) + * @param children_end a pointer to the end node pointer (if your list has one) + * @param loc_prev the location of a \c prev pointer within your node struct + * @param loc_next the location of a \c next pointer within your node struct + * @param new_node a pointer to the node that shall be appended + * @param loc_parent the location of a \c parent pointer within your node struct + * @param parent the parent node + */ +void cx_tree_add_child(void **children_begin, void **children_end, + ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node, + ptrdiff_t loc_parent, void *parent) +__attribute__((__nonnull__ (5))); + + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // UCX_TREE_H + diff -r a569148841ff -r efbd59642577 ucx/cx/utils.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/utils.h Fri Apr 21 21:25:32 2023 +0200 @@ -0,0 +1,196 @@ +/* + * 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. + */ + +/** + * \file utils.h + * + * \brief General purpose utility functions. + * + * \author Mike Becker + * \author Olaf Wintermann + * \version 3.0 + * \copyright 2-Clause BSD License + */ + +#ifndef UCX_UTILS_H +#define UCX_UTILS_H + +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Convenience macro for a for loop that counts from zero to n-1. + */ +#define cx_for_n(varname, n) for (size_t varname = 0 ; (varname) < (n) ; (varname)++) + +/** + * Convenience macro for swapping two pointers. + */ +#ifdef __cplusplus +#define cx_swap_ptr(left, right) do {auto cx_tmp_swap_var = left; left = right; right = cx_tmp_swap_var;} while(0) +#else +#define cx_swap_ptr(left, right) do {void *cx_tmp_swap_var = left; left = right; right = cx_tmp_swap_var;} while(0) +#endif + +// cx_szmul() definition + +#if (__GNUC__ >= 5 || defined(__clang__)) && !defined(CX_NO_SZMUL_BUILTIN) +#define CX_SZMUL_BUILTIN + +/** + * Alias for \c __builtin_mul_overflow. + * + * Performs a multiplication of size_t values and checks for overflow. + * + * @param a first operand + * @param b second operand + * @param result a pointer to a size_t, where the result should + * be stored + * @return zero, if no overflow occurred and the result is correct, non-zero + * otherwise + */ +#define cx_szmul(a, b, result) __builtin_mul_overflow(a, b, result) + +#else // no GNUC or clang bultin + +/** + * Performs a multiplication of size_t values and checks for overflow. + * + * @param a first operand + * @param b second operand + * @param result a pointer to a size_t, where the result should + * be stored + * @return zero, if no overflow occurred and the result is correct, non-zero + * otherwise + */ +#define cx_szmul(a, b, result) cx_szmul_impl(a, b, result) + +/** + * Performs a multiplication of size_t values and checks for overflow. + * + * This is a custom implementation in case there is no compiler builtin + * available. + * + * @param a first operand + * @param b second operand + * @param result a pointer to a size_t where the result should be stored + * @return zero, if no overflow occurred and the result is correct, non-zero + * otherwise + */ +int cx_szmul_impl(size_t a, size_t b, size_t *result); + +#endif // cx_szmul + + +/** + * Reads data from a stream and writes it to another stream. + * + * @param src the source stream + * @param dest the destination stream + * @param rfnc the read function + * @param wfnc the write function + * @param buf a pointer to the copy buffer or \c NULL if a buffer + * shall be implicitly created on the heap + * @param bufsize the size of the copy buffer - if \p buf is \c NULL you can + * set this to zero to let the implementation decide + * @param n the maximum number of bytes that shall be copied. + * If this is larger than \p bufsize, the content is copied over multiple + * iterations. + * @return the total number of bytes copied + */ +__attribute__((__nonnull__(1, 2, 3, 4))) +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 +); + +/** + * Reads data from a stream and writes it to another stream. + * + * @param src the source stream + * @param dest the destination stream + * @param rfnc the read function + * @param wfnc the write function + * @param buf a pointer to the copy buffer or \c NULL if a buffer + * shall be implicitly created on the heap + * @param bufsize the size of the copy buffer - if \p buf is \c NULL you can + * set this to zero to let the implementation decide + * @return total number of bytes copied + */ +#define cx_stream_bcopy(src, dest, rfnc, wfnc, buf, bufsize) \ + cx_stream_bncopy(src, dest, rfnc, wfnc, buf, bufsize, SIZE_MAX) + +/** + * Reads data from a stream and writes it to another stream. + * + * The data is temporarily stored in a stack allocated buffer. + * + * @param src the source stream + * @param dest the destination stream + * @param rfnc the read function + * @param wfnc the write function + * @param n the maximum number of bytes that shall be copied. + * @return total number of bytes copied + */ +__attribute__((__nonnull__)) +size_t cx_stream_ncopy( + void *src, + void *dest, + cx_read_func rfnc, + cx_write_func wfnc, + size_t n +); + +/** + * Reads data from a stream and writes it to another stream. + * + * The data is temporarily stored in a stack allocated buffer. + * + * @param src the source stream + * @param dest the destination stream + * @param rfnc the read function + * @param wfnc the write function + * @param n the maximum number of bytes that shall be copied. + * @return total number of bytes copied + */ +#define cx_stream_copy(src, dest, rfnc, wfnc) \ + cx_stream_ncopy(src, dest, rfnc, wfnc, SIZE_MAX) + +#ifdef __cplusplus +} +#endif + +#endif // UCX_UTILS_H diff -r a569148841ff -r efbd59642577 ucx/hash_key.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/hash_key.c Fri Apr 21 21:25:32 2023 +0200 @@ -0,0 +1,112 @@ +/* + * 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/hash_key.h" +#include + +void cx_hash_murmur(CxHashKey *key) { + unsigned char const *data = key->data; + if (data == NULL) { + // extension: special value for NULL + key->hash = 1574210520u; + return; + } + size_t len = key->len; + + unsigned m = 0x5bd1e995; + unsigned r = 24; + unsigned h = 25 ^ len; + unsigned i = 0; + while (len >= 4) { + unsigned k = data[i + 0] & 0xFF; + k |= (data[i + 1] & 0xFF) << 8; + k |= (data[i + 2] & 0xFF) << 16; + k |= (data[i + 3] & 0xFF) << 24; + + k *= m; + k ^= k >> r; + k *= m; + + h *= m; + h ^= k; + + i += 4; + len -= 4; + } + + switch (len) { + case 3: + h ^= (data[i + 2] & 0xFF) << 16; + __attribute__((__fallthrough__)); + case 2: + h ^= (data[i + 1] & 0xFF) << 8; + __attribute__((__fallthrough__)); + case 1: + h ^= (data[i + 0] & 0xFF); + h *= m; + __attribute__((__fallthrough__)); + default: // do nothing + ; + } + + h ^= h >> 13; + h *= m; + h ^= h >> 15; + + key->hash = h; +} + +CxHashKey cx_hash_key_str(char const *str) { + CxHashKey key; + key.data = str; + key.len = str == NULL ? 0 : strlen(str); + cx_hash_murmur(&key); + return key; +} + +CxHashKey cx_hash_key_bytes( + unsigned char const *bytes, + size_t len +) { + CxHashKey key; + key.data = bytes; + key.len = len; + cx_hash_murmur(&key); + return key; +} + +CxHashKey cx_hash_key( + void const *obj, + size_t len +) { + CxHashKey key; + key.data = obj; + key.len = len; + cx_hash_murmur(&key); + return key; +} diff -r a569148841ff -r efbd59642577 ucx/hash_map.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/hash_map.c Fri Apr 21 21:25:32 2023 +0200 @@ -0,0 +1,509 @@ +/* + * 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 +#include "cx/hash_map.h" +#include "cx/utils.h" + +struct cx_hash_map_element_s { + /** A pointer to the next element in the current bucket. */ + struct cx_hash_map_element_s *next; + + /** The corresponding key. */ + CxHashKey key; + + /** The value data. */ + char data[]; +}; + +static void cx_hash_map_clear(struct cx_map_s *map) { + struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; + cx_for_n(i, hash_map->bucket_count) { + struct cx_hash_map_element_s *elem = hash_map->buckets[i]; + if (elem != NULL) { + do { + struct cx_hash_map_element_s *next = elem->next; + // invoke the destructor + cx_invoke_destructor(map, elem->data); + // free the key data + cxFree(map->allocator, (void *) elem->key.data); + // free the node + cxFree(map->allocator, elem); + // proceed + elem = next; + } while (elem != NULL); + + // do not leave a dangling pointer + hash_map->buckets[i] = NULL; + } + } + map->size = 0; +} + +static void cx_hash_map_destructor(struct cx_map_s *map) { + struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; + + // free the buckets + cx_hash_map_clear(map); + cxFree(map->allocator, hash_map->buckets); + + // free the map structure + cxFree(map->allocator, map); +} + +static int cx_hash_map_put( + CxMap *map, + CxHashKey key, + void *value +) { + struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; + CxAllocator const *allocator = map->allocator; + + unsigned hash = key.hash; + if (hash == 0) { + cx_hash_murmur(&key); + hash = key.hash; + } + + size_t slot = hash % hash_map->bucket_count; + struct cx_hash_map_element_s *elm = hash_map->buckets[slot]; + struct cx_hash_map_element_s *prev = NULL; + + while (elm != NULL && elm->key.hash < hash) { + prev = elm; + elm = elm->next; + } + + if (elm != NULL && elm->key.hash == hash && elm->key.len == key.len && + memcmp(elm->key.data, key.data, key.len) == 0) { + // overwrite existing element + if (map->store_pointer) { + memcpy(elm->data, &value, sizeof(void *)); + } else { + memcpy(elm->data, value, map->item_size); + } + } else { + // allocate new element + struct cx_hash_map_element_s *e = cxMalloc( + allocator, + sizeof(struct cx_hash_map_element_s) + map->item_size + ); + if (e == NULL) { + return -1; + } + + // write the value + if (map->store_pointer) { + memcpy(e->data, &value, sizeof(void *)); + } else { + memcpy(e->data, value, map->item_size); + } + + // copy the key + void *kd = cxMalloc(allocator, key.len); + if (kd == NULL) { + return -1; + } + memcpy(kd, key.data, key.len); + e->key.data = kd; + e->key.len = key.len; + e->key.hash = hash; + + // insert the element into the linked list + if (prev == NULL) { + hash_map->buckets[slot] = e; + } else { + prev->next = e; + } + e->next = elm; + + // increase the size + map->size++; + } + + return 0; +} + +static void cx_hash_map_unlink( + struct cx_hash_map_s *hash_map, + size_t slot, + struct cx_hash_map_element_s *prev, + struct cx_hash_map_element_s *elm +) { + // unlink + if (prev == NULL) { + hash_map->buckets[slot] = elm->next; + } else { + prev->next = elm->next; + } + // free element + cxFree(hash_map->base.allocator, (void *) elm->key.data); + cxFree(hash_map->base.allocator, elm); + // decrease size + hash_map->base.size--; +} + +/** + * Helper function to avoid code duplication. + * + * @param map the map + * @param key the key to look up + * @param remove flag indicating whether the looked up entry shall be removed + * @param destroy flag indicating whether the destructor shall be invoked + * @return a pointer to the value corresponding to the key or \c NULL + */ +static void *cx_hash_map_get_remove( + CxMap *map, + CxHashKey key, + bool remove, + bool destroy +) { + struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; + + unsigned hash = key.hash; + if (hash == 0) { + cx_hash_murmur(&key); + hash = key.hash; + } + + size_t slot = hash % hash_map->bucket_count; + struct cx_hash_map_element_s *elm = hash_map->buckets[slot]; + struct cx_hash_map_element_s *prev = NULL; + while (elm && elm->key.hash <= hash) { + if (elm->key.hash == hash && elm->key.len == key.len) { + if (memcmp(elm->key.data, key.data, key.len) == 0) { + void *data = NULL; + if (destroy) { + cx_invoke_destructor(map, elm->data); + } else { + if (map->store_pointer) { + data = *(void **) elm->data; + } else { + data = elm->data; + } + } + if (remove) { + cx_hash_map_unlink(hash_map, slot, prev, elm); + } + return data; + } + } + prev = elm; + elm = prev->next; + } + + return NULL; +} + +static void *cx_hash_map_get( + CxMap const *map, + CxHashKey key +) { + // we can safely cast, because we know the map stays untouched + return cx_hash_map_get_remove((CxMap *) map, key, false, false); +} + +static void *cx_hash_map_remove( + CxMap *map, + CxHashKey key, + bool destroy +) { + return cx_hash_map_get_remove(map, key, true, destroy); +} + +static void *cx_hash_map_iter_current_entry(void const *it) { + struct cx_iterator_s const *iter = it; + // struct has to have a compatible signature + return (struct cx_map_entry_s *) &(iter->kv_data); +} + +static void *cx_hash_map_iter_current_key(void const *it) { + struct cx_iterator_s const *iter = it; + struct cx_hash_map_element_s *elm = iter->elem_handle; + return &elm->key; +} + +static void *cx_hash_map_iter_current_value(void const *it) { + struct cx_iterator_s const *iter = it; + struct cx_hash_map_s const *map = iter->src_handle; + struct cx_hash_map_element_s *elm = iter->elem_handle; + if (map->base.store_pointer) { + return *(void **) elm->data; + } else { + return elm->data; + } +} + +static bool cx_hash_map_iter_valid(void const *it) { + struct cx_iterator_s const *iter = it; + return iter->elem_handle != 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; + + // remove current element, if asked + if (iter->base.remove) { + // obtain mutable pointer to the map + struct cx_mut_iterator_s *miter = it; + struct cx_hash_map_s *map = miter->src_handle; + + // clear the flag + iter->base.remove = false; + + // determine the next element + struct cx_hash_map_element_s *next = elm->next; + + // search the previous element + struct cx_hash_map_element_s *prev = NULL; + if (map->buckets[iter->slot] != elm) { + prev = map->buckets[iter->slot]; + while (prev->next != elm) { + prev = prev->next; + } + } + + // destroy + cx_invoke_destructor((struct cx_map_s *) map, elm->data); + + // unlink + cx_hash_map_unlink(map, iter->slot, prev, elm); + + // advance + elm = next; + } else { + // just advance + elm = elm->next; + iter->index++; + } + + // search the next bucket, if required + struct cx_hash_map_s const *map = iter->src_handle; + while (elm == NULL && ++iter->slot < map->bucket_count) { + elm = map->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.store_pointer) { + iter->kv_data.value = *(void **) elm->data; + } else { + iter->kv_data.value = elm->data; + } + } +} + +static bool cx_hash_map_iter_flag_rm(void *it) { + struct cx_iterator_base_s *iter = it; + if (iter->mutating) { + iter->remove = true; + return true; + } else { + return false; + } +} + +static CxIterator cx_hash_map_iterator(CxMap const *map) { + CxIterator iter; + + iter.src_handle = map; + iter.base.valid = cx_hash_map_iter_valid; + iter.base.next = cx_hash_map_iter_next; + iter.base.current = cx_hash_map_iter_current_entry; + iter.base.flag_removal = cx_hash_map_iter_flag_rm; + iter.base.remove = false; + iter.base.mutating = false; + + iter.slot = 0; + iter.index = 0; + + if (map->size > 0) { + struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; + struct cx_hash_map_element_s *elm = hash_map->buckets[0]; + while (elm == NULL) { + elm = hash_map->buckets[++iter.slot]; + } + iter.elem_handle = elm; + iter.kv_data.key = &elm->key; + if (map->store_pointer) { + iter.kv_data.value = *(void **) elm->data; + } else { + iter.kv_data.value = elm->data; + } + } else { + iter.elem_handle = NULL; + iter.kv_data.key = NULL; + iter.kv_data.value = NULL; + } + + return iter; +} + +static CxIterator cx_hash_map_iterator_keys(CxMap const *map) { + CxIterator iter = cx_hash_map_iterator(map); + iter.base.current = cx_hash_map_iter_current_key; + return iter; +} + +static CxIterator cx_hash_map_iterator_values(CxMap const *map) { + CxIterator iter = cx_hash_map_iterator(map); + iter.base.current = cx_hash_map_iter_current_value; + return iter; +} + +static CxMutIterator cx_hash_map_mut_iterator(CxMap *map) { + CxIterator it = cx_hash_map_iterator(map); + it.base.mutating = true; + + // we know the iterators share the same memory layout + CxMutIterator iter; + memcpy(&iter, &it, sizeof(CxMutIterator)); + return iter; +} + +static CxMutIterator cx_hash_map_mut_iterator_keys(CxMap *map) { + CxMutIterator iter = cx_hash_map_mut_iterator(map); + iter.base.current = cx_hash_map_iter_current_key; + return iter; +} + +static CxMutIterator cx_hash_map_mut_iterator_values(CxMap *map) { + CxMutIterator iter = cx_hash_map_mut_iterator(map); + iter.base.current = cx_hash_map_iter_current_value; + return iter; +} + +static cx_map_class cx_hash_map_class = { + cx_hash_map_destructor, + cx_hash_map_clear, + cx_hash_map_put, + cx_hash_map_get, + cx_hash_map_remove, + cx_hash_map_iterator, + cx_hash_map_iterator_keys, + cx_hash_map_iterator_values, + cx_hash_map_mut_iterator, + cx_hash_map_mut_iterator_keys, + cx_hash_map_mut_iterator_values, +}; + +CxMap *cxHashMapCreate( + CxAllocator const *allocator, + size_t itemsize, + size_t buckets +) { + if (buckets == 0) { + // implementation defined default + buckets = 16; + } + + struct cx_hash_map_s *map = cxCalloc(allocator, 1, + sizeof(struct cx_hash_map_s)); + if (map == NULL) return NULL; + + // initialize hash map members + map->bucket_count = buckets; + map->buckets = cxCalloc(allocator, buckets, + sizeof(struct cx_hash_map_element_s *)); + if (map->buckets == NULL) { + cxFree(allocator, map); + return NULL; + } + + // initialize base members + map->base.cl = &cx_hash_map_class; + map->base.allocator = allocator; + + if (itemsize > 0) { + map->base.store_pointer = false; + map->base.item_size = itemsize; + } else { + map->base.store_pointer = true; + map->base.item_size = sizeof(void *); + } + + return (CxMap *) map; +} + +int cxMapRehash(CxMap *map) { + struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; + if (map->size > ((hash_map->bucket_count * 3) >> 2)) { + + size_t new_bucket_count = (map->size * 5) >> 1; + struct cx_hash_map_element_s **new_buckets = cxCalloc( + map->allocator, + new_bucket_count, sizeof(struct cx_hash_map_element_s *) + ); + + if (new_buckets == NULL) { + return 1; + } + + // iterate through the elements and assign them to their new slots + cx_for_n(slot, hash_map->bucket_count) { + struct cx_hash_map_element_s *elm = hash_map->buckets[slot]; + while (elm != NULL) { + struct cx_hash_map_element_s *next = elm->next; + size_t new_slot = elm->key.hash % new_bucket_count; + + // find position where to insert + struct cx_hash_map_element_s *bucket_next = new_buckets[new_slot]; + struct cx_hash_map_element_s *bucket_prev = NULL; + while (bucket_next != NULL && + bucket_next->key.hash < elm->key.hash) { + bucket_prev = bucket_next; + bucket_next = bucket_next->next; + } + + // insert + if (bucket_prev == NULL) { + elm->next = new_buckets[new_slot]; + new_buckets[new_slot] = elm; + } else { + bucket_prev->next = elm; + elm->next = bucket_next; + } + + // advance + elm = next; + } + } + + // assign result to the map + hash_map->bucket_count = new_bucket_count; + cxFree(map->allocator, hash_map->buckets); + hash_map->buckets = new_buckets; + } + return 0; +} diff -r a569148841ff -r efbd59642577 ucx/linked_list.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/linked_list.c Fri Apr 21 21:25:32 2023 +0200 @@ -0,0 +1,919 @@ +/* + * 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/linked_list.h" +#include "cx/utils.h" +#include +#include + +// LOW LEVEL LINKED LIST FUNCTIONS + +#define CX_LL_PTR(cur, off) (*(void**)(((char*)(cur))+(off))) +#define ll_prev(node) CX_LL_PTR(node, loc_prev) +#define ll_next(node) CX_LL_PTR(node, loc_next) +#define ll_advance(node) CX_LL_PTR(node, loc_advance) +#define ll_data(node) (((char*)(node))+loc_data) + +void *cx_linked_list_at( + void const *start, + size_t start_index, + ptrdiff_t loc_advance, + size_t index +) { + assert(start != NULL); + assert(loc_advance >= 0); + size_t i = start_index; + void const *cur = start; + while (i != index && cur != NULL) { + cur = ll_advance(cur); + i < index ? i++ : i--; + } + return (void *) cur; +} + +size_t cx_linked_list_find( + void const *start, + ptrdiff_t loc_advance, + ptrdiff_t loc_data, + cx_compare_func cmp_func, + void const *elem +) { + assert(start != NULL); + assert(loc_advance >= 0); + assert(loc_data >= 0); + assert(cmp_func); + + void const *node = start; + size_t index = 0; + do { + void *current = ll_data(node); + if (cmp_func(current, elem) == 0) { + return index; + } + node = ll_advance(node); + index++; + } while (node != NULL); + return index; +} + +void *cx_linked_list_first( + void const *node, + ptrdiff_t loc_prev +) { + return cx_linked_list_last(node, loc_prev); +} + +void *cx_linked_list_last( + void const *node, + ptrdiff_t loc_next +) { + assert(node != NULL); + assert(loc_next >= 0); + + void const *cur = node; + void const *last; + do { + last = cur; + } while ((cur = ll_next(cur)) != NULL); + + return (void *) last; +} + +void *cx_linked_list_prev( + void const *begin, + ptrdiff_t loc_next, + void const *node +) { + assert(begin != NULL); + assert(node != NULL); + assert(loc_next >= 0); + if (begin == node) return NULL; + void const *cur = begin; + void const *next; + while (1) { + next = ll_next(cur); + if (next == node) return (void *) cur; + cur = next; + } +} + +void cx_linked_list_link( + void *left, + void *right, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +) { + assert(loc_next >= 0); + ll_next(left) = right; + if (loc_prev >= 0) { + ll_prev(right) = left; + } +} + +void cx_linked_list_unlink( + void *left, + void *right, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +) { + assert (loc_next >= 0); + assert(ll_next(left) == right); + ll_next(left) = NULL; + if (loc_prev >= 0) { + assert(ll_prev(right) == left); + ll_prev(right) = NULL; + } +} + +void cx_linked_list_add( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *new_node +) { + void *last; + if (end == NULL) { + assert(begin != NULL); + last = *begin == NULL ? NULL : cx_linked_list_last(*begin, loc_next); + } else { + last = *end; + } + cx_linked_list_insert_chain(begin, end, loc_prev, loc_next, last, new_node, new_node); +} + +void cx_linked_list_prepend( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *new_node +) { + cx_linked_list_insert_chain(begin, end, loc_prev, loc_next, NULL, new_node, new_node); +} + +void cx_linked_list_insert( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *node, + void *new_node +) { + cx_linked_list_insert_chain(begin, end, loc_prev, loc_next, node, new_node, new_node); +} + +void cx_linked_list_insert_chain( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *node, + void *insert_begin, + void *insert_end +) { + // find the end of the chain, if not specified + if (insert_end == NULL) { + insert_end = cx_linked_list_last(insert_begin, loc_next); + } + + // determine the successor + void *successor; + if (node == NULL) { + assert(begin != NULL || (end != NULL && loc_prev >= 0)); + if (begin != NULL) { + successor = *begin; + *begin = insert_begin; + } else { + successor = *end == NULL ? NULL : cx_linked_list_first(*end, loc_prev); + } + } else { + successor = ll_next(node); + cx_linked_list_link(node, insert_begin, loc_prev, loc_next); + } + + if (successor == NULL) { + // the list ends with the new chain + if (end != NULL) { + *end = insert_end; + } + } else { + cx_linked_list_link(insert_end, successor, loc_prev, loc_next); + } +} + +void cx_linked_list_remove( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *node +) { + assert(node != NULL); + assert(loc_next >= 0); + assert(loc_prev >= 0 || begin != NULL); + + // find adjacent nodes + void *next = ll_next(node); + void *prev; + if (loc_prev >= 0) { + prev = ll_prev(node); + } else { + prev = cx_linked_list_prev(*begin, loc_next, node); + } + + // update next pointer of prev node, or set begin + if (prev == NULL) { + if (begin != NULL) { + *begin = next; + } + } else { + ll_next(prev) = next; + } + + // update prev pointer of next node, or set end + if (next == NULL) { + if (end != NULL) { + *end = prev; + } + } else if (loc_prev >= 0) { + ll_prev(next) = prev; + } +} + +size_t cx_linked_list_size( + void const *node, + ptrdiff_t loc_next +) { + assert(loc_next >= 0); + size_t size = 0; + while (node != NULL) { + node = ll_next(node); + size++; + } + return size; +} + +#ifndef CX_LINKED_LIST_SORT_SBO_SIZE +#define CX_LINKED_LIST_SORT_SBO_SIZE 1024 +#endif + +static void *cx_linked_list_sort_merge( + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + ptrdiff_t loc_data, + size_t length, + void *ls, + void *le, + void *re, + cx_compare_func cmp_func +) { + void *sbo[CX_LINKED_LIST_SORT_SBO_SIZE]; + void **sorted = length >= CX_LINKED_LIST_SORT_SBO_SIZE ? + malloc(sizeof(void *) * length) : sbo; + if (sorted == NULL) abort(); + void *rc, *lc; + + lc = ls; + rc = le; + size_t n = 0; + while (lc && lc != le && rc != re) { + if (cmp_func(ll_data(lc), ll_data(rc)) <= 0) { + sorted[n] = lc; + lc = ll_next(lc); + } else { + sorted[n] = rc; + rc = ll_next(rc); + } + n++; + } + while (lc && lc != le) { + sorted[n] = lc; + lc = ll_next(lc); + n++; + } + while (rc && rc != re) { + sorted[n] = rc; + rc = ll_next(rc); + n++; + } + + // Update pointer + if (loc_prev >= 0) ll_prev(sorted[0]) = NULL; + cx_for_n (i, length - 1) { + cx_linked_list_link(sorted[i], sorted[i + 1], loc_prev, loc_next); + } + ll_next(sorted[length - 1]) = NULL; + + void *ret = sorted[0]; + if (sorted != sbo) { + free(sorted); + } + return ret; +} + +void cx_linked_list_sort( // NOLINT(misc-no-recursion) - purposely recursive function + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + ptrdiff_t loc_data, + cx_compare_func cmp_func +) { + assert(begin != NULL); + assert(loc_next >= 0); + assert(loc_data >= 0); + assert(cmp_func); + + void *lc, *ls, *le, *re; + + // set start node + ls = *begin; + + // check how many elements are already sorted + lc = ls; + size_t ln = 1; + while (ll_next(lc) != NULL && cmp_func(ll_data(ll_next(lc)), ll_data(lc)) > 0) { + lc = ll_next(lc); + ln++; + } + le = ll_next(lc); + + // if first unsorted node is NULL, the list is already completely sorted + if (le != NULL) { + void *rc; + size_t rn = 1; + rc = le; + // skip already sorted elements + while (ll_next(rc) != NULL && cmp_func(ll_data(ll_next(rc)), ll_data(rc)) > 0) { + rc = ll_next(rc); + rn++; + } + re = ll_next(rc); + + // {ls,...,le->prev} and {rs,...,re->prev} are sorted - merge them + void *sorted = cx_linked_list_sort_merge(loc_prev, loc_next, loc_data, + ln + rn, ls, le, re, cmp_func); + + // Something left? Sort it! + size_t remainder_length = cx_linked_list_size(re, loc_next); + if (remainder_length > 0) { + void *remainder = re; + cx_linked_list_sort(&remainder, NULL, loc_prev, loc_next, loc_data, cmp_func); + + // merge sorted list with (also sorted) remainder + *begin = cx_linked_list_sort_merge(loc_prev, loc_next, loc_data, + ln + rn + remainder_length, + sorted, remainder, NULL, cmp_func); + } else { + // no remainder - we've got our sorted list + *begin = sorted; + } + if (end) *end = cx_linked_list_last(sorted, loc_next); + } +} + +int cx_linked_list_compare( + void const *begin_left, + void const *begin_right, + ptrdiff_t loc_advance, + ptrdiff_t loc_data, + cx_compare_func cmp_func +) { + void const *left = begin_left, *right = begin_right; + + while (left != NULL && right != NULL) { + void const *left_data = ll_data(left); + void const *right_data = ll_data(right); + int result = cmp_func(left_data, right_data); + if (result != 0) return result; + left = ll_advance(left); + right = ll_advance(right); + } + + if (left != NULL) { return 1; } + else if (right != NULL) { return -1; } + else { return 0; } +} + +void cx_linked_list_reverse( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next +) { + assert(begin != NULL); + assert(loc_next >= 0); + + // swap all links + void *prev = NULL; + void *cur = *begin; + while (cur != NULL) { + void *next = ll_next(cur); + + ll_next(cur) = prev; + if (loc_prev >= 0) { + ll_prev(cur) = next; + } + + prev = cur; + cur = next; + } + + // update begin and end + if (end != NULL) { + *end = *begin; + } + *begin = prev; +} + +// HIGH LEVEL LINKED LIST IMPLEMENTATION + +bool CX_DISABLE_LINKED_LIST_SWAP_SBO = false; + +typedef struct cx_linked_list_node cx_linked_list_node; +struct cx_linked_list_node { + cx_linked_list_node *prev; + cx_linked_list_node *next; + char payload[]; +}; + +#define CX_LL_LOC_PREV offsetof(cx_linked_list_node, prev) +#define CX_LL_LOC_NEXT offsetof(cx_linked_list_node, next) +#define CX_LL_LOC_DATA offsetof(cx_linked_list_node, payload) + +typedef struct { + struct cx_list_s base; + cx_linked_list_node *begin; + cx_linked_list_node *end; +} cx_linked_list; + +static cx_linked_list_node *cx_ll_node_at( + cx_linked_list const *list, + size_t index +) { + if (index >= list->base.size) { + return NULL; + } else if (index > list->base.size / 2) { + return cx_linked_list_at(list->end, list->base.size - 1, CX_LL_LOC_PREV, index); + } else { + return cx_linked_list_at(list->begin, 0, CX_LL_LOC_NEXT, index); + } +} + +static int cx_ll_insert_at( + struct cx_list_s *list, + cx_linked_list_node *node, + void const *elem +) { + + // create the new new_node + cx_linked_list_node *new_node = cxMalloc(list->allocator, + sizeof(cx_linked_list_node) + list->item_size); + + // sortir if failed + if (new_node == NULL) return 1; + + // initialize new new_node + new_node->prev = new_node->next = NULL; + memcpy(new_node->payload, elem, list->item_size); + + // insert + cx_linked_list *ll = (cx_linked_list *) list; + cx_linked_list_insert_chain( + (void **) &ll->begin, (void **) &ll->end, + CX_LL_LOC_PREV, CX_LL_LOC_NEXT, + node, new_node, new_node + ); + + // increase the size and return + list->size++; + return 0; +} + +static size_t cx_ll_insert_array( + struct cx_list_s *list, + size_t index, + void const *array, + size_t n +) { + // out-of bounds and corner case check + if (index > list->size || n == 0) return 0; + + // find position efficiently + cx_linked_list_node *node = index == 0 ? NULL : cx_ll_node_at((cx_linked_list *) list, index - 1); + + // perform first insert + if (0 != cx_ll_insert_at(list, node, array)) { + return 1; + } + + // is there more? + if (n == 1) return 1; + + // we now know exactly where we are + node = node == NULL ? ((cx_linked_list *) list)->begin : node->next; + + // we can add the remaining nodes and immedately advance to the inserted node + char const *source = array; + for (size_t i = 1; i < n; i++) { + source += list->item_size; + if (0 != cx_ll_insert_at(list, node, source)) { + return i; + } + node = node->next; + } + return n; +} + +static int cx_ll_insert_element( + struct cx_list_s *list, + size_t index, + void const *element +) { + return 1 != cx_ll_insert_array(list, index, element, 1); +} + +static int cx_ll_remove( + struct cx_list_s *list, + size_t index +) { + cx_linked_list *ll = (cx_linked_list *) list; + cx_linked_list_node *node = cx_ll_node_at(ll, index); + + // out-of-bounds check + if (node == NULL) return 1; + + // element destruction + cx_invoke_destructor(list, node->payload); + + // remove + cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end, + CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node); + + // adjust size + list->size--; + + // free and return + cxFree(list->allocator, node); + + return 0; +} + +static void cx_ll_clear(struct cx_list_s *list) { + if (list->size == 0) return; + + cx_linked_list *ll = (cx_linked_list *) list; + cx_linked_list_node *node = ll->begin; + while (node != NULL) { + cx_invoke_destructor(list, node->payload); + cx_linked_list_node *next = node->next; + cxFree(list->allocator, node); + node = next; + } + ll->begin = ll->end = NULL; + list->size = 0; +} + +#ifndef CX_LINKED_LIST_SWAP_SBO_SIZE +#define CX_LINKED_LIST_SWAP_SBO_SIZE 16 +#endif + +static int cx_ll_swap( + struct cx_list_s *list, + size_t i, + size_t j +) { + if (i >= list->size || j >= list->size) return 1; + if (i == j) return 0; + + // perform an optimized search that finds both elements in one run + cx_linked_list *ll = (cx_linked_list *) list; + size_t mid = list->size / 2; + size_t left, right; + if (i < j) { + left = i; + right = j; + } else { + left = j; + right = i; + } + cx_linked_list_node *nleft, *nright; + if (left < mid && right < mid) { + // case 1: both items left from mid + nleft = cx_ll_node_at(ll, left); + nright = nleft; + for (size_t c = left; c < right; c++) { + nright = nright->next; + } + } else if (left >= mid && right >= mid) { + // case 2: both items right from mid + nright = cx_ll_node_at(ll, right); + nleft = nright; + for (size_t c = right; c > left; c--) { + nleft = nleft->prev; + } + } else { + // case 3: one item left, one item right + + // chose the closest to begin / end + size_t closest; + size_t other; + size_t diff2boundary = list->size - right - 1; + if (left <= diff2boundary) { + closest = left; + other = right; + nleft = cx_ll_node_at(ll, left); + } else { + closest = right; + other = left; + diff2boundary = left; + nright = cx_ll_node_at(ll, right); + } + + // is other element closer to us or closer to boundary? + if (right - left <= diff2boundary) { + // search other element starting from already found element + if (closest == left) { + nright = nleft; + for (size_t c = left; c < right; c++) { + nright = nright->next; + } + } else { + nleft = nright; + for (size_t c = right; c > left; c--) { + nleft = nleft->prev; + } + } + } else { + // search other element starting at the boundary + if (closest == left) { + nright = cx_ll_node_at(ll, other); + } else { + nleft = cx_ll_node_at(ll, other); + } + } + } + + if (list->item_size > CX_LINKED_LIST_SWAP_SBO_SIZE || CX_DISABLE_LINKED_LIST_SWAP_SBO) { + 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; + } + } else { + // swap payloads to avoid relinking + char buf[CX_LINKED_LIST_SWAP_SBO_SIZE]; + memcpy(buf, nleft->payload, list->item_size); + memcpy(nleft->payload, nright->payload, list->item_size); + memcpy(nright->payload, buf, list->item_size); + } + + return 0; +} + +static void *cx_ll_at( + struct cx_list_s const *list, + size_t index +) { + cx_linked_list *ll = (cx_linked_list *) list; + cx_linked_list_node *node = cx_ll_node_at(ll, index); + return node == NULL ? NULL : node->payload; +} + +static size_t cx_ll_find( + struct cx_list_s const *list, + void const *elem +) { + return cx_linked_list_find(((cx_linked_list *) list)->begin, + CX_LL_LOC_NEXT, CX_LL_LOC_DATA, + list->cmpfunc, elem); +} + +static void cx_ll_sort(struct cx_list_s *list) { + cx_linked_list *ll = (cx_linked_list *) list; + cx_linked_list_sort((void **) &ll->begin, (void **) &ll->end, + CX_LL_LOC_PREV, CX_LL_LOC_NEXT, CX_LL_LOC_DATA, + list->cmpfunc); +} + +static void cx_ll_reverse(struct cx_list_s *list) { + cx_linked_list *ll = (cx_linked_list *) list; + cx_linked_list_reverse((void **) &ll->begin, (void **) &ll->end, CX_LL_LOC_PREV, CX_LL_LOC_NEXT); +} + +static int cx_ll_compare( + struct cx_list_s const *list, + struct cx_list_s const *other +) { + cx_linked_list *left = (cx_linked_list *) list; + cx_linked_list *right = (cx_linked_list *) other; + return cx_linked_list_compare(left->begin, right->begin, + CX_LL_LOC_NEXT, CX_LL_LOC_DATA, + list->cmpfunc); +} + +static bool cx_ll_iter_valid(void const *it) { + struct cx_iterator_s const *iter = it; + return iter->elem_handle != NULL; +} + +static void cx_ll_iter_next(void *it) { + struct cx_iterator_base_s *itbase = it; + if (itbase->remove) { + itbase->remove = false; + struct cx_mut_iterator_s *iter = it; + struct cx_list_s *list = iter->src_handle; + cx_linked_list *ll = iter->src_handle; + cx_linked_list_node *node = iter->elem_handle; + iter->elem_handle = node->next; + 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->size--; + cxFree(list->allocator, node); + } else { + struct cx_iterator_s *iter = it; + iter->index++; + cx_linked_list_node *node = iter->elem_handle; + iter->elem_handle = node->next; + } +} + +static void cx_ll_iter_prev(void *it) { + struct cx_iterator_base_s *itbase = it; + if (itbase->remove) { + itbase->remove = false; + struct cx_mut_iterator_s *iter = it; + struct cx_list_s *list = iter->src_handle; + cx_linked_list *ll = iter->src_handle; + cx_linked_list_node *node = iter->elem_handle; + iter->elem_handle = node->prev; + iter->index--; + 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->size--; + cxFree(list->allocator, node); + } else { + struct cx_iterator_s *iter = it; + iter->index--; + cx_linked_list_node *node = iter->elem_handle; + iter->elem_handle = node->prev; + } +} + +static void *cx_ll_iter_current(void const *it) { + struct cx_iterator_s const *iter = it; + cx_linked_list_node *node = iter->elem_handle; + return node->payload; +} + +static bool cx_ll_iter_flag_rm(void *it) { + struct cx_iterator_base_s *iter = it; + if (iter->mutating) { + iter->remove = true; + return true; + } else { + return false; + } +} + +static CxIterator cx_ll_iterator( + struct cx_list_s const *list, + size_t index, + bool backwards +) { + CxIterator iter; + iter.index = index; + iter.src_handle = list; + iter.elem_handle = cx_ll_node_at((cx_linked_list const *) list, index); + iter.base.valid = cx_ll_iter_valid; + iter.base.current = cx_ll_iter_current; + iter.base.next = backwards ? cx_ll_iter_prev : cx_ll_iter_next; + iter.base.flag_removal = cx_ll_iter_flag_rm; + iter.base.mutating = false; + iter.base.remove = false; + return iter; +} + +static int cx_ll_insert_iter( + CxMutIterator *iter, + void const *elem, + int prepend +) { + struct cx_list_s *list = iter->src_handle; + cx_linked_list_node *node = iter->elem_handle; + if (node != NULL) { + assert(prepend >= 0 && prepend <= 1); + cx_linked_list_node *choice[2] = {node, node->prev}; + int result = cx_ll_insert_at(list, choice[prepend], elem); + iter->index += prepend * (0 == result); + return result; + } else { + int result = cx_ll_insert_element(list, list->size, elem); + iter->index = list->size; + return result; + } +} + +static void cx_ll_destructor(CxList *list) { + cx_linked_list *ll = (cx_linked_list *) list; + + cx_linked_list_node *node = ll->begin; + while (node) { + void *next = node->next; + cxFree(list->allocator, node); + node = next; + } + // do not free the list pointer, this is just a destructor! +} + +static cx_list_class cx_linked_list_class = { + cx_ll_destructor, + cx_ll_insert_element, + cx_ll_insert_array, + cx_ll_insert_iter, + cx_ll_remove, + cx_ll_clear, + cx_ll_swap, + cx_ll_at, + cx_ll_find, + cx_ll_sort, + cx_ll_compare, + cx_ll_reverse, + cx_ll_iterator, +}; + +CxList *cxLinkedListCreate( + CxAllocator const *allocator, + cx_compare_func comparator, + size_t item_size +) { + if (allocator == NULL) { + allocator = cxDefaultAllocator; + } + + 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.allocator = allocator; + list->base.cmpfunc = comparator; + + if (item_size > 0) { + list->base.item_size = item_size; + } else { + cxListStorePointers((CxList *) list); + } + + return (CxList *) list; +} diff -r a569148841ff -r efbd59642577 ucx/list.c --- a/ucx/list.c Sun Apr 16 14:12:24 2023 +0200 +++ b/ucx/list.c Fri Apr 21 21:25:32 2023 +0200 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. + * 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: @@ -26,311 +26,249 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include "ucx/list.h" +#include "cx/list.h" + +#include + +// + +static _Thread_local cx_compare_func cx_pl_cmpfunc_impl; + +static int cx_pl_cmpfunc( + void const *l, + void const *r +) { + void *const *lptr = l; + void *const *rptr = r; + void const *left = lptr == NULL ? NULL : *lptr; + void const *right = rptr == NULL ? NULL : *rptr; + return cx_pl_cmpfunc_impl(left, right); +} -UcxList *ucx_list_clone(UcxList *l, copy_func fnc, void *data) { - return ucx_list_clone_a(ucx_default_allocator(), l, fnc, data); +static void cx_pl_hack_cmpfunc(struct cx_list_s const *list) { + // cast away const - this is the hacky thing + struct cx_list_s *l = (struct cx_list_s *) list; + cx_pl_cmpfunc_impl = l->cmpfunc; + l->cmpfunc = cx_pl_cmpfunc; +} + +static void cx_pl_unhack_cmpfunc(struct cx_list_s const *list) { + // cast away const - this is the hacky thing + struct cx_list_s *l = (struct cx_list_s *) list; + l->cmpfunc = cx_pl_cmpfunc_impl; +} + +static void cx_pl_destructor(struct cx_list_s *list) { + list->climpl->destructor(list); +} + +static int cx_pl_insert_element( + struct cx_list_s *list, + size_t index, + void const *element +) { + return list->climpl->insert_element(list, index, &element); } -UcxList *ucx_list_clone_a(UcxAllocator *alloc, UcxList *l, - copy_func fnc, void *data) { - UcxList *ret = NULL; - while (l) { - if (fnc) { - ret = ucx_list_append_a(alloc, ret, fnc(l->data, data)); - } else { - ret = ucx_list_append_a(alloc, ret, l->data); - } - l = l->next; - } +static size_t cx_pl_insert_array( + struct cx_list_s *list, + size_t index, + void const *array, + size_t n +) { + return list->climpl->insert_array(list, index, array, n); +} + +static int cx_pl_insert_iter( + struct cx_mut_iterator_s *iter, + void const *elem, + int prepend +) { + struct cx_list_s *list = iter->src_handle; + return list->climpl->insert_iter(iter, &elem, prepend); +} + +static int cx_pl_remove( + struct cx_list_s *list, + size_t index +) { + return list->climpl->remove(list, index); +} + +static void cx_pl_clear(struct cx_list_s *list) { + list->climpl->clear(list); +} + +static int cx_pl_swap( + struct cx_list_s *list, + size_t i, + size_t j +) { + return list->climpl->swap(list, i, j); +} + +static void *cx_pl_at( + struct cx_list_s const *list, + size_t index +) { + void **ptr = list->climpl->at(list, index); + return ptr == NULL ? NULL : *ptr; +} + +static size_t cx_pl_find( + struct cx_list_s const *list, + void const *elem +) { + cx_pl_hack_cmpfunc(list); + size_t ret = list->climpl->find(list, &elem); + cx_pl_unhack_cmpfunc(list); return ret; } -int ucx_list_equals(const UcxList *l1, const UcxList *l2, - cmp_func fnc, void* data) { - if (l1 == l2) return 1; - - while (l1 != NULL && l2 != NULL) { - if (fnc == NULL) { - if (l1->data != l2->data) return 0; - } else { - if (fnc(l1->data, l2->data, data) != 0) return 0; - } - l1 = l1->next; - l2 = l2->next; - } - - return (l1 == NULL && l2 == NULL); +static void cx_pl_sort(struct cx_list_s *list) { + cx_pl_hack_cmpfunc(list); + list->climpl->sort(list); + cx_pl_unhack_cmpfunc(list); } -void ucx_list_free(UcxList *l) { - ucx_list_free_a(ucx_default_allocator(), l); +static int cx_pl_compare( + struct cx_list_s const *list, + struct cx_list_s const *other +) { + cx_pl_hack_cmpfunc(list); + int ret = list->climpl->compare(list, other); + cx_pl_unhack_cmpfunc(list); + return ret; } -void ucx_list_free_a(UcxAllocator *alloc, UcxList *l) { - UcxList *e = l, *f; - while (e != NULL) { - f = e; - e = e->next; - alfree(alloc, f); - } +static void cx_pl_reverse(struct cx_list_s *list) { + list->climpl->reverse(list); } -void ucx_list_free_content(UcxList* list, ucx_destructor destr) { - if (!destr) destr = free; - while (list != NULL) { - destr(list->data); - list = list->next; - } -} - -UcxList *ucx_list_append(UcxList *l, void *data) { - return ucx_list_append_a(ucx_default_allocator(), l, data); +static void *cx_pl_iter_current(void const *it) { + struct cx_iterator_s const *iter = it; + void **ptr = iter->base.current_impl(it); + return ptr == NULL ? NULL : *ptr; } -UcxList *ucx_list_append_a(UcxAllocator *alloc, UcxList *l, void *data) { - UcxList *nl = (UcxList*) almalloc(alloc, sizeof(UcxList)); - if (!nl) { - return NULL; - } - - nl->data = data; - nl->next = NULL; - if (l) { - UcxList *t = ucx_list_last(l); - t->next = nl; - nl->prev = t; - return l; - } else { - nl->prev = NULL; - return nl; - } -} - -UcxList *ucx_list_prepend(UcxList *l, void *data) { - return ucx_list_prepend_a(ucx_default_allocator(), l, data); +static struct cx_iterator_s cx_pl_iterator( + struct cx_list_s const *list, + size_t index, + bool backwards +) { + struct cx_iterator_s iter = list->climpl->iterator(list, index, backwards); + iter.base.current_impl = iter.base.current; + iter.base.current = cx_pl_iter_current; + return iter; } -UcxList *ucx_list_prepend_a(UcxAllocator *alloc, UcxList *l, void *data) { - UcxList *nl = ucx_list_append_a(alloc, NULL, data); - if (!nl) { - return NULL; - } - l = ucx_list_first(l); - - if (l) { - nl->next = l; - l->prev = nl; - } - return nl; -} +static cx_list_class cx_pointer_list_class = { + cx_pl_destructor, + cx_pl_insert_element, + cx_pl_insert_array, + cx_pl_insert_iter, + cx_pl_remove, + cx_pl_clear, + cx_pl_swap, + cx_pl_at, + cx_pl_find, + cx_pl_sort, + cx_pl_compare, + cx_pl_reverse, + cx_pl_iterator, +}; -UcxList *ucx_list_concat(UcxList *l1, UcxList *l2) { - if (l1) { - UcxList *last = ucx_list_last(l1); - last->next = l2; - if (l2) { - l2->prev = last; - } - return l1; - } else { - return l2; +void cxListStoreObjects(CxList *list) { + list->store_pointer = false; + if (list->climpl != NULL) { + list->cl = list->climpl; + list->climpl = NULL; } } -UcxList *ucx_list_last(const UcxList *l) { - if (l == NULL) return NULL; - - const UcxList *e = l; - while (e->next != NULL) { - e = e->next; - } - return (UcxList*)e; -} - -ssize_t ucx_list_indexof(const UcxList *list, const UcxList *elem) { - ssize_t index = 0; - while (list) { - if (list == elem) { - return index; - } - list = list->next; - index++; - } - return -1; +void cxListStorePointers(CxList *list) { + list->item_size = sizeof(void *); + list->store_pointer = true; + list->climpl = list->cl; + list->cl = &cx_pointer_list_class; } -UcxList *ucx_list_get(const UcxList *l, size_t index) { - if (l == NULL) return NULL; - - const UcxList *e = l; - while (e->next && index > 0) { - e = e->next; - index--; - } - - return (UcxList*)(index == 0 ? e : NULL); -} +// -ssize_t ucx_list_find(UcxList *l, void *elem, cmp_func fnc, void *cmpdata) { - ssize_t index = 0; - UCX_FOREACH(e, l) { - if (fnc) { - if (fnc(elem, e->data, cmpdata) == 0) { - return index; - } - } else { - if (elem == e->data) { - return index; - } +void cxListDestroy(CxList *list) { + if (list->simple_destructor) { + CxIterator iter = cxListIterator(list); + cx_foreach(void*, elem, iter) { + // already correctly resolved pointer - immediately invoke dtor + list->simple_destructor(elem); } - index++; } - return -1; -} - -int ucx_list_contains(UcxList *l, void *elem, cmp_func fnc, void *cmpdata) { - return ucx_list_find(l, elem, fnc, cmpdata) > -1; -} - -size_t ucx_list_size(const UcxList *l) { - if (l == NULL) return 0; - - const UcxList *e = l; - size_t s = 1; - while (e->next != NULL) { - e = e->next; - s++; + if (list->advanced_destructor) { + CxIterator iter = cxListIterator(list); + cx_foreach(void*, elem, iter) { + // already correctly resolved pointer - immediately invoke dtor + list->advanced_destructor(list->destructor_data, elem); + } } - return s; + list->cl->destructor(list); + cxFree(list->allocator, list); } -static UcxList *ucx_list_sort_merge(int length, - UcxList* ls, UcxList* le, UcxList* re, - cmp_func fnc, void* data) { - - UcxList** sorted = (UcxList**) malloc(sizeof(UcxList*)*length); - UcxList *rc, *lc; - - lc = ls; rc = le; - int n = 0; - while (lc && lc != le && rc != re) { - if (fnc(lc->data, rc->data, data) <= 0) { - sorted[n] = lc; - lc = lc->next; +int cxListCompare( + CxList const *list, + CxList const *other +) { + if ((list->store_pointer ^ other->store_pointer) || + ((list->climpl == NULL) ^ (other->climpl != NULL)) || + ((list->climpl != NULL ? list->climpl->compare : list->cl->compare) != + (other->climpl != NULL ? other->climpl->compare : other->cl->compare))) { + // lists are definitely different - cannot use internal compare function + if (list->size == other->size) { + CxIterator left = cxListIterator(list); + CxIterator right = cxListIterator(other); + for (size_t i = 0; i < list->size; i++) { + void *leftValue = cxIteratorCurrent(left); + void *rightValue = cxIteratorCurrent(right); + int d = list->cmpfunc(leftValue, rightValue); + if (d != 0) { + return d; + } + cxIteratorNext(left); + cxIteratorNext(right); + } + return 0; } else { - sorted[n] = rc; - rc = rc->next; + return list->size < other->size ? -1 : 1; } - n++; - } - while (lc && lc != le) { - sorted[n] = lc; - lc = lc->next; - n++; - } - while (rc && rc != re) { - sorted[n] = rc; - rc = rc->next; - n++; - } - - // Update pointer - sorted[0]->prev = NULL; - for (int i = 0 ; i < length-1 ; i++) { - sorted[i]->next = sorted[i+1]; - sorted[i+1]->prev = sorted[i]; - } - sorted[length-1]->next = NULL; - - UcxList *ret = sorted[0]; - free(sorted); - return ret; -} - -UcxList *ucx_list_sort(UcxList *l, cmp_func fnc, void *data) { - if (l == NULL) { - return NULL; - } - - UcxList *lc; - int ln = 1; - - UcxList *ls = l, *le, *re; - - // check how many elements are already sorted - lc = ls; - while (lc->next != NULL && fnc(lc->next->data, lc->data, data) > 0) { - lc = lc->next; - ln++; - } - le = lc->next; - - if (le == NULL) { - return l; // this list is already sorted :) } else { - UcxList *rc; - int rn = 1; - rc = le; - // skip already sorted elements - while (rc->next != NULL && fnc(rc->next->data, rc->data, data) > 0) { - rc = rc->next; - rn++; - } - re = rc->next; - - // {ls,...,le->prev} and {rs,...,re->prev} are sorted - merge them - UcxList *sorted = ucx_list_sort_merge(ln+rn, - ls, le, re, - fnc, data); - - // Something left? Sort it! - size_t remainder_length = ucx_list_size(re); - if (remainder_length > 0) { - UcxList *remainder = ucx_list_sort(re, fnc, data); - - // merge sorted list with (also sorted) remainder - l = ucx_list_sort_merge(ln+rn+remainder_length, - sorted, remainder, NULL, fnc, data); - } else { - // no remainder - we've got our sorted list - l = sorted; - } - - return l; + // lists are compatible + return list->cl->compare(list, other); } } -UcxList *ucx_list_first(const UcxList *l) { - if (!l) { - return NULL; - } - - const UcxList *e = l; - while (e->prev) { - e = e->prev; - } - return (UcxList *)e; +CxMutIterator cxListMutIteratorAt( + CxList *list, + size_t index +) { + CxIterator it = list->cl->iterator(list, index, false); + it.base.mutating = true; + + // we know the iterators share the same memory layout + CxMutIterator iter; + memcpy(&iter, &it, sizeof(CxMutIterator)); + return iter; } -UcxList *ucx_list_remove(UcxList *l, UcxList *e) { - return ucx_list_remove_a(ucx_default_allocator(), l, e); +CxMutIterator cxListMutBackwardsIteratorAt( + CxList *list, + size_t index +) { + CxIterator it = list->cl->iterator(list, index, true); + it.base.mutating = true; + + // we know the iterators share the same memory layout + CxMutIterator iter; + memcpy(&iter, &it, sizeof(CxMutIterator)); + return iter; } - -UcxList *ucx_list_remove_a(UcxAllocator *alloc, UcxList *l, UcxList *e) { - if (l == e) { - l = e->next; - } - - if (e->next) { - e->next->prev = e->prev; - } - - if (e->prev) { - e->prev->next = e->next; - } - - alfree(alloc, e); - return l; -} diff -r a569148841ff -r efbd59642577 ucx/logging.c --- a/ucx/logging.c Sun Apr 16 14:12:24 2023 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,113 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 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 "ucx/logging.h" - -#include -#include -#include -#include - -UcxLogger *ucx_logger_new(void *stream, unsigned int level, unsigned int mask) { - UcxLogger *logger = (UcxLogger*) malloc(sizeof(UcxLogger)); - if (logger != NULL) { - logger->stream = stream; - logger->writer = (write_func)fwrite; - logger->dateformat = (char*) "%F %T %z "; - logger->level = level; - logger->mask = mask; - logger->levels = ucx_map_new(8); - - unsigned int l; - l = UCX_LOGGER_ERROR; - ucx_map_int_put(logger->levels, l, (void*) "[ERROR]"); - l = UCX_LOGGER_WARN; - ucx_map_int_put(logger->levels, l, (void*) "[WARNING]"); - l = UCX_LOGGER_INFO; - ucx_map_int_put(logger->levels, l, (void*) "[INFO]"); - l = UCX_LOGGER_DEBUG; - ucx_map_int_put(logger->levels, l, (void*) "[DEBUG]"); - l = UCX_LOGGER_TRACE; - ucx_map_int_put(logger->levels, l, (void*) "[TRACE]"); - } - - return logger; -} - -void ucx_logger_free(UcxLogger *logger) { - ucx_map_free(logger->levels); - free(logger); -} - -// estimated max. message length (documented) -#define UCX_LOGGER_MSGMAX 4096 - -void ucx_logger_logf(UcxLogger *logger, unsigned int level, const char* file, - const unsigned int line, const char *format, ...) { - if (level <= logger->level) { - char msg[UCX_LOGGER_MSGMAX]; - const char *text; - size_t k = 0; - size_t n; - - if ((logger->mask & UCX_LOGGER_LEVEL) > 0) { - text = (const char*) ucx_map_int_get(logger->levels, level); - if (!text) { - text = "[UNKNOWN]"; - } - n = strlen(text); - n = n > 256 ? 256 : n; - memcpy(msg+k, text, n); - k += n; - msg[k++] = ' '; - } - if ((logger->mask & UCX_LOGGER_TIMESTAMP) > 0) { - time_t now = time(NULL); - k += strftime(msg+k, 128, logger->dateformat, localtime(&now)); - } - if ((logger->mask & UCX_LOGGER_SOURCE) > 0) { - n = strlen(file); - memcpy(msg+k, file, n); - k += n; - k += sprintf(msg+k, ":%u ", line); - } - - if (k > 0) { - msg[k++] = '-'; msg[k++] = ' '; - } - - va_list args; - va_start (args, format); - k += vsnprintf(msg+k, UCX_LOGGER_MSGMAX-k-1, format, args); - va_end (args); - - msg[k++] = '\n'; - - logger->writer(msg, 1, k, logger->stream); - } -} diff -r a569148841ff -r efbd59642577 ucx/map.c --- a/ucx/map.c Sun Apr 16 14:12:24 2023 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,337 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 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 "ucx/map.h" - -#include -#include - -UcxMap *ucx_map_new(size_t size) { - return ucx_map_new_a(NULL, size); -} - -UcxMap *ucx_map_new_a(UcxAllocator *allocator, size_t size) { - if(size == 0) { - size = 16; - } - - if(!allocator) { - allocator = ucx_default_allocator(); - } - - UcxMap *map = (UcxMap*)almalloc(allocator, sizeof(UcxMap)); - if (!map) { - return NULL; - } - - map->allocator = allocator; - map->map = (UcxMapElement**)alcalloc( - allocator, size, sizeof(UcxMapElement*)); - if(map->map == NULL) { - alfree(allocator, map); - return NULL; - } - map->size = size; - map->count = 0; - - return map; -} - -static void ucx_map_free_elmlist_contents(UcxMap *map) { - for (size_t n = 0 ; n < map->size ; n++) { - UcxMapElement *elem = map->map[n]; - if (elem != NULL) { - do { - UcxMapElement *next = elem->next; - alfree(map->allocator, elem->key.data); - alfree(map->allocator, elem); - elem = next; - } while (elem != NULL); - } - } -} - -void ucx_map_free(UcxMap *map) { - ucx_map_free_elmlist_contents(map); - alfree(map->allocator, map->map); - alfree(map->allocator, map); -} - -void ucx_map_free_content(UcxMap *map, ucx_destructor destr) { - UcxMapIterator iter = ucx_map_iterator(map); - void *val; - UCX_MAP_FOREACH(key, val, iter) { - if (destr) { - destr(val); - } else { - alfree(map->allocator, val); - } - } -} - -void ucx_map_clear(UcxMap *map) { - if (map->count == 0) { - return; // nothing to do - } - ucx_map_free_elmlist_contents(map); - memset(map->map, 0, map->size*sizeof(UcxMapElement*)); - map->count = 0; -} - -int ucx_map_copy(UcxMap *from, UcxMap *to, copy_func fnc, void *data) { - UcxMapIterator i = ucx_map_iterator(from); - void *value; - UCX_MAP_FOREACH(key, value, i) { - if (ucx_map_put(to, key, fnc ? fnc(value, data) : value)) { - return 1; - } - } - return 0; -} - -UcxMap *ucx_map_clone(UcxMap *map, copy_func fnc, void *data) { - size_t bs = (map->count * 5) >> 1; - UcxMap *newmap = ucx_map_new(bs > map->size ? bs : map->size); - if (!newmap) { - return NULL; - } - ucx_map_copy(map, newmap, fnc, data); - return newmap; -} - -int ucx_map_rehash(UcxMap *map) { - size_t load = (map->size * 3) >> 2; - if (map->count > load) { - UcxMap oldmap; - oldmap.map = map->map; - oldmap.size = map->size; - oldmap.count = map->count; - oldmap.allocator = map->allocator; - - map->size = (map->count * 5) >> 1; - map->map = (UcxMapElement**)alcalloc( - map->allocator, map->size, sizeof(UcxMapElement*)); - if (!map->map) { - *map = oldmap; - return 1; - } - map->count = 0; - ucx_map_copy(&oldmap, map, NULL, NULL); - - /* free the UcxMapElement list of oldmap */ - ucx_map_free_elmlist_contents(&oldmap); - alfree(map->allocator, oldmap.map); - } - return 0; -} - -int ucx_map_put(UcxMap *map, UcxKey key, void *data) { - UcxAllocator *allocator = map->allocator; - - if (key.hash == 0) { - key.hash = ucx_hash((const char*)key.data, key.len); - } - - struct UcxMapKey mapkey; - mapkey.hash = key.hash; - - size_t slot = mapkey.hash%map->size; - UcxMapElement *elm = map->map[slot]; - UcxMapElement *prev = NULL; - - while (elm && elm->key.hash < mapkey.hash) { - prev = elm; - elm = elm->next; - } - - if (!elm || elm->key.hash != mapkey.hash) { - UcxMapElement *e = (UcxMapElement*)almalloc( - allocator, sizeof(UcxMapElement)); - if (!e) { - return -1; - } - e->key.data = NULL; - if (prev) { - prev->next = e; - } else { - map->map[slot] = e; - } - e->next = elm; - elm = e; - } - - if (!elm->key.data) { - void *kd = almalloc(allocator, key.len); - if (!kd) { - return -1; - } - memcpy(kd, key.data, key.len); - mapkey.data = kd; - mapkey.len = key.len; - elm->key = mapkey; - map->count++; - } - elm->data = data; - - return 0; -} - -static void* ucx_map_get_and_remove(UcxMap *map, UcxKey key, int remove) { - if(key.hash == 0) { - key.hash = ucx_hash((const char*)key.data, key.len); - } - - size_t slot = key.hash%map->size; - UcxMapElement *elm = map->map[slot]; - UcxMapElement *pelm = NULL; - while (elm && elm->key.hash <= key.hash) { - if(elm->key.hash == key.hash) { - int n = (key.len > elm->key.len) ? elm->key.len : key.len; - if (memcmp(elm->key.data, key.data, n) == 0) { - void *data = elm->data; - if (remove) { - if (pelm) { - pelm->next = elm->next; - } else { - map->map[slot] = elm->next; - } - alfree(map->allocator, elm->key.data); - alfree(map->allocator, elm); - map->count--; - } - - return data; - } - } - pelm = elm; - elm = pelm->next; - } - - return NULL; -} - -void *ucx_map_get(UcxMap *map, UcxKey key) { - return ucx_map_get_and_remove(map, key, 0); -} - -void *ucx_map_remove(UcxMap *map, UcxKey key) { - return ucx_map_get_and_remove(map, key, 1); -} - -UcxKey ucx_key(const void *data, size_t len) { - UcxKey key; - key.data = data; - key.len = len; - key.hash = ucx_hash((const char*)data, len); - return key; -} - - -int ucx_hash(const char *data, size_t len) { - /* murmur hash 2 */ - - int m = 0x5bd1e995; - int r = 24; - - int h = 25 ^ len; - - int i = 0; - while (len >= 4) { - int k = data[i + 0] & 0xFF; - k |= (data[i + 1] & 0xFF) << 8; - k |= (data[i + 2] & 0xFF) << 16; - k |= (data[i + 3] & 0xFF) << 24; - - k *= m; - k ^= k >> r; - k *= m; - - h *= m; - h ^= k; - - i += 4; - len -= 4; - } - - switch (len) { - case 3: h ^= (data[i + 2] & 0xFF) << 16; - /* no break */ - case 2: h ^= (data[i + 1] & 0xFF) << 8; - /* no break */ - case 1: h ^= (data[i + 0] & 0xFF); h *= m; - /* no break */ - } - - h ^= h >> 13; - h *= m; - h ^= h >> 15; - - return h; -} - -UcxMapIterator ucx_map_iterator(UcxMap *map) { - UcxMapIterator i; - i.map = map; - i.cur = NULL; - i.index = 0; - return i; -} - -int ucx_map_iter_next(UcxMapIterator *i, UcxKey *key, void **elm) { - UcxMapElement *e = i->cur; - - if (e) { - e = e->next; - } else { - e = i->map->map[0]; - } - - while (i->index < i->map->size) { - if (e) { - if (e->data) { - i->cur = e; - *elm = e->data; - key->data = e->key.data; - key->hash = e->key.hash; - key->len = e->key.len; - return 1; - } - - e = e->next; - } else { - i->index++; - - if (i->index < i->map->size) { - e = i->map->map[i->index]; - } - } - } - - return 0; -} - diff -r a569148841ff -r efbd59642577 ucx/mempool.c --- a/ucx/mempool.c Sun Apr 16 14:12:24 2023 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,237 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 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 "ucx/mempool.h" - -#include -#include -#include -#ifdef __cplusplus -#define __STDC_FORMAT_MACROS -#endif -#include - -/** Capsule for destructible memory chunks. */ -typedef struct { - /** The destructor for the memory chunk. */ - ucx_destructor destructor; - /** - * First byte of the memory chunk. - * Note, that the address &c is also the address - * of the whole memory chunk. - */ - char c; -} ucx_memchunk; - -/** Capsule for data and its destructor. */ -typedef struct { - /** The destructor for the data. */ - ucx_destructor destructor; - /** A pointer to the data. */ - void *ptr; -} ucx_regdestr; - -#ifdef __cplusplus -extern "C" -#endif -void ucx_mempool_shared_destr(void* ptr) { - ucx_regdestr *rd = (ucx_regdestr*)ptr; - rd->destructor(rd->ptr); -} - -UcxMempool *ucx_mempool_new(size_t n) { - size_t poolsz; - if(ucx_szmul(n, sizeof(void*), &poolsz)) { - return NULL; - } - - UcxMempool *pool = (UcxMempool*)malloc(sizeof(UcxMempool)); - if (!pool) { - return NULL; - } - - pool->data = (void**) malloc(poolsz); - if (pool->data == NULL) { - free(pool); - return NULL; - } - - pool->ndata = 0; - pool->size = n; - - UcxAllocator *allocator = (UcxAllocator*)malloc(sizeof(UcxAllocator)); - if(!allocator) { - free(pool->data); - free(pool); - return NULL; - } - allocator->malloc = (ucx_allocator_malloc)ucx_mempool_malloc; - allocator->calloc = (ucx_allocator_calloc)ucx_mempool_calloc; - allocator->realloc = (ucx_allocator_realloc)ucx_mempool_realloc; - allocator->free = (ucx_allocator_free)ucx_mempool_free; - allocator->pool = pool; - pool->allocator = allocator; - - return pool; -} - -int ucx_mempool_chcap(UcxMempool *pool, size_t newcap) { - if (newcap < pool->ndata) { - return 1; - } - - size_t newcapsz; - if(ucx_szmul(newcap, sizeof(void*), &newcapsz)) { - return 1; - } - - void **data = (void**) realloc(pool->data, newcapsz); - if (data) { - pool->data = data; - pool->size = newcap; - return 0; - } else { - return 1; - } -} - -void *ucx_mempool_malloc(UcxMempool *pool, size_t n) { - if(((size_t)-1) - sizeof(ucx_destructor) < n) { - return NULL; - } - - if (pool->ndata >= pool->size) { - size_t newcap = pool->size*2; - if (newcap < pool->size || ucx_mempool_chcap(pool, newcap)) { - return NULL; - } - } - - void *p = malloc(sizeof(ucx_destructor) + n); - ucx_memchunk *mem = (ucx_memchunk*)p; - if (!mem) { - return NULL; - } - - mem->destructor = NULL; - pool->data[pool->ndata] = mem; - pool->ndata++; - - return &(mem->c); -} - -void *ucx_mempool_calloc(UcxMempool *pool, size_t nelem, size_t elsize) { - size_t msz; - if(ucx_szmul(nelem, elsize, &msz)) { - return NULL; - } - - void *ptr = ucx_mempool_malloc(pool, msz); - if (!ptr) { - return NULL; - } - memset(ptr, 0, nelem * elsize); - return ptr; -} - -void *ucx_mempool_realloc(UcxMempool *pool, void *ptr, size_t n) { - if(((size_t)-1) - sizeof(ucx_destructor) < n) { - return NULL; - } - - char *mem = ((char*)ptr) - sizeof(ucx_destructor); - char *newm = (char*) realloc(mem, n + sizeof(ucx_destructor)); - if (!newm) { - return NULL; - } - if (mem != newm) { - for(size_t i=0 ; i < pool->ndata ; i++) { - if(pool->data[i] == mem) { - pool->data[i] = newm; - return newm + sizeof(ucx_destructor); - } - } - fprintf(stderr, "FATAL: 0x%08" PRIxPTR" not in mpool 0x%08" PRIxPTR"\n", - (intptr_t)ptr, (intptr_t)pool); - abort(); - } else { - return newm + sizeof(ucx_destructor); - } -} - -void ucx_mempool_free(UcxMempool *pool, void *ptr) { - ucx_memchunk *chunk = (ucx_memchunk*)((char*)ptr-sizeof(ucx_destructor)); - for(size_t i=0 ; indata ; i++) { - if(chunk == pool->data[i]) { - if(chunk->destructor != NULL) { - chunk->destructor(&(chunk->c)); - } - free(chunk); - size_t last_index = pool->ndata - 1; - if(i != last_index) { - pool->data[i] = pool->data[last_index]; - pool->data[last_index] = NULL; - } - pool->ndata--; - return; - } - } - fprintf(stderr, "FATAL: 0x%08" PRIxPTR" not in mpool 0x%08" PRIxPTR"\n", - (intptr_t)ptr, (intptr_t)pool); - abort(); -} - -void ucx_mempool_destroy(UcxMempool *pool) { - ucx_memchunk *chunk; - for(size_t i=0 ; indata ; i++) { - chunk = (ucx_memchunk*) pool->data[i]; - if(chunk) { - if(chunk->destructor) { - chunk->destructor(&(chunk->c)); - } - free(chunk); - } - } - free(pool->data); - free(pool->allocator); - free(pool); -} - -void ucx_mempool_set_destr(void *ptr, ucx_destructor func) { - *(ucx_destructor*)((char*)ptr-sizeof(ucx_destructor)) = func; -} - -void ucx_mempool_reg_destr(UcxMempool *pool, void *ptr, ucx_destructor destr) { - ucx_regdestr *rd = (ucx_regdestr*)ucx_mempool_malloc( - pool, - sizeof(ucx_regdestr)); - rd->destructor = destr; - rd->ptr = ptr; - ucx_mempool_set_destr(rd, ucx_mempool_shared_destr); -} - diff -r a569148841ff -r efbd59642577 ucx/printf.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/printf.c Fri Apr 21 21:25:32 2023 +0200 @@ -0,0 +1,129 @@ +/* + * 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/printf.h" + +#include +#include + +#ifndef CX_PRINTF_SBO_SIZE +#define CX_PRINTF_SBO_SIZE 512 +#endif + +int cx_fprintf( + void *stream, + cx_write_func wfc, + char const *fmt, + ... +) { + int ret; + va_list ap; + va_start(ap, fmt); + ret = cx_vfprintf(stream, wfc, fmt, ap); + va_end(ap); + return ret; +} + +int cx_vfprintf( + void *stream, + cx_write_func wfc, + char const *fmt, + va_list ap +) { + char buf[CX_PRINTF_SBO_SIZE]; + va_list ap2; + va_copy(ap2, ap); + int ret = vsnprintf(buf, CX_PRINTF_SBO_SIZE, fmt, ap); + if (ret < 0) { + return ret; + } else if (ret < CX_PRINTF_SBO_SIZE) { + return (int) wfc(buf, 1, ret, stream); + } else { + int len = ret + 1; + char *newbuf = malloc(len); + if (!newbuf) { + return -1; + } + + ret = vsnprintf(newbuf, len, fmt, ap2); + if (ret > 0) { + ret = (int) wfc(newbuf, 1, ret, stream); + } + free(newbuf); + } + return ret; +} + +cxmutstr cx_asprintf_a( + CxAllocator const *allocator, + char const *fmt, + ... +) { + va_list ap; + cxmutstr ret; + va_start(ap, fmt); + ret = cx_vasprintf_a(allocator, fmt, ap); + va_end(ap); + return ret; +} + +cxmutstr cx_vasprintf_a( + CxAllocator const *a, + char const *fmt, + va_list ap +) { + cxmutstr s; + s.ptr = NULL; + s.length = 0; + char buf[CX_PRINTF_SBO_SIZE]; + va_list ap2; + va_copy(ap2, ap); + int ret = vsnprintf(buf, CX_PRINTF_SBO_SIZE, fmt, ap); + if (ret > 0 && ret < CX_PRINTF_SBO_SIZE) { + s.ptr = cxMalloc(a, ret + 1); + if (s.ptr) { + s.length = (size_t) ret; + memcpy(s.ptr, buf, ret); + s.ptr[s.length] = '\0'; + } + } else { + int len = ret + 1; + s.ptr = cxMalloc(a, len); + if (s.ptr) { + ret = vsnprintf(s.ptr, len, fmt, ap2); + if (ret < 0) { + free(s.ptr); + s.ptr = NULL; + } else { + s.length = (size_t) ret; + } + } + } + return s; +} + diff -r a569148841ff -r efbd59642577 ucx/properties.c --- a/ucx/properties.c Sun Apr 16 14:12:24 2023 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,264 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 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 "ucx/properties.h" - -#include -#include -#include - -UcxProperties *ucx_properties_new() { - UcxProperties *parser = (UcxProperties*)malloc( - sizeof(UcxProperties)); - if(!parser) { - return NULL; - } - - parser->buffer = NULL; - parser->buflen = 0; - parser->pos = 0; - parser->tmp = NULL; - parser->tmplen = 0; - parser->tmpcap = 0; - parser->error = 0; - parser->delimiter = '='; - parser->comment1 = '#'; - parser->comment2 = 0; - parser->comment3 = 0; - - return parser; -} - -void ucx_properties_free(UcxProperties *parser) { - if(parser->tmp) { - free(parser->tmp); - } - free(parser); -} - -void ucx_properties_fill(UcxProperties *parser, char *buf, size_t len) { - parser->buffer = buf; - parser->buflen = len; - parser->pos = 0; -} - -static void parser_tmp_append(UcxProperties *parser, char *buf, size_t len) { - if(parser->tmpcap - parser->tmplen < len) { - size_t newcap = parser->tmpcap + len + 64; - parser->tmp = (char*)realloc(parser->tmp, newcap); - parser->tmpcap = newcap; - } - memcpy(parser->tmp + parser->tmplen, buf, len); - parser->tmplen += len; -} - -int ucx_properties_next(UcxProperties *parser, sstr_t *name, sstr_t *value) { - if(parser->tmplen > 0) { - char *buf = parser->buffer + parser->pos; - size_t len = parser->buflen - parser->pos; - sstr_t str = sstrn(buf, len); - sstr_t nl = sstrchr(str, '\n'); - if(nl.ptr) { - size_t newlen = (size_t)(nl.ptr - buf) + 1; - parser_tmp_append(parser, buf, newlen); - // the tmp buffer contains exactly one line now - - char *orig_buf = parser->buffer; - size_t orig_len = parser->buflen; - - parser->buffer = parser->tmp; - parser->buflen = parser->tmplen; - parser->pos = 0; - parser->tmp = NULL; - parser->tmpcap = 0; - parser->tmplen = 0; - // run ucx_properties_next with the tmp buffer as main buffer - int ret = ucx_properties_next(parser, name, value); - - // restore original buffer - parser->tmp = parser->buffer; - parser->buffer = orig_buf; - parser->buflen = orig_len; - parser->pos = newlen; - - /* - * if ret == 0 the tmp buffer contained just space or a comment - * we parse again with the original buffer to get a name/value - * or a new tmp buffer - */ - return ret ? ret : ucx_properties_next(parser, name, value); - } else { - parser_tmp_append(parser, buf, len); - return 0; - } - } else if(parser->tmp) { - free(parser->tmp); - parser->tmp = NULL; - } - - char comment1 = parser->comment1; - char comment2 = parser->comment2; - char comment3 = parser->comment3; - char delimiter = parser->delimiter; - - // get one line and parse it - while(parser->pos < parser->buflen) { - char *buf = parser->buffer + parser->pos; - size_t len = parser->buflen - parser->pos; - - /* - * First we check if we have at least one line. We also get indices of - * delimiter and comment chars - */ - size_t delimiter_index = 0; - size_t comment_index = 0; - int has_comment = 0; - - size_t i = 0; - char c = 0; - for(;itmpcap = len + 128; - parser->tmp = (char*)malloc(parser->tmpcap); - parser->tmplen = len; - memcpy(parser->tmp, buf, len); - return 0; - } - - sstr_t line = has_comment ? sstrn(buf, comment_index) : sstrn(buf, i); - // check line - if(delimiter_index == 0) { - line = sstrtrim(line); - if(line.length != 0) { - parser->error = 1; - } - } else { - sstr_t n = sstrn(buf, delimiter_index); - sstr_t v = sstrn( - buf + delimiter_index + 1, - line.length - delimiter_index - 1); - n = sstrtrim(n); - v = sstrtrim(v); - if(n.length != 0 || v.length != 0) { - *name = n; - *value = v; - parser->pos += i + 1; - return 1; - } else { - parser->error = 1; - } - } - - parser->pos += i + 1; - } - - return 0; -} - -int ucx_properties2map(UcxProperties *parser, UcxMap *map) { - sstr_t name; - sstr_t value; - while(ucx_properties_next(parser, &name, &value)) { - value = sstrdup_a(map->allocator, value); - if(!value.ptr) { - return 1; - } - if(ucx_map_sstr_put(map, name, value.ptr)) { - alfree(map->allocator, value.ptr); - return 1; - } - } - if (parser->error) { - return parser->error; - } else { - return 0; - } -} - -// buffer size is documented - change doc, when you change bufsize! -#define UCX_PROPLOAD_BUFSIZE 1024 -int ucx_properties_load(UcxMap *map, FILE *file) { - UcxProperties *parser = ucx_properties_new(); - if(!(parser && map && file)) { - return 1; - } - - int error = 0; - size_t r; - char buf[UCX_PROPLOAD_BUFSIZE]; - while((r = fread(buf, 1, UCX_PROPLOAD_BUFSIZE, file)) != 0) { - ucx_properties_fill(parser, buf, r); - error = ucx_properties2map(parser, map); - if (error) { - break; - } - } - ucx_properties_free(parser); - return error; -} - -int ucx_properties_store(UcxMap *map, FILE *file) { - UcxMapIterator iter = ucx_map_iterator(map); - void *v; - sstr_t value; - size_t written; - - UCX_MAP_FOREACH(k, v, iter) { - value = sstr((char*)v); - - written = 0; - written += fwrite(k.data, 1, k.len, file); - written += fwrite(" = ", 1, 3, file); - written += fwrite(value.ptr, 1, value.length, file); - written += fwrite("\n", 1, 1, file); - - if (written != k.len + value.length + 4) { - return 1; - } - } - - return 0; -} - diff -r a569148841ff -r efbd59642577 ucx/stack.c --- a/ucx/stack.c Sun Apr 16 14:12:24 2023 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,165 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 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 "ucx/stack.h" - -#include - -static size_t ucx_stack_align(size_t n) { - int align = n % sizeof(void*); - if (align) { - n += sizeof(void*) - align; - } - return n; -} - -void ucx_stack_init(UcxStack *stack, char* space, size_t size) { - stack->size = size - size % sizeof(void*); - stack->space = space; - stack->top = NULL; - - stack->allocator.pool = stack; - stack->allocator.malloc = (ucx_allocator_malloc) ucx_stack_malloc; - stack->allocator.calloc = (ucx_allocator_calloc) ucx_stack_calloc; - stack->allocator.realloc = (ucx_allocator_realloc) ucx_stack_realloc; - stack->allocator.free = (ucx_allocator_free) ucx_stack_free; -} - -void *ucx_stack_malloc(UcxStack *stack, size_t n) { - - if (ucx_stack_avail(stack) < ucx_stack_align(n)) { - return NULL; - } else { - char *prev = stack->top; - if (stack->top) { - stack->top += ucx_stack_align(ucx_stack_topsize(stack)); - } else { - stack->top = stack->space; - } - - ((struct ucx_stack_metadata*)stack->top)->prev = prev; - ((struct ucx_stack_metadata*)stack->top)->size = n; - stack->top += sizeof(struct ucx_stack_metadata); - - return stack->top; - } -} - -void *ucx_stack_calloc(UcxStack *stack, size_t nelem, size_t elsize) { - void *mem = ucx_stack_malloc(stack, nelem*elsize); - memset(mem, 0, nelem*elsize); - return mem; -} - -void *ucx_stack_realloc(UcxStack *stack, void *ptr, size_t n) { - if (ptr == stack->top) { - if (stack->size - (stack->top - stack->space) < ucx_stack_align(n)) { - return NULL; - } else { - ((struct ucx_stack_metadata*)stack->top - 1)->size = n; - return ptr; - } - } else { - if (ucx_stack_align(((struct ucx_stack_metadata*)ptr - 1)->size) < - ucx_stack_align(n)) { - void *nptr = ucx_stack_malloc(stack, n); - if (nptr) { - memcpy(nptr, ptr, n); - ucx_stack_free(stack, ptr); - - return nptr; - } else { - return NULL; - } - } else { - ((struct ucx_stack_metadata*)ptr - 1)->size = n; - return ptr; - } - } -} - -void ucx_stack_free(UcxStack *stack, void *ptr) { - if (ptr == stack->top) { - stack->top = ((struct ucx_stack_metadata*) stack->top - 1)->prev; - } else { - struct ucx_stack_metadata *next = (struct ucx_stack_metadata*)( - (char*)ptr + - ucx_stack_align(((struct ucx_stack_metadata*) ptr - 1)->size) - ); - next->prev = ((struct ucx_stack_metadata*) ptr - 1)->prev; - } -} - -void ucx_stack_popn(UcxStack *stack, void *dest, size_t n) { - if (ucx_stack_empty(stack)) { - return; - } - - if (dest) { - size_t len = ucx_stack_topsize(stack); - if (len > n) { - len = n; - } - - memcpy(dest, stack->top, len); - } - - ucx_stack_free(stack, stack->top); -} - -size_t ucx_stack_avail(UcxStack *stack) { - size_t avail = ((stack->top ? (stack->size - - (stack->top - stack->space) - - ucx_stack_align(ucx_stack_topsize(stack))) - : stack->size)); - - if (avail > sizeof(struct ucx_stack_metadata)) { - return avail - sizeof(struct ucx_stack_metadata); - } else { - return 0; - } -} - -void *ucx_stack_push(UcxStack *stack, size_t n, const void *data) { - void *space = ucx_stack_malloc(stack, n); - if (space) { - memcpy(space, data, n); - } - return space; -} - -void *ucx_stack_pusharr(UcxStack *stack, - size_t nelem, size_t elsize, const void *data) { - - // skip the memset by using malloc - void *space = ucx_stack_malloc(stack, nelem*elsize); - if (space) { - memcpy(space, data, nelem*elsize); - } - return space; -} diff -r a569148841ff -r efbd59642577 ucx/string.c --- a/ucx/string.c Sun Apr 16 14:12:24 2023 +0200 +++ b/ucx/string.c Fri Apr 21 21:25:32 2023 +0200 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. + * 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: @@ -26,57 +26,71 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include "ucx/string.h" +#include "cx/string.h" +#include "cx/utils.h" -#include "ucx/allocator.h" - -#include #include #include -#include #include -sstr_t sstr(char *cstring) { - sstr_t string; - string.ptr = cstring; - string.length = strlen(cstring); - return string; +#ifndef _WIN32 + +#include // for strncasecmp() + +#endif // _WIN32 + +cxmutstr cx_mutstr(char *cstring) { + return (cxmutstr) {cstring, strlen(cstring)}; } -sstr_t sstrn(char *cstring, size_t length) { - sstr_t string; - string.ptr = cstring; - string.length = length; - return string; +cxmutstr cx_mutstrn( + char *cstring, + size_t length +) { + return (cxmutstr) {cstring, length}; +} + +cxstring cx_str(const char *cstring) { + return (cxstring) {cstring, strlen(cstring)}; } -scstr_t scstr(const char *cstring) { - scstr_t string; - string.ptr = cstring; - string.length = strlen(cstring); - return string; +cxstring cx_strn( + const char *cstring, + size_t length +) { + return (cxstring) {cstring, length}; } -scstr_t scstrn(const char *cstring, size_t length) { - scstr_t string; - string.ptr = cstring; - string.length = length; - return string; +cxstring cx_strcast(cxmutstr str) { + return (cxstring) {str.ptr, str.length}; +} + +void cx_strfree(cxmutstr *str) { + free(str->ptr); + str->ptr = NULL; + str->length = 0; } - -size_t scstrnlen(size_t n, ...) { - va_list ap; - va_start(ap, n); - - size_t size = 0; +void cx_strfree_a( + CxAllocator const *alloc, + cxmutstr *str +) { + cxFree(alloc, str->ptr); + str->ptr = NULL; + str->length = 0; +} - for (size_t i = 0 ; i < n ; i++) { - scstr_t str = va_arg(ap, scstr_t); - if(SIZE_MAX - str.length < size) { - size = SIZE_MAX; - break; - } +size_t cx_strlen( + size_t count, + ... +) { + if (count == 0) return 0; + + va_list ap; + va_start(ap, count); + size_t size = 0; + cx_for_n(i, count) { + cxstring str = va_arg(ap, cxstring); size += str.length; } va_end(ap); @@ -84,410 +98,333 @@ return size; } -static sstr_t sstrvcat_a( - UcxAllocator *a, +cxmutstr cx_strcat_a( + CxAllocator const *alloc, size_t count, - scstr_t s1, - va_list ap) { - sstr_t str; - str.ptr = NULL; - str.length = 0; - if(count < 2) { - return str; - } - - scstr_t s2 = va_arg (ap, scstr_t); - - if(((size_t)-1) - s1.length < s2.length) { - return str; - } - - scstr_t *strings = (scstr_t*) calloc(count, sizeof(scstr_t)); - if(!strings) { - return str; - } - + ... +) { + cxstring *strings = calloc(count, sizeof(cxstring)); + if (!strings) abort(); + + va_list ap; + va_start(ap, count); + // get all args and overall length - strings[0] = s1; - strings[1] = s2; - size_t slen = s1.length + s2.length; - int error = 0; - for (size_t i=2;i str_length) { - return 0; + size_t length +) { + if (start > string.length) { + return (cxstring) {NULL, 0}; } - - if(length > str_length - start) { - length = str_length - start; + + size_t rem_len = string.length - start; + if (length > rem_len) { + length = rem_len; } - *newlen = length; - *newpos = start; - return 1; + + return (cxstring) {string.ptr + start, length}; } -sstr_t sstrsubs(sstr_t s, size_t start) { - return sstrsubsl (s, start, s.length-start); +cxmutstr cx_strsubsl_m( + cxmutstr string, + size_t start, + size_t length +) { + cxstring result = cx_strsubsl(cx_strcast(string), start, length); + return (cxmutstr) {(char *) result.ptr, result.length}; } -sstr_t sstrsubsl(sstr_t s, size_t start, size_t length) { - size_t pos; - sstr_t ret = { NULL, 0 }; - if(ucx_substring(s.length, start, length, &ret.length, &pos)) { - ret.ptr = s.ptr + pos; +cxstring cx_strchr( + cxstring string, + int chr +) { + chr = 0xFF & chr; + // TODO: improve by comparing multiple bytes at once + cx_for_n(i, string.length) { + if (string.ptr[i] == chr) { + return cx_strsubs(string, i); + } } - return ret; -} - -scstr_t scstrsubs(scstr_t string, size_t start) { - return scstrsubsl(string, start, string.length-start); + return (cxstring) {NULL, 0}; } -scstr_t scstrsubsl(scstr_t s, size_t start, size_t length) { - size_t pos; - scstr_t ret = { NULL, 0 }; - if(ucx_substring(s.length, start, length, &ret.length, &pos)) { - ret.ptr = s.ptr + pos; - } - return ret; +cxmutstr cx_strchr_m( + cxmutstr string, + int chr +) { + cxstring result = cx_strchr(cx_strcast(string), chr); + return (cxmutstr) {(char *) result.ptr, result.length}; } - -static int ucx_strchr(const char *str, size_t length, int chr, size_t *pos) { - for(size_t i=0;i 0) { - for(size_t i=length ; i>0 ; i--) { - if(str[i-1] == chr) { - *pos = i-1; - return 1; - } +cxstring cx_strrchr( + cxstring string, + int chr +) { + chr = 0xFF & chr; + size_t i = string.length; + while (i > 0) { + i--; + // TODO: improve by comparing multiple bytes at once + if (string.ptr[i] == chr) { + return cx_strsubs(string, i); } } - return 0; -} - -sstr_t sstrchr(sstr_t s, int c) { - size_t pos = 0; - if(ucx_strchr(s.ptr, s.length, c, &pos)) { - return sstrsubs(s, pos); - } - return sstrn(NULL, 0); + return (cxstring) {NULL, 0}; } -sstr_t sstrrchr(sstr_t s, int c) { - size_t pos = 0; - if(ucx_strrchr(s.ptr, s.length, c, &pos)) { - return sstrsubs(s, pos); - } - return sstrn(NULL, 0); -} - -scstr_t scstrchr(scstr_t s, int c) { - size_t pos = 0; - if(ucx_strchr(s.ptr, s.length, c, &pos)) { - return scstrsubs(s, pos); - } - return scstrn(NULL, 0); -} - -scstr_t scstrrchr(scstr_t s, int c) { - size_t pos = 0; - if(ucx_strrchr(s.ptr, s.length, c, &pos)) { - return scstrsubs(s, pos); - } - return scstrn(NULL, 0); +cxmutstr cx_strrchr_m( + cxmutstr string, + int chr +) { + cxstring result = cx_strrchr(cx_strcast(string), chr); + return (cxmutstr) {(char *) result.ptr, result.length}; } -#define ptable_r(dest, useheap, ptable, index) (dest = useheap ? \ - ((size_t*)ptable)[index] : (size_t) ((uint8_t*)ptable)[index]) - -#define ptable_w(useheap, ptable, index, src) do {\ - if (!useheap) ((uint8_t*)ptable)[index] = (uint8_t) src;\ - else ((size_t*)ptable)[index] = src;\ - } while (0); - +#ifndef CX_STRSTR_SBO_SIZE +#define CX_STRSTR_SBO_SIZE 512 +#endif -static const char* ucx_strstr( - const char *str, - size_t length, - const char *match, - size_t matchlen, - size_t *newlen) -{ - *newlen = length; - if (matchlen == 0) { - return str; +cxstring cx_strstr( + cxstring haystack, + cxstring needle +) { + if (needle.length == 0) { + return haystack; } - - const char *result = NULL; - size_t resultlen = 0; - + + // optimize for single-char needles + if (needle.length == 1) { + return cx_strchr(haystack, *needle.ptr); + } + /* * IMPORTANT: - * our prefix table contains the prefix length PLUS ONE - * this is our decision, because we want to use the full range of size_t - * the original algorithm needs a (-1) at one single place - * and we want to avoid that + * Our prefix table contains the prefix length PLUS ONE + * this is our decision, because we want to use the full range of size_t. + * The original algorithm needs a (-1) at one single place, + * and we want to avoid that. */ - - /* static prefix table */ - static uint8_t s_prefix_table[256]; - - /* check pattern length and use appropriate prefix table */ - /* if the pattern exceeds static prefix table, allocate on the heap */ - register int useheap = matchlen > 255; - register void* ptable = useheap ? - calloc(matchlen+1, sizeof(size_t)): s_prefix_table; - - /* keep counter in registers */ + + // local prefix table + size_t s_prefix_table[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; + register size_t *ptable = useheap ? calloc(needle.length + 1, + sizeof(size_t)) : s_prefix_table; + + // keep counter in registers register size_t i, j; - - /* fill prefix table */ - i = 0; j = 0; - ptable_w(useheap, ptable, i, j); - while (i < matchlen) { - while (j >= 1 && match[j-1] != match[i]) { - ptable_r(j, useheap, ptable, j-1); + + // fill prefix table + i = 0; + j = 0; + ptable[i] = j; + while (i < needle.length) { + while (j >= 1 && needle.ptr[j - 1] != needle.ptr[i]) { + j = ptable[j - 1]; } - i++; j++; - ptable_w(useheap, ptable, i, j); + i++; + j++; + ptable[i] = j; } - /* search */ - i = 0; j = 1; - while (i < length) { - while (j >= 1 && str[i] != match[j-1]) { - ptable_r(j, useheap, ptable, j-1); + // search + cxstring result = {NULL, 0}; + i = 0; + j = 1; + while (i < haystack.length) { + while (j >= 1 && haystack.ptr[i] != needle.ptr[j - 1]) { + j = ptable[j - 1]; } - i++; j++; - if (j-1 == matchlen) { - size_t start = i - matchlen; - result = str + start; - resultlen = length - start; + i++; + j++; + if (j - 1 == needle.length) { + size_t start = i - needle.length; + result.ptr = haystack.ptr + start; + result.length = haystack.length - start; break; } } - /* if prefix table was allocated on the heap, free it */ + // if prefix table was allocated on the heap, free it if (ptable != s_prefix_table) { free(ptable); } - - *newlen = resultlen; - return result; -} - -sstr_t scstrsstr(sstr_t string, scstr_t match) { - sstr_t result; - - size_t reslen; - const char *resstr = ucx_strstr(string.ptr, string.length, match.ptr, match.length, &reslen); - if(!resstr) { - result.ptr = NULL; - result.length = 0; - return result; - } - - size_t pos = resstr - string.ptr; - result.ptr = string.ptr + pos; - result.length = reslen; - - return result; -} - -scstr_t scstrscstr(scstr_t string, scstr_t match) { - scstr_t result; - - size_t reslen; - const char *resstr = ucx_strstr(string.ptr, string.length, match.ptr, match.length, &reslen); - if(!resstr) { - result.ptr = NULL; - result.length = 0; - return result; - } - - size_t pos = resstr - string.ptr; - result.ptr = string.ptr + pos; - result.length = reslen; - - return result; -} - -#undef ptable_r -#undef ptable_w - -sstr_t* scstrsplit(scstr_t s, scstr_t d, ssize_t *n) { - return scstrsplit_a(ucx_default_allocator(), s, d, n); -} - -sstr_t* scstrsplit_a(UcxAllocator *allocator, scstr_t s, scstr_t d, ssize_t *n) { - if (s.length == 0 || d.length == 0) { - *n = -1; - return NULL; - } - - /* special cases: delimiter is at least as large as the string */ - if (d.length >= s.length) { - /* exact match */ - if (sstrcmp(s, d) == 0) { - *n = 0; - return NULL; - } else /* no match possible */ { - *n = 1; - sstr_t *result = (sstr_t*) almalloc(allocator, sizeof(sstr_t)); - if(result) { - *result = sstrdup_a(allocator, s); - } else { - *n = -2; - } - return result; - } - } - - ssize_t nmax = *n; - size_t arrlen = 16; - sstr_t* result = (sstr_t*) alcalloc(allocator, arrlen, sizeof(sstr_t)); - - if (result) { - scstr_t curpos = s; - ssize_t j = 1; - while (1) { - scstr_t match; - /* optimize for one byte delimiters */ - if (d.length == 1) { - match = curpos; - for (size_t i = 0 ; i < curpos.length ; i++) { - if (curpos.ptr[i] == *(d.ptr)) { - match.ptr = curpos.ptr + i; - break; - } - match.length--; - } - } else { - match = scstrscstr(curpos, d); - } - if (match.length > 0) { - /* is this our last try? */ - if (nmax == 0 || j < nmax) { - /* copy the current string to the array */ - scstr_t item = scstrn(curpos.ptr, match.ptr - curpos.ptr); - result[j-1] = sstrdup_a(allocator, item); - size_t processed = item.length + d.length; - curpos.ptr += processed; - curpos.length -= processed; - - /* allocate memory for the next string */ - j++; - if (j > arrlen) { - arrlen *= 2; - size_t reallocsz; - sstr_t* reallocated = NULL; - if(!ucx_szmul(arrlen, sizeof(sstr_t), &reallocsz)) { - reallocated = (sstr_t*) alrealloc( - allocator, result, reallocsz); - } - if (reallocated) { - result = reallocated; - } else { - for (ssize_t i = 0 ; i < j-1 ; i++) { - alfree(allocator, result[i].ptr); - } - alfree(allocator, result); - *n = -2; - return NULL; - } - } - } else { - /* nmax reached, copy the _full_ remaining string */ - result[j-1] = sstrdup_a(allocator, curpos); - break; - } - } else { - /* no more matches, copy last string */ - result[j-1] = sstrdup_a(allocator, curpos); - break; - } - } - *n = j; - } else { - *n = -2; - } return result; } -int scstrcmp(scstr_t s1, scstr_t s2) { +cxmutstr cx_strstr_m( + cxmutstr haystack, + cxstring needle +) { + cxstring result = cx_strstr(cx_strcast(haystack), needle); + return (cxmutstr) {(char *) result.ptr, result.length}; +} + +size_t cx_strsplit( + cxstring string, + cxstring delim, + size_t limit, + cxstring *output +) { + // special case: output limit is zero + if (limit == 0) return 0; + + // special case: delimiter is empty + if (delim.length == 0) { + output[0] = string; + return 1; + } + + // special cases: delimiter is at least as large as the string + if (delim.length >= string.length) { + // exact match + if (cx_strcmp(string, delim) == 0) { + output[0] = cx_strn(string.ptr, 0); + output[1] = cx_strn(string.ptr + string.length, 0); + return 2; + } else { + // no match possible + output[0] = string; + return 1; + } + } + + size_t n = 0; + cxstring curpos = string; + while (1) { + ++n; + cxstring match = cx_strstr(curpos, delim); + if (match.length > 0) { + // is the limit reached? + if (n < limit) { + // copy the current string to the array + cxstring item = cx_strn(curpos.ptr, match.ptr - curpos.ptr); + output[n - 1] = item; + size_t processed = item.length + delim.length; + curpos.ptr += processed; + curpos.length -= processed; + } else { + // limit reached, copy the _full_ remaining string + output[n - 1] = curpos; + break; + } + } else { + // no more matches, copy last string + output[n - 1] = curpos; + break; + } + } + + return n; +} + +size_t cx_strsplit_a( + CxAllocator const *allocator, + cxstring string, + cxstring delim, + size_t limit, + cxstring **output +) { + // find out how many splits we're going to make and allocate memory + size_t n = 0; + cxstring curpos = string; + while (1) { + ++n; + cxstring match = cx_strstr(curpos, delim); + if (match.length > 0) { + // is the limit reached? + if (n < limit) { + size_t processed = match.ptr - curpos.ptr + delim.length; + curpos.ptr += processed; + curpos.length -= processed; + } else { + // limit reached + break; + } + } else { + // no more matches + break; + } + } + *output = cxCalloc(allocator, n, sizeof(cxstring)); + return cx_strsplit(string, delim, n, *output); +} + +size_t cx_strsplit_m( + cxmutstr string, + cxstring delim, + size_t limit, + cxmutstr *output +) { + return cx_strsplit(cx_strcast(string), + delim, limit, (cxstring *) output); +} + +size_t cx_strsplit_ma( + CxAllocator const *allocator, + cxmutstr string, + cxstring delim, + size_t limit, + cxmutstr **output +) { + return cx_strsplit_a(allocator, cx_strcast(string), + delim, limit, (cxstring **) output); +} + +int cx_strcmp( + cxstring s1, + cxstring s2 +) { if (s1.length == s2.length) { return memcmp(s1.ptr, s2.ptr, s1.length); } else if (s1.length > s2.length) { @@ -497,7 +434,10 @@ } } -int scstrcasecmp(scstr_t s1, scstr_t s2) { +int cx_strcasecmp( + cxstring s1, + cxstring s2 +) { if (s1.length == s2.length) { #ifdef _WIN32 return _strnicmp(s1.ptr, s2.ptr, s1.length); @@ -511,129 +451,328 @@ } } -sstr_t scstrdup(scstr_t s) { - return sstrdup_a(ucx_default_allocator(), s); +int cx_strcmp_p( + void const *s1, + void const *s2 +) { + cxstring const *left = s1; + cxstring const *right = s2; + return cx_strcmp(*left, *right); +} + +int cx_strcasecmp_p( + void const *s1, + void const *s2 +) { + cxstring const *left = s1; + cxstring const *right = s2; + return cx_strcasecmp(*left, *right); } -sstr_t scstrdup_a(UcxAllocator *allocator, scstr_t s) { - sstr_t newstring; - newstring.ptr = (char*)almalloc(allocator, s.length + 1); - if (newstring.ptr) { - newstring.length = s.length; - newstring.ptr[newstring.length] = 0; - - memcpy(newstring.ptr, s.ptr, s.length); - } else { - newstring.length = 0; +cxmutstr cx_strdup_a( + CxAllocator const *allocator, + cxstring string +) { + cxmutstr result = { + cxMalloc(allocator, string.length + 1), + string.length + }; + if (result.ptr == NULL) { + result.length = 0; + return result; } - - return newstring; + memcpy(result.ptr, string.ptr, string.length); + result.ptr[string.length] = '\0'; + return result; +} + +cxstring cx_strtrim(cxstring string) { + cxstring result = string; + // TODO: optimize by comparing multiple bytes at once + while (result.length > 0 && isspace(*result.ptr)) { + result.ptr++; + result.length--; + } + while (result.length > 0 && isspace(result.ptr[result.length - 1])) { + result.length--; + } + return result; +} + +cxmutstr cx_strtrim_m(cxmutstr string) { + cxstring result = cx_strtrim(cx_strcast(string)); + return (cxmutstr) {(char *) result.ptr, result.length}; +} + +bool cx_strprefix( + cxstring string, + cxstring prefix +) { + if (string.length < prefix.length) return false; + return memcmp(string.ptr, prefix.ptr, prefix.length) == 0; } +bool cx_strsuffix( + cxstring string, + cxstring suffix +) { + if (string.length < suffix.length) return false; + return memcmp(string.ptr + string.length - suffix.length, + suffix.ptr, suffix.length) == 0; +} -static size_t ucx_strtrim(const char *s, size_t len, size_t *newlen) { - const char *newptr = s; - size_t length = len; - - while(length > 0 && isspace(*newptr)) { - newptr++; - length--; - } - while(length > 0 && isspace(newptr[length-1])) { - length--; - } - - *newlen = length; - return newptr - s; +bool cx_strcaseprefix( + cxstring string, + cxstring prefix +) { + if (string.length < prefix.length) return false; +#ifdef _WIN32 + return _strnicmp(string.ptr, prefix.ptr, prefix.length) == 0; +#else + return strncasecmp(string.ptr, prefix.ptr, prefix.length) == 0; +#endif } -sstr_t sstrtrim(sstr_t string) { - sstr_t newstr; - newstr.ptr = string.ptr - + ucx_strtrim(string.ptr, string.length, &newstr.length); - return newstr; +bool cx_strcasesuffix( + cxstring string, + cxstring suffix +) { + if (string.length < suffix.length) return false; +#ifdef _WIN32 + return _strnicmp(string.ptr+string.length-suffix.length, + suffix.ptr, suffix.length) == 0; +#else + return strncasecmp(string.ptr + string.length - suffix.length, + suffix.ptr, suffix.length) == 0; +#endif +} + +void cx_strlower(cxmutstr string) { + cx_for_n(i, string.length) { + string.ptr[i] = (char) tolower(string.ptr[i]); + } } -scstr_t scstrtrim(scstr_t string) { - scstr_t newstr; - newstr.ptr = string.ptr - + ucx_strtrim(string.ptr, string.length, &newstr.length); - return newstr; +void cx_strupper(cxmutstr string) { + cx_for_n(i, string.length) { + string.ptr[i] = (char) toupper(string.ptr[i]); + } } -int scstrprefix(scstr_t string, scstr_t prefix) { - if (string.length == 0) { - return prefix.length == 0; - } - if (prefix.length == 0) { - return 1; - } - - if (prefix.length > string.length) { - return 0; - } else { - return memcmp(string.ptr, prefix.ptr, prefix.length) == 0; +#ifndef CX_STRREPLACE_INDEX_BUFFER_SIZE +#define CX_STRREPLACE_INDEX_BUFFER_SIZE 64 +#endif + +struct cx_strreplace_ibuf { + size_t *buf; + struct cx_strreplace_ibuf *next; + unsigned int len; +}; + +static void cx_strrepl_free_ibuf(struct cx_strreplace_ibuf *buf) { + while (buf) { + struct cx_strreplace_ibuf *next = buf->next; + free(buf->buf); + free(buf); + buf = next; } } -int scstrsuffix(scstr_t string, scstr_t suffix) { - if (string.length == 0) { - return suffix.length == 0; +cxmutstr cx_strreplacen_a( + CxAllocator const *allocator, + cxstring str, + cxstring pattern, + cxstring replacement, + size_t replmax +) { + + if (pattern.length == 0 || pattern.length > str.length || replmax == 0) + return cx_strdup_a(allocator, str); + + // Compute expected buffer length + size_t ibufmax = str.length / pattern.length; + size_t ibuflen = replmax < ibufmax ? replmax : ibufmax; + if (ibuflen > CX_STRREPLACE_INDEX_BUFFER_SIZE) { + ibuflen = CX_STRREPLACE_INDEX_BUFFER_SIZE; } - if (suffix.length == 0) { - return 1; + + // 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); } - - if (suffix.length > string.length) { - return 0; - } else { - return memcmp(string.ptr+string.length-suffix.length, - suffix.ptr, suffix.length) == 0; + + // Search occurrences + cxstring searchstr = str; + size_t found = 0; + do { + cxstring match = cx_strstr(searchstr, pattern); + 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); + return cx_mutstrn(NULL, 0); + } + nextbuf->buf = calloc(ibuflen, sizeof(size_t)); + if (!nextbuf->buf) { + free(nextbuf); + cx_strrepl_free_ibuf(firstbuf); + return cx_mutstrn(NULL, 0); + } + curbuf->next = nextbuf; + curbuf = nextbuf; + } + + // Record match index + 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; + } else { + break; + } + } while (searchstr.length > 0 && found < replmax); + + // Allocate result string + cxmutstr result; + { + ssize_t adjlen = (ssize_t) replacement.length - (ssize_t) pattern.length; + size_t rcount = 0; + curbuf = firstbuf; + do { + rcount += curbuf->len; + curbuf = curbuf->next; + } while (curbuf); + result.length = str.length + rcount * adjlen; + result.ptr = cxMalloc(allocator, result.length + 1); + if (!result.ptr) { + cx_strrepl_free_ibuf(firstbuf); + return cx_mutstrn(NULL, 0); + } } -} + + // Build result string + curbuf = firstbuf; + size_t srcidx = 0; + char *destptr = result.ptr; + do { + for (size_t i = 0; i < curbuf->len; i++) { + // Copy source part up to next match + size_t idx = curbuf->buf[i]; + size_t srclen = idx - srcidx; + if (srclen > 0) { + memcpy(destptr, str.ptr + srcidx, srclen); + destptr += srclen; + srcidx += srclen; + } -sstr_t scstrlower(scstr_t string) { - sstr_t ret = sstrdup(string); - for (size_t i = 0; i < ret.length ; i++) { - ret.ptr[i] = tolower(ret.ptr[i]); - } - return ret; + // Copy the replacement and skip the source pattern + srcidx += pattern.length; + memcpy(destptr, replacement.ptr, replacement.length); + destptr += replacement.length; + } + curbuf = curbuf->next; + } while (curbuf); + memcpy(destptr, str.ptr + srcidx, str.length - srcidx); + + // Result is guaranteed to be zero-terminated + result.ptr[result.length] = '\0'; + + // Free index buffer + cx_strrepl_free_ibuf(firstbuf); + + return result; } -sstr_t scstrlower_a(UcxAllocator *allocator, scstr_t string) { - sstr_t ret = sstrdup_a(allocator, string); - for (size_t i = 0; i < ret.length ; i++) { - ret.ptr[i] = tolower(ret.ptr[i]); - } - return ret; +CxStrtokCtx cx_strtok( + cxstring str, + cxstring delim, + size_t limit +) { + CxStrtokCtx ctx; + ctx.str = str; + ctx.delim = delim; + ctx.limit = limit; + ctx.pos = 0; + ctx.next_pos = 0; + ctx.delim_pos = 0; + ctx.found = 0; + ctx.delim_more = NULL; + ctx.delim_more_count = 0; + return ctx; } -sstr_t scstrupper(scstr_t string) { - sstr_t ret = sstrdup(string); - for (size_t i = 0; i < ret.length ; i++) { - ret.ptr[i] = toupper(ret.ptr[i]); - } - return ret; +CxStrtokCtx cx_strtok_m( + cxmutstr str, + cxstring delim, + size_t limit +) { + return cx_strtok(cx_strcast(str), delim, limit); } -sstr_t scstrupper_a(UcxAllocator *allocator, scstr_t string) { - sstr_t ret = sstrdup_a(allocator, string); - for (size_t i = 0; i < ret.length ; i++) { - ret.ptr[i] = toupper(ret.ptr[i]); +bool cx_strtok_next( + CxStrtokCtx *ctx, + cxstring *token +) { + // abortion criteria + if (ctx->found >= ctx->limit || ctx->delim_pos >= ctx->str.length) { + return false; + } + + // determine the search start + cxstring haystack = cx_strsubs(ctx->str, ctx->next_pos); + + // search the next delimiter + cxstring delim = cx_strstr(haystack, ctx->delim); + + // if found, make delim capture exactly the delimiter + if (delim.length > 0) { + delim.length = ctx->delim.length; } - return ret; + + // if more delimiters are specified, check them now + if (ctx->delim_more_count > 0) { + cx_for_n(i, ctx->delim_more_count) { + cxstring d = cx_strstr(haystack, ctx->delim_more[i]); + if (d.length > 0 && (delim.length == 0 || d.ptr < delim.ptr)) { + delim.ptr = d.ptr; + delim.length = ctx->delim_more[i].length; + } + } + } + + // store the token information and adjust the context + ctx->found++; + ctx->pos = ctx->next_pos; + token->ptr = &ctx->str.ptr[ctx->pos]; + ctx->delim_pos = delim.length == 0 ? + ctx->str.length : (size_t) (delim.ptr - ctx->str.ptr); + token->length = ctx->delim_pos - ctx->pos; + ctx->next_pos = ctx->delim_pos + delim.length; + + return true; } -// type adjustment functions -scstr_t ucx_sc2sc(scstr_t str) { - return str; +bool cx_strtok_next_m( + CxStrtokCtx *ctx, + cxmutstr *token +) { + return cx_strtok_next(ctx, (cxstring *) token); } -scstr_t ucx_ss2sc(sstr_t str) { - scstr_t cs; - cs.ptr = str.ptr; - cs.length = str.length; - return cs; + +void cx_strtok_delim( + CxStrtokCtx *ctx, + cxstring const *delim, + size_t count +) { + ctx->delim_more = delim; + ctx->delim_more_count = count; } -scstr_t ucx_ss2c_s(scstr_t c) { - return c; -} diff -r a569148841ff -r efbd59642577 ucx/szmul.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/szmul.c Fri Apr 21 21:25:32 2023 +0200 @@ -0,0 +1,46 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2023 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. + */ + +int cx_szmul_impl( + size_t a, + size_t b, + size_t *result +) { + if (a == 0 || b == 0) { + *result = 0; + return 0; + } + size_t r = a * b; + if (r / b == a) { + *result = r; + return 0; + } else { + *result = 0; + return 1; + } +} \ No newline at end of file diff -r a569148841ff -r efbd59642577 ucx/test.c --- a/ucx/test.c Sun Apr 16 14:12:24 2023 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,91 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 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 "ucx/test.h" - -UcxTestSuite* ucx_test_suite_new() { - UcxTestSuite* suite = (UcxTestSuite*) malloc(sizeof(UcxTestSuite)); - if (suite != NULL) { - suite->success = 0; - suite->failure = 0; - suite->tests = NULL; - } - - return suite; -} - -void ucx_test_suite_free(UcxTestSuite* suite) { - UcxTestList *l = suite->tests; - while (l != NULL) { - UcxTestList *e = l; - l = l->next; - free(e); - } - free(suite); -} - -int ucx_test_register(UcxTestSuite* suite, UcxTest test) { - if (suite->tests) { - UcxTestList *newelem = (UcxTestList*) malloc(sizeof(UcxTestList)); - if (newelem) { - newelem->test = test; - newelem->next = NULL; - - UcxTestList *last = suite->tests; - while (last->next) { - last = last->next; - } - last->next = newelem; - - return EXIT_SUCCESS; - } else { - return EXIT_FAILURE; - } - } else { - suite->tests = (UcxTestList*) malloc(sizeof(UcxTestList)); - if (suite->tests) { - suite->tests->test = test; - suite->tests->next = NULL; - - return EXIT_SUCCESS; - } else { - return EXIT_FAILURE; - } - } -} - -void ucx_test_run(UcxTestSuite* suite, FILE* output) { - suite->success = 0; - suite->failure = 0; - for (UcxTestList* elem = suite->tests ; elem ; elem = elem->next) { - elem->test(suite, output); - } - fwrite("\nAll test completed.\n", 1, 21, output); - fprintf(output, " Total: %u\n Success: %u\n Failure: %u\n", - suite->success+suite->failure, suite->success, suite->failure); -} diff -r a569148841ff -r efbd59642577 ucx/tree.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/tree.c Fri Apr 21 21:25:32 2023 +0200 @@ -0,0 +1,52 @@ +/* + * 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/tree.h" +#include "cx/linked_list.h" + +#define CX_TR_PTR(cur, off) *((void**)(((char*)(cur))+(off))) + +void cx_tree_add_sibling(void *node, ptrdiff_t loc_prev, ptrdiff_t loc_next, ptrdiff_t loc_parent, void *new_node) { + cx_linked_list_add(&node, NULL, loc_prev, loc_next, new_node); + + // optional parent link + if (loc_parent >= 0) { + CX_TR_PTR(new_node, loc_parent) = CX_TR_PTR(node, loc_parent); + } +} + +void cx_tree_add_child(void **children_begin, void **children_end, + ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node, + ptrdiff_t loc_parent, void *parent) { + cx_linked_list_add(children_begin, children_end, loc_prev, loc_next, new_node); + + // optional parent link + if (loc_parent >= 0) { + CX_TR_PTR(new_node, loc_parent) = parent; + } +} diff -r a569148841ff -r efbd59642577 ucx/ucx.c --- a/ucx/ucx.c Sun Apr 16 14:12:24 2023 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -/** - * @mainpage UAP Common Extensions - * Library with common and useful functions, macros and data structures. - *

      - * Latest available source:
      - * - * https://sourceforge.net/projects/ucx/files/ - *

      - * - *

      - * Repositories:
      - * - * https://sourceforge.net/p/ucx/code - * - or - - * - * https://develop.uap-core.de/hg/ucx - *

      - * - *

      LICENCE

      - * - * Copyright 2017 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 "ucx/ucx.h" - -int ucx_szmul_impl(size_t a, size_t b, size_t *result) { - if(a == 0 || b == 0) { - *result = 0; - return 0; - } - size_t r = a * b; - if(r / b == a) { - *result = r; - return 0; - } else { - *result = 0; - return 1; - } -} - diff -r a569148841ff -r efbd59642577 ucx/ucx/allocator.h --- a/ucx/ucx/allocator.h Sun Apr 16 14:12:24 2023 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,206 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 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. - */ -/** - * Allocator for custom memory management. - * - * A UCX allocator consists of a pointer to the memory area / pool and four - * function pointers to memory management functions operating on this memory - * area / pool. These functions shall behave equivalent to the standard libc - * functions malloc(), calloc(), realloc() and free(). - * - * The signature of the memory management functions is based on the signature - * of the respective libc function but each of them takes the pointer to the - * memory area / pool as first argument. - * - * As the pointer to the memory area / pool can be arbitrarily chosen, any data - * can be provided to the memory management functions. A UcxMempool is just - * one example. - * - * @see mempool.h - * @see UcxMap - * - * @file allocator.h - * @author Mike Becker - * @author Olaf Wintermann - */ - -#ifndef UCX_ALLOCATOR_H -#define UCX_ALLOCATOR_H - -#include "ucx.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * A function pointer to the allocators malloc() function. - * @see UcxAllocator - */ -typedef void*(*ucx_allocator_malloc)(void *pool, size_t n); - -/** - * A function pointer to the allocators calloc() function. - * @see UcxAllocator - */ -typedef void*(*ucx_allocator_calloc)(void *pool, size_t n, size_t size); - -/** - * A function pointer to the allocators realloc() function. - * @see UcxAllocator - */ -typedef void*(*ucx_allocator_realloc)(void *pool, void *data, size_t n); - -/** - * A function pointer to the allocators free() function. - * @see UcxAllocator - */ -typedef void(*ucx_allocator_free)(void *pool, void *data); - -/** - * UCX allocator data structure containing memory management functions. - */ -typedef struct { - /** Pointer to an area of memory or a complex memory pool. - * This pointer will be passed to any memory management function as first - * argument. - */ - void *pool; - /** - * The malloc() function for this allocator. - */ - ucx_allocator_malloc malloc; - /** - * The calloc() function for this allocator. - */ - ucx_allocator_calloc calloc; - /** - * The realloc() function for this allocator. - */ - ucx_allocator_realloc realloc; - /** - * The free() function for this allocator. - */ - ucx_allocator_free free; -} UcxAllocator; - -/** - * Returns a pointer to the default allocator. - * - * The default allocator contains wrappers to the standard libc memory - * management functions. Use this function to get a pointer to a globally - * available allocator. You may also define an own UcxAllocator by assigning - * #UCX_ALLOCATOR_DEFAULT to a variable and pass the address of this variable - * to any function that takes a UcxAllocator as argument. Note that using - * this function is the recommended way of passing a default allocator, thus - * it never runs out of scope. - * - * @return a pointer to the default allocator - * - * @see UCX_ALLOCATOR_DEFAULT - */ -UcxAllocator *ucx_default_allocator(); - -/** - * A wrapper for the standard libc malloc() function. - * @param ignore ignored (may be used by allocators for pooled memory) - * @param n argument passed to malloc() - * @return return value of malloc() - */ -void *ucx_default_malloc(void *ignore, size_t n); -/** - * A wrapper for the standard libc calloc() function. - * @param ignore ignored (may be used by allocators for pooled memory) - * @param n argument passed to calloc() - * @param size argument passed to calloc() - * @return return value of calloc() - */ -void *ucx_default_calloc(void *ignore, size_t n, size_t size); -/** - * A wrapper for the standard libc realloc() function. - * @param ignore ignored (may be used by allocators for pooled memory) - * @param data argumend passed to realloc() - * @param n argument passed to realloc() - * @return return value of realloc() - */ -void *ucx_default_realloc(void *ignore, void *data, size_t n); -/** - * A wrapper for the standard libc free() function. - * @param ignore ignored (may be used by allocators for pooled memory) - * @param data argument passed to free() - */ -void ucx_default_free(void *ignore, void *data); - -/** - * Shorthand for calling an allocators malloc function. - * @param allocator the allocator to use - * @param n size of space to allocate - * @return a pointer to the allocated memory area - */ -#define almalloc(allocator, n) ((allocator)->malloc((allocator)->pool, n)) - -/** - * Shorthand for calling an allocators calloc function. - * @param allocator the allocator to use - * @param n the count of elements the space should be allocated for - * @param size the size of each element - * @return a pointer to the allocated memory area - */ -#define alcalloc(allocator, n, size) \ - ((allocator)->calloc((allocator)->pool, n, size)) - -/** - * Shorthand for calling an allocators realloc function. - * @param allocator the allocator to use - * @param ptr the pointer to the memory area that shall be reallocated - * @param n the new size of the allocated memory area - * @return a pointer to the reallocated memory area - */ -#define alrealloc(allocator, ptr, n) \ - ((allocator)->realloc((allocator)->pool, ptr, n)) - -/** - * Shorthand for calling an allocators free function. - * @param allocator the allocator to use - * @param ptr the pointer to the memory area that shall be freed - */ -#define alfree(allocator, ptr) ((allocator)->free((allocator)->pool, ptr)) - -/** - * Convenient macro for a default allocator struct definition. - */ -#define UCX_ALLOCATOR_DEFAULT {NULL, \ - ucx_default_malloc, ucx_default_calloc, ucx_default_realloc, \ - ucx_default_free } - -#ifdef __cplusplus -} -#endif - -#endif /* UCX_ALLOCATOR_H */ - diff -r a569148841ff -r efbd59642577 ucx/ucx/avl.h --- a/ucx/ucx/avl.h Sun Apr 16 14:12:24 2023 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,353 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 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. - */ - - -/** - * @file avl.h - * - * AVL tree implementation. - * - * This binary search tree implementation allows average O(1) insertion and - * removal of elements (excluding binary search time). - * - * @author Mike Becker - * @author Olaf Wintermann - */ - -#ifndef UCX_AVL_H -#define UCX_AVL_H - -#include "ucx.h" -#include "allocator.h" -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * UCX AVL Node type. - * - * @see UcxAVLNode - */ -typedef struct UcxAVLNode UcxAVLNode; - -/** - * UCX AVL Node. - */ -struct UcxAVLNode { - /** - * The key for this node. - */ - intptr_t key; - /** - * Data contained by this node. - */ - void *value; - /** - * The height of this (sub)-tree. - */ - size_t height; - /** - * Parent node. - */ - UcxAVLNode *parent; - /** - * Root node of left subtree. - */ - UcxAVLNode *left; - /** - * Root node of right subtree. - */ - UcxAVLNode *right; -}; - -/** - * UCX AVL Tree. - */ -typedef struct { - /** - * The UcxAllocator that shall be used to manage the memory for node data. - */ - UcxAllocator *allocator; - /** - * Root node of the tree. - */ - UcxAVLNode *root; - /** - * Compare function that shall be used to compare the UcxAVLNode keys. - * @see UcxAVLNode.key - */ - cmp_func cmpfunc; - /** - * Custom user data. - * This data will also be provided to the cmpfunc. - */ - void *userdata; -} UcxAVLTree; - -/** - * Initializes a new UcxAVLTree with a default allocator. - * - * @param cmpfunc the compare function that shall be used - * @return a new UcxAVLTree object - * @see ucx_avl_new_a() - */ -UcxAVLTree *ucx_avl_new(cmp_func cmpfunc); - -/** - * Initializes a new UcxAVLTree with the specified allocator. - * - * The cmpfunc should be capable of comparing two keys within this AVL tree. - * So if you want to use null terminated strings as keys, you could use the - * ucx_cmp_str() function here. - * - * @param cmpfunc the compare function that shall be used - * @param allocator the UcxAllocator that shall be used - * @return a new UcxAVLTree object - */ -UcxAVLTree *ucx_avl_new_a(cmp_func cmpfunc, UcxAllocator *allocator); - -/** - * Destroys a UcxAVLTree. - * - * Note, that the contents are not automatically freed. - * Use may use #ucx_avl_free_content() before calling this function. - * - * @param tree the tree to destroy - * @see ucx_avl_free_content() - */ -void ucx_avl_free(UcxAVLTree *tree); - -/** - * Frees the contents of a UcxAVLTree. - * - * This is a convenience function that iterates over the tree and passes all - * values to the specified destructor function. - * - * If no destructor is specified (NULL), the free() function of - * the tree's own allocator is used. - * - * You must ensure, that it is valid to pass each value in the map to the same - * destructor function. - * - * You should free the entire tree afterwards, as the contents will be invalid. - * - * @param tree for which the contents shall be freed - * @param destr optional pointer to a destructor function - * @see ucx_avl_free() - */ -void ucx_avl_free_content(UcxAVLTree *tree, ucx_destructor destr); - -/** - * Macro for initializing a new UcxAVLTree with the default allocator and a - * ucx_cmp_ptr() compare function. - * - * @return a new default UcxAVLTree object - */ -#define ucx_avl_default_new() \ - ucx_avl_new_a(ucx_cmp_ptr, ucx_default_allocator()) - -/** - * Gets the node from the tree, that is associated with the specified key. - * @param tree the UcxAVLTree - * @param key the key - * @return the node (or NULL, if the key is not present) - */ -UcxAVLNode *ucx_avl_get_node(UcxAVLTree *tree, intptr_t key); - -/** - * Gets the value from the tree, that is associated with the specified key. - * @param tree the UcxAVLTree - * @param key the key - * @return the value (or NULL, if the key is not present) - */ -void *ucx_avl_get(UcxAVLTree *tree, intptr_t key); - -/** - * A mode for #ucx_avl_find_node() with the same behavior as - * #ucx_avl_get_node(). - */ -#define UCX_AVL_FIND_EXACT 0 -/** - * A mode for #ucx_avl_find_node() finding the node whose key is at least - * as large as the specified key. - */ -#define UCX_AVL_FIND_LOWER_BOUNDED 1 -/** - * A mode for #ucx_avl_find_node() finding the node whose key is at most - * as large as the specified key. - */ -#define UCX_AVL_FIND_UPPER_BOUNDED 2 -/** - * A mode for #ucx_avl_find_node() finding the node with a key that is as close - * to the specified key as possible. If the key is present, the behavior is - * like #ucx_avl_get_node(). This mode only returns NULL on - * empty trees. - */ -#define UCX_AVL_FIND_CLOSEST 3 - -/** - * Finds a node within the tree. The following modes are supported: - *
        - *
      • #UCX_AVL_FIND_EXACT: the same behavior as #ucx_avl_get_node()
      • - *
      • #UCX_AVL_FIND_LOWER_BOUNDED: finds the node whose key is at least - * as large as the specified key
      • - *
      • #UCX_AVL_FIND_UPPER_BOUNDED: finds the node whose key is at most - * as large as the specified key
      • - *
      • #UCX_AVL_FIND_CLOSEST: finds the node with a key that is as close to - * the specified key as possible. If the key is present, the behavior is - * like #ucx_avl_get_node(). This mode only returns NULL on - * empty trees.
      • - *
      - * - * The distance function provided MUST agree with the compare function of - * the AVL tree. - * - * @param tree the UcxAVLTree - * @param key the key - * @param dfnc the distance function - * @param mode the find mode - * @return the node (or NULL, if no node can be found) - */ -UcxAVLNode *ucx_avl_find_node(UcxAVLTree *tree, intptr_t key, - distance_func dfnc, int mode); - -/** - * Finds a value within the tree. - * See #ucx_avl_find_node() for details. - * - * @param tree the UcxAVLTree - * @param key the key - * @param dfnc the distance function - * @param mode the find mode - * @return the value (or NULL, if no value can be found) - */ -void *ucx_avl_find(UcxAVLTree *tree, intptr_t key, - distance_func dfnc, int mode); - -/** - * Puts a key/value pair into the tree. - * - * Attention: use this function only, if a possible old value does not need - * to be preserved. - * - * @param tree the UcxAVLTree - * @param key the key - * @param value the new value - * @return zero, if and only if the operation succeeded - */ -int ucx_avl_put(UcxAVLTree *tree, intptr_t key, void *value); - -/** - * Puts a key/value pair into the tree. - * - * This is a secure function which saves the old value to the variable pointed - * at by oldvalue. - * - * @param tree the UcxAVLTree - * @param key the key - * @param value the new value - * @param oldvalue optional: a pointer to the location where a possible old - * value shall be stored - * @return zero, if and only if the operation succeeded - */ -int ucx_avl_put_s(UcxAVLTree *tree, intptr_t key, void *value, void **oldvalue); - -/** - * Removes a node from the AVL tree. - * - * Note: the specified node is logically removed. The tree implementation - * decides which memory area is freed. In most cases the here provided node - * is freed, so its further use is generally undefined. - * - * @param tree the UcxAVLTree - * @param node the node to remove - * @return zero, if and only if an element has been removed - */ -int ucx_avl_remove_node(UcxAVLTree *tree, UcxAVLNode *node); - -/** - * Removes an element from the AVL tree. - * - * @param tree the UcxAVLTree - * @param key the key - * @return zero, if and only if an element has been removed - */ -int ucx_avl_remove(UcxAVLTree *tree, intptr_t key); - -/** - * Removes an element from the AVL tree. - * - * This is a secure function which saves the old key and value data from node - * to the variables at the location of oldkey and oldvalue (if specified), so - * they can be freed afterwards (if necessary). - * - * Note: the returned key in oldkey is possibly not the same as the provided - * key for the lookup (in terms of memory location). - * - * @param tree the UcxAVLTree - * @param key the key of the element to remove - * @param oldkey optional: a pointer to the location where the old key shall be - * stored - * @param oldvalue optional: a pointer to the location where the old value - * shall be stored - * @return zero, if and only if an element has been removed - */ -int ucx_avl_remove_s(UcxAVLTree *tree, intptr_t key, - intptr_t *oldkey, void **oldvalue); - -/** - * Counts the nodes in the specified UcxAVLTree. - * @param tree the AVL tree - * @return the node count - */ -size_t ucx_avl_count(UcxAVLTree *tree); - -/** - * Finds the in-order predecessor of the given node. - * @param node an AVL node - * @return the in-order predecessor of the given node, or NULL if - * the given node is the in-order minimum - */ -UcxAVLNode* ucx_avl_pred(UcxAVLNode* node); - -/** - * Finds the in-order successor of the given node. - * @param node an AVL node - * @return the in-order successor of the given node, or NULL if - * the given node is the in-order maximum - */ -UcxAVLNode* ucx_avl_succ(UcxAVLNode* node); - -#ifdef __cplusplus -} -#endif - -#endif /* UCX_AVL_H */ - diff -r a569148841ff -r efbd59642577 ucx/ucx/buffer.h --- a/ucx/ucx/buffer.h Sun Apr 16 14:12:24 2023 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,339 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 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. - */ - -/** - * @file buffer.h - * - * Advanced buffer implementation. - * - * Instances of UcxBuffer can be used to read from or to write to like one - * would do with a stream. This allows the use of ucx_stream_copy() to copy - * contents from one buffer to another. - * - * Some features for convenient use of the buffer - * can be enabled. See the documentation of the macro constants for more - * information. - * - * @author Mike Becker - * @author Olaf Wintermann - */ - -#ifndef UCX_BUFFER_H -#define UCX_BUFFER_H - -#include "ucx.h" -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * No buffer features enabled (all flags cleared). - */ -#define UCX_BUFFER_DEFAULT 0x00 - -/** - * If this flag is enabled, the buffer will automatically free its contents. - */ -#define UCX_BUFFER_AUTOFREE 0x01 - -/** - * If this flag is enabled, the buffer will automatically extends its capacity. - */ -#define UCX_BUFFER_AUTOEXTEND 0x02 - -/** UCX Buffer. */ -typedef struct { - /** A pointer to the buffer contents. */ - char *space; - /** Current position of the buffer. */ - size_t pos; - /** Current capacity (i.e. maximum size) of the buffer. */ - size_t capacity; - /** Current size of the buffer content. */ - size_t size; - /** - * Flag register for buffer features. - * @see #UCX_BUFFER_DEFAULT - * @see #UCX_BUFFER_AUTOFREE - * @see #UCX_BUFFER_AUTOEXTEND - */ - int flags; -} UcxBuffer; - -/** - * Creates a new buffer. - * - * Note: you may provide NULL as argument for - * space. Then this function will allocate the space and enforce - * the #UCX_BUFFER_AUTOFREE flag. - * - * @param space pointer to the memory area, or NULL to allocate - * new memory - * @param capacity the capacity of the buffer - * @param flags buffer features (see UcxBuffer.flags) - * @return the new buffer - */ -UcxBuffer *ucx_buffer_new(void *space, size_t capacity, int flags); - -/** - * Destroys a buffer. - * - * If the #UCX_BUFFER_AUTOFREE feature is enabled, the contents of the buffer - * are also freed. - * - * @param buffer the buffer to destroy - */ -void ucx_buffer_free(UcxBuffer* buffer); - -/** - * Creates a new buffer and fills it with extracted content from another buffer. - * - * Note: the #UCX_BUFFER_AUTOFREE feature is enforced for the new buffer. - * - * @param src the source buffer - * @param start the start position of extraction - * @param length the count of bytes to extract (must not be zero) - * @param flags feature mask for the new buffer - * @return a new buffer containing the extraction - */ -UcxBuffer* ucx_buffer_extract(UcxBuffer *src, - size_t start, size_t length, int flags); - -/** - * A shorthand macro for the full extraction of the buffer. - * - * @param src the source buffer - * @param flags feature mask for the new buffer - * @return a new buffer with the extracted content - */ -#define ucx_buffer_clone(src,flags) \ - ucx_buffer_extract(src, 0, (src)->capacity, flags) - - -/** - * Shifts the contents of the buffer by the given offset. - * - * If the offset is positive, the contents are shifted to the right. - * If auto extension is enabled, the buffer grows, if necessary. - * In case the auto extension fails, this function returns a non-zero value and - * no contents are changed. - * If auto extension is disabled, the contents that do not fit into the buffer - * are discarded. - * - * If the offset is negative, the contents are shifted to the left where the - * first shift bytes are discarded. - * The new size of the buffer is the old size minus - * the absolute shift value. - * If this value is larger than the buffer size, the buffer is emptied (but - * not cleared, see the security note below). - * - * The buffer position gets shifted alongside with the content but is kept - * within the boundaries of the buffer. - * - * Security note: the shifting operation does not erase the - * previously occupied memory cells. You can easily do that manually, e.g. by - * calling memset(buffer->space, 0, shift) for a right shift or - * memset(buffer->size, 0, buffer->capacity-buffer->size) - * for a left shift. - * - * @param buffer the buffer - * @param shift the shift offset (negative means left shift) - * @return 0 on success, non-zero if a required auto-extension fails - */ -int ucx_buffer_shift(UcxBuffer* buffer, off_t shift); - -/** - * Shifts the buffer to the right. - * See ucx_buffer_shift() for details. - * - * @param buffer the buffer - * @param shift the shift offset - * @return 0 on success, non-zero if a required auto-extension fails - * @see ucx_buffer_shift() - */ -int ucx_buffer_shift_right(UcxBuffer* buffer, size_t shift); - -/** - * Shifts the buffer to the left. - * - * See ucx_buffer_shift() for details. Note, however, that this method expects - * a positive shift offset. - * - * Since a left shift cannot fail due to memory allocation problems, this - * function always returns zero. - * - * @param buffer the buffer - * @param shift the shift offset - * @return always zero - * @see ucx_buffer_shift() - */ -int ucx_buffer_shift_left(UcxBuffer* buffer, size_t shift); - - -/** - * Moves the position of the buffer. - * - * The new position is relative to the whence argument. - * - * SEEK_SET marks the start of the buffer. - * SEEK_CUR marks the current position. - * SEEK_END marks the end of the buffer. - * - * With an offset of zero, this function sets the buffer position to zero - * (SEEK_SET), the buffer size (SEEK_END) or leaves the buffer position - * unchanged (SEEK_CUR). - * - * @param buffer - * @param offset position offset relative to whence - * @param whence one of SEEK_SET, SEEK_CUR or SEEK_END - * @return 0 on success, non-zero if the position is invalid - * - */ -int ucx_buffer_seek(UcxBuffer *buffer, off_t offset, int whence); - -/** - * Clears the buffer by resetting the position and deleting the data. - * - * The data is deleted by a zeroing it with call to memset(). - * - * @param buffer the buffer to be cleared - */ -#define ucx_buffer_clear(buffer) memset((buffer)->space, 0, (buffer)->size); \ - (buffer)->size = 0; (buffer)->pos = 0; - -/** - * Tests, if the buffer position has exceeded the buffer capacity. - * - * @param buffer the buffer to test - * @return non-zero, if the current buffer position has exceeded the last - * available byte of the buffer. - */ -int ucx_buffer_eof(UcxBuffer *buffer); - - -/** - * Extends the capacity of the buffer. - * - * Note: The buffer capacity increased by a power of two. I.e. - * the buffer capacity is doubled, as long as it would not hold the current - * content plus the additional required bytes. - * - * Attention: the argument provided is the number of additional - * bytes the buffer shall hold. It is NOT the total number of bytes the - * buffer shall hold. - * - * @param buffer the buffer to extend - * @param additional_bytes the number of additional bytes the buffer shall - * at least hold - * @return 0 on success or a non-zero value on failure - */ -int ucx_buffer_extend(UcxBuffer *buffer, size_t additional_bytes); - -/** - * Writes data to a UcxBuffer. - * - * The position of the buffer is increased by the number of bytes written. - * - * @param ptr a pointer to the memory area containing the bytes to be written - * @param size the length of one element - * @param nitems the element count - * @param buffer the UcxBuffer to write to - * @return the total count of bytes written - */ -size_t ucx_buffer_write(const void *ptr, size_t size, size_t nitems, - UcxBuffer *buffer); - -/** - * Reads data from a UcxBuffer. - * - * The position of the buffer is increased by the number of bytes read. - * - * @param ptr a pointer to the memory area where to store the read data - * @param size the length of one element - * @param nitems the element count - * @param buffer the UcxBuffer to read from - * @return the total number of elements read - */ -size_t ucx_buffer_read(void *ptr, size_t size, size_t nitems, - UcxBuffer *buffer); - -/** - * Writes a character to a buffer. - * - * The least significant byte of the argument is written to the buffer. If the - * end of the buffer is reached and #UCX_BUFFER_AUTOEXTEND feature is enabled, - * the buffer capacity is extended by ucx_buffer_extend(). If the feature is - * disabled or buffer extension fails, EOF is returned. - * - * On successful write the position of the buffer is increased. - * - * @param buffer the buffer to write to - * @param c the character to write as int value - * @return the byte that has bean written as int value or - * EOF when the end of the stream is reached and automatic - * extension is not enabled or not possible - */ -int ucx_buffer_putc(UcxBuffer *buffer, int c); - -/** - * Gets a character from a buffer. - * - * The current position of the buffer is increased after a successful read. - * - * @param buffer the buffer to read from - * @return the character as int value or EOF, if the - * end of the buffer is reached - */ -int ucx_buffer_getc(UcxBuffer *buffer); - -/** - * Writes a string to a buffer. - * - * @param buffer the buffer - * @param str the string - * @return the number of bytes written - */ -size_t ucx_buffer_puts(UcxBuffer *buffer, const char *str); - -/** - * Returns the complete buffer content as sstr_t. - * @param buffer the buffer - * @return the result of sstrn() with the buffer space and size - * as arguments - */ -#define ucx_buffer_to_sstr(buffer) sstrn((buffer)->space, (buffer)->size) - -#ifdef __cplusplus -} -#endif - -#endif /* UCX_BUFFER_H */ - diff -r a569148841ff -r efbd59642577 ucx/ucx/list.h --- a/ucx/ucx/list.h Sun Apr 16 14:12:24 2023 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,396 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 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. - */ -/** - * Doubly linked list implementation. - * - * @file list.h - * @author Mike Becker - * @author Olaf Wintermann - */ - -#ifndef UCX_LIST_H -#define UCX_LIST_H - -#include "ucx.h" -#include "allocator.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Loop statement for UCX lists. - * - * The first argument is the name of the iteration variable. The scope of - * this variable is limited to the UCX_FOREACH statement. - * - * The second argument is a pointer to the list. In most cases this will be the - * pointer to the first element of the list, but it may also be an arbitrary - * element of the list. The iteration will then start with that element. - * - * @param list The first element of the list - * @param elem The variable name of the element - */ -#define UCX_FOREACH(elem,list) \ - for (UcxList* elem = list ; elem != NULL ; elem = elem->next) - -/** - * UCX list type. - * @see UcxList - */ -typedef struct UcxList UcxList; - -/** - * UCX list structure. - */ -struct UcxList { - /** - * List element payload. - */ - void *data; - /** - * Pointer to the next list element or NULL, if this is the - * last element. - */ - UcxList *next; - /** - * Pointer to the previous list element or NULL, if this is - * the first element. - */ - UcxList *prev; -}; - -/** - * Creates an element-wise copy of a list. - * - * This function clones the specified list by creating new list elements and - * copying the data with the specified copy_func(). If no copy_func() is - * specified, a shallow copy is created and the new list will reference the - * same data as the source list. - * - * @param list the list to copy - * @param cpyfnc a pointer to the function that shall copy an element (may be - * NULL) - * @param data additional data for the copy_func() - * @return a pointer to the copy - */ -UcxList *ucx_list_clone(UcxList *list, copy_func cpyfnc, void* data); - -/** - * Creates an element-wise copy of a list using a UcxAllocator. - * - * See ucx_list_clone() for details. - * - * You might want to pass the allocator via the data parameter, - * to access it within the copy function for making deep copies. - * - * @param allocator the allocator to use - * @param list the list to copy - * @param cpyfnc a pointer to the function that shall copy an element (may be - * NULL) - * @param data additional data for the copy_func() - * @return a pointer to the copy - * @see ucx_list_clone() - */ -UcxList *ucx_list_clone_a(UcxAllocator *allocator, UcxList *list, - copy_func cpyfnc, void* data); - -/** - * Compares two UCX lists element-wise by using a compare function. - * - * Each element of the two specified lists are compared by using the specified - * compare function and the additional data. The type and content of this - * additional data depends on the cmp_func() used. - * - * If the list pointers denote elements within a list, the lists are compared - * starting with the denoted elements. Thus any previous elements are not taken - * into account. This might be useful to check, if certain list tails match - * each other. - * - * @param list1 the first list - * @param list2 the second list - * @param cmpfnc the compare function - * @param data additional data for the compare function - * @return 1, if and only if the two lists equal element-wise, 0 otherwise - */ -int ucx_list_equals(const UcxList *list1, const UcxList *list2, - cmp_func cmpfnc, void* data); - -/** - * Destroys the entire list. - * - * The members of the list are not automatically freed, so ensure they are - * otherwise referenced or destroyed by ucx_list_free_contents(). - * Otherwise, a memory leak is likely to occur. - * - * Caution: the argument MUST denote an entire list (i.e. a call - * to ucx_list_first() on the argument must return the argument itself) - * - * @param list the list to free - * @see ucx_list_free_contents() - */ -void ucx_list_free(UcxList *list); - -/** - * Destroys the entire list using a UcxAllocator. - * - * See ucx_list_free() for details. - * - * @param allocator the allocator to use - * @param list the list to free - * @see ucx_list_free() - */ -void ucx_list_free_a(UcxAllocator *allocator, UcxList *list); - -/** - * Destroys the contents of the specified list by calling the specified - * destructor on each of them. - * - * Note, that the contents are not usable afterwards and the list should be - * destroyed with ucx_list_free(). - * - * If no destructor is specified (NULL), stdlib's free() is used. - * - * @param list the list for which the contents shall be freed - * @param destr optional destructor function - * @see ucx_list_free() - */ -void ucx_list_free_content(UcxList* list, ucx_destructor destr); - - -/** - * Inserts an element at the end of the list. - * - * This is generally an O(n) operation, as the end of the list is retrieved with - * ucx_list_last(). - * - * @param list the list where to append the data, or NULL to - * create a new list - * @param data the data to insert - * @return list, if it is not NULL or a pointer to - * the newly created list otherwise - */ -UcxList *ucx_list_append(UcxList *list, void *data); - -/** - * Inserts an element at the end of the list using a UcxAllocator. - * - * See ucx_list_append() for details. - * - * @param allocator the allocator to use - * @param list the list where to append the data, or NULL to - * create a new list - * @param data the data to insert - * @return list, if it is not NULL or a pointer to - * the newly created list otherwise - * @see ucx_list_append() - */ -UcxList *ucx_list_append_a(UcxAllocator *allocator, UcxList *list, void *data); - - -/** - * Inserts an element at the beginning of the list. - * - * You should overwrite the old list pointer by calling - * mylist = ucx_list_prepend(mylist, mydata);. However, you may - * also perform successive calls of ucx_list_prepend() on the same list pointer, - * as this function always searchs for the head of the list with - * ucx_list_first(). - * - * @param list the list where to insert the data or NULL to create - * a new list - * @param data the data to insert - * @return a pointer to the new list head - */ -UcxList *ucx_list_prepend(UcxList *list, void *data); - -/** - * Inserts an element at the beginning of the list using a UcxAllocator. - * - * See ucx_list_prepend() for details. - * - * @param allocator the allocator to use - * @param list the list where to insert the data or NULL to create - * a new list - * @param data the data to insert - * @return a pointer to the new list head - * @see ucx_list_prepend() - */ -UcxList *ucx_list_prepend_a(UcxAllocator *allocator, UcxList *list, void *data); - -/** - * Concatenates two lists. - * - * Either of the two arguments may be NULL. - * - * This function modifies the references to the next/previous element of - * the last/first element of list1/ - * list2. - * - * @param list1 first list - * @param list2 second list - * @return if list1 is NULL, list2 is - * returned, otherwise list1 is returned - */ -UcxList *ucx_list_concat(UcxList *list1, UcxList *list2); - -/** - * Returns the first element of a list. - * - * If the argument is the list pointer, it is directly returned. Otherwise - * this function traverses to the first element of the list and returns the - * list pointer. - * - * @param elem one element of the list - * @return the first element of the list, the specified element is a member of - */ -UcxList *ucx_list_first(const UcxList *elem); - -/** - * Returns the last element of a list. - * - * If the argument has no successor, it is the last element and therefore - * directly returned. Otherwise this function traverses to the last element of - * the list and returns it. - * - * @param elem one element of the list - * @return the last element of the list, the specified element is a member of - */ -UcxList *ucx_list_last(const UcxList *elem); - -/** - * Returns the list element at the specified index. - * - * @param list the list to retrieve the element from - * @param index index of the element to return - * @return the element at the specified index or NULL, if the - * index is greater than the list size - */ -UcxList *ucx_list_get(const UcxList *list, size_t index); - -/** - * Returns the index of an element. - * - * @param list the list where to search for the element - * @param elem the element to find - * @return the index of the element or -1 if the list does not contain the - * element - */ -ssize_t ucx_list_indexof(const UcxList *list, const UcxList *elem); - -/** - * Returns the element count of the list. - * - * @param list the list whose elements are counted - * @return the element count - */ -size_t ucx_list_size(const UcxList *list); - -/** - * Returns the index of an element containing the specified data. - * - * This function uses a cmp_func() to compare the data of each list element - * with the specified data. If no cmp_func is provided, the pointers are - * compared. - * - * If the list contains the data more than once, the index of the first - * occurrence is returned. - * - * @param list the list where to search for the data - * @param elem the element data - * @param cmpfnc the compare function - * @param data additional data for the compare function - * @return the index of the element containing the specified data or -1 if the - * data is not found in this list - */ -ssize_t ucx_list_find(UcxList *list, void *elem, cmp_func cmpfnc, void *data); - -/** - * Checks, if a list contains a specific element. - * - * An element is found, if ucx_list_find() returns a value greater than -1. - * - * @param list the list where to search for the data - * @param elem the element data - * @param cmpfnc the compare function - * @param data additional data for the compare function - * @return 1, if and only if the list contains the specified element data - * @see ucx_list_find() - */ -int ucx_list_contains(UcxList *list, void *elem, cmp_func cmpfnc, void *data); - -/** - * Sorts a UcxList with natural merge sort. - * - * This function uses O(n) additional temporary memory for merge operations - * that is automatically freed after each merge. - * - * As the head of the list might change, you MUST call this function - * as follows: mylist = ucx_list_sort(mylist, mycmpfnc, mydata);. - * - * @param list the list to sort - * @param cmpfnc the function that shall be used to compare the element data - * @param data additional data for the cmp_func() - * @return the sorted list - */ -UcxList *ucx_list_sort(UcxList *list, cmp_func cmpfnc, void *data); - -/** - * Removes an element from the list. - * - * If the first element is removed, the list pointer changes. So it is - * highly recommended to always update the pointer by calling - * mylist = ucx_list_remove(mylist, myelem);. - * - * @param list the list from which the element shall be removed - * @param element the element to remove - * @return returns the updated list pointer or NULL, if the list - * is now empty - */ -UcxList *ucx_list_remove(UcxList *list, UcxList *element); - -/** - * Removes an element from the list using a UcxAllocator. - * - * See ucx_list_remove() for details. - * - * @param allocator the allocator to use - * @param list the list from which the element shall be removed - * @param element the element to remove - * @return returns the updated list pointer or NULL, if the list - * @see ucx_list_remove() - */ -UcxList *ucx_list_remove_a(UcxAllocator *allocator, UcxList *list, - UcxList *element); - -#ifdef __cplusplus -} -#endif - -#endif /* UCX_LIST_H */ - diff -r a569148841ff -r efbd59642577 ucx/ucx/logging.h --- a/ucx/ucx/logging.h Sun Apr 16 14:12:24 2023 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,250 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 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. - */ -/** - * Logging API. - * - * @file logging.h - * @author Mike Becker, Olaf Wintermann - */ -#ifndef UCX_LOGGING_H -#define UCX_LOGGING_H - -#include "ucx.h" -#include "map.h" -#include "string.h" -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* leave enough space for custom log levels */ - -/** Log level for error messages. */ -#define UCX_LOGGER_ERROR 0x00 - -/** Log level for warning messages. */ -#define UCX_LOGGER_WARN 0x10 - -/** Log level for information messages. */ -#define UCX_LOGGER_INFO 0x20 - -/** Log level for debug messages. */ -#define UCX_LOGGER_DEBUG 0x30 - -/** Log level for trace messages. */ -#define UCX_LOGGER_TRACE 0x40 - -/** - * Output flag for the log level. - * If this flag is set, the log message will contain the log level. - * @see UcxLogger.mask - */ -#define UCX_LOGGER_LEVEL 0x01 - -/** - * Output flag for the timestmap. - * If this flag is set, the log message will contain the timestmap. - * @see UcxLogger.mask - */ -#define UCX_LOGGER_TIMESTAMP 0x02 - -/** - * Output flag for the source. - * If this flag is set, the log message will contain the source file and line - * number. - * @see UcxLogger.mask - */ -#define UCX_LOGGER_SOURCE 0x04 - -/** - * The UCX Logger object. - */ -typedef struct { - /** The stream this logger writes its messages to.*/ - void *stream; - - /** - * The write function that shall be used. - * For standard file or stdout loggers this might be standard fwrite - * (default). - */ - write_func writer; - - /** - * The date format for timestamp outputs including the delimiter - * (default: "%F %T %z "). - * @see UCX_LOGGER_TIMESTAMP - */ - char *dateformat; - - /** - * The level, this logger operates on. - * If a log command is issued, the message will only be logged, if the log - * level of the message is less or equal than the log level of the logger. - */ - unsigned int level; - - /** - * A configuration mask for automatic output. - * For each flag that is set, the logger automatically outputs some extra - * information like the timestamp or the source file and line number. - * See the documentation for the flags for details. - */ - unsigned int mask; - - /** - * A map of valid log levels for this logger. - * - * The keys represent all valid log levels and the values provide string - * representations, that are used, if the UCX_LOGGER_LEVEL flag is set. - * - * The exact data types are unsigned int for the key and - * const char* for the value. - * - * @see UCX_LOGGER_LEVEL - */ - UcxMap* levels; -} UcxLogger; - -/** - * Creates a new logger. - * @param stream the stream, which the logger shall write to - * @param level the level on which the logger shall operate - * @param mask configuration mask (cf. UcxLogger.mask) - * @return a new logger object - */ -UcxLogger *ucx_logger_new(void *stream, unsigned int level, unsigned int mask); - -/** - * Destroys the logger. - * - * The map containing the valid log levels is also automatically destroyed. - * - * @param logger the logger to destroy - */ -void ucx_logger_free(UcxLogger* logger); - -/** - * Internal log function - use macros instead. - * - * This function uses the format and variadic arguments for a - * printf()-style output of the log message. - * - * Dependent on the UcxLogger.mask some information is prepended. The complete - * format is: - * - * [LEVEL] [TIMESTAMP] [SOURCEFILE]:[LINENO] message - * - * Attention: the message (including automatically generated information) - * is limited to 4096 characters. The level description is limited to - * 256 characters and the timestamp string is limited to 128 characters. - * - * @param logger the logger to use - * @param level the level to log on - * @param file information about the source file - * @param line information about the source line number - * @param format format string - * @param ... arguments - * @see ucx_logger_log() - */ -void ucx_logger_logf(UcxLogger *logger, unsigned int level, const char* file, - const unsigned int line, const char* format, ...); - -/** - * Registers a custom log level. - * @param logger the logger - * @param level the log level as unsigned integer - * @param name a string literal describing the level - */ -#define ucx_logger_register_level(logger, level, name) {\ - unsigned int l; \ - l = level; \ - ucx_map_int_put(logger->levels, l, (void*) "[" name "]"); \ - } while (0); - -/** - * Logs a message at the specified level. - * @param logger the logger to use - * @param level the level to log the message on - * @param ... format string and arguments - * @see ucx_logger_logf() - */ -#define ucx_logger_log(logger, level, ...) \ - ucx_logger_logf(logger, level, __FILE__, __LINE__, __VA_ARGS__) - -/** - * Shortcut for logging an error message. - * @param logger the logger to use - * @param ... format string and arguments - * @see ucx_logger_logf() - */ -#define ucx_logger_error(logger, ...) \ - ucx_logger_log(logger, UCX_LOGGER_ERROR, __VA_ARGS__) - -/** - * Shortcut for logging an information message. - * @param logger the logger to use - * @param ... format string and arguments - * @see ucx_logger_logf() - */ -#define ucx_logger_info(logger, ...) \ - ucx_logger_log(logger, UCX_LOGGER_INFO, __VA_ARGS__) - -/** - * Shortcut for logging a warning message. - * @param logger the logger to use - * @param ... format string and arguments - * @see ucx_logger_logf() - */ -#define ucx_logger_warn(logger, ...) \ - ucx_logger_log(logger, UCX_LOGGER_WARN, __VA_ARGS__) - -/** - * Shortcut for logging a debug message. - * @param logger the logger to use - * @param ... format string and arguments - * @see ucx_logger_logf() - */ -#define ucx_logger_debug(logger, ...) \ - ucx_logger_log(logger, UCX_LOGGER_DEBUG, __VA_ARGS__) - -/** - * Shortcut for logging a trace message. - * @param logger the logger to use - * @param ... format string and arguments - * @see ucx_logger_logf() - */ -#define ucx_logger_trace(logger, ...) \ - ucx_logger_log(logger, UCX_LOGGER_TRACE, __VA_ARGS__) - -#ifdef __cplusplus -} -#endif - -#endif /* UCX_LOGGING_H */ diff -r a569148841ff -r efbd59642577 ucx/ucx/map.h --- a/ucx/ucx/map.h Sun Apr 16 14:12:24 2023 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,435 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 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. - */ - -/** - * @file map.h - * - * Hash map implementation. - * - * This implementation uses murmur hash 2 and separate chaining with linked - * lists. - * - * @author Mike Becker - * @author Olaf Wintermann - */ - -#ifndef UCX_MAP_H -#define UCX_MAP_H - -#include "ucx.h" -#include "string.h" -#include "allocator.h" -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Loop statement for UCX maps. - * - * The key variable is implicitly defined, but the - * value variable must be already declared as type information - * cannot be inferred. - * - * @param key the variable name for the key - * @param value the variable name for the value - * @param iter a UcxMapIterator - * @see ucx_map_iterator() - */ -#define UCX_MAP_FOREACH(key,value,iter) \ - for(UcxKey key;ucx_map_iter_next(&iter,&key, (void**)&value);) - -/** Type for the UCX map. @see UcxMap */ -typedef struct UcxMap UcxMap; - -/** Type for a key of a UcxMap. @see UcxKey */ -typedef struct UcxKey UcxKey; - -/** Type for an element of a UcxMap. @see UcxMapElement */ -typedef struct UcxMapElement UcxMapElement; - -/** Type for an iterator over a UcxMap. @see UcxMapIterator */ -typedef struct UcxMapIterator UcxMapIterator; - -/** Structure for the UCX map. */ -struct UcxMap { - /** An allocator that is used for the map elements. */ - UcxAllocator *allocator; - /** The array of map element lists. */ - UcxMapElement **map; - /** The size of the map is the length of the element list array. */ - size_t size; - /** The count of elements currently stored in this map. */ - size_t count; -}; - -/** Structure to publicly denote a key of a UcxMap. */ -struct UcxKey { - /** The key data. */ - const void *data; - /** The length of the key data. */ - size_t len; - /** A cache for the hash value of the key data. */ - int hash; -}; - -/** Internal structure for a key of a UcxMap. */ -struct UcxMapKey { - /** The key data. */ - void *data; - /** The length of the key data. */ - size_t len; - /** The hash value of the key data. */ - int hash; -}; - -/** Structure for an element of a UcxMap. */ -struct UcxMapElement { - /** The value data. */ - void *data; - - /** A pointer to the next element in the current list. */ - UcxMapElement *next; - - /** The corresponding key. */ - struct UcxMapKey key; -}; - -/** Structure for an iterator over a UcxMap. */ -struct UcxMapIterator { - /** The map to iterate over. */ - UcxMap *map; - - /** The current map element. */ - UcxMapElement *cur; - - /** - * The current index of the element list array. - * Attention: this is NOT the element index! Do NOT - * manually iterate over the map by increasing this index. Use - * ucx_map_iter_next(). - * @see UcxMap.map*/ - size_t index; -}; - -/** - * Creates a new hash map with the specified size. - * @param size the size of the hash map - * @return a pointer to the new hash map - */ -UcxMap *ucx_map_new(size_t size); - -/** - * Creates a new hash map with the specified size using a UcxAllocator. - * @param allocator the allocator to use - * @param size the size of the hash map - * @return a pointer to the new hash map - */ -UcxMap *ucx_map_new_a(UcxAllocator *allocator, size_t size); - -/** - * Frees a hash map. - * - * Note: the contents are not freed, use ucx_map_free_content() - * before calling this function to achieve that. - * - * @param map the map to be freed - * @see ucx_map_free_content() - */ -void ucx_map_free(UcxMap *map); - -/** - * Frees the contents of a hash map. - * - * This is a convenience function that iterates over the map and passes all - * values to the specified destructor function. - * - * If no destructor is specified (NULL), the free() function of - * the map's own allocator is used. - * - * You must ensure, that it is valid to pass each value in the map to the same - * destructor function. - * - * You should free or clear the map afterwards, as the contents will be invalid. - * - * @param map for which the contents shall be freed - * @param destr optional pointer to a destructor function - * @see ucx_map_free() - * @see ucx_map_clear() - */ -void ucx_map_free_content(UcxMap *map, ucx_destructor destr); - -/** - * Clears a hash map. - * - * Note: the contents are not freed, use ucx_map_free_content() - * before calling this function to achieve that. - * - * @param map the map to be cleared - * @see ucx_map_free_content() - */ -void ucx_map_clear(UcxMap *map); - - -/** - * Copies contents from a map to another map using a copy function. - * - * Note: The destination map does not need to be empty. However, if it - * contains data with keys that are also present in the source map, the contents - * are overwritten. - * - * @param from the source map - * @param to the destination map - * @param fnc the copy function or NULL if the pointer address - * shall be copied - * @param data additional data for the copy function - * @return 0 on success or a non-zero value on memory allocation errors - */ -int ucx_map_copy(UcxMap *from, UcxMap *to, copy_func fnc, void *data); - -/** - * Clones the map and rehashes if necessary. - * - * Note: In contrast to ucx_map_rehash() the load factor is irrelevant. - * This function always ensures a new UcxMap.size of at least - * 2.5*UcxMap.count. - * - * @param map the map to clone - * @param fnc the copy function to use or NULL if the new and - * the old map shall share the data pointers - * @param data additional data for the copy function - * @return the cloned map - * @see ucx_map_copy() - */ -UcxMap *ucx_map_clone(UcxMap *map, copy_func fnc, void *data); - -/** - * Increases size of the hash map, if necessary. - * - * The load value is 0.75*UcxMap.size. If the element count exceeds the load - * value, the map needs to be rehashed. Otherwise no action is performed and - * this function simply returns 0. - * - * The rehashing process ensures, that the UcxMap.size is at least - * 2.5*UcxMap.count. So there is enough room for additional elements without - * the need of another soon rehashing. - * - * You can use this function to dramatically increase access performance. - * - * @param map the map to rehash - * @return 1, if a memory allocation error occurred, 0 otherwise - */ -int ucx_map_rehash(UcxMap *map); - -/** - * Puts a key/value-pair into the map. - * - * @param map the map - * @param key the key - * @param value the value - * @return 0 on success, non-zero value on failure - */ -int ucx_map_put(UcxMap *map, UcxKey key, void *value); - -/** - * Retrieves a value by using a key. - * - * @param map the map - * @param key the key - * @return the value - */ -void* ucx_map_get(UcxMap *map, UcxKey key); - -/** - * Removes a key/value-pair from the map by using the key. - * - * @param map the map - * @param key the key - * @return the removed value - */ -void* ucx_map_remove(UcxMap *map, UcxKey key); - -/** - * Shorthand for putting data with a sstr_t key into the map. - * @param map the map - * @param key the key - * @param value the value - * @return 0 on success, non-zero value on failure - * @see ucx_map_put() - */ -#define ucx_map_sstr_put(map, key, value) \ - ucx_map_put(map, ucx_key(key.ptr, key.length), (void*)value) - -/** - * Shorthand for putting data with a C string key into the map. - * @param map the map - * @param key the key - * @param value the value - * @return 0 on success, non-zero value on failure - * @see ucx_map_put() - */ -#define ucx_map_cstr_put(map, key, value) \ - ucx_map_put(map, ucx_key(key, strlen(key)), (void*)value) - -/** - * Shorthand for putting data with an integer key into the map. - * @param map the map - * @param key the key - * @param value the value - * @return 0 on success, non-zero value on failure - * @see ucx_map_put() - */ -#define ucx_map_int_put(map, key, value) \ - ucx_map_put(map, ucx_key(&key, sizeof(key)), (void*)value) - -/** - * Shorthand for getting data from the map with a sstr_t key. - * @param map the map - * @param key the key - * @return the value - * @see ucx_map_get() - */ -#define ucx_map_sstr_get(map, key) \ - ucx_map_get(map, ucx_key(key.ptr, key.length)) - -/** - * Shorthand for getting data from the map with a C string key. - * @param map the map - * @param key the key - * @return the value - * @see ucx_map_get() - */ -#define ucx_map_cstr_get(map, key) \ - ucx_map_get(map, ucx_key(key, strlen(key))) - -/** - * Shorthand for getting data from the map with an integer key. - * @param map the map - * @param key the key - * @return the value - * @see ucx_map_get() - */ -#define ucx_map_int_get(map, key) \ - ucx_map_get(map, ucx_key(&key, sizeof(int))) - -/** - * Shorthand for removing data from the map with a sstr_t key. - * @param map the map - * @param key the key - * @return the removed value - * @see ucx_map_remove() - */ -#define ucx_map_sstr_remove(map, key) \ - ucx_map_remove(map, ucx_key(key.ptr, key.length)) - -/** - * Shorthand for removing data from the map with a C string key. - * @param map the map - * @param key the key - * @return the removed value - * @see ucx_map_remove() - */ -#define ucx_map_cstr_remove(map, key) \ - ucx_map_remove(map, ucx_key(key, strlen(key))) - -/** - * Shorthand for removing data from the map with an integer key. - * @param map the map - * @param key the key - * @return the removed value - * @see ucx_map_remove() - */ -#define ucx_map_int_remove(map, key) \ - ucx_map_remove(map, ucx_key(&key, sizeof(key))) - -/** - * Creates a UcxKey based on the given data. - * - * This function implicitly computes the hash. - * - * @param data the data for the key - * @param len the length of the data - * @return a UcxKey with implicitly computed hash - * @see ucx_hash() - */ -UcxKey ucx_key(const void *data, size_t len); - -/** - * Computes a murmur hash-2. - * - * @param data the data to hash - * @param len the length of the data - * @return the murmur hash-2 of the data - */ -int ucx_hash(const char *data, size_t len); - -/** - * Creates an iterator for a map. - * - * Note: A UcxMapIterator iterates over all elements in all element - * lists successively. Therefore the order highly depends on the key hashes and - * may vary under different map sizes. So generally you may NOT rely on - * the iteration order. - * - * Note: The iterator is NOT initialized. You need to call - * ucx_map_iter_next() at least once before accessing any information. However, - * it is not recommended to access the fields of a UcxMapIterator directly. - * - * @param map the map to create the iterator for - * @return an iterator initialized on the first element of the - * first element list - * @see ucx_map_iter_next() - */ -UcxMapIterator ucx_map_iterator(UcxMap *map); - -/** - * Proceeds to the next element of the map (if any). - * - * Subsequent calls on the same iterator proceed to the next element and - * store the key/value-pair into the memory specified as arguments of this - * function. - * - * If no further elements are found, this function returns zero and leaves the - * last found key/value-pair in memory. - * - * @param iterator the iterator to use - * @param key a pointer to the memory where to store the key - * @param value a pointer to the memory where to store the value - * @return 1, if another element was found, 0 if all elements has been processed - * @see ucx_map_iterator() - */ -int ucx_map_iter_next(UcxMapIterator *iterator, UcxKey *key, void **value); - - -#ifdef __cplusplus -} -#endif - -#endif /* UCX_MAP_H */ - diff -r a569148841ff -r efbd59642577 ucx/ucx/mempool.h --- a/ucx/ucx/mempool.h Sun Apr 16 14:12:24 2023 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,209 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 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. - */ - -/** - * @file mempool.h - * - * Memory pool implementation. - * - * @author Mike Becker - * @author Olaf Wintermann - */ - -#ifndef UCX_MEMPOOL_H -#define UCX_MEMPOOL_H - -#include "ucx.h" -#include "allocator.h" -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * UCX mempool structure. - */ -typedef struct { - /** UcxAllocator based on this pool */ - UcxAllocator *allocator; - - /** List of pointers to pooled memory. */ - void **data; - - /** Count of pooled memory items. */ - size_t ndata; - - /** Memory pool size. */ - size_t size; -} UcxMempool; - -/** Shorthand for a new default memory pool with a capacity of 16 elements. */ -#define ucx_mempool_new_default() ucx_mempool_new(16) - - -/** - * Creates a memory pool with the specified initial size. - * - * As the created memory pool automatically grows in size by factor two when - * trying to allocate memory on a full pool, it is recommended that you use - * a power of two for the initial size. - * - * @param n initial pool size (should be a power of two, e.g. 16) - * @return a pointer to the new memory pool - * @see ucx_mempool_new_default() - */ -UcxMempool *ucx_mempool_new(size_t n); - -/** - * Resizes a memory pool. - * - * This function will fail if the new capacity is not sufficient for the - * present data. - * - * @param pool the pool to resize - * @param newcap the new capacity - * @return zero on success or non-zero on failure - */ -int ucx_mempool_chcap(UcxMempool *pool, size_t newcap); - -/** - * Allocates pooled memory. - * - * @param pool the memory pool - * @param n amount of memory to allocate - * @return a pointer to the allocated memory - * @see ucx_allocator_malloc() - */ -void *ucx_mempool_malloc(UcxMempool *pool, size_t n); -/** - * Allocates a pooled memory array. - * - * The content of the allocated memory is set to zero. - * - * @param pool the memory pool - * @param nelem amount of elements to allocate - * @param elsize amount of memory per element - * @return a pointer to the allocated memory - * @see ucx_allocator_calloc() - */ -void *ucx_mempool_calloc(UcxMempool *pool, size_t nelem, size_t elsize); - -/** - * Reallocates pooled memory. - * - * If the memory to be reallocated is not contained by the specified pool, the - * behavior is undefined. - * - * @param pool the memory pool - * @param ptr a pointer to the memory that shall be reallocated - * @param n the new size of the memory - * @return a pointer to the new location of the memory - * @see ucx_allocator_realloc() - */ -void *ucx_mempool_realloc(UcxMempool *pool, void *ptr, size_t n); - -/** - * Frees pooled memory. - * - * Before freeing the memory, the specified destructor function (if any) - * is called. - * - * If you specify memory, that is not pooled by the specified memory pool, the - * program will terminate with a call to abort(). - * - * @param pool the memory pool - * @param ptr a pointer to the memory that shall be freed - * @see ucx_mempool_set_destr() - */ -void ucx_mempool_free(UcxMempool *pool, void *ptr); - -/** - * Destroys a memory pool. - * - * For each element the destructor function (if any) is called and the element - * is freed. - * - * Each of the registered destructor function that has no corresponding element - * within the pool (namely those registered by ucx_mempool_reg_destr) is - * called interleaving with the element destruction, but with guarantee to the - * order in which they were registered (FIFO order). - * - * - * @param pool the mempool to destroy - */ -void ucx_mempool_destroy(UcxMempool *pool); - -/** - * Sets a destructor function for the specified memory. - * - * The destructor is automatically called when the memory is freed or the - * pool is destroyed. - * A destructor for pooled memory MUST NOT free the memory itself, - * as this is done by the pool. Use a destructor to free any resources - * managed by the pooled object. - * - * The only requirement for the specified memory is, that it MUST be - * pooled memory by a UcxMempool or an element-compatible mempool. The pointer - * to the destructor function is saved in a reserved area before the actual - * memory. - * - * @param ptr pooled memory - * @param func a pointer to the destructor function - * @see ucx_mempool_free() - * @see ucx_mempool_destroy() - */ -void ucx_mempool_set_destr(void *ptr, ucx_destructor func); - -/** - * Registers a destructor function for the specified (non-pooled) memory. - * - * This is useful, if you have memory that has not been allocated by a mempool, - * but shall be managed by a mempool. - * - * This function creates an entry in the specified mempool and the memory will - * therefore (logically) convert to pooled memory. - * However, this does not cause the memory to be freed automatically!. - * If you want to use this function, make the memory pool free non-pooled - * memory, the specified destructor function must call free() - * by itself. But keep in mind, that you then MUST NOT use this destructor - * function with pooled memory (e.g. in ucx_mempool_set_destr()), as it - * would cause a double-free. - * - * @param pool the memory pool - * @param ptr data the destructor is registered for - * @param destr a pointer to the destructor function - */ -void ucx_mempool_reg_destr(UcxMempool *pool, void *ptr, ucx_destructor destr); - -#ifdef __cplusplus -} -#endif - -#endif /* UCX_MEMPOOL_H */ - diff -r a569148841ff -r efbd59642577 ucx/ucx/properties.h --- a/ucx/ucx/properties.h Sun Apr 16 14:12:24 2023 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,221 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 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. - */ -/** - * @file properties.h - * - * Load / store utilities for properties files. - * - * @author Mike Becker - * @author Olaf Wintermann - */ - -#ifndef UCX_PROPERTIES_H -#define UCX_PROPERTIES_H - -#include "ucx.h" -#include "map.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * UcxProperties object for parsing properties data. - * Most of the fields are for internal use only. You may configure the - * properties parser, e.g. by changing the used delimiter or specifying - * up to three different characters that shall introduce comments. - */ -typedef struct { - /** - * Input buffer (don't set manually). - * Automatically set by calls to ucx_properties_fill(). - */ - char *buffer; - - /** - * Length of the input buffer (don't set manually). - * Automatically set by calls to ucx_properties_fill(). - */ - size_t buflen; - - /** - * Current buffer position (don't set manually). - * Used by ucx_properties_next(). - */ - size_t pos; - - /** - * Internal temporary buffer (don't set manually). - * Used by ucx_properties_next(). - */ - char *tmp; - - /** - * Internal temporary buffer length (don't set manually). - * Used by ucx_properties_next(). - */ - size_t tmplen; - - /** - * Internal temporary buffer capacity (don't set manually). - * Used by ucx_properties_next(). - */ - size_t tmpcap; - - /** - * Parser error code. - * This is always 0 on success and a nonzero value on syntax errors. - * The value is set by ucx_properties_next(). - */ - int error; - - /** - * The delimiter that shall be used. - * This is '=' by default. - */ - char delimiter; - - /** - * The first comment character. - * This is '#' by default. - */ - char comment1; - - /** - * The second comment character. - * This is not set by default. - */ - char comment2; - - /** - * The third comment character. - * This is not set by default. - */ - char comment3; -} UcxProperties; - - -/** - * Constructs a new UcxProperties object. - * @return a pointer to the new UcxProperties object - */ -UcxProperties *ucx_properties_new(); - -/** - * Destroys a UcxProperties object. - * @param prop the UcxProperties object to destroy - */ -void ucx_properties_free(UcxProperties *prop); - -/** - * Sets the input buffer for the properties parser. - * - * After calling this function, you may parse the data by calling - * ucx_properties_next() until it returns 0. The function ucx_properties2map() - * is a convenience function that reads as much data as possible by using this - * function. - * - * - * @param prop the UcxProperties object - * @param buf a pointer to the new buffer - * @param len the payload length of the buffer - * @see ucx_properties_next() - * @see ucx_properties2map() - */ -void ucx_properties_fill(UcxProperties *prop, char *buf, size_t len); - -/** - * Retrieves the next key/value-pair. - * - * This function returns a nonzero value as long as there are key/value-pairs - * found. If no more key/value-pairs are found, you may refill the input buffer - * with ucx_properties_fill(). - * - * Attention: the sstr_t.ptr pointers of the output parameters point to - * memory within the input buffer of the parser and will get invalid some time. - * If you want long term copies of the key/value-pairs, use sstrdup() after - * calling this function. - * - * @param prop the UcxProperties object - * @param name a pointer to the sstr_t that shall contain the property name - * @param value a pointer to the sstr_t that shall contain the property value - * @return Nonzero, if a key/value-pair was successfully retrieved - * @see ucx_properties_fill() - */ -int ucx_properties_next(UcxProperties *prop, sstr_t *name, sstr_t *value); - -/** - * Retrieves all available key/value-pairs and puts them into a UcxMap. - * - * This is done by successive calls to ucx_properties_next() until no more - * key/value-pairs can be retrieved. - * - * The memory for the map values is allocated by the map's own allocator. - * - * @param prop the UcxProperties object - * @param map the target map - * @return The UcxProperties.error code (i.e. 0 on success). - * @see ucx_properties_fill() - * @see UcxMap.allocator - */ -int ucx_properties2map(UcxProperties *prop, UcxMap *map); - -/** - * Loads a properties file to a UcxMap. - * - * This is a convenience function that reads data from an input - * stream until the end of the stream is reached. - * - * @param map the map object to write the key/value-pairs to - * @param file the FILE* stream to read from - * @return 0 on success, or a non-zero value on error - * - * @see ucx_properties_fill() - * @see ucx_properties2map() - */ -int ucx_properties_load(UcxMap *map, FILE *file); - -/** - * Stores a UcxMap to a file. - * - * The key/value-pairs are written by using the following format: - * - * [key] = [value]\\n - * - * @param map the map to store - * @param file the FILE* stream to write to - * @return 0 on success, or a non-zero value on error - */ -int ucx_properties_store(UcxMap *map, FILE *file); - -#ifdef __cplusplus -} -#endif - -#endif /* UCX_PROPERTIES_H */ - diff -r a569148841ff -r efbd59642577 ucx/ucx/stack.h --- a/ucx/ucx/stack.h Sun Apr 16 14:12:24 2023 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,240 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 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. - */ - -/** - * @file stack.h - * - * Default stack memory allocation implementation. - * - * @author Mike Becker - * @author Olaf Wintermann - */ - -#ifndef UCX_STACK_H -#define UCX_STACK_H - -#include "ucx.h" -#include "allocator.h" - -#ifdef __cplusplus -extern "C" { -#endif - - -/** - * UCX stack structure. - */ -typedef struct { - /** UcxAllocator based on this stack */ - UcxAllocator allocator; - - /** Stack size. */ - size_t size; - - /** Pointer to the bottom of the stack */ - char *space; - - /** Pointer to the top of the stack */ - char *top; -} UcxStack; - -/** - * Metadata for each UCX stack element. - */ -struct ucx_stack_metadata { - /** - * Location of the previous element (NULL if this is the first) - */ - char *prev; - - /** Size of this element */ - size_t size; -}; - -/** - * Initializes UcxStack structure with memory. - * - * @param stack a pointer to an uninitialized stack structure - * @param space the memory area that shall be managed - * @param size size of the memory area - * @return a new UcxStack structure - */ -void ucx_stack_init(UcxStack *stack, char* space, size_t size); - -/** - * Allocates stack memory. - * - * @param stack a pointer to the stack - * @param n amount of memory to allocate - * @return a pointer to the allocated memory or NULL on stack - * overflow - * @see ucx_allocator_malloc() - */ -void *ucx_stack_malloc(UcxStack *stack, size_t n); - -/** - * Allocates memory with #ucx_stack_malloc() and copies the specified data if - * the allocation was successful. - * - * @param stack a pointer to the stack - * @param n amount of memory to allocate - * @param data a pointer to the data to copy - * @return a pointer to the allocated memory - * @see ucx_stack_malloc - */ -void *ucx_stack_push(UcxStack *stack, size_t n, const void *data); - -/** - * Allocates an array of stack memory - * - * The content of the allocated memory is set to zero. - * - * @param stack a pointer to the stack - * @param nelem amount of elements to allocate - * @param elsize amount of memory per element - * @return a pointer to the allocated memory - * @see ucx_allocator_calloc() - */ -void *ucx_stack_calloc(UcxStack *stack, size_t nelem, size_t elsize); - -/** - * Allocates memory with #ucx_stack_calloc() and copies the specified data if - * the allocation was successful. - * - * @param stack a pointer to the stack - * @param nelem amount of elements to allocate - * @param elsize amount of memory per element - * @param data a pointer to the data - * @return a pointer to the allocated memory - * @see ucx_stack_calloc - */ -void *ucx_stack_pusharr(UcxStack *stack, - size_t nelem, size_t elsize, const void *data); - -/** - * Reallocates memory on the stack. - * - * Shrinking memory is always safe. Extending memory can be very expensive. - * - * @param stack the stack - * @param ptr a pointer to the memory that shall be reallocated - * @param n the new size of the memory - * @return a pointer to the new location of the memory - * @see ucx_allocator_realloc() - */ -void *ucx_stack_realloc(UcxStack *stack, void *ptr, size_t n); - -/** - * Frees memory on the stack. - * - * Freeing stack memory behaves in a special way. - * - * If the element, that should be freed, is the top most element of the stack, - * it is removed from the stack. Otherwise it is marked as freed. Marked - * elements are removed, when they become the top most elements of the stack. - * - * @param stack a pointer to the stack - * @param ptr a pointer to the memory that shall be freed - */ -void ucx_stack_free(UcxStack *stack, void *ptr); - - -/** - * Returns the size of the top most element. - * @param stack a pointer to the stack - * @return the size of the top most element - */ -#define ucx_stack_topsize(stack) ((stack)->top ? ((struct ucx_stack_metadata*)\ - (stack)->top - 1)->size : 0) - -/** - * Removes the top most element from the stack and copies the content to - * dest, if specified. - * - * Use #ucx_stack_topsize()# to get the amount of memory that must be available - * at the location of dest. - * - * @param stack a pointer to the stack - * @param dest the location where the contents shall be written to, or - * NULL, if the element shall only be removed. - * @see ucx_stack_free - * @see ucx_stack_popn - */ -#define ucx_stack_pop(stack, dest) ucx_stack_popn(stack, dest, (size_t)-1) - -/** - * Removes the top most element from the stack and copies the content to - * dest. - * - * This function copies at most n bytes to the destination, but - * the element is always freed as a whole. - * If the element was larger than n, the remaining data is lost. - * - * @param stack a pointer to the stack - * @param dest the location where the contents shall be written to - * @param n copies at most n bytes to dest - * @see ucx_stack_pop - */ -void ucx_stack_popn(UcxStack *stack, void *dest, size_t n); - -/** - * Returns the remaining available memory on the specified stack. - * - * @param stack a pointer to the stack - * @return the remaining available memory - */ -size_t ucx_stack_avail(UcxStack *stack); - -/** - * Checks, if the stack is empty. - * - * @param stack a pointer to the stack - * @return nonzero, if the stack is empty, zero otherwise - */ -#define ucx_stack_empty(stack) (!(stack)->top) - -/** - * Computes a recommended size for the stack memory area. Note, that - * reallocations have not been taken into account, so you might need to reserve - * twice as much memory to allow many reallocations. - * - * @param size the approximate payload - * @param elems the approximate count of element allocations - * @return a recommended size for the stack space based on the information - * provided - */ -#define ucx_stack_dim(size, elems) (size+sizeof(struct ucx_stack_metadata) * \ - (elems + 1)) - - -#ifdef __cplusplus -} -#endif - -#endif /* UCX_STACK_H */ - diff -r a569148841ff -r efbd59642577 ucx/ucx/string.h --- a/ucx/ucx/string.h Sun Apr 16 14:12:24 2023 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,934 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 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. - */ -/** - * Bounded string implementation. - * - * The UCX strings (sstr_t) provide an alternative to C strings. - * The main difference to C strings is, that sstr_t does not - * need to be NULL-terminated. Instead the length is stored - * within the structure. - * - * When using sstr_t, developers must be full aware of what type - * of string (NULL-terminated) or not) they are using, when - * accessing the char* ptr directly. - * - * The UCX string module provides some common string functions, known from - * standard libc, working with sstr_t. - * - * @file string.h - * @author Mike Becker - * @author Olaf Wintermann - */ - -#ifndef UCX_STRING_H -#define UCX_STRING_H - -#include "ucx.h" -#include "allocator.h" -#include - -/* - * Use this macro to disable the shortcuts if you experience macro collision. - */ -#ifndef UCX_NO_SSTR_SHORTCUTS -/** - * Shortcut for a sstr_t struct - * or scstr_t struct literal. - */ -#define ST(s) { s, sizeof(s)-1 } - -/** Shortcut for the conversion of a C string to a sstr_t. */ -#define S(s) sstrn(s, sizeof(s)-1) - -/** Shortcut for the conversion of a C string to a scstr_t. */ -#define SC(s) scstrn(s, sizeof(s)-1) -#endif /* UCX_NO_SSTR_SHORTCUTS */ - -/* - * Use this macro to disable the format macros. - */ -#ifndef UCX_NO_SSTR_FORMAT_MACROS -/** Expands a sstr_t or scstr_t to printf arguments. */ -#define SFMT(s) (int) (s).length, (s).ptr - -/** Format specifier for a sstr_t or scstr_t. */ -#define PRIsstr ".*s" -#endif /* UCX_NO_SSTR_FORMAT_MACROS */ - -#ifdef __cplusplus -extern "C" { -#endif -/** - * The UCX string structure. - */ -typedef struct { - /** A pointer to the string - * (not necessarily NULL-terminated) */ - char *ptr; - /** The length of the string */ - size_t length; -} sstr_t; - -/** - * The UCX string structure for immutable (constant) strings. - */ -typedef struct { - /** A constant pointer to the immutable string - * (not necessarily NULL-terminated) */ - const char *ptr; - /** The length of the string */ - size_t length; -} scstr_t; - -#ifdef __cplusplus -} -#endif - - -#ifdef __cplusplus -/** - * One of two type adjustment functions that return a scstr_t. - * - * Used internally to convert a UCX string to an immutable UCX string. - * - * Do not use this function manually. - * - * @param str some sstr_t - * @return an immutable (scstr_t) version of the provided string. - */ -inline scstr_t s2scstr(sstr_t s) { - scstr_t c; - c.ptr = s.ptr; - c.length = s.length; - return c; -} - -/** - * One of two type adjustment functions that return a scstr_t. - * - * Used internally to convert a UCX string to an immutable UCX string. - * This variant is used, when the string is already immutable and no operation - * needs to be performed. - * - * Do not use this function manually. - * - * @param str some scstr_t - * @return the argument itself - */ -inline scstr_t s2scstr(scstr_t str) { - return str; -} - -/** - * Converts a UCX string to an immutable UCX string (scstr_t). - * @param str some UCX string - * @return the an immutable version of the provided string - */ -#define SCSTR(s) s2scstr(s) -#else - -/** - * One of two type adjustment functions that return a scstr_t. - * - * Used internally to convert a UCX string to an immutable UCX string. - * This variant is used, when the string is already immutable and no operation - * needs to be performed. - * - * Do not use this function manually. - * - * @param str some scstr_t - * @return the argument itself - */ -scstr_t ucx_sc2sc(scstr_t str); - -/** - * One of two type adjustment functions that return a scstr_t. - * - * Used internally to convert a UCX string to an immutable UCX string. - * - * Do not use this function manually. - * - * @param str some sstr_t - * @return an immutable (scstr_t) version of the provided string. - */ -scstr_t ucx_ss2sc(sstr_t str); - -#if __STDC_VERSION__ >= 201112L -/** - * Converts a UCX string to an immutable UCX string (scstr_t). - * @param str some UCX string - * @return the an immutable version of the provided string - */ -#define SCSTR(str) _Generic(str, sstr_t: ucx_ss2sc, scstr_t: ucx_sc2sc)(str) - -#elif defined(__GNUC__) || defined(__clang__) - -/** - * Converts a UCX string to an immutable UCX string (scstr_t). - * @param str some UCX string - * @return the an immutable version of the provided string - */ -#define SCSTR(str) __builtin_choose_expr( \ - __builtin_types_compatible_p(typeof(str), sstr_t), \ - ucx_ss2sc, \ - ucx_sc2sc)(str) - -#elif defined(__sun) - -/** - * Converts a UCX string to an immutable UCX string (scstr_t). - * @param str some UCX string - * @return the an immutable version of the provided string - */ -#define SCSTR(str) ({typeof(str) ucx_tmp_var_str = str; \ - scstr_t ucx_tmp_var_c; \ - ucx_tmp_var_c.ptr = ucx_tmp_var_str.ptr;\ - ucx_tmp_var_c.length = ucx_tmp_var_str.length;\ - ucx_tmp_var_c; }) -#else /* no generics and no builtins */ - -/** - * Converts a UCX string to an immutable UCX string (scstr_t). - * - * This internal function (ab)uses the C standard an expects one single - * argument which is then implicitly converted to scstr_t without a warning. - * - * Do not use this function manually. - * - * @return the an immutable version of the provided string - */ -scstr_t ucx_ss2c_s(); - -/** - * Converts a UCX string to an immutable UCX string (scstr_t). - * @param str some UCX string - * @return the an immutable version of the provided string - */ -#define SCSTR(str) ucx_ss2c_s(str) -#endif /* C11 feature test */ - -#endif /* C++ */ - -#ifdef __cplusplus -extern "C" { -#endif - - -/** - * Creates a new sstr_t based on a C string. - * - * The length is implicitly inferred by using a call to strlen(). - * - * Note: the sstr_t will hold a reference to the C string. If you - * do want a copy, use sstrdup() on the return value of this function. - * - * If you need to wrap a constant string, use scstr(). - * - * @param cstring the C string to wrap - * @return a new sstr_t containing the C string - * - * @see sstrn() - */ -sstr_t sstr(char *cstring); - -/** - * Creates a new sstr_t of the specified length based on a C string. - * - * Note: the sstr_t will hold a reference to the C string. If you - * do want a copy, use sstrdup() on the return value of this function. - * - * If you need to wrap a constant string, use scstrn(). - * - * @param cstring the C string to wrap - * @param length the length of the string - * @return a new sstr_t containing the C string - * - * @see sstr() - * @see S() - */ -sstr_t sstrn(char *cstring, size_t length); - -/** - * Creates a new scstr_t based on a constant C string. - * - * The length is implicitly inferred by using a call to strlen(). - * - * Note: the scstr_t will hold a reference to the C string. If you - * do want a copy, use scstrdup() on the return value of this function. - * - * @param cstring the C string to wrap - * @return a new scstr_t containing the C string - * - * @see scstrn() - */ -scstr_t scstr(const char *cstring); - - -/** - * Creates a new scstr_t of the specified length based on a constant C string. - * - * Note: the scstr_t will hold a reference to the C string. If you - * do want a copy, use scstrdup() on the return value of this function. - * - * - * @param cstring the C string to wrap - * @param length the length of the string - * @return a new scstr_t containing the C string - * - * @see scstr() - */ -scstr_t scstrn(const char *cstring, size_t length); - -/** - * Returns the cumulated length of all specified strings. - * - * Attention: if the count argument does not match the count of the - * specified strings, the behavior is undefined. - * - * @param count the total number of specified strings (so at least 1) - * @param ... all strings - * @return the cumulated length of all strings - */ -size_t scstrnlen(size_t count, ...); - -/** - * Alias for scstrnlen() which automatically converts the arguments. - * - * @param count the total number of specified strings (so at least 1) - * @param ... all strings - * @return the cumulated length of all strings - */ -#define sstrnlen(count, ...) scstrnlen(count, __VA_ARGS__) - -/** - * Concatenates two or more strings. - * - * The resulting string will be allocated by standard malloc(). - * So developers MUST pass the sstr_t.ptr to free(). - * - * The sstr_t.ptr of the return value will always be NULL- - * terminated. - * - * @param count the total number of strings to concatenate - * @param s1 first string - * @param ... all remaining strings - * @return the concatenated string - */ -sstr_t scstrcat(size_t count, scstr_t s1, ...); - -/** - * Alias for scstrcat() which automatically converts the arguments. - * - * @param count the total number of strings to concatenate - * @param s1 first string - * @param ... all remaining strings - * @return the concatenated string - */ -#define sstrcat(count, s1, ...) scstrcat(count, SCSTR(s1), __VA_ARGS__) - -/** - * Concatenates two or more strings using a UcxAllocator. - * - * See scstrcat() for details. - * - * @param a the allocator to use - * @param count the total number of strings to concatenate - * @param s1 first string - * @param ... all remaining strings - * @return the concatenated string - */ -sstr_t scstrcat_a(UcxAllocator *a, size_t count, scstr_t s1, ...); - -/** - * Alias for scstrcat_a() which automatically converts the arguments. - * - * See sstrcat() for details. - * - * @param a the allocator to use - * @param count the total number of strings to concatenate - * @param s1 first string - * @param ... all remaining strings - * @return the concatenated string - */ -#define sstrcat_a(a, count, s1, ...) \ - scstrcat_a(a, count, SCSTR(s1), __VA_ARGS__) - -/** - * Returns a substring starting at the specified location. - * - * Attention: the new string references the same memory area as the - * input string and will NOT be NULL-terminated. - * Use sstrdup() to get a copy. - * - * @param string input string - * @param start start location of the substring - * @return a substring of string starting at start - * - * @see sstrsubsl() - * @see sstrchr() - */ -sstr_t sstrsubs(sstr_t string, size_t start); - -/** - * Returns a substring with a maximum length starting at the specified location. - * - * Attention: the new string references the same memory area as the - * input string and will NOT be NULL-terminated. - * Use sstrdup() to get a copy. - * - * @param string input string - * @param start start location of the substring - * @param length the maximum length of the substring - * @return a substring of string starting at start - * with a maximum length of length - * - * @see sstrsubs() - * @see sstrchr() - */ -sstr_t sstrsubsl(sstr_t string, size_t start, size_t length); - -/** - * Returns a substring of an immutable string starting at the specified - * location. - * - * Attention: the new string references the same memory area as the - * input string and will NOT be NULL-terminated. - * Use scstrdup() to get a copy. - * - * @param string input string - * @param start start location of the substring - * @return a substring of string starting at start - * - * @see scstrsubsl() - * @see scstrchr() - */ -scstr_t scstrsubs(scstr_t string, size_t start); - -/** - * Returns a substring of an immutable string with a maximum length starting - * at the specified location. - * - * Attention: the new string references the same memory area as the - * input string and will NOT be NULL-terminated. - * Use scstrdup() to get a copy. - * - * @param string input string - * @param start start location of the substring - * @param length the maximum length of the substring - * @return a substring of string starting at start - * with a maximum length of length - * - * @see scstrsubs() - * @see scstrchr() - */ -scstr_t scstrsubsl(scstr_t string, size_t start, size_t length); - -/** - * Returns a substring starting at the location of the first occurrence of the - * specified character. - * - * If the string does not contain the character, an empty string is returned. - * - * @param string the string where to locate the character - * @param chr the character to locate - * @return a substring starting at the first location of chr - * - * @see sstrsubs() - */ -sstr_t sstrchr(sstr_t string, int chr); - -/** - * Returns a substring starting at the location of the last occurrence of the - * specified character. - * - * If the string does not contain the character, an empty string is returned. - * - * @param string the string where to locate the character - * @param chr the character to locate - * @return a substring starting at the last location of chr - * - * @see sstrsubs() - */ -sstr_t sstrrchr(sstr_t string, int chr); - -/** - * Returns an immutable substring starting at the location of the first - * occurrence of the specified character. - * - * If the string does not contain the character, an empty string is returned. - * - * @param string the string where to locate the character - * @param chr the character to locate - * @return a substring starting at the first location of chr - * - * @see scstrsubs() - */ -scstr_t scstrchr(scstr_t string, int chr); - -/** - * Returns an immutable substring starting at the location of the last - * occurrence of the specified character. - * - * If the string does not contain the character, an empty string is returned. - * - * @param string the string where to locate the character - * @param chr the character to locate - * @return a substring starting at the last location of chr - * - * @see scstrsubs() - */ -scstr_t scstrrchr(scstr_t string, int chr); - -/** - * Returns a substring starting at the location of the first occurrence of the - * specified string. - * - * If the string does not contain the other string, an empty string is returned. - * - * If match is an empty string, the complete string is - * returned. - * - * @param string the string to be scanned - * @param match string containing the sequence of characters to match - * @return a substring starting at the first occurrence of - * match, or an empty string, if the sequence is not - * present in string - */ -sstr_t scstrsstr(sstr_t string, scstr_t match); - -/** - * Alias for scstrsstr() which automatically converts the match string. - * - * @param string the string to be scanned - * @param match string containing the sequence of characters to match - * @return a substring starting at the first occurrence of - * match, or an empty string, if the sequence is not - * present in string - */ -#define sstrstr(string, match) scstrsstr(string, SCSTR(match)) - -/** - * Returns an immutable substring starting at the location of the - * first occurrence of the specified immutable string. - * - * If the string does not contain the other string, an empty string is returned. - * - * If match is an empty string, the complete string is - * returned. - * - * @param string the string to be scanned - * @param match string containing the sequence of characters to match - * @return a substring starting at the first occurrence of - * match, or an empty string, if the sequence is not - * present in string - */ -scstr_t scstrscstr(scstr_t string, scstr_t match); - -/** - * Alias for scstrscstr() which automatically converts the match string. - * - * @param string the string to be scanned - * @param match string containing the sequence of characters to match - * @return a substring starting at the first occurrence of - * match, or an empty string, if the sequence is not - * present in string - */ -#define sstrscstr(string, match) scstrscstr(string, SCSTR(match)) - -/** - * Splits a string into parts by using a delimiter string. - * - * This function will return NULL, if one of the following happens: - *
        - *
      • the string length is zero
      • - *
      • the delimeter length is zero
      • - *
      • the string equals the delimeter
      • - *
      • memory allocation fails
      • - *
      - * - * The integer referenced by count is used as input and determines - * the maximum size of the resulting array, i.e. the maximum count of splits to - * perform + 1. - * - * The integer referenced by count is also used as output and is - * set to - *
        - *
      • -2, on memory allocation errors
      • - *
      • -1, if either the string or the delimiter is an empty string
      • - *
      • 0, if the string equals the delimiter
      • - *
      • 1, if the string does not contain the delimiter
      • - *
      • the count of array items, otherwise
      • - *
      - * - * If the string starts with the delimiter, the first item of the resulting - * array will be an empty string. - * - * If the string ends with the delimiter and the maximum list size is not - * exceeded, the last array item will be an empty string. - * In case the list size would be exceeded, the last array item will be the - * remaining string after the last split, including the terminating - * delimiter. - * - * Attention: The array pointer AND all sstr_t.ptr of the array - * items must be manually passed to free(). Use sstrsplit_a() with - * an allocator to managed memory, to avoid this. - * - * @param string the string to split - * @param delim the delimiter string - * @param count IN: the maximum size of the resulting array (0 = no limit), - * OUT: the actual size of the array - * @return a sstr_t array containing the split strings or - * NULL on error - * - * @see scstrsplit_a() - */ -sstr_t* scstrsplit(scstr_t string, scstr_t delim, ssize_t *count); - -/** - * Alias for scstrsplit() which automatically converts the arguments. - * - * @param string the string to split - * @param delim the delimiter string - * @param count IN: the maximum size of the resulting array (0 = no limit), - * OUT: the actual size of the array - * @return a sstr_t array containing the split strings or - * NULL on error - * - * @see sstrsplit_a() - */ -#define sstrsplit(string, delim, count) \ - scstrsplit(SCSTR(string), SCSTR(delim), count) - -/** - * Performing scstrsplit() using a UcxAllocator. - * - * Read the description of scstrsplit() for details. - * - * The memory for the sstr_t.ptr pointers of the array items and the memory for - * the sstr_t array itself are allocated by using the UcxAllocator.malloc() - * function. - * - * Note: the allocator is not used for memory that is freed within the - * same call of this function (locally scoped variables). - * - * @param allocator the UcxAllocator used for allocating memory - * @param string the string to split - * @param delim the delimiter string - * @param count IN: the maximum size of the resulting array (0 = no limit), - * OUT: the actual size of the array - * @return a sstr_t array containing the split strings or - * NULL on error - * - * @see scstrsplit() - */ -sstr_t* scstrsplit_a(UcxAllocator *allocator, scstr_t string, scstr_t delim, - ssize_t *count); - -/** - * Alias for scstrsplit_a() which automatically converts the arguments. - * - * @param allocator the UcxAllocator used for allocating memory - * @param string the string to split - * @param delim the delimiter string - * @param count IN: the maximum size of the resulting array (0 = no limit), - * OUT: the actual size of the array - * @return a sstr_t array containing the split strings or - * NULL on error - * - * @see sstrsplit() - */ -#define sstrsplit_a(allocator, string, delim, count) \ - scstrsplit_a(allocator, SCSTR(string), SCSTR(delim), count) - -/** - * Compares two UCX strings with standard memcmp(). - * - * At first it compares the scstr_t.length attribute of the two strings. The - * memcmp() function is called, if and only if the lengths match. - * - * @param s1 the first string - * @param s2 the second string - * @return -1, if the length of s1 is less than the length of s2 or 1, if the - * length of s1 is greater than the length of s2 or the result of - * memcmp() otherwise (i.e. 0 if the strings match) - */ -int scstrcmp(scstr_t s1, scstr_t s2); - -/** - * Alias for scstrcmp() which automatically converts its arguments. - * - * @param s1 the first string - * @param s2 the second string - * @return -1, if the length of s1 is less than the length of s2 or 1, if the - * length of s1 is greater than the length of s2 or the result of - * memcmp() otherwise (i.e. 0 if the strings match) - */ -#define sstrcmp(s1, s2) scstrcmp(SCSTR(s1), SCSTR(s2)) - -/** - * Compares two UCX strings ignoring the case. - * - * At first it compares the scstr_t.length attribute of the two strings. If and - * only if the lengths match, both strings are compared char by char ignoring - * the case. - * - * @param s1 the first string - * @param s2 the second string - * @return -1, if the length of s1 is less than the length of s2 or 1, if the - * length of s1 is greater than the length of s2 or the result of the platform - * specific string comparison function ignoring the case. - */ -int scstrcasecmp(scstr_t s1, scstr_t s2); - -/** - * Alias for scstrcasecmp() which automatically converts the arguments. - * - * @param s1 the first string - * @param s2 the second string - * @return -1, if the length of s1 is less than the length of s2 or 1, if the - * length of s1 is greater than the length of s2 or the result of the platform - * specific string comparison function ignoring the case. - */ -#define sstrcasecmp(s1, s2) scstrcasecmp(SCSTR(s1), SCSTR(s2)) - -/** - * Creates a duplicate of the specified string. - * - * The new sstr_t will contain a copy allocated by standard - * malloc(). So developers MUST pass the sstr_t.ptr to - * free(). - * - * The sstr_t.ptr of the return value will always be NULL- - * terminated and mutable, regardless of the argument. - * - * @param string the string to duplicate - * @return a duplicate of the string - * @see scstrdup_a() - */ -sstr_t scstrdup(scstr_t string); - -/** - * Alias for scstrdup() which automatically converts the argument. - * - * @param string the string to duplicate - * @return a duplicate of the string - * @see sstrdup_a() - */ -#define sstrdup(string) scstrdup(SCSTR(string)) - -/** - * Creates a duplicate of the specified string using a UcxAllocator. - * - * The new sstr_t will contain a copy allocated by the allocators - * UcxAllocator.malloc() function. So it is implementation depended, whether the - * returned sstr_t.ptr pointer must be passed to the allocators - * UcxAllocator.free() function manually. - * - * The sstr_t.ptr of the return value will always be NULL- - * terminated and mutable, regardless of the argument. - * - * @param allocator a valid instance of a UcxAllocator - * @param string the string to duplicate - * @return a duplicate of the string - * @see scstrdup() - */ -sstr_t scstrdup_a(UcxAllocator *allocator, scstr_t string); - -/** - * Alias for scstrdup_a() which automatically converts the argument. - * - * @param allocator a valid instance of a UcxAllocator - * @param string the string to duplicate - * @return a duplicate of the string - * @see scstrdup() - */ -#define sstrdup_a(allocator, string) scstrdup_a(allocator, SCSTR(string)) - - -/** - * Omits leading and trailing spaces. - * - * This function returns a new sstr_t containing a trimmed version of the - * specified string. - * - * Note: the new sstr_t references the same memory, thus you - * MUST NOT pass the sstr_t.ptr of the return value to - * free(). It is also highly recommended to avoid assignments like - * mystr = sstrtrim(mystr); as you lose the reference to the - * source string. Assignments of this type are only permitted, if the - * sstr_t.ptr of the source string does not need to be freed or if another - * reference to the source string exists. - * - * @param string the string that shall be trimmed - * @return a new sstr_t containing the trimmed string - */ -sstr_t sstrtrim(sstr_t string); - -/** - * Omits leading and trailing spaces. - * - * This function returns a new scstr_t containing a trimmed version of the - * specified string. - * - * Note: the new scstr_t references the same memory, thus you - * MUST NOT pass the scstr_t.ptr of the return value to - * free(). It is also highly recommended to avoid assignments like - * mystr = scstrtrim(mystr); as you lose the reference to the - * source string. Assignments of this type are only permitted, if the - * scstr_t.ptr of the source string does not need to be freed or if another - * reference to the source string exists. - * - * @param string the string that shall be trimmed - * @return a new scstr_t containing the trimmed string - */ -scstr_t scstrtrim(scstr_t string); - -/** - * Checks, if a string has a specific prefix. - * @param string the string to check - * @param prefix the prefix the string should have - * @return 1, if and only if the string has the specified prefix, 0 otherwise - */ -int scstrprefix(scstr_t string, scstr_t prefix); - -/** - * Alias for scstrprefix() which automatically converts the arguments. - * - * @param string the string to check - * @param prefix the prefix the string should have - * @return 1, if and only if the string has the specified prefix, 0 otherwise - */ -#define sstrprefix(string, prefix) scstrprefix(SCSTR(string), SCSTR(prefix)) - -/** - * Checks, if a string has a specific suffix. - * @param string the string to check - * @param suffix the suffix the string should have - * @return 1, if and only if the string has the specified suffix, 0 otherwise - */ -int scstrsuffix(scstr_t string, scstr_t suffix); - -/** - * Alias for scstrsuffix() which automatically converts the arguments. - * - * @param string the string to check - * @param suffix the suffix the string should have - * @return 1, if and only if the string has the specified suffix, 0 otherwise - */ -#define sstrsuffix(string, suffix) scstrsuffix(SCSTR(string), SCSTR(suffix)) - -/** - * Returns a lower case version of a string. - * - * This function creates a duplicate of the input string, first. See the - * documentation of scstrdup() for the implications. - * - * @param string the input string - * @return the resulting lower case string - * @see scstrdup() - */ -sstr_t scstrlower(scstr_t string); - -/** - * Alias for scstrlower() which automatically converts the argument. - * - * @param string the input string - * @return the resulting lower case string - */ -#define sstrlower(string) scstrlower(SCSTR(string)) - -/** - * Returns a lower case version of a string. - * - * This function creates a duplicate of the input string, first. See the - * documentation of scstrdup_a() for the implications. - * - * @param allocator the allocator used for duplicating the string - * @param string the input string - * @return the resulting lower case string - * @see scstrdup_a() - */ -sstr_t scstrlower_a(UcxAllocator *allocator, scstr_t string); - - -/** - * Alias for scstrlower_a() which automatically converts the argument. - * - * @param allocator the allocator used for duplicating the string - * @param string the input string - * @return the resulting lower case string - */ -#define sstrlower_a(allocator, string) scstrlower_a(allocator, SCSTR(string)) - -/** - * Returns a upper case version of a string. - * - * This function creates a duplicate of the input string, first. See the - * documentation of scstrdup() for the implications. - * - * @param string the input string - * @return the resulting upper case string - * @see scstrdup() - */ -sstr_t scstrupper(scstr_t string); - -/** - * Alias for scstrupper() which automatically converts the argument. - * - * @param string the input string - * @return the resulting upper case string - */ -#define sstrupper(string) scstrupper(SCSTR(string)) - -/** - * Returns a upper case version of a string. - * - * This function creates a duplicate of the input string, first. See the - * documentation of scstrdup_a() for the implications. - * - * @param allocator the allocator used for duplicating the string - * @param string the input string - * @return the resulting upper case string - * @see scstrdup_a() - */ -sstr_t scstrupper_a(UcxAllocator *allocator, scstr_t string); - -/** - * Alias for scstrupper_a() which automatically converts the argument. - * - * @param allocator the allocator used for duplicating the string - * @param string the input string - * @return the resulting upper case string - */ -#define sstrupper_a(allocator, string) scstrupper_a(allocator, string) - -#ifdef __cplusplus -} -#endif - -#endif /* UCX_STRING_H */ diff -r a569148841ff -r efbd59642577 ucx/ucx/test.h --- a/ucx/ucx/test.h Sun Apr 16 14:12:24 2023 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,241 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 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. - */ - -/** - * @file: test.h - * - * UCX Test Framework. - * - * Usage of this test framework: - * - * **** IN HEADER FILE: **** - * - *
      - * UCX_TEST(function_name);
      - * UCX_TEST_SUBROUTINE(subroutine_name, paramlist); // optional
      - * 
      - * - * **** IN SOURCE FILE: **** - *
      - * UCX_TEST_SUBROUTINE(subroutine_name, paramlist) {
      - *   // tests with UCX_TEST_ASSERT()
      - * }
      - * 
      - * UCX_TEST(function_name) {
      - *   // memory allocation and other stuff here
      - *   #UCX_TEST_BEGIN
      - *   // tests with UCX_TEST_ASSERT() and/or
      - *   // calls with UCX_TEST_CALL_SUBROUTINE() here
      - *   #UCX_TEST_END
      - *   // cleanup of memory here
      - * }
      - * 
      - * - * Note: if a test fails, a longjump is performed - * back to the #UCX_TEST_BEGIN macro! - * - * Attention: Do not call own functions within a test, that use - * UCX_TEST_ASSERT() macros and are not defined by using UCX_TEST_SUBROUTINE(). - * - * - * @author Mike Becker - * @author Olaf Wintermann - * - */ - -#ifndef UCX_TEST_H -#define UCX_TEST_H - -#include "ucx.h" -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef __FUNCTION__ - -/** - * Alias for the __func__ preprocessor macro. - * Some compilers use __func__ and others use __FUNCTION__. - * We use __FUNCTION__ so we define it for those compilers which use - * __func__. - */ -#define __FUNCTION__ __func__ -#endif - -/** Type for the UcxTestSuite. */ -typedef struct UcxTestSuite UcxTestSuite; - -/** Pointer to a test function. */ -typedef void(*UcxTest)(UcxTestSuite*,FILE*); - -/** Type for the internal list of test cases. */ -typedef struct UcxTestList UcxTestList; - -/** Structure for the internal list of test cases. */ -struct UcxTestList { - - /** Test case. */ - UcxTest test; - - /** Pointer to the next list element. */ - UcxTestList *next; -}; - -/** - * A test suite containing multiple test cases. - */ -struct UcxTestSuite { - - /** The number of successful tests after the suite has been run. */ - unsigned int success; - - /** The number of failed tests after the suite has been run. */ - unsigned int failure; - - /** - * Internal list of test cases. - * Use ucx_test_register() to add tests to this list. - */ - UcxTestList *tests; -}; - -/** - * Creates a new test suite. - * @return a new test suite - */ -UcxTestSuite* ucx_test_suite_new(); - -/** - * Destroys a test suite. - * @param suite the test suite to destroy - */ -void ucx_test_suite_free(UcxTestSuite* suite); - -/** - * Registers a test function with the specified test suite. - * - * @param suite the suite, the test function shall be added to - * @param test the test function to register - * @return EXIT_SUCCESS on success or - * EXIT_FAILURE on failure - */ -int ucx_test_register(UcxTestSuite* suite, UcxTest test); - -/** - * Runs a test suite and writes the test log to the specified stream. - * @param suite the test suite to run - * @param outstream the stream the log shall be written to - */ -void ucx_test_run(UcxTestSuite* suite, FILE* outstream); - -/** - * Macro for a #UcxTest function header. - * - * Use this macro to declare and/or define a #UcxTest function. - * - * @param name the name of the test function - */ -#define UCX_TEST(name) void name(UcxTestSuite* _suite_,FILE *_output_) - -/** - * Marks the begin of a test. - * Note: Any UCX_TEST_ASSERT() calls must be performed after - * #UCX_TEST_BEGIN. - * - * @see #UCX_TEST_END - */ -#define UCX_TEST_BEGIN fwrite("Running ", 1, 8, _output_);\ - fwrite(__FUNCTION__, 1, strlen(__FUNCTION__), _output_);\ - fwrite("... ", 1, 4, _output_);\ - jmp_buf _env_; \ - if (!setjmp(_env_)) { - -/** - * Checks a test assertion. - * If the assertion is correct, the test carries on. If the assertion is not - * correct, the specified message (terminated by a dot and a line break) is - * written to the test suites output stream. - * @param condition the condition to check - * @param message the message that shall be printed out on failure - */ -#define UCX_TEST_ASSERT(condition,message) if (!(condition)) { \ - fwrite(message".\n", 1, 2+strlen(message), _output_); \ - _suite_->failure++; \ - longjmp(_env_, 1);\ - } - -/** - * Macro for a test subroutine function header. - * - * Use this to declare and/or define a subroutine that can be called by using - * UCX_TEST_CALL_SUBROUTINE(). - * - * @param name the name of the subroutine - * @param ... the parameter list - * - * @see UCX_TEST_CALL_SUBROUTINE() - */ -#define UCX_TEST_SUBROUTINE(name,...) void name(UcxTestSuite* _suite_,\ - FILE *_output_, jmp_buf _env_, __VA_ARGS__) - -/** - * Macro for calling a test subroutine. - * - * Subroutines declared with UCX_TEST_SUBROUTINE() can be called by using this - * macro. - * - * Note: You may only call subroutines within a #UCX_TEST_BEGIN- - * #UCX_TEST_END-block. - * - * @param name the name of the subroutine - * @param ... the argument list - * - * @see UCX_TEST_SUBROUTINE() - */ -#define UCX_TEST_CALL_SUBROUTINE(name,...) \ - name(_suite_,_output_,_env_,__VA_ARGS__); - -/** - * Marks the end of a test. - * Note: Any UCX_TEST_ASSERT() calls must be performed before - * #UCX_TEST_END. - * - * @see #UCX_TEST_BEGIN - */ -#define UCX_TEST_END fwrite("success.\n", 1, 9, _output_); _suite_->success++;} - -#ifdef __cplusplus -} -#endif - -#endif /* UCX_TEST_H */ - diff -r a569148841ff -r efbd59642577 ucx/ucx/ucx.h --- a/ucx/ucx/ucx.h Sun Apr 16 14:12:24 2023 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,195 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 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. - */ -/** - * Main UCX Header providing most common definitions. - * - * @file ucx.h - * @author Mike Becker - * @author Olaf Wintermann - */ - -#ifndef UCX_H -#define UCX_H - -/** Major UCX version as integer constant. */ -#define UCX_VERSION_MAJOR 2 - -/** Minor UCX version as integer constant. */ -#define UCX_VERSION_MINOR 0 - -/** Version constant which ensures to increase monotonically. */ -#define UCX_VERSION (((UCX_VERSION_MAJOR)<<16)|UCX_VERSION_MINOR) - -#include -#include - -#ifdef _WIN32 -#if !(defined __ssize_t_defined || defined _SSIZE_T_) -#include -typedef SSIZE_T ssize_t; -#define __ssize_t_defined -#define _SSIZE_T_ -#endif /* __ssize_t_defined and _SSIZE_T */ -#else /* !_WIN32 */ -#include -#endif /* _WIN32 */ - -#ifdef __cplusplus -extern "C" { -#endif - - -/** - * A function pointer to a destructor function. - * @see ucx_mempool_setdestr() - * @see ucx_mempool_regdestr() - */ -typedef void(*ucx_destructor)(void*); - -/** - * Function pointer to a compare function. - * - * The compare function shall take three arguments: the two values that shall be - * compared and optional additional data. - * The function shall then return -1 if the first argument is less than the - * second argument, 1 if the first argument is greater than the second argument - * and 0 if both arguments are equal. If the third argument is - * NULL, it shall be ignored. - */ -typedef int(*cmp_func)(const void*,const void*,void*); - -/** - * Function pointer to a distance function. - * - * The distance function shall take three arguments: the two values for which - * the distance shall be computed and optional additional data. - * The function shall then return the signed distance as integer value. - */ -typedef intmax_t(*distance_func)(const void*,const void*,void*); - -/** - * Function pointer to a copy function. - * - * The copy function shall create a copy of the first argument and may use - * additional data provided by the second argument. If the second argument is - * NULL, it shall be ignored. - - * Attention: if pointers returned by functions of this type may be - * passed to free() depends on the implementation of the - * respective copy_func. - */ -typedef void*(*copy_func)(const void*,void*); - -/** - * Function pointer to a write function. - * - * The signature of the write function shall be compatible to the signature - * of standard fwrite, though it may use arbitrary data types for - * source and destination. - * - * The arguments shall contain (in ascending order): a pointer to the source, - * the length of one element, the element count and a pointer to the - * destination. - */ -typedef size_t(*write_func)(const void*, size_t, size_t, void*); - -/** - * Function pointer to a read function. - * - * The signature of the read function shall be compatible to the signature - * of standard fread, though it may use arbitrary data types for - * source and destination. - * - * The arguments shall contain (in ascending order): a pointer to the - * destination, the length of one element, the element count and a pointer to - * the source. - */ -typedef size_t(*read_func)(void*, size_t, size_t, void*); - - - -#if __GNUC__ >= 5 || defined(__clang__) -#define UCX_MUL_BUILTIN - -#if __WORDSIZE == 32 -/** - * Alias for __builtin_umul_overflow. - * - * Performs a multiplication of size_t values and checks for overflow. - * - * @param a first operand - * @param b second operand - * @param result a pointer to a size_t, where the result should - * be stored - * @return zero, if no overflow occurred and the result is correct, non-zero - * otherwise - */ -#define ucx_szmul(a, b, result) __builtin_umul_overflow(a, b, result) -#else /* __WORDSIZE != 32 */ -/** - * Alias for __builtin_umull_overflow. - * - * Performs a multiplication of size_t values and checks for overflow. - * - * @param a first operand - * @param b second operand - * @param result a pointer to a size_t, where the result should - * be stored - * @return zero, if no overflow occurred and the result is correct, non-zero - * otherwise - */ -#define ucx_szmul(a, b, result) __builtin_umull_overflow(a, b, result) -#endif /* __WORDSIZE */ - -#else /* no GNUC or clang bultin */ - -/** - * Performs a multiplication of size_t values and checks for overflow. - * - * This is a custom implementation in case there is no compiler builtin - * available. - * - * @param a first operand - * @param b second operand - * @param result a pointer to a size_t, where the result should - * be stored - * @return zero, if no overflow occurred and the result is correct, non-zero - * otherwise - */ -#define ucx_szmul(a, b, result) ucx_szmul_impl(a, b, result) - -int ucx_szmul_impl(size_t a, size_t b, size_t *result); - -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* UCX_H */ - diff -r a569148841ff -r efbd59642577 ucx/ucx/utils.h --- a/ucx/ucx/utils.h Sun Apr 16 14:12:24 2023 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,319 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2017 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. - */ - -/** - * @file utils.h - * - * Compare, copy and printf functions. - * - * @author Mike Becker - * @author Olaf Wintermann - */ - -#ifndef UCX_UTILS_H -#define UCX_UTILS_H - -#include "ucx.h" -#include "string.h" -#include "allocator.h" -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Default buffer size for ucx_stream_copy() and ucx_stream_ncopy(). - */ -#define UCX_STREAM_COPY_BUFSIZE 4096 - -/** - * Copies a string. - * @param s the string to copy - * @param data omitted - * @return a pointer to a copy of s1 that can be passed to free(void*) - */ -void *ucx_strcpy(const void *s, void *data); - -/** - * Copies a memory area. - * @param m a pointer to the memory area - * @param n a pointer to the size_t containing the size of the memory area - * @return a pointer to a copy of the specified memory area that can - * be passed to free(void*) - */ -void *ucx_memcpy(const void *m, void *n); - - -/** - * Reads data from a stream and writes it to another stream. - * - * @param src the source stream - * @param dest the destination stream - * @param rfnc the read function - * @param wfnc the write function - * @param buf a pointer to the copy buffer or NULL if a buffer - * shall be implicitly created on the heap - * @param bufsize the size of the copy buffer - if NULL was - * provided for buf, this is the size of the buffer that shall be - * implicitly created - * @param n the maximum number of bytes that shall be copied - * @return the total number of bytes copied - */ -size_t ucx_stream_bncopy(void *src, void *dest, read_func rfnc, write_func wfnc, - char* buf, size_t bufsize, size_t n); - -/** - * Shorthand for an unbounded ucx_stream_bncopy call using a default buffer. - * - * @param src the source stream - * @param dest the destination stream - * @param rfnc the read function - * @param wfnc the write function - * @return total number of bytes copied - * - * @see #UCX_STREAM_COPY_BUFSIZE - */ -#define ucx_stream_copy(src,dest,rfnc,wfnc) ucx_stream_bncopy(\ - src, dest, (read_func)rfnc, (write_func)wfnc, \ - NULL, UCX_STREAM_COPY_BUFSIZE, (size_t)-1) - -/** - * Shorthand for ucx_stream_bncopy using a default copy buffer. - * - * @param src the source stream - * @param dest the destination stream - * @param rfnc the read function - * @param wfnc the write function - * @param n maximum number of bytes that shall be copied - * @return total number of bytes copied - */ -#define ucx_stream_ncopy(src,dest,rfnc,wfnc, n) ucx_stream_bncopy(\ - src, dest, (read_func)rfnc, (write_func)wfnc, \ - NULL, UCX_STREAM_COPY_BUFSIZE, n) - -/** - * Shorthand for an unbounded ucx_stream_bncopy call using the specified buffer. - * - * @param src the source stream - * @param dest the destination stream - * @param rfnc the read function - * @param wfnc the write function - * @param buf a pointer to the copy buffer or NULL if a buffer - * shall be implicitly created on the heap - * @param bufsize the size of the copy buffer - if NULL was - * provided for buf, this is the size of the buffer that shall be - * implicitly created - * @return total number of bytes copied - */ -#define ucx_stream_bcopy(src,dest,rfnc,wfnc, buf, bufsize) ucx_stream_bncopy(\ - src, dest, (read_func)rfnc, (write_func)wfnc, \ - buf, bufsize, (size_t)-1) - -/** - * Wraps the strcmp function. - * @param s1 string one - * @param s2 string two - * @param data omitted - * @return the result of strcmp(s1, s2) - */ -int ucx_cmp_str(const void *s1, const void *s2, void *data); - -/** - * Wraps the strncmp function. - * @param s1 string one - * @param s2 string two - * @param n a pointer to the size_t containing the third strncmp parameter - * @return the result of strncmp(s1, s2, *n) - */ -int ucx_cmp_strn(const void *s1, const void *s2, void *n); - -/** - * Wraps the sstrcmp function. - * @param s1 sstr one - * @param s2 sstr two - * @param data ignored - * @return the result of sstrcmp(s1, s2) - */ -int ucx_cmp_sstr(const void *s1, const void *s2, void *data); - -/** - * Compares two integers of type int. - * @param i1 pointer to integer one - * @param i2 pointer to integer two - * @param data omitted - * @return -1, if *i1 is less than *i2, 0 if both are equal, - * 1 if *i1 is greater than *i2 - */ -int ucx_cmp_int(const void *i1, const void *i2, void *data); - -/** - * Compares two integers of type long int. - * @param i1 pointer to long integer one - * @param i2 pointer to long integer two - * @param data omitted - * @return -1, if *i1 is less than *i2, 0 if both are equal, - * 1 if *i1 is greater than *i2 - */ -int ucx_cmp_longint(const void *i1, const void *i2, void *data); - - -/** - * Distance function for integers of type int. - * @param i1 pointer to integer one - * @param i2 pointer to integer two - * @param data omitted - * @return i1 minus i2 - */ -intmax_t ucx_dist_int(const void *i1, const void *i2, void *data); - -/** - * Distance function for integers of type long int. - * @param i1 pointer to long integer one - * @param i2 pointer to long integer two - * @param data omitted - * @return i1 minus i2 - */ -intmax_t ucx_dist_longint(const void *i1, const void *i2, void *data); - -/** - * Compares two real numbers of type float. - * @param f1 pointer to float one - * @param f2 pointer to float two - * @param data if provided: a pointer to precision (default: 1e-6f) - * @return -1, if *f1 is less than *f2, 0 if both are equal, - * 1 if *f1 is greater than *f2 - */ - -int ucx_cmp_float(const void *f1, const void *f2, void *data); - -/** - * Compares two real numbers of type double. - * @param d1 pointer to double one - * @param d2 pointer to double two - * @param data if provided: a pointer to precision (default: 1e-14) - * @return -1, if *d1 is less than *d2, 0 if both are equal, - * 1 if *d1 is greater than *d2 - */ -int ucx_cmp_double(const void *d1, const void *d2, void *data); - -/** - * Compares two pointers. - * @param ptr1 pointer one - * @param ptr2 pointer two - * @param data omitted - * @return -1 if ptr1 is less than ptr2, 0 if both are equal, - * 1 if ptr1 is greater than ptr2 - */ -int ucx_cmp_ptr(const void *ptr1, const void *ptr2, void *data); - -/** - * Compares two memory areas. - * @param ptr1 pointer one - * @param ptr2 pointer two - * @param n a pointer to the size_t containing the third parameter for memcmp - * @return the result of memcmp(ptr1, ptr2, *n) - */ -int ucx_cmp_mem(const void *ptr1, const void *ptr2, void *n); - -/** - * A printf() like function which writes the output to a stream by - * using a write_func(). - * @param stream the stream the data is written to - * @param wfc the write function - * @param fmt format string - * @param ... additional arguments - * @return the total number of bytes written - */ -int ucx_fprintf(void *stream, write_func wfc, const char *fmt, ...); - -/** - * va_list version of ucx_fprintf(). - * @param stream the stream the data is written to - * @param wfc the write function - * @param fmt format string - * @param ap argument list - * @return the total number of bytes written - * @see ucx_fprintf() - */ -int ucx_vfprintf(void *stream, write_func wfc, const char *fmt, va_list ap); - -/** - * A printf() like function which allocates space for a sstr_t - * the result is written to. - * - * Attention: The sstr_t data is allocated with the allocators - * ucx_allocator_malloc() function. So it is implementation dependent, if - * the returned sstr_t.ptr pointer must be passed to the allocators - * ucx_allocator_free() function manually. - * - * Note: The sstr_t.ptr of the return value will always be - * NULL-terminated. - * - * @param allocator the UcxAllocator used for allocating the result sstr_t - * @param fmt format string - * @param ... additional arguments - * @return a sstr_t containing the formatted string - */ -sstr_t ucx_asprintf(UcxAllocator *allocator, const char *fmt, ...); - -/** - * va_list version of ucx_asprintf(). - * - * @param allocator the UcxAllocator used for allocating the result sstr_t - * @param fmt format string - * @param ap argument list - * @return a sstr_t containing the formatted string - * @see ucx_asprintf() - */ -sstr_t ucx_vasprintf(UcxAllocator *allocator, const char *fmt, va_list ap); - -/** Shortcut for ucx_asprintf() with default allocator. */ -#define ucx_sprintf(...) \ - ucx_asprintf(ucx_default_allocator(), __VA_ARGS__) - -/** - * A printf() like function which writes the output to a - * UcxBuffer. - * - * @param buffer the buffer the data is written to - * @param ... format string and additional arguments - * @return the total number of bytes written - * @see ucx_fprintf() - */ -#define ucx_bprintf(buffer, ...) ucx_fprintf((UcxBuffer*)buffer, \ - (write_func)ucx_buffer_write, __VA_ARGS__) - -#ifdef __cplusplus -} -#endif - -#endif /* UCX_UTILS_H */ - diff -r a569148841ff -r efbd59642577 ucx/utils.c --- a/ucx/utils.c Sun Apr 16 14:12:24 2023 +0200 +++ b/ucx/utils.c Fri Apr 21 21:25:32 2023 +0200 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. + * 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: @@ -26,263 +26,69 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include "ucx/utils.h" +#include "cx/utils.h" -#include -#include -#include -#include +#define CX_STREAM_BCOPY_BUF_SIZE 8192 +#define CX_STREAM_COPY_BUF_SIZE 1024 -/* COPY FUCNTIONS */ -void* ucx_strcpy(const void* s, void* data) { - const char *str = (const char*) s; - size_t n = 1+strlen(str); - char *cpy = (char*) malloc(n); - memcpy(cpy, str, n); - return cpy; -} - -void* ucx_memcpy(const void* m, void* n) { - size_t k = *((size_t*)n); - void *cpy = malloc(k); - memcpy(cpy, m, k); - return cpy; -} - -size_t ucx_stream_bncopy(void *src, void *dest, read_func readfnc, - write_func writefnc, char* buf, size_t bufsize, size_t n) { - if(n == 0 || bufsize == 0) { +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; + + char *lbuf; size_t ncp = 0; - - if(buf) { + + if (buf) { + if (bufsize == 0) return 0; lbuf = buf; } else { - lbuf = (char*)malloc(bufsize); - if(lbuf == NULL) { + 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 = readfnc(lbuf, 1, rn, src)) != 0) { - r = writefnc(lbuf, 1, r, dest); + 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) { + if (r == 0 || n == 0) { break; } } - + if (lbuf != buf) { free(lbuf); } - + return ncp; } -/* COMPARE FUNCTIONS */ - -int ucx_cmp_str(const void *s1, const void *s2, void *data) { - return strcmp((const char*)s1, (const char*)s2); -} - -int ucx_cmp_strn(const void *s1, const void *s2, void *n) { - return strncmp((const char*)s1, (const char*)s2, *((size_t*) n)); -} - -int ucx_cmp_sstr(const void *s1, const void *s2, void *data) { - sstr_t a = *(const sstr_t*) s1; - sstr_t b = *(const sstr_t*) s2; - return sstrcmp(a, b); -} - -int ucx_cmp_int(const void *i1, const void *i2, void *data) { - int a = *((const int*) i1); - int b = *((const int*) i2); - if (a == b) { - return 0; - } else { - return a < b ? -1 : 1; - } -} - -int ucx_cmp_longint(const void *i1, const void *i2, void *data) { - int a = *((const long int*) i1); - int b = *((const long int*) i2); - if (a == b) { - return 0; - } else { - return a < b ? -1 : 1; - } -} - -intmax_t ucx_dist_int(const void *i1, const void *i2, void *data) { - intmax_t a = *((const int*) i1); - intmax_t b = *((const int*) i2); - return a - b; -} - -intmax_t ucx_dist_longint(const void *i1, const void *i2, void *data) { - intmax_t a = *((const long int*) i1); - intmax_t b = *((const long int*) i2); - return a - b; -} - -int ucx_cmp_float(const void *f1, const void *f2, void *epsilon) { - float a = *((const float*) f1); - float b = *((const float*) f2); - float e = !epsilon ? 1e-6f : *((float*)epsilon); - if (fabsf(a - b) < e) { - return 0; - } else { - return a < b ? -1 : 1; - } -} - -int ucx_cmp_double(const void *d1, const void *d2, void *epsilon) { - double a = *((const double*) d1); - double b = *((const double*) d2); - double e = !epsilon ? 1e-14 : *((double*)epsilon); - if (fabs(a - b) < e) { - return 0; - } else { - return a < b ? -1 : 1; - } -} - -int ucx_cmp_ptr(const void *ptr1, const void *ptr2, void *data) { - const intptr_t p1 = (const intptr_t) ptr1; - const intptr_t p2 = (const intptr_t) ptr2; - if (p1 == p2) { - return 0; - } else { - return p1 < p2 ? -1 : 1; - } -} - -int ucx_cmp_mem(const void *ptr1, const void *ptr2, void *n) { - return memcmp(ptr1, ptr2, *((size_t*)n)); +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); } -/* PRINTF FUNCTIONS */ - -#ifdef va_copy -#define UCX_PRINTF_BUFSIZE 256 -#else -#pragma message("WARNING: C99 va_copy macro not supported by this platform" \ - " - limiting ucx_*printf to 2 KiB") -#define UCX_PRINTF_BUFSIZE 0x800 +#ifndef CX_SZMUL_BUILTIN +#include "szmul.c" #endif - -int ucx_fprintf(void *stream, write_func wfc, const char *fmt, ...) { - int ret; - va_list ap; - va_start(ap, fmt); - ret = ucx_vfprintf(stream, wfc, fmt, ap); - va_end(ap); - return ret; -} - -int ucx_vfprintf(void *stream, write_func wfc, const char *fmt, va_list ap) { - char buf[UCX_PRINTF_BUFSIZE]; -#ifdef va_copy - va_list ap2; - va_copy(ap2, ap); - int ret = vsnprintf(buf, UCX_PRINTF_BUFSIZE, fmt, ap); - if (ret < 0) { - return ret; - } else if (ret < UCX_PRINTF_BUFSIZE) { - return (int)wfc(buf, 1, ret, stream); - } else { - if (ret == INT_MAX) { - errno = ENOMEM; - return -1; - } - - int len = ret + 1; - char *newbuf = (char*)malloc(len); - if (!newbuf) { - return -1; - } - - ret = vsnprintf(newbuf, len, fmt, ap2); - if (ret > 0) { - ret = (int)wfc(newbuf, 1, ret, stream); - } - free(newbuf); - } - return ret; -#else - int ret = vsnprintf(buf, UCX_PRINTF_BUFSIZE, fmt, ap); - if (ret < 0) { - return ret; - } else if (ret < UCX_PRINTF_BUFSIZE) { - return (int)wfc(buf, 1, ret, stream); - } else { - errno = ENOMEM; - return -1; - } -#endif -} - -sstr_t ucx_asprintf(UcxAllocator *allocator, const char *fmt, ...) { - va_list ap; - sstr_t ret; - va_start(ap, fmt); - ret = ucx_vasprintf(allocator, fmt, ap); - va_end(ap); - return ret; -} - -sstr_t ucx_vasprintf(UcxAllocator *a, const char *fmt, va_list ap) { - sstr_t s; - s.ptr = NULL; - s.length = 0; - char buf[UCX_PRINTF_BUFSIZE]; -#ifdef va_copy - va_list ap2; - va_copy(ap2, ap); - int ret = vsnprintf(buf, UCX_PRINTF_BUFSIZE, fmt, ap); - if (ret > 0 && ret < UCX_PRINTF_BUFSIZE) { - s.ptr = (char*)almalloc(a, ret + 1); - if (s.ptr) { - s.length = (size_t)ret; - memcpy(s.ptr, buf, ret); - s.ptr[s.length] = '\0'; - } - } else if (ret == INT_MAX) { - errno = ENOMEM; - } else { - int len = ret + 1; - s.ptr = (char*)almalloc(a, len); - if (s.ptr) { - ret = vsnprintf(s.ptr, len, fmt, ap2); - if (ret < 0) { - free(s.ptr); - s.ptr = NULL; - } else { - s.length = (size_t)ret; - } - } - } -#else - int ret = vsnprintf(buf, UCX_PRINTF_BUFSIZE, fmt, ap); - if (ret > 0 && ret < UCX_PRINTF_BUFSIZE) { - s.ptr = (char*)almalloc(a, ret + 1); - if (s.ptr) { - s.length = (size_t)ret; - memcpy(s.ptr, buf, ret); - s.ptr[s.length] = '\0'; - } - } else { - errno = ENOMEM; - } -#endif - return s; -}