Sun, 06 Nov 2022 15:53:32 +0100
switch from ucx 2 to 3
--- a/src/server/Makefile Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/Makefile Sun Nov 06 15:53:32 2022 +0100 @@ -71,13 +71,13 @@ mkdir -p $@ $(MAIN_TARGET): preparation $(MAINOBJS) $(DAEMON_STARTOBJ) - $(CXX) -o $(MAIN_TARGET) $(MAINOBJS) $(DAEMON_STARTOBJ) -L$(BUILD_ROOT)/build/lib $(LDFLAGS) + $(CC) -o $(MAIN_TARGET) $(MAINOBJS) $(DAEMON_STARTOBJ) -L$(BUILD_ROOT)/build/lib $(LDFLAGS) $(LIB_WSCFG): $(CONFOBJS) $(CC) $(SHLIB_LDFLAGS) -o $@ $(CONFOBJS) $(TEST_TARGET): $(TESTOBJS) $(PLUGINS) - $(CXX) -o $(TEST_TARGET) $(TESTOBJS) -L$(BUILD_ROOT)/build/lib $(LDFLAGS) $(TEST_PLUGIN_LDFLAGS) + $(CC) -o $(TEST_TARGET) $(TESTOBJS) -L$(BUILD_ROOT)/build/lib $(LDFLAGS) $(TEST_PLUGIN_LDFLAGS) $(PLUGINS): $(MAIN_TARGET) FORCE
--- a/src/server/config/acl.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/config/acl.c Sun Nov 06 15:53:32 2022 +0100 @@ -32,7 +32,7 @@ #include "acl.h" -ACLFile* load_acl_file(char *file) { +ACLFile* load_acl_file(const char *file) { FILE *in = fopen(file, "r"); if(in == NULL) { return NULL; @@ -40,9 +40,9 @@ ACLFile *conf = malloc(sizeof(ACLFile)); conf->parser.parse = acl_parse; - conf->namedACLs = NULL; - conf->uriACLs = NULL; - conf->pathACLs = NULL; + conf->namedACLs = cxPointerLinkedListCreate(cxDefaultAllocator, cx_cmp_ptr); + conf->uriACLs = cxPointerLinkedListCreate(cxDefaultAllocator, cx_cmp_ptr); + conf->pathACLs = cxPointerLinkedListCreate(cxDefaultAllocator, cx_cmp_ptr); int r = cfg_parse_basic_file((ConfigParser*)conf, in); if(r != 0) { @@ -56,45 +56,45 @@ } void free_acl_file(ACLFile *conf) { - ucx_mempool_destroy(conf->parser.mp->pool); + //ucx_mempool_destroy(conf->parser.mp->pool); free(conf); } -int acl_parse(void *p, ConfigLine *begin, ConfigLine *end, sstr_t line) { +int acl_parse(void *p, ConfigLine *begin, ConfigLine *end, cxmutstr line) { ACLFile *aclf = p; - UcxAllocator *mp = aclf->parser.mp; + CxAllocator *mp = aclf->parser.mp; - if(sstrprefix(line, sstr("ACL "))) { - sstr_t param = sstrsubs(line, 4); - UcxList *plist = cfg_param_list(param, mp); + if(cx_strprefix(cx_strcast(line), cx_str("ACL "))) { + cxmutstr param = cx_strsubs_m(line, 4); + ConfigParam *plist = cfg_param_list(param, mp); ACLConfig *acl = OBJ_NEW(mp, ACLConfig); acl->type.ptr = NULL; acl->authparam = NULL; acl->entries = NULL; aclf->cur = acl; - sstr_t type = cfg_param_get(plist, sstr("type")); - sstr_t name = cfg_param_get(plist, sstr("name")); - sstr_t path = cfg_param_get(plist, sstr("path")); - sstr_t uri = cfg_param_get(plist, sstr("uri")); + cxmutstr type = cfg_param_get(plist, cx_str("type")); + cxmutstr name = cfg_param_get(plist, cx_str("name")); + cxmutstr path = cfg_param_get(plist, cx_str("path")); + cxmutstr uri = cfg_param_get(plist, cx_str("uri")); if(name.ptr) { acl->id = name; - aclf->namedACLs = ucx_list_append_a(mp, aclf->namedACLs, acl); + cxListAdd(aclf->namedACLs, acl); } else if(path.ptr) { acl->id = path; - aclf->pathACLs = ucx_list_append_a(mp, aclf->pathACLs, acl); + cxListAdd(aclf->pathACLs, acl); } else if(uri.ptr) { acl->id = uri; - aclf->uriACLs = ucx_list_append_a(mp, aclf->uriACLs, acl); + cxListAdd(aclf->uriACLs, acl); } if(type.ptr) { acl->type = type; } - } else if(sstrprefix(line, sstr("Authenticate "))) { - sstr_t param = sstrsubs(line, 13); - UcxList *plist = cfg_param_list(param, mp); + } else if(cx_strprefix(cx_strcast(line), cx_str("Authenticate "))) { + cxmutstr param = cx_strsubs_m(line, 13); + ConfigParam *plist = cfg_param_list(param, mp); aclf->cur->authparam = plist; } else { if(parse_ace(aclf, line)) { @@ -106,14 +106,16 @@ return 0; } -int parse_ace(ACLFile *f, sstr_t line) { +#define ACE_MAX_TOKENS 2048 + +int parse_ace(ACLFile *f, cxmutstr line) { ACLConfig *cur = f->cur; - UcxAllocator *mp = f->parser.mp; + CxAllocator *mp = f->parser.mp; - ssize_t tkn = 0; - sstr_t *tk = sstrsplit(line, sstr(":"), &tkn); + cxstring *tk = NULL; + ssize_t tkn = cx_strsplit_a(mp, cx_strcast(line), cx_str(":"), ACE_MAX_TOKENS, &tk); if(!tk || tkn < 3) { - log_ereport(LOG_FAILURE, "parse_ace: to few tokens"); + log_ereport(LOG_FAILURE, "parse_ace: to few tokens: %.*s", (int)line.length, line.ptr); return 1; } @@ -124,28 +126,28 @@ * first step: determine who is affected by this ace */ int n = 0; - sstr_t s = tk[0]; + cxstring s = tk[0]; - if(!sstrcmp(s, sstr("user"))) { + if(!cx_strcmp(s, cx_str("user"))) { // next token is the user name s = tk[1]; n++; - ace->who = sstrdup_a(mp, s); - } else if(!sstrcmp(s, sstr("group"))) { + ace->who = cx_strdup_a(mp, s); + } else if(!cx_strcmp(s, cx_str("group"))) { // next token is the group name s = tk[1]; n++; - ace->who = sstrdup_a(mp, s); + ace->who = cx_strdup_a(mp, s); ace->flags = ACLCFG_IDENTIFIER_GROUP; - } else if(!sstrcmp(s, sstr("owner@"))) { + } else if(!cx_strcmp(s, cx_str("owner@"))) { ace->flags = ACLCFG_OWNER; - } else if(!sstrcmp(s, sstr("group@"))) { + } else if(!cx_strcmp(s, cx_str("group@"))) { ace->flags = ACLCFG_GROUP; - } else if(!sstrcmp(s, sstr("everyone@"))) { + } else if(!cx_strcmp(s, cx_str("everyone@"))) { ace->flags = ACLCFG_EVERYONE; } else { // you can specify only the user name in the ace - ace->who = sstrdup_a(mp, s); + ace->who = cx_strdup_a(mp, s); } n++; //next token @@ -161,13 +163,13 @@ } s = tk[n]; - ssize_t maskn = 0; - sstr_t *accessmask = sstrsplit(s, sstr(","), &maskn); + cxstring *accessmask = NULL; + ssize_t maskn = cx_strsplit_a(mp, s, cx_str(","), ACE_MAX_TOKENS, &accessmask); for(int i=0;i<maskn;i++) { - sstr_t access = accessmask[i]; + cxstring access = accessmask[i]; ace->access_mask = ace->access_mask | accstr2int(access); } - free(accessmask); + cxFree(mp, accessmask); n++; // next token /* @@ -177,31 +179,31 @@ int complete = 0; while(n < tkn) { s = tk[n]; - if(!sstrcmp(s, sstr("allow"))) { + if(!cx_strcmp(s, cx_str("allow"))) { ace->type = ACLCFG_TYPE_ALLOWED; complete = 1; break; - } else if(!sstrcmp(s, sstr("deny"))) { + } else if(!cx_strcmp(s, cx_str("deny"))) { ace->type = ACLCFG_TYPE_DENIED; complete = 1; break; - } else if(!sstrcmp(s, sstr("audit"))) { + } else if(!cx_strcmp(s, cx_str("audit"))) { ace->type = ACLCFG_TYPE_AUDIT; complete = 1; break; - } else if(!sstrcmp(s, sstr("alarm"))) { + } else if(!cx_strcmp(s, cx_str("alarm"))) { ace->type = ACLCFG_TYPE_ALARM; complete = 1; break; } else { // set flags - ssize_t fln = 0; - sstr_t *flags = sstrsplit(s, sstr(","), &fln); + cxstring *flags = NULL; + ssize_t fln = cx_strsplit_a(mp, s, cx_str(","), ACE_MAX_TOKENS, &flags); for(int i=0;i<fln;i++) { - sstr_t flag = flags[i]; - if(!sstrcmp(flag, sstr("successful_access_flag"))) { + cxstring flag = flags[i]; + if(!cx_strcmp(flag, cx_str("successful_access_flag"))) { ace->flags = ace->flags | ACLCFG_SUCCESSFUL_ACCESS_FLAG; - } else if(!sstrcmp(flag, sstr("failed_access_flag"))) { + } else if(!cx_strcmp(flag, cx_str("failed_access_flag"))) { ace->flags = ace->flags | ACLCFG_FAILED_ACCESS_ACE_FLAG; } // TODO: other flags @@ -216,52 +218,52 @@ return 1; } - cur->entries = ucx_list_append_a(mp, cur->entries, ace); + CFG_ACE_ADD(&cur->entries, ace); return 0; } -uint32_t accstr2int(sstr_t access) { +uint32_t accstr2int(cxstring access) { uint32_t val = 0; - if(!sstrcmp(access, sstr("read"))) { + if(!cx_strcmp(access, cx_str("read"))) { val = ACLCFG_READ; - } else if(!sstrcmp(access, sstr("write"))) { + } else if(!cx_strcmp(access, cx_str("write"))) { val = ACLCFG_WRITE; - } else if(!sstrcmp(access, sstr("read_data"))) { + } else if(!cx_strcmp(access, cx_str("read_data"))) { val = ACLCFG_READ_DATA; - } else if(!sstrcmp(access, sstr("write_data"))) { + } else if(!cx_strcmp(access, cx_str("write_data"))) { val = ACLCFG_WRITE_DATA; - } else if(!sstrcmp(access, sstr("append"))) { + } else if(!cx_strcmp(access, cx_str("append"))) { val = ACLCFG_APPEND; - } else if(!sstrcmp(access, sstr("add"))) { + } else if(!cx_strcmp(access, cx_str("add"))) { val = ACLCFG_ADD_FILE; - } else if(!sstrcmp(access, sstr("add_file"))) { + } else if(!cx_strcmp(access, cx_str("add_file"))) { val = ACLCFG_ADD_FILE; - } else if(!sstrcmp(access, sstr("add_subdirectory"))) { + } else if(!cx_strcmp(access, cx_str("add_subdirectory"))) { val = ACLCFG_ADD_SUBDIRECTORY; - } else if(!sstrcmp(access, sstr("read_xattr"))) { + } else if(!cx_strcmp(access, cx_str("read_xattr"))) { val = ACLCFG_READ_XATTR; - } else if(!sstrcmp(access, sstr("write_xattr"))) { + } else if(!cx_strcmp(access, cx_str("write_xattr"))) { val = ACLCFG_WRITE_XATTR; - } else if(!sstrcmp(access, sstr("execute"))) { + } else if(!cx_strcmp(access, cx_str("execute"))) { val = ACLCFG_EXECUTE; - } else if(!sstrcmp(access, sstr("delete_child"))) { + } else if(!cx_strcmp(access, cx_str("delete_child"))) { val = ACLCFG_DELETE_CHILD; - } else if(!sstrcmp(access, sstr("delete"))) { + } else if(!cx_strcmp(access, cx_str("delete"))) { val = ACLCFG_DELETE; - } else if(!sstrcmp(access, sstr("read_attributes"))) { + } else if(!cx_strcmp(access, cx_str("read_attributes"))) { val = ACLCFG_READ_ATTRIBUTES; - } else if(!sstrcmp(access, sstr("write_attributes"))) { + } else if(!cx_strcmp(access, cx_str("write_attributes"))) { val = ACLCFG_WRITE_ATTRIBUTES; - } else if(!sstrcmp(access, sstr("list"))) { + } else if(!cx_strcmp(access, cx_str("list"))) { val = ACLCFG_LIST; - } else if(!sstrcmp(access, sstr("read_acl"))) { + } else if(!cx_strcmp(access, cx_str("read_acl"))) { val = ACLCFG_READ_ACL; - } else if(!sstrcmp(access, sstr("write_acl"))) { + } else if(!cx_strcmp(access, cx_str("write_acl"))) { val = ACLCFG_WRITE_ACL; - } else if(!sstrcmp(access, sstr("write_owner"))) { + } else if(!cx_strcmp(access, cx_str("write_owner"))) { val = ACLCFG_WRITE_OWNER; - } else if(!sstrcmp(access, sstr("synchronize"))) { + } else if(!cx_strcmp(access, cx_str("synchronize"))) { val = ACLCFG_SYNCHRONIZE; } return val;
--- a/src/server/config/acl.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/config/acl.h Sun Nov 06 15:53:32 2022 +0100 @@ -36,30 +36,38 @@ extern "C" { #endif +#define CFG_ACE_ADD(list_begin, elm) \ + cx_linked_list_add((void**)list_begin, NULL, -1, offsetof(ACEConfig, next), elm) + +#define CFG_ACE_LIST_SIZE(list) \ + cx_linked_list_size(list, offsetof(ACEConfig, next)) + typedef struct _acl_conf ACLConfig; +typedef struct _ace_conf ACEConfig; typedef struct _acl_file { ConfigParser parser; - UcxList *namedACLs; // ACLConfig list - UcxList *uriACLs; // ACLConfig list - UcxList *pathACLs; // ACLConfig list + CxList *namedACLs; // ACLConfig list + CxList *uriACLs; // ACLConfig list + CxList *pathACLs; // ACLConfig list // temp data ACLConfig *cur; } ACLFile; struct _acl_conf { - sstr_t id; // name, uri or path - sstr_t type; // webserver ACL or file system ACL - UcxList *authparam; // authentication parameters - UcxList *entries; // ACEConfig list + cxmutstr id; // name, uri or path + cxmutstr type; // webserver ACL or file system ACL + ConfigParam *authparam; // authentication parameters + ACEConfig *entries; // ACEConfig list }; -typedef struct _ace_conf { - sstr_t who; - uint32_t access_mask; - uint16_t flags; - uint16_t type; -} ACEConfig; +struct _ace_conf { + cxmutstr who; + uint32_t access_mask; + uint16_t flags; + uint16_t type; + ACEConfig *next; +}; /* @@ -115,18 +123,18 @@ #define ACLCFG_TYPE_ALARM 0x04 -ACLFile* load_acl_file(char *file); +ACLFile* load_acl_file(const char *file); void free_acl_file(ACLFile *aclfile); -int acl_parse(void *p, ConfigLine *begin, ConfigLine *end, sstr_t line); -int parse_ace(ACLFile *f, sstr_t line); +int acl_parse(void *p, ConfigLine *begin, ConfigLine *end, cxmutstr line); +int parse_ace(ACLFile *f, cxmutstr line); /* * converts a access right string to an integer value */ -uint32_t accstr2int(sstr_t access); +uint32_t accstr2int(cxstring access); #ifdef __cplusplus }
--- a/src/server/config/conf.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/config/conf.c Sun Nov 06 15:53:32 2022 +0100 @@ -31,32 +31,30 @@ #include <string.h> int cfg_parse_basic_file(ConfigParser *parser, FILE *in) { - parser->lines = NULL; - UcxMempool *mp = ucx_mempool_new(512); - parser->mp = mp->allocator; + parser->lines_begin = NULL; + parser->lines_end = NULL; + CxMempool *mp = cxBasicMempoolCreate(512); + CxAllocator *a = (CxAllocator*)mp->allocator; + parser->mp = a; // one logical line over many lines - sstr_t mline; + cxmutstr mline; mline.ptr = NULL; mline.length = 0; - ConfigLine *start_line; - ConfigLine *end_line; + ConfigLine *start_line = NULL; + ConfigLine *end_line = NULL; // read file - sstr_t l; + cxmutstr l; while((l = cfg_readln(in)).ptr != NULL) { void *org_ptr = l.ptr; // put the line to the list ConfigLine *line = OBJ_NEW(parser->mp, ConfigLine); - line->line = sstrdup_a(parser->mp, l); // TODO: check for 0-len str + line->line = cx_strdup_a(parser->mp, cx_strcast(l)); // TODO: check for 0-len str line->object = NULL; line->type = LINE_OTHER; - if(parser->lines) { - parser->lines = ucx_list_append_a(parser->mp, parser->lines, line); - } else { - parser->lines = ucx_list_append_a(parser->mp, parser->lines, line); - } + CFG_LINE_ADD(&parser->lines_begin, &parser->lines_end, line); // check if the line contains something l = cfg_trim_comment(l); @@ -66,10 +64,8 @@ // check for multi line if(mline.ptr != NULL) { // concate lines - char *ptr = ucx_mempool_malloc( - mp, - mline.length + l.length + 1); - + char *ptr = cxMalloc(a, mline.length + l.length + 1); + // TODO: maybe we can use cx_strcat memcpy(ptr, mline.ptr, mline.length); memcpy(ptr + mline.length - 1, l.ptr, l.length); mline.length += l.length; @@ -83,12 +79,12 @@ } if(l.ptr[l.length - 1] == '\\') { if(mline.ptr == NULL) { - mline = sstrdup_a(parser->mp, l); + mline = cx_strdup_a(parser->mp, cx_strcast(l)); start_line = line; } } else { // this line is complete so we can parse it - sstr_t ll; // we parse this line + cxmutstr ll; // we parse this line if(mline.ptr == NULL) { // single line @@ -124,8 +120,8 @@ return 0; } -sstr_t cfg_readln(FILE *file) { - sstr_t ns; +cxmutstr cfg_readln(FILE *file) { + cxmutstr ns; ns.ptr = NULL; ns.length = 0; @@ -148,11 +144,11 @@ ptr[0] = 0; } - sstr_t line = sstr(buf); - return sstrdup(line); + cxmutstr line = cx_mutstr(buf); + return cx_strdup(cx_strcast(line)); } - sstr_t s; + cxmutstr s; s.ptr = NULL; s.length = 0; return s; @@ -162,8 +158,8 @@ /* * removes a comment from the line */ -sstr_t cfg_trim_comment(sstr_t line) { - sstr_t nl = line; +cxmutstr cfg_trim_comment(cxmutstr line) { + cxmutstr nl = line; for(int i=0;i<line.length;i++) { if(line.ptr[i] == '#') { if(i > 0) { @@ -177,7 +173,7 @@ } } } - return sstrtrim(nl); + return cx_strtrim_m(nl); } /* @@ -185,7 +181,7 @@ * containing the other parameters or an empty string, if there are no more * parameters */ -sstr_t cfg_param(sstr_t params, sstr_t *name, sstr_t *value) { +cxmutstr cfg_param(cxmutstr params, cxmutstr *name, cxmutstr *value) { name->ptr = NULL; name->length = 0; value->ptr = NULL; @@ -204,7 +200,7 @@ params.ptr = params.ptr + i; params.length -= i; - return sstrtrim(params); + return cx_strtrim_m(params); } } @@ -214,7 +210,7 @@ // get value if(i>=params.length) { - sstr_t ns; + cxmutstr ns; ns.ptr = NULL; ns.length = 0; return ns; @@ -253,37 +249,33 @@ // create new params string params.ptr += i; params.length -= i; - return sstrtrim(params); + return cx_strtrim_m(params); } /* * gets a value from a parameter */ -sstr_t cfg_param_get(UcxList *list, sstr_t name) { - while(list != NULL) { - ConfigParam *param = list->data; - if(!sstrcmp(param->name, name)) { +cxmutstr cfg_param_get(ConfigParam *param, cxstring name) { + while(param != NULL) { + if(!cx_strcmp((cxstring){param->name.ptr, param->name.length}, name)) { return param->value; } - list = list->next; + param = param->next; } - sstr_t ns; - ns.ptr = NULL; - ns.length = 0; - return ns; + return (cxmutstr){ NULL, 0 }; } /* * parses a line containing a directive and returns a ConfigDirective object * or NULL if an error occurs */ -ConfigDirective* cfg_parse_directive(sstr_t line, UcxAllocator *mp) { +ConfigDirective* cfg_parse_directive(cxmutstr line, CxAllocator *mp) { if(line.length < 6) { log_ereport(LOG_FAILURE, "cfg_parse_directive: line too short"); return NULL; // line too short } - sstr_t name; + cxstring name; int i; for(i=0;i<line.length;i++) { @@ -296,20 +288,20 @@ // create directive object ConfigDirective *directive = OBJ_NEW(mp, ConfigDirective); - directive->directive_type = sstrdup_a(mp, name); + directive->directive_type = cx_strdup_a(mp, name); directive->type_num = cfg_get_directive_type_num(name); directive->condition = NULL; // set later by main parsing function //directive->param = NULL; - sstr_t param_str; + cxstring param_str; param_str.ptr = name.ptr + i; param_str.length = line.length - i; - param_str = sstrtrim(param_str); - directive->value = sstrdup_a(mp, param_str); + param_str = cx_strtrim(param_str); + directive->value = cx_strdup_a(mp, param_str); /* - sstr_t pname; - sstr_t pvalue; + cxmutstr pname; + cxmutstr pvalue; for(;;) { param_str = cfg_param(param_str, &pname, &pvalue); if(pname.length <= 0) { @@ -319,10 +311,10 @@ // create param object ConfigParam *param = OBJ_NEW(mp, ConfigParam); - param->name = sstrdup_mp(mp, pname); + param->name = cx_strdup_mp(mp, pname); if(pvalue.length > 0) { - param->value = sstrdup_mp(mp, pvalue); + param->value = cx_strdup_mp(mp, pvalue); } else { param->value.ptr = NULL; param->value.length = 0; @@ -337,10 +329,11 @@ return directive; } -UcxList* cfg_param_list(sstr_t param_str, UcxAllocator *mp) { - sstr_t pname; - sstr_t pvalue; - UcxList *plist = NULL; +ConfigParam* cfg_param_list(cxmutstr param_str, CxAllocator *mp) { + cxmutstr pname; + cxmutstr pvalue; + ConfigParam *plist_begin = NULL; + ConfigParam *plist_end = NULL; for(;;) { param_str = cfg_param(param_str, &pname, &pvalue); if(pname.length <= 0) { @@ -349,19 +342,20 @@ // create param object ConfigParam *param = OBJ_NEW(mp, ConfigParam); - param->name = sstrdup_a(mp, pname); + param->name = cx_strdup_a(mp, cx_strcast(pname)); + param->next = NULL; if(pvalue.length > 0) { - param->value = sstrdup_a(mp, pvalue); + param->value = cx_strdup_a(mp, cx_strcast(pvalue)); } else { param->value.ptr = NULL; param->value.length = 0; } // add param to list - plist = ucx_list_append_a(mp, plist, param); + CFG_PARAM_ADD(&plist_begin, &plist_end, param); } - return plist; + return plist_begin; } @@ -377,26 +371,26 @@ * AddLog 5 * Init 6 */ -int cfg_get_directive_type_num(sstr_t type) { +int cfg_get_directive_type_num(cxstring type) { /* get nsapi function type */ // TODO: replace hard coded numbers int dt = -1; - if(sstrcmp(type, sstr("AuthTrans")) == 0) { + if(cx_strcmp(type, cx_str("AuthTrans")) == 0) { dt = NSAPIAuthTrans; - } else if(sstrcmp(type, sstr("NameTrans")) == 0) { + } else if(cx_strcmp(type, cx_str("NameTrans")) == 0) { dt = NSAPINameTrans; - } else if(sstrcmp(type, sstr("PathCheck")) == 0) { + } else if(cx_strcmp(type, cx_str("PathCheck")) == 0) { dt = NSAPIPathCheck; - } else if(sstrcmp(type, sstr("ObjectType")) == 0) { + } else if(cx_strcmp(type, cx_str("ObjectType")) == 0) { dt = NSAPIObjectType; - } else if(sstrcmp(type, sstr("Service")) == 0) { + } else if(cx_strcmp(type, cx_str("Service")) == 0) { dt = NSAPIService; - } else if(sstrcmp(type, sstr("Error")) == 0) { + } else if(cx_strcmp(type, cx_str("Error")) == 0) { dt = NSAPIError; - } else if(sstrcmp(type, sstr("AddLog")) == 0) { + } else if(cx_strcmp(type, cx_str("AddLog")) == 0) { dt = NSAPIAddLog; - } else if(sstrcmp(type, sstr("Init")) == 0) { + } else if(cx_strcmp(type, cx_str("Init")) == 0) { dt = INIT_DIRECTIVE; } return dt; @@ -405,7 +399,7 @@ /* * checks if the line contains only a comment or space */ -int cfg_get_basic_type(sstr_t line) { +int cfg_get_basic_type(cxmutstr line) { if(line.length == 0) { return LINE_NOCONTENT; } else if(line.ptr[0] == '#') { @@ -417,7 +411,7 @@ /* * checks if the line contains a begin/end tag or a directive */ -int cfg_get_line_type(sstr_t line) { +int cfg_get_line_type(cxmutstr line) { if(line.length < 3) { // this line is to short to be correct return LINE_ERROR; @@ -436,16 +430,16 @@ } } -int cfg_get_tag_type(sstr_t tag) { - if(!sstrcmp(tag, sstr("Object"))) { +int cfg_get_tag_type(cxstring tag) { + if(!cx_strcmp(tag, cx_str("Object"))) { return TAG_OBJECT; - } else if(!sstrcmp(tag, sstr("If"))) { + } else if(!cx_strcmp(tag, cx_str("If"))) { return TAG_IF; - } else if(!sstrcmp(tag, sstr("ElseIf"))) { + } else if(!cx_strcmp(tag, cx_str("ElseIf"))) { return TAG_ELSEIF; - } else if(!sstrcmp(tag, sstr("Else"))) { + } else if(!cx_strcmp(tag, cx_str("Else"))) { return TAG_ELSE; - } else if(!sstrcmp(tag, sstr("Client"))) { + } else if(!cx_strcmp(tag, cx_str("Client"))) { return TAG_CLIENT; } return -1; @@ -455,8 +449,8 @@ * returns the name of the ending tag * on error, this functions returns a zero length string */ -sstr_t cfg_get_end_tag_name(sstr_t line) { - sstr_t ns; +cxmutstr cfg_get_end_tag_name(cxmutstr line) { + cxmutstr ns; ns.ptr = NULL; ns.length = 0; @@ -465,7 +459,7 @@ return ns; } - sstr_t name; + cxmutstr name; name.ptr = line.ptr + 2; name.length = line.length - 3; @@ -477,10 +471,10 @@ return ns; } - return sstrtrim(name); + return cx_strtrim_m(name); } -ConfigTag* cfg_parse_begin_tag(sstr_t line, UcxAllocator *mp) { +ConfigTag* cfg_parse_begin_tag(cxmutstr line, CxAllocator *mp) { if(line.length < 4) { return NULL; // this line can't contain a valid tag } @@ -489,7 +483,7 @@ return NULL; // syntax error } - sstr_t name; + cxmutstr name; name.ptr = line.ptr + 1; int i; for(i=1;i<line.length - 1;i++) { @@ -504,21 +498,21 @@ // create tag object ConfigTag *tag = OBJ_NEW(mp, ConfigTag); - tag->name = sstrdup_a(mp, name); + tag->name = cx_strdup_a(mp, cx_strcast(name)); tag->param = NULL; // parse parameters - sstr_t param_str; + cxmutstr param_str; param_str.ptr = line.ptr + i; param_str.length = line.length - name.length - 2; - param_str = sstrtrim(param_str); + param_str = cx_strtrim_m(param_str); if(param_str.length == 0) { return tag; // no parameters } - tag->param_str = sstrdup_a(mp, param_str); + tag->param_str = cx_strdup_a(mp, cx_strcast(param_str)); - sstr_t pname; - sstr_t pvalue; + cxmutstr pname; + cxmutstr pvalue; for(;;) { param_str = cfg_param(param_str, &pname, &pvalue); if(pname.length == 0) { @@ -527,16 +521,17 @@ // create param object ConfigParam *param = OBJ_NEW(mp, ConfigParam); - param->name = sstrdup_a(mp, pname); + param->next = NULL; + param->name = cx_strdup_a(mp, cx_strcast(pname)); if(pvalue.length > 0) { - param->value = sstrdup_a(mp, pvalue); + param->value = cx_strdup_a(mp, cx_strcast(pvalue)); } else { param->value.ptr = NULL; param->value.length = 0; } // add param to list - tag->param = ucx_list_append_a(mp, tag->param, param); + CFG_PARAM_ADD(&tag->param, NULL, param); } return tag; @@ -549,11 +544,13 @@ * gets a ConfigDirective with a specific name from a List of directives * returns a directive or NULL, if the directive cannot be found */ -ConfigDirective* cfg_directivelist_get(UcxList *dirs, sstr_t name) { +// TODO: remove +/* +ConfigDirective* cfg_directivelist_get(UcxList *dirs, cxmutstr name) { while(dirs != NULL) { ConfigDirective *d = dirs->data; if(d != NULL) { - if(!sstrcmp(d->directive_type, name)) { + if(!cx_strcmp(d->directive_type, name)) { return d; } } @@ -562,10 +559,10 @@ return NULL; } -sstr_t cfg_directivelist_get_str(UcxList *dirs, sstr_t name) { +cxmutstr cfg_directivelist_get_str(UcxList *dirs, cxmutstr name) { ConfigDirective *d = cfg_directivelist_get(dirs, name); if(d == NULL) { - sstr_t n; + cxmutstr n; n.ptr = NULL; n.length = 0; return n; @@ -573,16 +570,17 @@ //return cfg_directive_pstr1(d); return d->value; } +*/ /* * returns the name of the first parameter of the directive * useful for 'name value' directives */ /* -sstr_t cfg_directive_pstr1(ConfigDirective *dir) { +cxmutstr cfg_directive_pstr1(ConfigDirective *dir) { if(dir->param == NULL) { fprintf(stderr, "%s", "Error: cfg_directive_pstr1: param is NULL\n"); - sstr_t n; + cxmutstr n; n.ptr = NULL; n.length = 0; return n; @@ -591,7 +589,7 @@ ConfigParam *p = dir->param->data; return p->name; } -*/ + static void cfg_list_free(void *list) { ucx_list_free(list); @@ -600,14 +598,8 @@ static void cfg_map_free(void *map) { ucx_map_free(map); } +*/ -void cfg_map_destr(UcxMempool *mp, UcxMap *map) { - if(map) { - ucx_mempool_reg_destr(mp, map, cfg_map_free); - } -} - -
--- a/src/server/config/conf.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/config/conf.h Sun Nov 06 15:53:32 2022 +0100 @@ -32,10 +32,13 @@ #include <stdio.h> #include <stdlib.h> -#include <ucx/list.h> -#include <ucx/map.h> -#include <ucx/mempool.h> -#include <ucx/string.h> +#include <cx/linked_list.h> +#include <cx/hash_map.h> +#include <cx/mempool.h> +#include <cx/basic_mempool.h> +#include <cx/string.h> +#include <cx/utils.h> +#include <cx/compare.h> #include "../util/object.h" @@ -44,8 +47,8 @@ #endif // mempool malloc macro -#define OBJ_NEW(p, type) (type*)(p)->malloc((p)->pool, sizeof(type)) -#define OBJ_NEW_N(p, type) (type*)(p)->calloc((p)->pool, 1, sizeof(type)) +#define OBJ_NEW(p, type) (type*)cxMalloc(p, sizeof(type)) +#define OBJ_NEW_N(p, type) (type*)cxCalloc(p, 1, sizeof(type)) // line types #define LINE_OTHER 0 @@ -65,85 +68,111 @@ #define INIT_DIRECTIVE 16 + +#define CFG_LINE_ADD(list_begin, list_end, elm) \ + cx_linked_list_add((void**)list_begin, (void**)list_end, offsetof(ConfigLineList, prev), offsetof(ConfigLineList, next), elm) + +#define CFG_PARAM_ADD(list_begin, list_end, elm) \ + cx_linked_list_add((void**)list_begin, (void**)list_end, -1, offsetof(ConfigParam, next), elm) +#define CFG_DIRECTIVES_ADD(list, dir) \ + cx_linked_list_add((void**)list, NULL, -1, offsetof(ConfigDirectiveList, next), dir) + +#define CFG_NUM_PARAMS(param) cx_linked_list_size(param, offsetof(ConfigParam, next)) + typedef struct _cfg_line { - sstr_t line; // raw line string + cxmutstr line; // raw line string void *object; // pointer to data struct int type; // type, see line types } ConfigLine; -typedef int (*cfg_parse_f)(void *, ConfigLine *, ConfigLine *, sstr_t); +typedef int (*cfg_parse_f)(void *, ConfigLine *, ConfigLine *, cxmutstr); -typedef struct _cfg_param { - sstr_t name; - sstr_t value; -} ConfigParam; +typedef struct _cfg_param ConfigParam; +struct _cfg_param { + cxmutstr name; + cxmutstr value; + ConfigParam *next; +}; + +typedef struct ConfigLineList ConfigLineList; +struct ConfigLineList { + ConfigLine *line; + ConfigLineList *prev; + ConfigLineList *next; +}; typedef struct _cfg_parser { - UcxAllocator *mp; - UcxList *lines; - cfg_parse_f parse; + CxAllocator *mp; + ConfigLineList *lines_begin; + ConfigLineList *lines_end; + cfg_parse_f parse; } ConfigParser; typedef struct _conf_tag ConfigTag; struct _conf_tag { - ConfigLine *begin; - ConfigLine *end; + ConfigLine *begin; + ConfigLine *end; - sstr_t name; - UcxList *param; - sstr_t param_str; - ConfigTag *parent; - ConfigTag *iftag; // only used by <ElseIf> and <Else> - int type_num; + cxmutstr name; + ConfigParam *param; + cxmutstr param_str; + ConfigTag *parent; + ConfigTag *iftag; // only used by <ElseIf> and <Else> + int type_num; }; typedef struct _conf_directive { ConfigLine *begin; ConfigLine *end; - sstr_t directive_type; - sstr_t value; + cxmutstr directive_type; + cxmutstr value; //UcxList *param; ConfigTag *condition; int type_num; } ConfigDirective; +typedef struct ConfigDirectiveList ConfigDirectiveList; +struct ConfigDirectiveList { + ConfigDirective *directive; + ConfigDirectiveList *next; +}; int cfg_parse_basic_file(ConfigParser *parser, FILE *in); -sstr_t cfg_readln(FILE *file); +cxmutstr cfg_readln(FILE *file); -sstr_t cfg_trim_comment(sstr_t line); +cxmutstr cfg_trim_comment(cxmutstr line); -sstr_t cfg_param(sstr_t params, sstr_t *name, sstr_t *value); +cxmutstr cfg_param(cxmutstr params, cxmutstr *name, cxmutstr *value); -sstr_t cfg_param_get(UcxList *list, sstr_t name); +cxmutstr cfg_param_get(ConfigParam *list, cxstring name); -ConfigDirective* cfg_parse_directive(sstr_t line, UcxAllocator *mp); +ConfigDirective* cfg_parse_directive(cxmutstr line, CxAllocator *mp); -UcxList* cfg_param_list(sstr_t param_str, UcxAllocator *mp); +ConfigParam* cfg_param_list(cxmutstr param_str, CxAllocator *mp); -int cfg_get_directive_type_num(sstr_t type); +int cfg_get_directive_type_num(cxstring type); -int cfg_get_basic_type(sstr_t line); +int cfg_get_basic_type(cxmutstr line); -int cfg_get_line_type(sstr_t line); +int cfg_get_line_type(cxmutstr line); -int cfg_get_tag_type(sstr_t tag); +int cfg_get_tag_type(cxstring tag); -sstr_t cfg_get_end_tag_name(sstr_t line); +cxmutstr cfg_get_end_tag_name(cxmutstr line); -ConfigTag* cfg_parse_begin_tag(sstr_t line, UcxAllocator *mp); +ConfigTag* cfg_parse_begin_tag(cxmutstr line, CxAllocator *mp); -ConfigDirective* cfg_directivelist_get(UcxList *dirs, sstr_t name); +//ConfigDirective* cfg_directivelist_get(UcxList *dirs, cxmutstr name); -sstr_t cfg_directivelist_get_str(UcxList *dirs, sstr_t name); +//cxmutstr cfg_directivelist_get_str(UcxList *dirs, cxmutstr name); -sstr_t cfg_directive_pstr1(ConfigDirective *dir); +cxmutstr cfg_directive_pstr1(ConfigDirective *dir); -void cfg_map_destr(UcxMempool *mp, UcxMap *map); + #ifdef __cplusplus }
--- a/src/server/config/initconf.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/config/initconf.c Sun Nov 06 15:53:32 2022 +0100 @@ -54,11 +54,12 @@ } void free_init_config(InitConfig *conf) { - ucx_mempool_destroy(conf->parser.mp->pool); + // TODO: fix + //ucx_mempool_destroy(conf->parser.mp->pool); free(conf); } -int initconf_parse(void *p, ConfigLine *begin, ConfigLine *end, sstr_t line) { +int initconf_parse(void *p, ConfigLine *begin, ConfigLine *end, cxmutstr line) { InitConfig *conf = p; // parse directive @@ -70,7 +71,11 @@ d->begin = begin; d->end = end; if(d->type_num == INIT_DIRECTIVE) { - conf->directives = ucx_list_append(conf->directives, d); + //conf->directives = ucx_list_append(conf->directives, d); + ConfigDirectiveList *dir_entry = cxMalloc(conf->parser.mp, sizeof(ConfigDirectiveList)); + dir_entry->directive = d; + dir_entry->next = NULL; + CFG_DIRECTIVES_ADD(&conf->directives, dir_entry); } else { log_ereport(LOG_WARN, "Non Init directive in init.conf"); }
--- a/src/server/config/initconf.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/config/initconf.h Sun Nov 06 15:53:32 2022 +0100 @@ -36,16 +36,16 @@ #endif typedef struct _init_conf { - ConfigParser parser; - char *file; - UcxList *directives; + ConfigParser parser; + char *file; + ConfigDirectiveList *directives; } InitConfig; InitConfig *load_init_config(char *file); void free_init_config(InitConfig *conf); -int initconf_parse(void *p, ConfigLine *begin, ConfigLine *end, sstr_t line); +int initconf_parse(void *p, ConfigLine *begin, ConfigLine *end, cxmutstr line); #ifdef __cplusplus }
--- a/src/server/config/keyfile.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/config/keyfile.c Sun Nov 06 15:53:32 2022 +0100 @@ -31,6 +31,8 @@ #include "keyfile.h" +#define KEYFILE_MAX_TOKENS 4096 + KeyfileConfig *load_keyfile_config(const char *file) { FILE *in = fopen(file, "r"); if(in == NULL) { @@ -40,7 +42,8 @@ KeyfileConfig *conf = malloc(sizeof(KeyfileConfig)); conf->parser.parse = keyfile_parse; conf->file = strdup(file); - conf->users = NULL; + conf->users_begin = NULL; + conf->users_end = NULL; int r = cfg_parse_basic_file((ConfigParser*)conf, in); if(r != 0) { @@ -56,33 +59,34 @@ } void free_keyfile_config(KeyfileConfig *conf) { + /* if(conf->users) { ucx_list_free_a(conf->parser.mp, conf->users); } ucx_mempool_destroy(conf->parser.mp->pool); + */ free(conf); } -int keyfile_parse(void *p, ConfigLine *begin, ConfigLine *end, sstr_t line) { +int keyfile_parse(void *p, ConfigLine *begin, ConfigLine *end, cxmutstr line) { KeyfileConfig *conf = p; - UcxAllocator *mp = conf->parser.mp; + CxAllocator *mp = conf->parser.mp; - ssize_t tkn = 0; - sstr_t *tk = sstrsplit(line, sstrn(";", 1), &tkn); + cxstring *tk = NULL; + ssize_t tkn = cx_strsplit_a(mp, cx_strcast(line), cx_strn(";", 1), KEYFILE_MAX_TOKENS, &tk); if(tkn < 2) { return 1; } KeyfileEntry *entry = OBJ_NEW(mp, KeyfileEntry); - entry->groups = NULL; - entry->numgroups = 0; + ZERO(entry, sizeof(KeyfileEntry)); // get user name - entry->name = sstrdup_a(mp, tk[0]); + entry->name = cx_strdup_a(mp, tk[0]); // get hash - sstr_t hash = sstrtrim(tk[1]); + cxstring hash = cx_strtrim(tk[1]); if(hash.length < 4) { // to short return 1; @@ -93,56 +97,52 @@ } // get hash type and data - sstr_t hash_type; - sstr_t hash_data; + cxstring hash_type; + cxstring hash_data; for(int i=1;i<hash.length;i++) { if(hash.ptr[i] == '}') { - hash_type = sstrsubsl(hash, 1, i-1); - hash_data = sstrsubs(hash, i+1); + hash_type = cx_strsubsl(hash, 1, i-1); + hash_data = cx_strsubs(hash, i+1); } } - if(!sstrcmp(hash_type, sstr("SSHA"))) { + if(!cx_strcmp(hash_type, cx_str("SSHA"))) { entry->hashtype = KEYFILE_SSHA; - } else if(!sstrcmp(hash_type, sstr("SSHA256"))) { + } else if(!cx_strcmp(hash_type, cx_str("SSHA256"))) { entry->hashtype = KEYFILE_SSHA256; - } else if(!sstrcmp(hash_type, sstr("SSHA512"))) { + } else if(!cx_strcmp(hash_type, cx_str("SSHA512"))) { entry->hashtype = KEYFILE_SSHA512; } else { // unkown hash type log_ereport( LOG_FAILURE, "keyfile_parse: unknown hash type: %s", - sstrdup_a(mp, hash_type).ptr); + cx_strdup_a(mp, hash_type).ptr); return 1; } - entry->hashdata = sstrdup_a(mp, hash_data); + entry->hashdata = cx_strdup_a(mp, hash_data); // get groups if(tkn == 3) { - sstr_t groups_str = sstrtrim(tk[2]); - ssize_t ngroups = 0; - sstr_t *groups = sstrsplit(groups_str, sstrn(",", 1), &ngroups); + cxstring groups_str = cx_strtrim(tk[2]); + cxstring *groups = NULL; + ssize_t ngroups = cx_strsplit_a(mp, groups_str, cx_strn(",", 1), KEYFILE_MAX_TOKENS, &groups); if(ngroups > 0) { - entry->groups = mp->calloc(mp->pool, ngroups, sizeof(sstr_t)); + entry->groups = cxCalloc(mp, ngroups, sizeof(cxmutstr)); entry->numgroups = ngroups; for(int i=0;i<ngroups;i++) { - entry->groups[i] = sstrdup_a(mp, sstrtrim(groups[i])); - free(groups[i].ptr); + entry->groups[i] = cx_strdup_a(mp, cx_strtrim(groups[i])); } - free(groups); + cxFree(mp, groups); } } // add user - conf->users = ucx_list_append_a(mp, conf->users, entry); + CFG_KEYFILE_ADD(&conf->users_begin, &conf->users_end, entry); // free tokens - for(int i=0;i<tkn;i++) { - free(tk[i].ptr); - } - free(tk); + cxFree(mp, tk); return 0; }
--- a/src/server/config/keyfile.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/config/keyfile.h Sun Nov 06 15:53:32 2022 +0100 @@ -36,23 +36,30 @@ extern "C" { #endif +#define CFG_KEYFILE_ADD(list_begin, list_end, elm) \ + cx_linked_list_add((void**)list_begin, (void**)list_end, -1, offsetof(KeyfileEntry, next), elm) + +typedef struct _keyfile_entry KeyfileEntry; + typedef struct _keyfile_conf { ConfigParser parser; char *file; - UcxList *users; // KeyfileEntry list + KeyfileEntry *users_begin; // KeyfileEntry list + KeyfileEntry *users_end; } KeyfileConfig; -typedef struct _keyfile_entry { - sstr_t name; +struct _keyfile_entry { + cxmutstr name; enum KeyfileHashType hashtype; - sstr_t hashdata; - sstr_t *groups; + cxmutstr hashdata; + cxmutstr *groups; size_t numgroups; -} KeyfileEntry; + KeyfileEntry *next; +}; KeyfileConfig *load_keyfile_config(const char *file); void free_keyfile_config(KeyfileConfig *conf); -int keyfile_parse(void *p, ConfigLine *begin, ConfigLine *end, sstr_t line); +int keyfile_parse(void *p, ConfigLine *begin, ConfigLine *end, cxmutstr line); #ifdef __cplusplus }
--- a/src/server/config/mimeconf.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/config/mimeconf.c Sun Nov 06 15:53:32 2022 +0100 @@ -30,13 +30,10 @@ #include <stdlib.h> #include <string.h> -#include <ucx/mempool.h> +#include <cx/mempool.h> #include "mimeconf.h" -typedef struct { - ucx_destructor destructor; - void *ptr; -} ucx_regdestr; +#define MIMECONFIG_MAX_TOKENS 4096 MimeConfig *load_mime_config(const char *file) { FILE *in = fopen(file, "r"); @@ -47,7 +44,8 @@ MimeConfig *conf = malloc(sizeof(MimeConfig)); conf->parser.parse = mimeconf_parse; - conf->directives = NULL; + conf->directives_begin = NULL; + conf->directives_end = NULL; conf->ntypes = 0; int r = cfg_parse_basic_file((ConfigParser*)conf, in); if(r != 0) { @@ -64,38 +62,29 @@ } void free_mime_config(MimeConfig *conf) { - ucx_mempool_destroy(conf->parser.mp->pool); + //ucx_mempool_destroy(conf->parser.mp->pool); free(conf); } -int mimeconf_parse(void *p, ConfigLine *begin, ConfigLine *end, sstr_t line) { +int mimeconf_parse(void *p, ConfigLine *begin, ConfigLine *end, cxmutstr line) { MimeConfig *conf = p; - UcxAllocator *mp = conf->parser.mp; + CxAllocator *mp = conf->parser.mp; // parse mime directive MimeDirective *dir = OBJ_NEW_N(mp, MimeDirective); - UcxList *params = cfg_param_list(line, mp); - UCX_FOREACH(pl, params) { - ConfigParam *param = pl->data; - - if(!sstrcmp(param->name, sstr("type"))) { + ConfigParam *params = cfg_param_list(line, mp); + for(ConfigParam *param=params;param;param=param->next) { + if(!cx_strcmp(cx_strcast(param->name), cx_str("type"))) { dir->type = param->value; - } else if(!sstrcmp(param->name, sstr("exts"))) { + } else if(!cx_strcmp(cx_strcast(param->name), cx_str("exts"))) { // comma-separated file extensions - - ssize_t nx = 0; - sstr_t *exts = sstrsplit(param->value, sstrn(",", 1), &nx); - for(int i=0;i<nx;i++) { - sstr_t extstr = sstrdup_a(mp, exts[i]); - dir->exts = ucx_list_append_a(mp, dir->exts, extstr.ptr); - free(exts[i].ptr); - } - free(exts); + dir->nextensions = cx_strsplit_a(mp, cx_strcast(param->value), cx_strn(",", 1), MIMECONFIG_MAX_TOKENS, &dir->extensions); } } - conf->directives = ucx_list_append_a(mp, conf->directives, dir); + CFG_MIME_ADD(&conf->directives_begin, &conf->directives_end, dir); + conf->ntypes++; return 0;
--- a/src/server/config/mimeconf.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/config/mimeconf.h Sun Nov 06 15:53:32 2022 +0100 @@ -35,22 +35,32 @@ extern "C" { #endif +#define CFG_MIME_ADD(list_begin, list_end, directive) \ + cx_linked_list_add((void**)list_begin, (void**)list_end, -1, offsetof(MimeDirective, next), directive) + +typedef struct _mime_dir MimeDirective; + typedef struct _mime_conf { - ConfigParser parser; - UcxList *directives; // MimeDirective list - int ntypes; + ConfigParser parser; + MimeDirective *directives_begin; + MimeDirective *directives_end; + int ntypes; } MimeConfig; -typedef struct _mime_dir { - sstr_t type; - UcxList *exts; // char* -} MimeDirective; +struct _mime_dir { + cxmutstr type; + //UcxList *exts; // char* + ssize_t nextensions; + cxstring *extensions; + + MimeDirective *next; +}; MimeConfig *load_mime_config(const char *file); void free_mime_config(MimeConfig *conf); -int mimeconf_parse(void *p, ConfigLine *begin, ConfigLine *end, sstr_t line); +int mimeconf_parse(void *p, ConfigLine *begin, ConfigLine *end, cxmutstr line); #ifdef __cplusplus }
--- a/src/server/config/objconf.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/config/objconf.c Sun Nov 06 15:53:32 2022 +0100 @@ -30,6 +30,8 @@ #include <string.h> +#include <cx/utils.h> + /* dev notes: * * to free ObjectConfig, free: @@ -47,9 +49,9 @@ ObjectConfig *conf = malloc(sizeof(ObjectConfig)); conf->parser.parse = objconf_parse; conf->file = file; - conf->conditions = NULL; + //conf->conditions = NULL; conf->levels = NULL; - conf->objects = NULL; + conf->objects = cxPointerLinkedListCreate(cxDefaultAllocator, cx_cmp_ptr); //conf->lines = NULL; int r = cfg_parse_basic_file((ConfigParser*)conf, in); @@ -70,13 +72,13 @@ } // free mempool - ucx_mempool_destroy(conf->parser.mp->pool); + //ucx_mempool_destroy(conf->parser.mp->pool); free(conf); } -int objconf_parse(void *p, ConfigLine *begin, ConfigLine *end, sstr_t line) { +int objconf_parse(void *p, ConfigLine *begin, ConfigLine *end, cxmutstr line) { ObjectConfig *conf = p; begin->type = cfg_get_line_type(line); @@ -89,8 +91,8 @@ } tag->begin = begin; tag->end = end; - tag->type_num = cfg_get_tag_type(tag->name); - //printf("line {%s}\n", sstrdub(ll).ptr); + tag->type_num = cfg_get_tag_type(cx_strcast(tag->name)); + //printf("line {%s}\n", cx_strdub(ll).ptr); if(objconf_on_begin_tag(conf, tag) != 0) { fprintf(stderr, "1error\n"); exit(-1); @@ -98,7 +100,7 @@ break; } case LINE_END_TAG: { - sstr_t tag = cfg_get_end_tag_name(line); + cxmutstr tag = cfg_get_end_tag_name(line); if(objconf_on_end_tag(conf, tag) != 0) { fprintf(stderr, "2error\n"); exit(-1); @@ -122,9 +124,9 @@ } int objconf_on_begin_tag(ObjectConfig *conf, ConfigTag *tag) { - UcxAllocator *mp = conf->parser.mp; + CxAllocator *mp = conf->parser.mp; if(tag->type_num != TAG_OBJECT) { - ConfigParserLevel *l = conf->levels->data; + ConfigParserLevel *l = conf->levels; if(l->tag->type_num != TAG_OBJECT) { tag->parent = l->tag; } @@ -137,31 +139,35 @@ obj->begin = tag->begin; obj->end = tag->end; - obj->name = cfg_param_get(tag->param, sstr("name")); - obj->ppath = cfg_param_get(tag->param, sstr("ppath")); + obj->name = cfg_param_get(tag->param, cx_str("name")); + obj->ppath = cfg_param_get(tag->param, cx_str("ppath")); conf->obj = obj; - conf->objects = ucx_list_append_a(mp, conf->objects, obj); + //conf->objects = ucx_list_append_a(mp, conf->objects, obj); + cxListAdd(conf->objects, obj); // create tree level object ConfigParserLevel *lvl = OBJ_NEW(mp, ConfigParserLevel); lvl->iftag = NULL; lvl->levelnum = 1; lvl->tag = tag; - conf->levels = ucx_list_prepend_a(mp, conf->levels, lvl); + lvl->next = NULL; + //conf->levels = ucx_list_prepend_a(mp, conf->levels, lvl); + CFG_LEVEL_PREPEND(&conf->levels, lvl); break; } case TAG_IF: { // create tree level object - ConfigParserLevel *last_lvl = conf->levels->data; + ConfigParserLevel *last_lvl = conf->levels; ConfigParserLevel *lvl = OBJ_NEW(mp, ConfigParserLevel); lvl->iftag = NULL; lvl->levelnum = last_lvl->levelnum + 1; lvl->tag = tag; - conf->levels = ucx_list_prepend_a(mp, conf->levels, lvl); + //conf->levels = ucx_list_prepend_a(mp, conf->levels, lvl); + CFG_LEVEL_PREPEND(&conf->levels, lvl); last_lvl->iftag = tag; break; @@ -170,7 +176,7 @@ } case TAG_ELSE: { // create tree level object - ConfigParserLevel *last_lvl = conf->levels->data; + ConfigParserLevel *last_lvl = conf->levels; tag->iftag = last_lvl->iftag; ConfigParserLevel *lvl = OBJ_NEW( @@ -180,7 +186,8 @@ lvl->iftag = last_lvl->tag; lvl->levelnum = last_lvl->levelnum + 1; lvl->tag = tag; - conf->levels = ucx_list_prepend(conf->levels, lvl); + //conf->levels = ucx_list_prepend(conf->levels, lvl); + CFG_LEVEL_PREPEND(&conf->levels, lvl); break; } @@ -200,8 +207,8 @@ return 0; } -int objconf_on_end_tag(ObjectConfig *conf, sstr_t tagname) { - int type = cfg_get_tag_type(tagname); +int objconf_on_end_tag(ObjectConfig *conf, cxmutstr tagname) { + int type = cfg_get_tag_type(cx_strcast(tagname)); if(type == -1) { log_ereport(LOG_FAILURE, "objconf: unknown tag"); return 1; @@ -211,17 +218,20 @@ } // remove level + /* conf->levels = ucx_list_remove_a( conf->parser.mp, conf->levels, conf->levels); + */ + conf->levels = conf->levels->next; } return 0; } int objconf_on_directive(ObjectConfig *conf, ConfigDirective *dir) { - ConfigParserLevel *lvl = conf->levels->data; + ConfigParserLevel *lvl = conf->levels; // check if we have a condition for the directive // if the level tag is not an object tag, use it as condition @@ -230,10 +240,17 @@ } // add directive to current object + /* conf->obj->directives[dir->type_num] = ucx_list_append_a( conf->parser.mp, conf->obj->directives[dir->type_num], dir); + */ + + ConfigDirectiveList *dir_entry = cxMalloc(conf->parser.mp, sizeof(ConfigDirectiveList)); + dir_entry->directive = dir; + dir_entry->next = NULL; + CFG_DIRECTIVES_ADD(&conf->obj->directives[dir->type_num], dir_entry); return 0; }
--- a/src/server/config/objconf.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/config/objconf.h Sun Nov 06 15:53:32 2022 +0100 @@ -34,37 +34,42 @@ #ifdef __cplusplus extern "C" { #endif - + +#define CFG_LEVEL_PREPEND(list, level) \ + cx_linked_list_prepend((void**)list, NULL, -1, offsetof(ConfigParserLevel, next), level) + typedef struct _conf_object { ConfigLine *begin; ConfigLine *end; - sstr_t name; - sstr_t ppath; + cxmutstr name; + cxmutstr ppath; // directives - UcxList *directives[7]; + ConfigDirectiveList *directives[7]; } ConfigObject; /* * representing a xml like level in the obj.conf tree */ -typedef struct _conf_parser_level { +typedef struct ConfigParserLevel ConfigParserLevel; +struct ConfigParserLevel { ConfigTag *iftag; // last if tag ConfigTag *tag; // root of this level int levelnum; -} ConfigParserLevel; + ConfigParserLevel *next; +}; typedef struct _obj_conf { ConfigParser parser; char *file; //UcxDlist *lines; - UcxList *conditions; - UcxList *objects; + //UcxList *conditions; + CxList *objects; // private parser temp var ConfigObject *obj; // add directives to this object // private parser temp var - UcxList *levels; // tree levels (stack) + ConfigParserLevel *levels; // tree levels (stack) } ObjectConfig; @@ -72,11 +77,11 @@ void free_object_config(ObjectConfig *conf); -int objconf_parse(void *p, ConfigLine *begin, ConfigLine *end, sstr_t line); +int objconf_parse(void *p, ConfigLine *begin, ConfigLine *end, cxmutstr line); int objconf_on_begin_tag(ObjectConfig *conf, ConfigTag *tag); -int objconf_on_end_tag(ObjectConfig *conf, sstr_t tagname); +int objconf_on_end_tag(ObjectConfig *conf, cxmutstr tagname); int objconf_on_directive(ObjectConfig *conf, ConfigDirective *dir);
--- a/src/server/config/serverconfig.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/config/serverconfig.c Sun Nov 06 15:53:32 2022 +0100 @@ -28,14 +28,15 @@ #include "serverconfig.h" +#include "conf.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> -#include <ucx/buffer.h> -#include <ucx/utils.h> +#include <cx/buffer.h> +#include <cx/utils.h> ServerConfig* serverconfig_load(const char *file) { FILE *in = fopen(file, "r"); @@ -43,23 +44,25 @@ return NULL; } - UcxBuffer *buf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND); - if(!buf) { - fclose(in); - return NULL; + CxBuffer buf; + cxBufferInit(&buf, NULL, 16384, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS); + + //ucx_stream_copy(in, buf, (read_func)fread, (write_func)ucx_buffer_write); + char readbuf[2048]; + size_t r; + while((r = fread(readbuf, 1, 2048, in)) > 0) { + cxBufferWrite(readbuf, 1, r, &buf); } - - ucx_stream_copy(in, buf, (read_func)fread, (write_func)ucx_buffer_write); fclose(in); - ServerConfig *scfg = serverconfig_parse(scstrn(buf->space, buf->size)); + ServerConfig *scfg = serverconfig_parse(cx_strn(buf.space, buf.size)); - ucx_buffer_free(buf); + cxBufferDestroy(&buf); return scfg; } -static CFGToken get_next_token(scstr_t content, int *pos) { +static CFGToken get_next_token(cxstring content, int *pos) { CFGToken token = { {NULL, 0}, CFG_NO_TOKEN }; CFGTokenType type = CFG_TOKEN; @@ -130,7 +133,7 @@ } token.type = type; - token.content = scstrsubsl(content, token_begin, token_end - token_begin); + token.content = cx_strsubsl(content, token_begin, token_end - token_begin); return token; } @@ -156,16 +159,16 @@ } */ -static void config_arg_set_value(UcxAllocator *a, ConfigArg *arg, CFGToken token) { - scstr_t nv = scstrchr(token.content, '='); +static void config_arg_set_value(CxAllocator *a, ConfigParam *arg, CFGToken token) { + cxstring nv = cx_strchr(token.content, '='); if(!nv.ptr) { - arg->value = sstrdup_a(a, token.content); + arg->value = cx_strdup_a(a, token.content); } else { intptr_t eq = (intptr_t)(nv.ptr - token.content.ptr); - scstr_t name = token.content; + cxstring name = token.content; name.length = (size_t)eq; - scstr_t value = nv; + cxstring value = nv; value.ptr++; value.length--; if(value.length > 1 && value.ptr[0] == '"' && value.ptr[value.length-1] == '"') { @@ -173,19 +176,28 @@ value.length -= 2; // remove quote } - arg->name = sstrdup_a(a, name); - arg->value = sstrdup_a(a, value); + arg->name = cx_strdup_a(a, name); + arg->value = cx_strdup_a(a, value); } } -ServerConfig* serverconfig_parse(scstr_t content) { - UcxMempool *mp = ucx_mempool_new(512); +static int nodestack_prepend(CxAllocator *a, ConfigNodeStack **stack, ConfigNode *node) { + ConfigNodeStack *elm = cxMalloc(a, sizeof(ConfigNodeStack)); + if(!elm) return 1; + elm->node = node; + elm->next = NULL; + cx_linked_list_prepend((void**)stack, NULL, -1, offsetof(ConfigNodeStack, next), elm); + return 0; +} + +ServerConfig* serverconfig_parse(cxstring content) { + CxMempool *mp = cxBasicMempoolCreate(512); if(!mp) return NULL; - UcxAllocator *a = mp->allocator; + CxAllocator *a = (CxAllocator*)mp->allocator; - ServerConfig *config = ucx_mempool_malloc(mp, sizeof(ServerConfig)); + ServerConfig *config = cxMalloc(a, sizeof(ServerConfig)); if(!config) { - ucx_mempool_destroy(mp); + cxMempoolDestroy(mp); return NULL; } config->mp = mp; @@ -199,12 +211,14 @@ int pos = 0; // needed for tokenizer CFGToken token; - ConfigNode *root_obj = ucx_mempool_calloc(mp, 1, sizeof(ConfigNode)); + ConfigNode *root_obj = cxCalloc(a, 1, sizeof(ConfigNode)); root_obj->type = CONFIG_NODE_OBJECT; - UcxList *node_stack = ucx_list_prepend(NULL, root_obj); + ConfigNodeStack *node_stack = cxMalloc(a, sizeof(ConfigNodeStack)); + node_stack->node = root_obj; + node_stack->next = NULL; - ConfigNode *current = ucx_mempool_calloc(mp, 1, sizeof(ConfigNode)); + ConfigNode *current = cxCalloc(a, 1, sizeof(ConfigNode)); current->type = CONFIG_NODE_SPACE; ConfigNode *obj = NULL; int obj_closed = 0; @@ -213,7 +227,7 @@ int err = 0; while((token = get_next_token(content, &pos)).type != CFG_NO_TOKEN) { //printf("%s [%.*s]\n", token_type_str(token.type), (int)token.content.length, token.content.ptr); - + switch(token.type) { case CFG_NO_TOKEN: break; case CFG_TOKEN_COMMENT: { @@ -224,39 +238,56 @@ } case CFG_TOKEN_SPACE: break; case CFG_TOKEN_NEWLINE: { - scstr_t line = scstrsubsl(content, text_start, pos - text_start); + cxstring line = cx_strsubsl(content, text_start, pos - text_start); text_start = pos; - sstr_t line_cp = sstrdup_a(a, line); + cxmutstr line_cp = cx_strdup_a(a, line); - ConfigNode *parent = node_stack->data; + ConfigNode *parent = node_stack->node; if(current->type == CONFIG_NODE_CLOSE_OBJECT) { + // this is a newline after a object is closed with '}' + // the line containing "}\n" should be added to the object parent->text_end = line_cp; - node_stack = ucx_list_remove_a(a, node_stack, node_stack); + // done with this object, remove it from the stack + ConfigNodeStack *remove_item = node_stack; + node_stack = node_stack->next; + cxFree(a, remove_item); } else if(current->type == CONFIG_NODE_OPEN_OBJECT) { - sstr_t new_textbegin = sstrcat_a(a, 2, obj->text_begin, line_cp); - alfree(a, obj->text_begin.ptr); - alfree(a, line_cp.ptr); + // newline after a object is opened with '{' + // append '{' to the object text + cxmutstr new_textbegin = cx_strcat_a(a, 2, obj->text_begin, line_cp); + cxFree(a, obj->text_begin.ptr); + cxFree(a, line_cp.ptr); obj->text_begin = new_textbegin; } else { + // normal line containing a directive, space or comment + // add it to parent node current->text_begin = line_cp; - ConfigNode *parent = node_stack->data; - parent->children = ucx_list_append_a(a, parent->children, current); + CFG_NODE_ADD(&parent->children_begin, &parent->children_end, current); } + // obj points to the previous node that started as a directive + // the type is set to CONFIG_NODE_OBECT if it was followed by + // a '{' character if(obj && obj->type == CONFIG_NODE_OBJECT) { - node_stack = ucx_list_prepend_a(a, node_stack, obj); + // new object started, add it to the stack + nodestack_prepend(a, &node_stack, obj); obj = NULL; } - current = ucx_mempool_calloc(mp, 1, sizeof(ConfigNode)); + current = cxCalloc(a, 1, sizeof(ConfigNode)); current->type = CONFIG_NODE_SPACE; obj_closed = 0; break; } case CFG_TOKEN: { - if(!sstrcmp(token.content, S("{"))) { + // normal text token + // either a directive/obj name, parameter or { } + + if(!cx_strcmp(token.content, cx_str("{"))) { + // obj is pointing to the previous node that started + // a directive if(!obj) { err = 1; break; @@ -265,7 +296,7 @@ if(current != obj) { current->type = CONFIG_NODE_OPEN_OBJECT; } - } else if(!sstrcmp(token.content, S("}"))) { + } else if(!cx_strcmp(token.content, cx_str("}"))) { obj_closed = 1; // force newline before next directive obj = NULL; current->type = CONFIG_NODE_CLOSE_OBJECT; @@ -276,13 +307,16 @@ } if(!current->name.ptr) { - current->name = sstrdup_a(a, token.content); + // currently this could be a directive or object + current->name = cx_strdup_a(a, token.content); current->type = CONFIG_NODE_DIRECTIVE; - obj = current; + obj = current; // potential object } else { - ConfigArg *arg = ucx_mempool_calloc(mp, 1, sizeof(ConfigArg)); + // name already set, therefore this token must + // be a parameter + ConfigParam *arg = cxCalloc(a, 1, sizeof(ConfigParam)); config_arg_set_value(a, arg, token); - current->args = ucx_list_append_a(a, current->args, arg); + CFG_PARAM_ADD(¤t->args, NULL, arg); } } break; @@ -296,54 +330,52 @@ if(pos < content.length || err) { // content not fully parsed because of an error - ucx_mempool_destroy(mp); + cxMempoolDestroy(mp); return NULL; } //test_print_config(&root_obj); config->root = root_obj; - config->tab = sstrdup_a(a, SC("\t")); + config->tab = cx_strdup_a(a, cx_str("\t")); return config; } void serverconfig_free(ServerConfig *cfg) { - ucx_mempool_destroy(cfg->mp); + cxMempoolDestroy(cfg->mp); } -ConfigNode* serverconfig_get_node(ConfigNode *parent, ConfigNodeType type, scstr_t name) { - UCX_FOREACH(elm, parent->children) { - ConfigNode *node = elm->data; - if(node->type == type && !sstrcasecmp(node->name, name)) { +ConfigNode* serverconfig_get_node(ConfigNode *parent, ConfigNodeType type, cxstring name) { + for(ConfigNode *node=parent->children_begin;node;node=node->next) { + if(node->type == type && !cx_strcasecmp(cx_strcast(node->name), name)) { return node; } } return NULL; } -UcxList* serverconfig_get_node_list(ConfigNode *parent, ConfigNodeType type, scstr_t name) { - UcxList *nodes = NULL; +CxList* serverconfig_get_node_list(ConfigNode *parent, ConfigNodeType type, cxstring name) { + CxList *nodes = cxPointerLinkedListCreate(cxDefaultAllocator, cx_cmp_ptr); - UCX_FOREACH(elm, parent->children) { - ConfigNode *node = elm->data; - if(node->type == type && !sstrcasecmp(node->name, name)) { - nodes = ucx_list_append(nodes, node); + for(ConfigNode *node=parent->children_begin;node;node=node->next) { + if(node->type == type && !cx_strcasecmp(cx_strcast(node->name), name)) { + cxListAdd(nodes, node); } } return nodes; } -scstr_t serverconfig_directive_value(ConfigNode *obj, scstr_t name) { +cxstring serverconfig_directive_value(ConfigNode *obj, cxstring name) { ConfigNode *node = serverconfig_get_node(obj, CONFIG_NODE_DIRECTIVE, name); - if(node && ucx_list_size(node->args) == 1) { - ConfigArg *arg = node->args->data; - return SCSTR(arg->value); + if(node && CFG_NUM_PARAMS(node->args) == 1) { + ConfigParam *arg = node->args; + return (cxstring){ arg->value.ptr, arg->value.length }; } - return scstrn(NULL, 0); + return (cxstring){ NULL, 0 }; } -sstr_t serverconfig_arg_name_value(UcxAllocator *a, scstr_t str, scstr_t *name) { +cxmutstr serverconfig_arg_name_value(CxAllocator *a, cxstring str, cxstring *name) { int valstart = 0; for(int i=0;i<str.length;i++) { if(str.ptr[i] == '=') { @@ -356,6 +388,6 @@ } } - sstr_t ret; + cxmutstr ret; return ret; }
--- a/src/server/config/serverconfig.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/config/serverconfig.h Sun Nov 06 15:53:32 2022 +0100 @@ -29,27 +29,31 @@ #ifndef WS_CONFIG_SERVERCONFIG_H #define WS_CONFIG_SERVERCONFIG_H -#include <ucx/list.h> -#include <ucx/map.h> -#include <ucx/mempool.h> +#include <cx/linked_list.h> +#include <cx/hash_map.h> +#include <cx/mempool.h> +#include <cx/string.h> + +#include "conf.h" #ifdef __cplusplus extern "C" { #endif - +#define CFG_NODE_ADD(list_begin, list_end, elm) \ + cx_linked_list_add((void**)list_begin, (void**)list_end, offsetof(ConfigNode, prev), offsetof(ConfigNode, next), elm) + typedef struct ServerConfig ServerConfig; typedef struct ConfigNode ConfigNode; -typedef struct ConfigArg ConfigArg; typedef struct CFGToken CFGToken; typedef enum ConfigNodeType ConfigNodeType; typedef enum CFGTokenType CFGTokenType; struct ServerConfig { - UcxMempool *mp; + CxMempool *mp; ConfigNode *root; - sstr_t tab; + cxmutstr tab; }; enum ConfigNodeType { @@ -62,21 +66,25 @@ }; struct ConfigNode { - sstr_t text_begin; - sstr_t text_end; + cxmutstr text_begin; + cxmutstr text_end; ConfigNodeType type; - sstr_t name; - UcxList *args; - UcxList *children; + cxmutstr name; + ConfigParam *args; + ConfigNode *children_begin; + ConfigNode *children_end; + + ConfigNode *prev; + ConfigNode *next; }; -struct ConfigArg { - sstr_t name; - sstr_t value; +typedef struct ConfigNodeStack ConfigNodeStack; +struct ConfigNodeStack { + ConfigNode *node; + ConfigNodeStack *next; }; - enum CFGTokenType { CFG_NO_TOKEN = 0, CFG_TOKEN_COMMENT, @@ -86,23 +94,23 @@ }; struct CFGToken { - scstr_t content; + cxstring content; CFGTokenType type; }; ServerConfig* serverconfig_load(const char *file); -ServerConfig* serverconfig_parse(scstr_t content); +ServerConfig* serverconfig_parse(cxstring content); void serverconfig_free(ServerConfig *cfg); -ConfigNode* serverconfig_get_node(ConfigNode *parent, ConfigNodeType type, scstr_t name); +ConfigNode* serverconfig_get_node(ConfigNode *parent, ConfigNodeType type, cxstring name); -UcxList* serverconfig_get_node_list(ConfigNode *parent, ConfigNodeType type, scstr_t name); +CxList* serverconfig_get_node_list(ConfigNode *parent, ConfigNodeType type, cxstring name); -scstr_t serverconfig_directive_value(ConfigNode *obj, scstr_t name); +cxstring serverconfig_directive_value(ConfigNode *obj, cxstring name); -sstr_t serverconfig_arg_name_value(UcxAllocator *a, scstr_t str, scstr_t *name); +cxmutstr serverconfig_arg_name_value(CxAllocator *a, cxstring str, cxstring *name); #ifdef __cplusplus }
--- a/src/server/daemon/acl.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/acl.c Sun Nov 06 15:53:32 2022 +0100 @@ -318,7 +318,7 @@ gid_t owninggroup); int fs_acl_check(SysACL *acl, User *user, const char *path, uint32_t access_mask) { - sstr_t p; + cxmutstr p; if(path[0] != '/') { size_t n = 128; char *cwd = malloc(n); @@ -331,12 +331,12 @@ return 0; } } - sstr_t wd = sstr(cwd); - sstr_t pp = sstr((char*)path); + cxmutstr wd = cx_str(cwd); + cxmutstr pp = cx_str((char*)path); - p = sstrcat(3, wd, sstrn("/", 1), pp); + p = cx_strcat(3, wd, cx_strn("/", 1), pp); } else { - p = sstrdup(sstr((char*)path)); + p = cx_strdup(cx_str((char*)path)); } if(p.ptr[p.length-1] == '/') { p.ptr[p.length-1] = 0;
--- a/src/server/daemon/acldata.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/acldata.c Sun Nov 06 15:53:32 2022 +0100 @@ -32,13 +32,13 @@ #include "acldata.h" -ACLData* acl_data_new(UcxAllocator *a) { - ACLData *dat = almalloc(a, sizeof(ACLData)); +ACLData* acl_data_new(CxAllocator *a) { + ACLData *dat = cxMalloc(a, sizeof(ACLData)); if(!dat) { return NULL; } - dat->namedACLs = ucx_map_new_a(a, 16); + dat->namedACLs = cxHashMapCreate(a, 16); if(!dat->namedACLs) { return NULL; } @@ -46,7 +46,7 @@ return dat; } -ACLList* acl_get(ACLData *acldata, char *name) { - ACLList *acl = ucx_map_cstr_get(acldata->namedACLs, name); +ACLList* acl_get(ACLData *acldata, const char *name) { + ACLList *acl = cxMapGet(acldata->namedACLs, cx_hash_key_str(name)); return acl; }
--- a/src/server/daemon/acldata.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/acldata.h Sun Nov 06 15:53:32 2022 +0100 @@ -29,8 +29,8 @@ #ifndef ACLCONF_H #define ACLCONF_H -#include <ucx/list.h> -#include <ucx/map.h> +#include <cx/list.h> +#include <cx/map.h> #include "acl.h" #include "../config/acl.h" @@ -40,12 +40,12 @@ #endif typedef struct acl_data { - UcxMap *namedACLs; + CxMap *namedACLs; } ACLData; ACLData* acl_data_new(); -ACLList* acl_get(ACLData *acldata, char *name); +ACLList* acl_get(ACLData *acldata, const char *name); #ifdef __cplusplus }
--- a/src/server/daemon/auth.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/auth.c Sun Nov 06 15:53:32 2022 +0100 @@ -31,7 +31,7 @@ #include <string.h> #include <pthread.h> -#include <ucx/map.h> +#include <cx/map.h> #include "../public/nsapi.h" #include "../util/atomic.h" @@ -52,7 +52,7 @@ cache.trail = NULL; } -User* auth_cache_get(char *authdb, char *user) { +User* auth_cache_get(char *authdb, const char *user) { //printf("auth_cache_get: %s\n", user); /* * create the key to access the map @@ -62,12 +62,12 @@ size_t userlen = strlen(user); size_t keylen = authdblen + userlen + 1; - char *key = malloc(keylen); + unsigned char *key = malloc(keylen); memcpy(key, authdb, authdblen); key[authdblen] = 0; memcpy(key + authdblen + 1, user, userlen); - UcxKey mapkey = ucx_key(key, keylen); + CxHashKey mapkey = cx_hash_key_bytes(key, keylen); // get cached user from map time_t now = time(NULL); @@ -84,7 +84,7 @@ if(elm) { // compare the key data to be sure it is the correct user int n = (mapkey.len > elm->key.len) ? elm->key.len : mapkey.len; - if (!memcmp(elm->key.data, mapkey.data, n)) { + if (!memcmp(elm->key.data.cbytes, mapkey.data.cbytes, n)) { // elm is now the correct UserCacheElm // TODO: use configuration for expire time if(now - elm->created > 120) { @@ -119,8 +119,8 @@ void auth_cache_add( char *authdb, User *user, - char *password, - char **groups, + const char *password, + const char **groups, size_t numgroups) { //printf("auth_cache_add: %s\n", user->name); @@ -140,10 +140,10 @@ cusr->authdb = strdup(authdb); cusr->password = strdup(password); - cusr->groups = numgroups ? calloc(numgroups, sizeof(sstr_t)) : NULL; + cusr->groups = numgroups ? calloc(numgroups, sizeof(cxmutstr)) : NULL; cusr->numgroups = numgroups; for(int i=0;i<numgroups;i++) { - cusr->groups[i] = sstrdup(sstr(groups[i])); + cusr->groups[i] = cx_strdup(cx_str(groups[i])); } cusr->ref = 1; @@ -164,13 +164,13 @@ size_t authdblen = strlen(authdb); size_t userlen = strlen(user->name); size_t keylen = authdblen + userlen + 1; - char *key = malloc(keylen); + unsigned char *key = malloc(keylen); memcpy(key, authdb, authdblen); key[authdblen] = 0; memcpy(key + authdblen + 1, user->name, userlen); - UcxKey mapkey = ucx_key(key, keylen); + CxHashKey mapkey = cx_hash_key_bytes(key, keylen); - elm->key.data = key; + elm->key.data.bytes = key; elm->key.len = mapkey.len; elm->key.hash = mapkey.hash; elm->slot = mapkey.hash%cache.size; @@ -237,14 +237,14 @@ cache.map[elm->slot] = elm->next_elm; } - free(elm->key.data); + free(elm->key.data.bytes); cached_user_unref(elm->user); free(elm); cache.count--; } -int cached_user_verify_password(CachedUser *user, char *password) { +int cached_user_verify_password(CachedUser *user, const char *password) { if(!strcmp(user->password, password)) { return 1; } else { @@ -252,10 +252,10 @@ } } -int cached_user_check_group(CachedUser *user, char *group) { - sstr_t grp = sstr(group); +int cached_user_check_group(CachedUser *user, const char *group) { + cxstring grp = cx_str(group); for(int i=0;i<user->numgroups;i++) { - if(!sstrcmp(user->groups[i], grp)) { + if(!cx_strcmp(cx_strcast(user->groups[i]), grp)) { return 1; } } @@ -283,7 +283,7 @@ * from public/auth.h */ -User* authdb_get_user(AuthDB *db, char *user) { +User* authdb_get_user(AuthDB *db, const char *user) { if(db->use_cache) { User *u = auth_cache_get(db->name, user); if(u) { @@ -293,7 +293,7 @@ return db->get_user(db, user); } -User* authdb_get_and_verify(AuthDB *db, char *user, char *password, int *pw) { +User* authdb_get_and_verify(AuthDB *db, const char *user, const char *password, int *pw) { User *u = NULL; // try getting the user from the cache if(db->use_cache) {
--- a/src/server/daemon/auth.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/auth.h Sun Nov 06 15:53:32 2022 +0100 @@ -33,7 +33,8 @@ #include <inttypes.h> #include "../public/auth.h" -#include <ucx/map.h> +#include <cx/map.h> +#include <cx/string.h> #ifdef __cplusplus extern "C" { @@ -42,7 +43,7 @@ typedef struct { User user; char *authdb; - sstr_t *groups; + cxmutstr *groups; size_t numgroups; char *password; uint32_t ref; @@ -52,7 +53,7 @@ struct user_cache_elm { CachedUser *user; UserCacheElm *next_user; // next elm in the cached user list - struct UcxMapKey key; // key to access this element + CxHashKey key; // key to access this element size_t slot; // slot in the map UserCacheElm *next_elm; // next element in this map slot time_t created; @@ -69,18 +70,18 @@ void auth_cache_init(); -User* auth_cache_get(char *authdb, char *user); +User* auth_cache_get(char *authdb, const char *user); void auth_cache_add( char *authdb, User *user, - char *password, - char **groups, + const char *password, + const char **groups, size_t numgroups); void auth_cache_remove_from_map(UserCacheElm *elm); -int cached_user_verify_password(CachedUser *user, char *password); -int cached_user_check_group(CachedUser *user, char *group); +int cached_user_verify_password(CachedUser *user, const char *password); +int cached_user_check_group(CachedUser *user, const char *group); void cached_user_unref(CachedUser *user); void cached_user_delete(CachedUser *user);
--- a/src/server/daemon/config.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/config.c Sun Nov 06 15:53:32 2022 +0100 @@ -37,8 +37,11 @@ #include <sys/stat.h> #include <sys/mman.h> -#include <ucx/string.h> -#include <ucx/utils.h> +#include <cx/string.h> +#include <cx/utils.h> +#include <cx/hash_map.h> +#include <cx/linked_list.h> +#include <cx/compare.h> #include "httplistener.h" #include "config.h" @@ -54,13 +57,13 @@ #include "../util/pblock.h" #include "../util/util.h" #include "../util/atomic.h" -#include "ucx/buffer.h" +#include "cx/buffer.h" pool_handle_t *init_pool; char* cfg_config_file_path(const char *file) { - sstr_t base = ST("config/"); - sstr_t path = sstrcat(2, base, scstr(file)); + cxstring base = CX_STR("config/"); + cxmutstr path = cx_strcat(2, base, cx_str(file)); return path.ptr; } @@ -72,24 +75,23 @@ log_ereport(LOG_FAILURE, "Cannot load init.conf"); return 1; } - UcxAllocator *mp = cfg->parser.mp; + CxAllocator *mp = cfg->parser.mp; init_pool = pool_create(); // one pool for one Configuration - UcxList *dirs = cfg->directives; + ConfigDirectiveList *dirs = cfg->directives; while(dirs != NULL) { - ConfigDirective *dir = dirs->data; + ConfigDirective *dir = dirs->directive; /* create NSAPI directive */ directive *d = malloc(sizeof(directive)); d->param = pblock_create_pool(init_pool, 8); - UcxList *param = cfg_param_list(dir->value, mp); + ConfigParam *param = cfg_param_list(dir->value, mp); while(param != NULL) { - ConfigParam *p = param->data; pblock_nvlinsert( - p->name.ptr, - p->name.length, - p->value.ptr, - p->value.length, + param->name.ptr, + param->name.length, + param->value.ptr, + param->value.length, d->param); param = param->next; @@ -148,15 +150,15 @@ serverconfig->ref = 1; serverconfig->pool = pool; - UcxAllocator allocator = util_pool_allocator(serverconfig->pool); - serverconfig->a = pool_malloc(pool, sizeof(UcxAllocator)); - *serverconfig->a = allocator; + CxAllocator *allocator = pool_allocator(serverconfig->pool); + serverconfig->a = allocator; - serverconfig->listeners = NULL; - serverconfig->host_vs = ucx_map_new_a(serverconfig->a, 16); - serverconfig->authdbs = ucx_map_new_a(serverconfig->a, 16); - serverconfig->resources = ucx_map_new_a(serverconfig->a, 16); - serverconfig->dav = ucx_map_new_a(serverconfig->a, 16); + serverconfig->listeners = cxPointerLinkedListCreate(serverconfig->a, cx_cmp_ptr); + serverconfig->logfiles = cxPointerLinkedListCreate(serverconfig->a, cx_cmp_ptr); + serverconfig->host_vs = cxHashMapCreate(serverconfig->a, 16); + serverconfig->authdbs = cxHashMapCreate(serverconfig->a, 16); + serverconfig->resources = cxHashMapCreate(serverconfig->a, 16); + serverconfig->dav = cxHashMapCreate(serverconfig->a, 16); // STAGE 1 load_server_conf: // At stage 1 we load the file and get the Runtime infos for changing @@ -168,25 +170,26 @@ // Listener (dependencies: Threadpool, EventHandler) // load Runtime config - UcxList *list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("Runtime")); - UCX_FOREACH(elm, list) { - ConfigNode *runtimeobj = elm->data; + CxList *list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, cx_str("Runtime")); + CxIterator iter = cxListIterator(list, 0); + cx_foreach(ConfigNode *, runtimeobj, iter) { if(cfg_handle_runtime(serverconfig, runtimeobj)) { // error return NULL; } } - ucx_list_free(list); + cxListDestroy(list); // load threadpool config log_ereport(LOG_DEBUG, "apply config: Threadpool"); - list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("Threadpool")); - UCX_FOREACH(elm, list) { - if(cfg_handle_threadpool(serverconfig, elm->data)) { + list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, cx_str("Threadpool")); + iter = cxListIterator(list, 0); + cx_foreach(ConfigNode *, elm, iter) { + if(cfg_handle_threadpool(serverconfig, elm)) { return NULL; } } - ucx_list_free(list); + cxListDestroy(list); // check thread pool config if(check_thread_pool_cfg() != 0) { /* critical error */ @@ -195,9 +198,10 @@ // load eventhandler config log_ereport(LOG_DEBUG, "apply config: EventHandler"); - list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("EventHandler")); - UCX_FOREACH(elm, list) { - if(cfg_handle_eventhandler(serverconfig, elm->data)) { + list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, cx_str("EventHandler")); + iter = cxListIterator(list, 0); + cx_foreach(ConfigNode *, elm, iter) { + if(cfg_handle_eventhandler(serverconfig, elm)) { // error return NULL; } @@ -207,18 +211,18 @@ /* critical error */ return NULL; } - ucx_list_free(list); + cxListDestroy(list); // load Listener config log_ereport(LOG_DEBUG, "apply config: Listener"); - list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("Listener")); - UCX_FOREACH(elm, list) { - ConfigNode *scfgobj = elm->data; + list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, cx_str("Listener")); + iter = cxListIterator(list, 0); + cx_foreach(ConfigNode *, scfgobj, iter) { if(cfg_handle_listener(serverconfig, scfgobj)) { return NULL; } } - ucx_list_free(list); + cxListDestroy(list); // we return here, to let the webserver use the runtime info to // change the uid if needed @@ -242,95 +246,92 @@ */ // init logfile first - UcxList *list; + CxList *list; log_ereport(LOG_DEBUG, "apply config: LogFile"); - list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("LogFile")); - if(list) { - ConfigNode *logobj = list->data; + list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, cx_str("LogFile")); + CxIterator iter = cxListIterator(list, 0); + cx_foreach(ConfigNode *, logobj, iter) { if(!logobj) { // error - return NULL; // TODO: fix memory leak + cxListDestroy(list); + return NULL; } int ret = cfg_handle_logfile(serverconfig, logobj); if(ret != 0) { // cannot initialize log file - return NULL; // TODO: fix memory leak + cxListDestroy(list); + return NULL; } - } else { - // horrible error - return NULL; } - ucx_list_free(list); + cxListDestroy(list); log_ereport(LOG_DEBUG, "apply config: AccessLog"); - list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("AccessLog")); - UCX_FOREACH(elm, list) { - ConfigNode *scfgobj = elm->data; + list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, cx_str("AccessLog")); + iter = cxListIterator(list, 0); + cx_foreach(ConfigNode *, scfgobj, iter) { if(cfg_handle_accesslog(serverconfig, scfgobj)) { return NULL; } } - ucx_list_free(list); + cxListDestroy(list); log_ereport(LOG_DEBUG, "apply config: AuthDB"); - list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("AuthDB")); - UCX_FOREACH(elm, list) { - ConfigNode *scfgobj = elm->data; + list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, cx_str("AuthDB")); + iter = cxListIterator(list, 0); + cx_foreach(ConfigNode *, scfgobj, iter) { if(cfg_handle_authdb(serverconfig, scfgobj)) { return NULL; } } - ucx_list_free(list); + cxListDestroy(list); log_ereport(LOG_DEBUG, "apply config: VirtualServer"); - list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("VirtualServer")); - UCX_FOREACH(elm, list) { - ConfigNode *scfgobj = elm->data; + list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, cx_str("VirtualServer")); + iter = cxListIterator(list, 0); + cx_foreach(ConfigNode *, scfgobj, iter) { if(cfg_handle_vs(serverconfig, scfgobj)) { return NULL; } } - ucx_list_free(list); + cxListDestroy(list); log_ereport(LOG_DEBUG, "apply config: ResourcePool"); - list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("ResourcePool")); - UCX_FOREACH(elm, list) { - ConfigNode *scfgobj = elm->data; + list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, cx_str("ResourcePool")); + iter = cxListIterator(list, 0); + cx_foreach(ConfigNode *, scfgobj, iter) { if(cfg_handle_resourcepool(serverconfig, scfgobj)) { return NULL; } } + cxListDestroy(list); log_ereport(LOG_DEBUG, "apply config: Dav"); - list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("Dav")); - UCX_FOREACH(elm, list) { - ConfigNode *scfgobj = elm->data; + list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, cx_str("Dav")); + iter = cxListIterator(list, 0); + cx_foreach(ConfigNode *, scfgobj, iter) { if(cfg_handle_dav(serverconfig, scfgobj)) { return NULL; } } + cxListDestroy(list); // set VirtualServer for all listeners - UcxList *ls = serverconfig->listeners; - while(ls) { - HttpListener *listener = ls->data; - - sstr_t vsname = sstr(listener->default_vs.vs_name); + CxList *ls = serverconfig->listeners; + iter = cxListIterator(ls, 0); + cx_foreach(HttpListener *, listener, iter) { + cxstring vsname = cx_str(listener->default_vs.vs_name); // search for VirtualServer //int b = 0; - UcxMapIterator iter = ucx_map_iterator(serverconfig->host_vs); - VirtualServer *vs; - UCX_MAP_FOREACH(key, vs, iter) { - if(!sstrcmp(vsname, vs->name)) { + CxIterator map_iter = cxMapIteratorValues(serverconfig->host_vs); + cx_foreach(VirtualServer *, vs, map_iter) { + if(!cx_strcmp(vsname, (cxstring){vs->name.ptr, vs->name.length})) { listener->default_vs.vs = vs; break; } } - - ls = ls->next; } serverconfig_free(serverconf); @@ -355,13 +356,13 @@ } int cfg_handle_runtime(ServerConfiguration *cfg, ConfigNode *obj) { - scstr_t user = serverconfig_directive_value(obj, SC("User")); + cxstring user = serverconfig_directive_value(obj, cx_str("User")); if(user.ptr) { - cfg->user = sstrdup_a(cfg->a, user); + cfg->user = cx_strdup_a(cfg->a, user); } - scstr_t tmp = serverconfig_directive_value(obj, SC("Temp")); + cxstring tmp = serverconfig_directive_value(obj, cx_str("Temp")); if(tmp.ptr) { - cfg->tmp = sstrdup_a(cfg->a, tmp); + cfg->tmp = cx_strdup_a(cfg->a, tmp); } else { // TODO: do this check after all config loading is done log_ereport(LOG_MISCONFIG, "no temporary directory specified"); @@ -369,9 +370,9 @@ } // mime file - scstr_t mf = serverconfig_directive_value(obj, SC("MimeFile")); - scstr_t base = SC("config/"); - sstr_t file = sstrcat(2, base, mf); + cxstring mf = serverconfig_directive_value(obj, cx_str("MimeFile")); + cxstring base = cx_str("config/"); + cxmutstr file = cx_strcat(2, base, mf); if(mime_conf_load(cfg, file)) { return -1; @@ -382,8 +383,8 @@ } int cfg_handle_logfile(ServerConfiguration *cfg, ConfigNode *obj) { - scstr_t file = serverconfig_directive_value(obj, SC("File")); - scstr_t lvl = serverconfig_directive_value(obj, SC("Level")); + cxstring file = serverconfig_directive_value(obj, cx_str("File")); + cxstring lvl = serverconfig_directive_value(obj, cx_str("Level")); int err = 0; if(file.ptr == NULL) { @@ -418,11 +419,11 @@ poolcfg.queue_size = 64; poolcfg.stack_size = 262144; - scstr_t name = serverconfig_directive_value(obj, SC("Name")); - scstr_t min = serverconfig_directive_value(obj, SC("MinThreads")); - scstr_t max = serverconfig_directive_value(obj, SC("MaxThreads")); - scstr_t stack = serverconfig_directive_value(obj, SC("StackSize")); - scstr_t queue = serverconfig_directive_value(obj, SC("QueueSize")); + cxstring name = serverconfig_directive_value(obj, cx_str("Name")); + cxstring min = serverconfig_directive_value(obj, cx_str("MinThreads")); + cxstring max = serverconfig_directive_value(obj, cx_str("MaxThreads")); + cxstring stack = serverconfig_directive_value(obj, cx_str("StackSize")); + cxstring queue = serverconfig_directive_value(obj, cx_str("QueueSize")); // TODO: Type if(name.length == 0) { @@ -477,9 +478,9 @@ int cfg_handle_eventhandler(ServerConfiguration *c, ConfigNode *obj) { EventHandlerConfig evcfg; - scstr_t name = serverconfig_directive_value(obj, SC("Name")); - scstr_t threads = serverconfig_directive_value(obj, SC("Threads")); - scstr_t isdefault = serverconfig_directive_value(obj, SC("Default")); + cxstring name = serverconfig_directive_value(obj, cx_str("Name")); + cxstring threads = serverconfig_directive_value(obj, cx_str("Threads")); + cxstring isdefault = serverconfig_directive_value(obj, cx_str("Default")); evcfg.name = name; @@ -501,8 +502,8 @@ } int cfg_handle_resourcepool(ServerConfiguration *cfg, ConfigNode *obj) { - scstr_t name = serverconfig_directive_value(obj, SC("Name")); - scstr_t type = serverconfig_directive_value(obj, SC("Type")); + cxstring name = serverconfig_directive_value(obj, cx_str("Name")); + cxstring type = serverconfig_directive_value(obj, cx_str("Type")); int ret = 0; if(resourcepool_new(cfg, type, name, obj)) { @@ -515,11 +516,11 @@ int cfg_handle_accesslog(ServerConfiguration *cfg, ConfigNode *obj) { // TODO: use a name to identify the log file - scstr_t file = serverconfig_directive_value(obj, SC("File")); + cxstring file = serverconfig_directive_value(obj, cx_str("File")); if(file.ptr == NULL) { return 0; } - sstr_t format; + cxmutstr format; format.ptr = NULL; format.length = 0; @@ -530,10 +531,10 @@ return 0; } AccessLog *log = pool_malloc(cfg->pool, sizeof(AccessLog)); - log->file = sstrdup_a(cfg->a, file); + log->file = cx_strdup_a(cfg->a, file); log->format = format; log->log = log_file; - cfg->logfiles = ucx_list_append_a(cfg->a, cfg->logfiles, log); + cxListAdd(cfg->logfiles, log); if(!cfg->default_log) { cfg->default_log = log; @@ -543,30 +544,30 @@ } int cfg_handle_authdb(ServerConfiguration *cfg, ConfigNode *obj) { - scstr_t name = serverconfig_directive_value(obj, SC("Name")); - scstr_t type = serverconfig_directive_value(obj, SC("Type")); + cxstring name = serverconfig_directive_value(obj, cx_str("Name")); + cxstring type = serverconfig_directive_value(obj, cx_str("Type")); AuthDB *authdb = NULL; - if(!sstrcmp(type, sstr("ldap"))) { + if(!cx_strcmp(type, cx_str("ldap"))) { LDAPConfig conf; - scstr_t host = serverconfig_directive_value(obj, SC("Host")); - scstr_t port = serverconfig_directive_value( obj, SC("Port")); - scstr_t basedn = serverconfig_directive_value(obj, SC("BaseDN")); - scstr_t binddn = serverconfig_directive_value(obj, SC("BindDN")); - scstr_t basepw = serverconfig_directive_value(obj, SC("BindPW")); + cxstring host = serverconfig_directive_value(obj, cx_str("Host")); + cxstring port = serverconfig_directive_value( obj, cx_str("Port")); + cxstring basedn = serverconfig_directive_value(obj, cx_str("BaseDN")); + cxstring binddn = serverconfig_directive_value(obj, cx_str("BindDN")); + cxstring basepw = serverconfig_directive_value(obj, cx_str("BindPW")); - conf.hostname = sstrdup_a(cfg->a, host).ptr; + conf.hostname = cx_strdup_a(cfg->a, host).ptr; conf.port = atoi(port.ptr); - conf.basedn = sstrdup_a(cfg->a, basedn).ptr; - conf.binddn = sstrdup_a(cfg->a, binddn).ptr; - conf.bindpw = sstrdup_a(cfg->a, basepw).ptr; + conf.basedn = cx_strdup_a(cfg->a, basedn).ptr; + conf.binddn = cx_strdup_a(cfg->a, binddn).ptr; + conf.bindpw = cx_strdup_a(cfg->a, basepw).ptr; authdb = create_ldap_authdb(cfg, name.ptr, &conf); - } else if(!sstrcmp(type, sstr("keyfile"))) { + } else if(!cx_strcmp(type, cx_str("keyfile"))) { // we only need the file parameter - scstr_t file = serverconfig_directive_value(obj, SC("File")); + cxstring file = serverconfig_directive_value(obj, cx_str("File")); if(file.length == 0) { log_ereport( LOG_MISCONFIG, @@ -579,7 +580,7 @@ } if(authdb) { - if(ucx_map_sstr_put(cfg->authdbs, name, authdb)) { + if(cxMapPut(cfg->authdbs, cx_hash_key_bytes((const unsigned char*)name.ptr, name.length), authdb)) { return -1; } } @@ -594,13 +595,13 @@ lc.port = 8080; lc.nacceptors = 1; - scstr_t name = serverconfig_directive_value(obj, SC("Name")); - scstr_t port = serverconfig_directive_value(obj, SC("Port")); - scstr_t vs = serverconfig_directive_value(obj, SC("DefaultVS")); - scstr_t thrp = serverconfig_directive_value(obj, SC("Threadpool")); - scstr_t blck = serverconfig_directive_value(obj, SC("BlockingIO")); + cxstring name = serverconfig_directive_value(obj, cx_str("Name")); + cxstring port = serverconfig_directive_value(obj, cx_str("Port")); + cxstring vs = serverconfig_directive_value(obj, cx_str("DefaultVS")); + cxstring thrp = serverconfig_directive_value(obj, cx_str("Threadpool")); + cxstring blck = serverconfig_directive_value(obj, cx_str("BlockingIO")); - // TODO: use sstrdup_pool? + // TODO: use cx_strdup_pool? int64_t port_value; if(!util_strtoint(port.ptr, &port_value)) { log_ereport(LOG_MISCONFIG, "Listener: Invalid argument for parameter 'Port': '%s'", port.ptr); @@ -611,19 +612,19 @@ return 1; } - lc.name = sstrdup(name); + lc.name = cx_strdup(name); lc.port = port_value; - lc.vs = sstrdup(vs); - lc.threadpool = sstrdup(thrp); + lc.vs = cx_strdup(vs); + lc.threadpool = cx_strdup(thrp); lc.blockingio = util_getboolean_s(blck, WS_FALSE); - scstr_t ssl = serverconfig_directive_value(obj, SC("SSL")); + cxstring ssl = serverconfig_directive_value(obj, cx_str("SSL")); if(util_getboolean_s(ssl, WS_FALSE)) { - scstr_t cert = serverconfig_directive_value(obj, SC("Cert")); - scstr_t privkey = serverconfig_directive_value(obj, SC("Key")); - scstr_t chain = serverconfig_directive_value(obj, SC("CertChain")); - scstr_t disableprot = serverconfig_directive_value(obj, SC("SSLDisableProtocol")); + cxstring cert = serverconfig_directive_value(obj, cx_str("Cert")); + cxstring privkey = serverconfig_directive_value(obj, cx_str("Key")); + cxstring chain = serverconfig_directive_value(obj, cx_str("CertChain")); + cxstring disableprot = serverconfig_directive_value(obj, cx_str("SSLDisableProtocol")); WSBool config_ok = WS_TRUE; // TODO: log error @@ -660,8 +661,8 @@ return 1; } - listener->default_vs.vs_name = sstrdup_a(cfg->a, lc.vs).ptr; - cfg->listeners = ucx_list_append_a(cfg->a, cfg->listeners, listener); + listener->default_vs.vs_name = cx_strdup_a(cfg->a, (cxstring){lc.vs.ptr, lc.vs.length}).ptr; + cxListAdd(cfg->listeners, listener); return 0; } @@ -669,18 +670,17 @@ int cfg_handle_vs(ServerConfiguration *cfg, ConfigNode *obj) { VirtualServer *vs = vs_new(); - vs->name = sstrdup_a(cfg->a, serverconfig_directive_value(obj, SC("Name"))); - vs->host = sstrdup_a(cfg->a, serverconfig_directive_value(obj, SC("Host"))); - vs->document_root = sstrdup_a(cfg->a, serverconfig_directive_value(obj, SC("DocRoot"))); + vs->name = cx_strdup_a(cfg->a, serverconfig_directive_value(obj, cx_str("Name"))); + vs->host = cx_strdup_a(cfg->a, serverconfig_directive_value(obj, cx_str("Host"))); + vs->document_root = cx_strdup_a(cfg->a, serverconfig_directive_value(obj, cx_str("DocRoot"))); - scstr_t objfile = serverconfig_directive_value(obj, SC("ObjectFile")); - scstr_t aclfile = serverconfig_directive_value(obj, SC("ACLFile")); + cxstring objfile = serverconfig_directive_value(obj, cx_str("ObjectFile")); + cxstring aclfile = serverconfig_directive_value(obj, cx_str("ACLFile")); // load the object config file - sstr_t base = sstr("config/"); - sstr_t file = sstrcat(2, base, objfile); - // sstrcat with allocator because we want to keep the string - file = sstrcat_a(cfg->a, 2, base, objfile); + cxstring base = cx_str("config/"); + // cx_strcat with allocator because we want to keep the string + cxmutstr file = cx_strcat_a(cfg->a, 2, base, objfile); HTTPObjectConfig *httpobj = objconf_load(cfg, file); if(!httpobj) { @@ -691,45 +691,44 @@ // load acl config file - file = sstrcat(2, base, aclfile); + cxmutstr acl_filepath = cx_strcat(2, base, aclfile); - ACLData *acldata = acl_conf_load(cfg, file); + ACLData *acldata = acl_conf_load(cfg, acl_filepath.ptr); + free(acl_filepath.ptr); if(!acldata) { return -1; } vs->acls = acldata; - free(file.ptr); - // set the access log for the virtual server // TODO: don't always use the default vs->log = cfg->default_log; - ucx_map_sstr_put(cfg->host_vs, vs->host, vs); + cxMapPut(cfg->host_vs, cx_hash_key_bytes((unsigned const char*)vs->host.ptr, vs->host.length), vs); return 0; } int cfg_handle_dav(ServerConfiguration *cfg, ConfigNode *obj) { - UcxList *backends = NULL; // list of ConfigArg* - UcxAllocator a = util_pool_allocator(cfg->pool); + CxAllocator *a = pool_allocator(cfg->pool); + CxList *backends = cxPointerLinkedListCreate(a, cx_cmp_ptr); // list of ConfigParam* int init_error; // parse args char *uri = NULL; char *ppath = NULL; char *name = NULL; - UCX_FOREACH(elm, obj->args) { - ConfigArg *arg = elm->data; + for(ConfigParam *arg=obj->args;arg;arg=arg->next) { + cxstring arg_name = (cxstring){ arg->name.ptr, arg->name.length }; if(arg->name.ptr == NULL) { // default: uri uri = arg->value.ptr; - } else if(!sstrcasecmp(arg->name, SC("uri"))) { + } else if(!cx_strcasecmp(arg_name, cx_str("uri"))) { uri = arg->value.ptr; - } else if(!sstrcasecmp(arg->name, SC("ppath"))) { + } else if(!cx_strcasecmp(arg_name, cx_str("ppath"))) { ppath = arg->value.ptr; - } else if(!sstrcasecmp(arg->name, SC("name"))) { + } else if(!cx_strcasecmp(arg_name, cx_str("name"))) { name = arg->value.ptr; } } @@ -738,21 +737,20 @@ } // get a list of all DavBackends - UCX_FOREACH(elm, obj->children) { - ConfigNode *node = elm->data; - if(!sstrcasecmp(node->name, SC("DavBackend"))) { + for(ConfigNode *node=obj->children_begin;node;node=node->next) { + cxstring node_name = cx_strn(node->name.ptr, node->name.length); + if(!cx_strcasecmp(node_name, cx_str("DavBackend"))) { if(node->type == CONFIG_NODE_DIRECTIVE) { - if(ucx_list_size(node->args) == 1) { - ConfigArg *arg = node->args->data; - backends = ucx_list_append(backends, arg); + if(CFG_NUM_PARAMS(node->args) == 1) { + cxListAdd(backends, node->args); } else { log_ereport(LOG_MISCONFIG, "DavBackend must have only one value"); - ucx_list_free(backends); + cxListDestroy(backends); return 1; } } else { log_ereport(LOG_MISCONFIG, "DavBackend must be a directive"); - ucx_list_free(backends); + cxListDestroy(backends); return 1; } } @@ -762,14 +760,14 @@ WebdavRepository *repository = pool_malloc(cfg->pool, sizeof(WebdavRepository)); repository->vfs = NULL; repository->vfsInitData = NULL; - repository->davBackends = NULL; + repository->davBackends = cxPointerLinkedListCreate(a, cx_cmp_ptr); // value type: WebdavBackendInitData* // initialize backends - UCX_FOREACH(elm, backends) { + CxIterator i = cxListIterator(backends, 0); + cx_foreach(ConfigParam *, backendArg, i) { // the DavBackend value should contain the dav class name - ConfigArg *backendArg = elm->data; - WebdavType *dav = webdav_get_type((scstr_t){backendArg->value.ptr, backendArg->value.length}); + WebdavType *dav = webdav_get_type((cxstring){backendArg->value.ptr, backendArg->value.length}); if(!dav) { log_ereport(LOG_MISCONFIG, "Unknown webdav backend type '%s'", backendArg->value.ptr); ret = 1; @@ -794,13 +792,14 @@ davInit->davType = dav; davInit->davInitData = init_data; - repository->davBackends = ucx_list_append_a(&a, repository->davBackends, davInit); + cxListAdd(repository->davBackends, davInit); } + cxListDestroy(backends); // initialize vfs - scstr_t vfs_class = serverconfig_directive_value(obj, SC("VFS")); + cxstring vfs_class = serverconfig_directive_value(obj, cx_str("VFS")); if(vfs_class.length > 0) { - VfsType *vfs = vfs_get_type((scstr_t){vfs_class.ptr, vfs_class.length}); + VfsType *vfs = vfs_get_type((cxstring){vfs_class.ptr, vfs_class.length}); if(vfs) { repository->vfs = vfs; repository->vfsInitData = vfs_init_backend(cfg, cfg->pool, vfs, obj, &init_error); @@ -813,9 +812,9 @@ } } - scstr_t object = serverconfig_directive_value(obj, SC("Object")); + cxstring object = serverconfig_directive_value(obj, cx_str("Object")); if(object.length > 0) { - repository->object = sstrdup_a(&a, object); + repository->object = cx_strdup_a(a, object); if(repository->object.length != object.length) { // OOM log_ereport(LOG_FAILURE, "Cannot create webdav repository: OOM"); @@ -825,7 +824,7 @@ if(!ret) { if(name) { - ucx_map_cstr_put(cfg->dav, name, repository); + cxMapPut(cfg->dav, cx_hash_key_str(name), repository); } else { log_ereport(LOG_FAILURE, "TODO: location based dav repositories not implemented"); ret = 1; @@ -835,23 +834,22 @@ return ret; } -static int convert_objconf(ServerConfiguration *scfg, ObjectConfig *cfg, HTTPObjectConfig *conf, sstr_t file) { +static int convert_objconf(ServerConfiguration *scfg, ObjectConfig *cfg, HTTPObjectConfig *conf, cxmutstr file) { pool_handle_t *pool = conf->pool; - UcxList *objlist = cfg->objects; + CxList *objlist = cfg->objects; + CxIterator iter = cxListIterator(objlist, 0); int i = 0; - while(objlist != NULL) { - ConfigObject *cob = objlist->data; - + cx_foreach(ConfigObject*, cob, iter) { // get name and ppath char *name = NULL; char *ppath = NULL; if(cob->name.length > 0) { - name = sstrdup_pool(pool, cob->name).ptr; + name = cx_strdup_pool(pool, cob->name).ptr; if(!name) return -1; } if(cob->ppath.length > 0) { - ppath = sstrdup_pool(pool, cob->ppath).ptr; + ppath = cx_strdup_pool(pool, cob->ppath).ptr; if(!ppath) return -1; } @@ -864,14 +862,14 @@ // add directives for(int j=0;j<NUM_NSAPI_TYPES-1;j++) { - UcxList *dirs = cob->directives[j]; + ConfigDirectiveList *dirs = cob->directives[j]; while(dirs != NULL) { - ConfigDirective *cfgdir = dirs->data; + ConfigDirective *cfgdir = dirs->directive; directive *d = pool_malloc(pool, sizeof(directive)); if(!d) return -1; if(cfgdir->condition) { - sstr_t expr = cfgdir->condition->param_str; + cxmutstr expr = cfgdir->condition->param_str; d->cond = condition_from_str(pool, expr.ptr, expr.length); } else { d->cond = NULL; @@ -879,14 +877,13 @@ d->param = pblock_create_pool(pool, 8); // add params - UcxList *param = cfg_param_list(cfgdir->value, scfg->a); + ConfigParam *param = cfg_param_list(cfgdir->value, scfg->a); while(param != NULL) { - ConfigParam *p = param->data; pblock_nvlinsert( - p->name.ptr, - p->name.length, - p->value.ptr, - p->value.length, + param->name.ptr, + param->name.length, + param->value.ptr, + param->value.length, d->param); param = param->next; } @@ -912,13 +909,12 @@ // next i++; - objlist = objlist->next; } return 0; } -HTTPObjectConfig* objconf_load(ServerConfiguration *scfg, sstr_t file) { +HTTPObjectConfig* objconf_load(ServerConfiguration *scfg, cxmutstr file) { log_ereport(LOG_VERBOSE, "load_obj_conf"); int ret = 0; @@ -940,7 +936,7 @@ // convert ObjectConfig to HTTPObjectConfig // add objects - conf->nobj = ucx_list_size(cfg->objects); + conf->nobj = cfg->objects->size; conf->objects = pool_calloc(pool, conf->nobj, sizeof(httpd_object*)); if(conf->objects) { ret = convert_objconf(scfg, cfg, conf, file); @@ -953,7 +949,7 @@ return !ret ? conf : NULL; } -int mime_conf_load(ServerConfiguration *cfg, sstr_t file) { +int mime_conf_load(ServerConfiguration *cfg, cxmutstr file) { MimeConfig *mimecfg = load_mime_config(file.ptr); if(!mimecfg) { return -1; @@ -962,20 +958,19 @@ int ret = 0; // cleanup in case of errors is done by the allocator - MimeMap *mimemap = almalloc(cfg->a, sizeof(MimeMap)); - UcxMap *map = ucx_map_new_a(cfg->a, (mimecfg->ntypes * 3) / 2); + MimeMap *mimemap = cxMalloc(cfg->a, sizeof(MimeMap)); + CxMap *map = cxHashMapCreate(cfg->a, (mimecfg->ntypes * 3) / 2); if(mimemap && map) { mimemap->map = map; // add ext type pairs - UCX_FOREACH(md, mimecfg->directives) { - MimeDirective *d = md->data; + for(MimeDirective *d=mimecfg->directives_begin;d;d=d->next) { // add the type for each extension to the map - UCX_FOREACH(xl, d->exts) { - sstr_t ext = sstr(xl->data); - sstr_t value = sstrdup(d->type); - if(ucx_map_sstr_put(map, ext, value.ptr)) { + for(int i=0;i<d->nextensions;i++) { + cxstring ext = d->extensions[i]; + cxmutstr value = cx_strdup(cx_strn(d->type.ptr, d->type.length)); + if(cxMapPut(map, cx_hash_key_bytes((const unsigned char *)ext.ptr, ext.length), value.ptr)) { log_ereport(LOG_CATASTROPHE, "OOM"); ret = -1; break; @@ -998,17 +993,21 @@ -ACLData* acl_conf_load(ServerConfiguration *cfg, sstr_t file) { - ACLFile *aclfile = load_acl_file(file.ptr); +ACLData* acl_conf_load(ServerConfiguration *cfg, const char *file) { + ACLFile *aclfile = load_acl_file(file); + if(!aclfile) { + log_ereport(LOG_FAILURE, "Cannot load acl file %s", file); + return NULL; + } // TODO: malloc return checks ACLData *acldata = acl_data_new(cfg->a); - UCX_FOREACH(elm, aclfile->namedACLs) { - ACLConfig *ac = elm->data; + CxIterator iter = cxListIterator(aclfile->namedACLs, 0); + cx_foreach(ACLConfig *, ac, iter) { ACLList *acl = acl_config_convert(cfg, ac); log_ereport(LOG_VERBOSE, "add acl: %.*s", (int)ac->id.length, ac->id.ptr); - ucx_map_sstr_put(acldata->namedACLs, ac->id, acl); + cxMapPut(acldata->namedACLs, cx_hash_key(ac->id.ptr, ac->id.length), acl); } free_acl_file(aclfile); @@ -1016,9 +1015,9 @@ } ACLList* acl_config_convert(ServerConfiguration *cfg, ACLConfig *acl) { - UcxAllocator *a = cfg->a; + CxAllocator *a = cfg->a; - WSAcl *acllist = almalloc(cfg->a, sizeof(WSAcl)); + WSAcl *acllist = cxMalloc(cfg->a, sizeof(WSAcl)); acllist->acl.check = (acl_check_f)wsacl_check; acllist->acl.authdb = NULL; acllist->acl.authprompt = NULL; @@ -1026,26 +1025,24 @@ acllist->ace = NULL; acllist->ece = NULL; - if(acl->type.ptr && !sstrcmp(acl->type, sstr("fs"))) { + if(acl->type.ptr && !cx_strcmp(cx_strn(acl->type.ptr, acl->type.length), cx_str("fs"))) { acllist->acl.isextern = 1; } - size_t s = ucx_list_size(acl->entries); + size_t s = CFG_ACE_LIST_SIZE(acl->entries); WSAce **tmp_aces = calloc(s, sizeof(WSAce*)); WSAce **tmp_eces = calloc(s, sizeof(WSAce*)); int ai = 0; int ei = 0; // convert entries - UCX_FOREACH(elm, acl->entries) { - ACEConfig *acecfg = elm->data; - + for(ACEConfig *acecfg=acl->entries;acecfg;acecfg=acecfg->next) { // copy data - WSAce *ace = almalloc(a, sizeof(WSAce)); + WSAce *ace = cxMalloc(a, sizeof(WSAce)); ace->access_mask = acecfg->access_mask; ace->flags = acecfg->flags; ace->type = acecfg->type; - ace->who = sstrdup_a(a, acecfg->who).ptr; + ace->who = cx_strdup_a(a, cx_strcast(acecfg->who)).ptr; // add the entry to the correct array if(ace->type >= ACL_TYPE_AUDIT) { @@ -1059,10 +1056,10 @@ // create new entrie arrays with perfect fitting size if(ai > 0) { - acllist->ace = alcalloc(a, ai, sizeof(WSAce*)); + acllist->ace = cxCalloc(a, ai, sizeof(WSAce*)); } if(ei > 0) { - acllist->ece = alcalloc(a, ei, sizeof(WSAce*)); + acllist->ece = cxCalloc(a, ei, sizeof(WSAce*)); } memcpy(acllist->ace, tmp_aces, ai*sizeof(WSAce*)); memcpy(acllist->ece, tmp_eces, ei*sizeof(WSAce*)); @@ -1074,14 +1071,14 @@ // get authentication information if(acl->authparam) { - sstr_t authdb_str = cfg_param_get(acl->authparam, sstr("authdb")); - sstr_t prompt_str = cfg_param_get(acl->authparam, sstr("prompt")); + cxmutstr authdb_str = cfg_param_get(acl->authparam, cx_str("authdb")); + cxmutstr prompt_str = cfg_param_get(acl->authparam, cx_str("prompt")); if(authdb_str.ptr) { - AuthDB *authdb = ucx_map_sstr_get(cfg->authdbs, authdb_str); + AuthDB *authdb = cxMapGet(cfg->authdbs, cx_hash_key(authdb_str.ptr, authdb_str.length)); acllist->acl.authdb = authdb; if(authdb && prompt_str.ptr) { - acllist->acl.authprompt = sstrdup_a(a, prompt_str).ptr; + acllist->acl.authprompt = cx_strdup_a(a, cx_strcast(prompt_str)).ptr; } } } @@ -1089,7 +1086,7 @@ return &acllist->acl; } -AuthDB* keyfile_load(ServerConfiguration *cfg, scstr_t file) { +AuthDB* keyfile_load(ServerConfiguration *cfg, cxstring file) { Keyfile *keyfile = keyfile_new(cfg->a); if(!keyfile) { return NULL; @@ -1102,8 +1099,7 @@ AuthDB *ret = &keyfile->authdb; - UCX_FOREACH(elm, conf->users) { - KeyfileEntry *user = elm->data; + for(KeyfileEntry *user=conf->users_begin;user;user=user->next) { if(keyfile_add_user( keyfile, user->name, @@ -1124,10 +1120,9 @@ pblock* config_obj2pblock(pool_handle_t *pool, ConfigNode *obj) { pblock *pb = pblock_create_pool(pool, 8); - UCX_FOREACH(elm, obj->children) { - ConfigNode *d = elm->data; - if(d->type == CONFIG_NODE_DIRECTIVE && d->name.length > 0 && ucx_list_size(d->args) == 1) { - ConfigArg *arg = d->args->data; + for(ConfigNode *d=obj->children_begin;d;d=d->next) { + if(d->type == CONFIG_NODE_DIRECTIVE && d->name.length > 0 && CFG_NUM_PARAMS(d->args) == 1) { + ConfigParam *arg = d->args; pblock_nvlinsert(d->name.ptr, d->name.length, arg->value.ptr, arg->value.length, pb); } }
--- a/src/server/daemon/config.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/config.h Sun Nov 06 15:53:32 2022 +0100 @@ -47,10 +47,10 @@ #include "../webdav/webdav.h" -#include <ucx/list.h> -#include <ucx/map.h> -#include <ucx/mempool.h> -#include <ucx/string.h> +#include <cx/linked_list.h> +#include <cx/hash_map.h> +#include <cx/mempool.h> +#include <cx/string.h> #ifdef __cplusplus extern "C" { @@ -67,27 +67,27 @@ } CfgManager; struct ServerConfiguration { - pool_handle_t *pool; - UcxAllocator *a; + pool_handle_t *pool; + CxAllocator *a; - UcxMap *host_vs; // map of all vservers. key is the host name - UcxList *listeners; // list of all listeners - UcxList *logfiles; - AccessLog *default_log; - UcxMap *authdbs; - MimeMap *mimetypes; - UcxMap *resources; - UcxMap *dav; - sstr_t tmp; - sstr_t user; - uint32_t ref; // reference counter + CxMap *host_vs; // map of all vservers. key is the host name + CxList *listeners; // list of all listeners + CxList *logfiles; + AccessLog *default_log; + CxMap *authdbs; + MimeMap *mimetypes; + CxMap *resources; + CxMap *dav; + cxmutstr tmp; + cxmutstr user; + uint32_t ref; // reference counter }; struct WebdavRepository { - VfsType *vfs; - void *vfsInitData; - UcxList *davBackends; // list of WebdavBackendInitData* - sstr_t object; + VfsType *vfs; + void *vfsInitData; + CxList *davBackends; // list of WebdavBackendInitData* + cxmutstr object; }; struct WebdavBackendInitData { @@ -96,7 +96,7 @@ }; struct mime_map { - UcxMap *map; + CxMap *map; }; char* cfg_config_file_path(const char *file); @@ -130,12 +130,12 @@ void cfg_ref(ServerConfiguration *cfg); void cfg_unref(ServerConfiguration *cfg); -HTTPObjectConfig* objconf_load(ServerConfiguration *scfg, sstr_t file); -int mime_conf_load(ServerConfiguration *cfg, sstr_t file); +HTTPObjectConfig* objconf_load(ServerConfiguration *scfg, cxmutstr file); +int mime_conf_load(ServerConfiguration *cfg, cxmutstr file); -ACLData* acl_conf_load(ServerConfiguration *cfg, sstr_t file); +ACLData* acl_conf_load(ServerConfiguration *cfg, const char *file); ACLList* acl_config_convert(ServerConfiguration *cfg, ACLConfig *acl); -AuthDB* keyfile_load(ServerConfiguration *cfg, scstr_t file); +AuthDB* keyfile_load(ServerConfiguration *cfg, cxstring file); pblock* config_obj2pblock(pool_handle_t *pool, ConfigNode *obj);
--- a/src/server/daemon/configmanager.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/configmanager.c Sun Nov 06 15:53:32 2022 +0100 @@ -31,7 +31,7 @@ #include "../public/nsapi.h" -#include <ucx/string.h> +#include <cx/string.h> #include "httplistener.h" #include "log.h"
--- a/src/server/daemon/configmanager.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/configmanager.h Sun Nov 06 15:53:32 2022 +0100 @@ -33,10 +33,10 @@ #include "vserver.h" -#include <ucx/list.h> -#include <ucx/map.h> -#include <ucx/mempool.h> -#include <ucx/string.h> +#include <cx/list.h> +#include <cx/map.h> +#include <cx/mempool.h> +#include <cx/string.h> #include <sys/types.h>
--- a/src/server/daemon/error.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/error.c Sun Nov 06 15:53:32 2022 +0100 @@ -28,24 +28,24 @@ #include "error.h" -#include <ucx/string.h> +#include <cx/string.h> #include "../util/pblock.h" #include "../util/strbuf.h" -// macro for creating an error string (ucx sstr_t) +// macro for creating an error string (ucx cxmutstr) #define ERRMSG(s) { s, sizeof(s)-1 } -static sstr_t error_400 = ERRMSG("<html><body>bad request</body></html>"); -static sstr_t error_403 = ERRMSG("<html><body>forbidden</body></html>"); -static sstr_t error_404 = ERRMSG("<html><body>not found</body></html>"); -static sstr_t error_500 = ERRMSG("<html><body>server error</body></html>"); -static sstr_t error_503 = ERRMSG("<html><body>service unavailable</body></html>"); +static cxmutstr error_400 = ERRMSG("<html><body>bad request</body></html>"); +static cxmutstr error_403 = ERRMSG("<html><body>forbidden</body></html>"); +static cxmutstr error_404 = ERRMSG("<html><body>not found</body></html>"); +static cxmutstr error_500 = ERRMSG("<html><body>server error</body></html>"); +static cxmutstr error_503 = ERRMSG("<html><body>service unavailable</body></html>"); -static sstr_t error_std = ERRMSG("<html><body>error</body></html>"); +static cxmutstr error_std = ERRMSG("<html><body>error</body></html>"); int nsapi_error_request(Session *sn, Request *rq) { short status = rq->status_num; - sstr_t msg; + cxmutstr msg; if(status < 400) { msg.ptr = NULL; msg.length = 0; @@ -82,7 +82,7 @@ } void fatal_error(HTTPRequest *req, int status) { - sstr_t msg = error_500; + cxmutstr msg = error_500; char *statusmsg = "Internal Server Error"; switch(status) { case 400: {
--- a/src/server/daemon/event.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/event.c Sun Nov 06 15:53:32 2022 +0100 @@ -26,12 +26,12 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include <ucx/map.h> +#include <cx/hash_map.h> #include "../util/atomic.h" #include "event.h" -UcxMap *event_handler_map = NULL; +CxMap *event_handler_map = NULL; int numevhandlers = 0; EVHandler *default_event_handler = NULL; @@ -40,11 +40,13 @@ int create_event_handler(EventHandlerConfig *cfg) { if(event_handler_map == NULL) { - event_handler_map = ucx_map_new(16); + event_handler_map = cxHashMapCreate(cxDefaultAllocator, 16); } + CxHashKey key = cx_hash_key_bytes((const unsigned char*)cfg->name.ptr, cfg->name.length); + /* if the event handler already exists, we don't modify it */ - if(ucx_map_sstr_get(event_handler_map, cfg->name)) { + if(cxMapGet(event_handler_map, key)) { /* TODO: log message */ /* TODO: set reload status */ return 1; @@ -64,7 +66,7 @@ default_event_handler = e; } - int ret = ucx_map_sstr_put(event_handler_map, cfg->name, e); + int ret = cxMapPut(event_handler_map, key, e); if(ret == 0) { last_handler_c = e; numevhandlers++; @@ -88,7 +90,7 @@ } EventHandlerConfig cfg; - cfg.name = SC("default"); + cfg.name = cx_str("default"); cfg.nthreads = 1; cfg.isdefault = 1; @@ -100,8 +102,8 @@ return default_event_handler; } -EVHandler* get_event_handler(char *name) { - return ucx_map_cstr_get(event_handler_map, name); +EVHandler* get_event_handler(const char *name) { + return cxMapGet(event_handler_map, cx_hash_key_str(name)); } EventHandler* ev_instance(EVHandler *ev) {
--- a/src/server/daemon/event.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/event.h Sun Nov 06 15:53:32 2022 +0100 @@ -30,7 +30,7 @@ #define EVENT_H #include "../public/nsapi.h" -#include <ucx/string.h> +#include <cx/string.h> #ifdef __cplusplus extern "C" { @@ -43,7 +43,7 @@ } EVHandler; typedef struct event_handler_conf { - scstr_t name; + cxstring name; int nthreads; int isdefault; } EventHandlerConfig; @@ -54,7 +54,7 @@ EVHandler* get_default_event_handler(); -EVHandler* get_event_handler(char *name); +EVHandler* get_event_handler(const char *name); EventHandler* ev_instance(EVHandler *ev);
--- a/src/server/daemon/func.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/func.c Sun Nov 06 15:53:32 2022 +0100 @@ -28,17 +28,18 @@ #include <stdlib.h> -#include <ucx/map.h> +#include <cx/map.h> +#include <cx/hash_map.h> #include "../public/nsapi.h" #include "../util/pblock.h" #include "func.h" -UcxMap *function_map; +CxMap *function_map; void func_init() { - function_map = ucx_map_new(1337); + function_map = cxHashMapCreate(cxDefaultAllocator, 256); } void add_function(FuncStruct *func) { @@ -52,7 +53,7 @@ } f->name = name; log_ereport(LOG_VERBOSE, "add_function %s", f->name); - ucx_map_cstr_put(function_map, (char*)f->name, f); + cxMapPut(function_map, cx_hash_key_str(f->name), f); } void add_functions(FuncStruct *funcs) { @@ -63,13 +64,16 @@ } } -FuncStruct* get_function(char *name) { - for(int i=0;name[i]!='\0';i++) { - if(name[i] == '_') { - name[i] = '-'; +FuncStruct* get_function(const char *name) { + char *name_dup = strdup(name); + for(int i=0;name_dup[i]!='\0';i++) { + if(name_dup[i] == '_') { + name_dup[i] = '-'; } } - return ucx_map_cstr_get(function_map, name); + void *ret = cxMapGet(function_map, cx_hash_key_str(name_dup)); + free(name_dup); + return ret; } FuncStruct* func_resolve(pblock *pb, Session *sn, Request *rq) {
--- a/src/server/daemon/func.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/func.h Sun Nov 06 15:53:32 2022 +0100 @@ -45,7 +45,7 @@ void add_functions(struct FuncStruct *funcs); -FuncStruct* get_function(char *name); +FuncStruct* get_function(const char *name); #ifdef __cplusplus
--- a/src/server/daemon/httplistener.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/httplistener.c Sun Nov 06 15:53:32 2022 +0100 @@ -54,7 +54,8 @@ #include <sys/socket.h> #include <unistd.h> -#include <ucx/map.h> + +#include <cx/hash_map.h> #include "../util/atomic.h" #include "httplistener.h" @@ -64,15 +65,16 @@ #include "configmanager.h" #include "log.h" -UcxMap *listener_map = NULL; +#define LISTENER_MAX_PROTOCOL_TOKENS 1024 + +CxMap *listener_map = NULL; int start_all_listener() { ServerConfiguration *conf = cfgmgr_get_server_config(); - UcxList *ls = conf->listeners; - while(ls) { - HttpListener *listener = ls->data; + CxList *ls = conf->listeners; + CxIterator iter = cxListIterator(ls, 0); + cx_foreach(HttpListener *, listener, iter) { http_listener_start(listener); - ls = ls->next; } return 0; @@ -80,10 +82,10 @@ HttpListener* http_listener_create(ListenerConfig *conf) { if(listener_map == NULL) { - listener_map = ucx_map_new(16); + listener_map = cxHashMapCreate(cxDefaultAllocator, 16); } - HttpListener *fl = ucx_map_sstr_get(listener_map, conf->name); + HttpListener *fl = cxMapGet(listener_map, cx_hash_key(conf->name.ptr, conf->name.length)); if(fl == NULL) { return http_listener_new(conf); } @@ -108,7 +110,7 @@ // the listener threadpool might be changed if(conf->threadpool.ptr != NULL) { - newls->threadpool = get_threadpool(conf->threadpool); + newls->threadpool = get_threadpool(cx_strcast(conf->threadpool)); } if(newls->threadpool == NULL) { newls->threadpool = get_default_threadpool(); @@ -130,7 +132,7 @@ fl->next = newls; - ucx_map_sstr_put(listener_map, newls->name, newls); + cxMapPut(listener_map, cx_hash_key(newls->name.ptr, newls->name.length), newls); for (int i=0;i<newls->nacceptors;i++) { //acceptor_start(newls->acceptors[i]); @@ -149,10 +151,10 @@ HttpListener* http_listener_new(ListenerConfig *conf) { // TODO: remove if(listener_map == NULL) { - listener_map = ucx_map_new(16); + listener_map = cxHashMapCreate(cxDefaultAllocator, 16); } - HttpListener *fl = ucx_map_sstr_get(listener_map, conf->name); + HttpListener *fl = cxMapGet(listener_map, cx_hash_key(conf->name.ptr, conf->name.length)); if(fl != NULL) { return fl; } @@ -165,7 +167,7 @@ listener->default_vs.vs_name = conf->vs.ptr; listener->threadpool = NULL; if(conf->threadpool.ptr != NULL) { - listener->threadpool = get_threadpool(conf->threadpool); + listener->threadpool = get_threadpool(cx_strcast(conf->threadpool)); } if(listener->threadpool == NULL) { listener->threadpool = get_default_threadpool(); @@ -191,23 +193,23 @@ ctx, SSL_OP_SINGLE_DH_USE | SSL_OP_NO_SSLv3); if(conf->disable_proto.ptr) { - ssize_t n = 0; - sstr_t *plist = sstrsplit(conf->disable_proto, S(","), &n); + cxstring *plist = NULL; + ssize_t n = cx_strsplit_a(cxDefaultAllocator, conf->disable_proto, cx_str(","), LISTENER_MAX_PROTOCOL_TOKENS, &plist); if(plist) { for(int i=0;i<n;i++) { - sstr_t proto = plist[i]; + cxstring proto = plist[i]; log_ereport( LOG_VERBOSE, "Listener %s: Disable protocol %s", listener->name.ptr, proto.ptr); - if(!sstrcasecmp(sstrtrim(proto), S("SSLv2"))) { + if(!cx_strcasecmp(cx_strtrim(proto), cx_str("SSLv2"))) { SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2); - } else if(!sstrcasecmp(sstrtrim(proto), S("SSLv3"))) { + } else if(!cx_strcasecmp(cx_strtrim(proto), cx_str("SSLv3"))) { SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3); - } else if(!sstrcasecmp(sstrtrim(proto), S("TLSv1"))) { + } else if(!cx_strcasecmp(cx_strtrim(proto), cx_str("TLSv1"))) { SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1); - } else if(!sstrcasecmp(sstrtrim(proto), S("TLSv1.1"))) { + } else if(!cx_strcasecmp(cx_strtrim(proto), cx_str("TLSv1.1"))) { #ifdef SSL_OP_NO_TLSv1_1 SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_1); #else @@ -216,7 +218,7 @@ "Listener: %s: TLSv1.1 already not supported", listener->name.ptr); #endif - } else if(sstrcasecmp(sstrtrim(proto), S("TLSv1.2"))) { + } else if(cx_strcasecmp(cx_strtrim(proto), cx_str("TLSv1.2"))) { #ifdef SSL_OP_NO_TLSv1_2 SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_2); #else @@ -225,7 +227,7 @@ "Listener: %s: TLSv1.2 already not supported", listener->name.ptr); #endif - } else if(sstrcasecmp(sstrtrim(proto), S("TLSv1.3"))) { + } else if(cx_strcasecmp(cx_strtrim(proto), cx_str("TLSv1.3"))) { #ifdef SSL_OP_NO_TLSv1_3 SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_3); #else @@ -242,7 +244,6 @@ listener->name.ptr, proto.ptr); } - free(proto.ptr); } free(plist); } @@ -284,7 +285,7 @@ } - ucx_map_sstr_put(listener_map, listener->name, listener); + cxMapPut(listener_map, cx_hash_key(listener->name.ptr, listener->name.length), listener); struct sockaddr_in servaddr; /* server address */ struct sockaddr_in6 servaddr6;
--- a/src/server/daemon/httplistener.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/httplistener.h Sun Nov 06 15:53:32 2022 +0100 @@ -56,18 +56,18 @@ }; struct _listener_config { ServerConfiguration *cfg; - sstr_t name; - sstr_t vs; - sstr_t threadpool; + cxmutstr name; + cxmutstr vs; + cxmutstr threadpool; char *address; int port; int nacceptors; WSBool blockingio; WSBool ssl; - scstr_t certfile; - scstr_t privkeyfile; - scstr_t chainfile; - scstr_t disable_proto; + cxstring certfile; + cxstring privkeyfile; + cxstring chainfile; + cxstring disable_proto; }; struct _acceptor { @@ -78,7 +78,7 @@ struct _http_listener { ServerConfiguration *cfg; - sstr_t name; + cxmutstr name; union vs default_vs; int port; int server_socket;
--- a/src/server/daemon/httpparser.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/httpparser.c Sun Nov 06 15:53:32 2022 +0100 @@ -173,7 +173,7 @@ } int parse_request_line(HttpParser *parser) { - sstr_t line = parser->start_line; + cxmutstr line = parser->start_line; parser->request->request_line = line; /*
--- a/src/server/daemon/httpparser.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/httpparser.h Sun Nov 06 15:53:32 2022 +0100 @@ -30,7 +30,7 @@ #define HTTPPARSER_H -#include <ucx/string.h> +#include <cx/string.h> #include "httprequest.h" #ifdef __cplusplus @@ -49,15 +49,15 @@ HTTPRequest *request; int state; - sstr_t start_line; + cxmutstr start_line; /* local parser variables */ int wl; /* only white space */ int tk; /* token: 0: header name 1: header value */ int offset; /* offset of parsed string */ int strend; /* end position */ - sstr_t name; - sstr_t value; + cxmutstr name; + cxmutstr value; } HttpParser;
--- a/src/server/daemon/httprequest.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/httprequest.c Sun Nov 06 15:53:32 2022 +0100 @@ -64,27 +64,27 @@ free(req); } -sstr_t http_request_get_abspath(HTTPRequest *req) { - sstr_t uri = req->uri; +cxmutstr http_request_get_abspath(HTTPRequest *req) { + cxmutstr uri = req->uri; int i = 0; if(uri.ptr[0] == '/') { return uri; - } else if(sstrprefix(uri, S("http://"))) { + } else if(cx_strprefix(cx_strcast(uri), (cxstring)CX_STR("http://"))) { i = 7; - } else if(sstrprefix(uri, S("https://"))) { + } else if(cx_strprefix(cx_strcast(uri), (cxstring)CX_STR("https://"))) { i = 8; - } else if(!sstrcmp(uri, S("*"))) { + } else if(!cx_strcmp(cx_strcast(uri), (cxstring)CX_STR("*"))) { return uri; } for(;i<uri.length;i++) { if(uri.ptr[i] == '/') { - return sstrsubs(uri, i); + return cx_strsubs_m(uri, i); } } - return S("/"); + return (cxmutstr){ "/", 1 }; } NSAPISession* nsapisession_create(pool_handle_t *pool) { @@ -96,7 +96,6 @@ ZERO(sn, sizeof(NSAPISession)); sn->sn.pool = pool; - sn->allocator = util_pool_allocator(pool); sn->sn.client = pblock_create_pool(sn->sn.pool, 8); if(!sn->sn.client) { @@ -198,7 +197,7 @@ // Pass request line as "clf-request" // remove \r\n - sstr_t clfreq = request->request_line; + cxmutstr clfreq = request->request_line; while(clfreq.length > 0 && clfreq.ptr[clfreq.length - 1] < 33) { clfreq.length--; } @@ -225,9 +224,9 @@ request->httpv.length, rq->rq.reqpb); - if(!sstrcmp(request->httpv, S("HTTP/1.1"))) { + if(!cx_strcmp(cx_strcast(request->httpv), (cxstring)CX_STR("HTTP/1.1"))) { rq->rq.protv_num = PROTOCOL_VERSION_HTTP11; - } else if(!sstrcmp(request->httpv, S("HTTP/1.0"))) { + } else if(!cx_strcmp(cx_strcast(request->httpv), (cxstring)CX_STR("HTTP/1.0"))) { rq->rq.protv_num = PROTOCOL_VERSION_HTTP10; } else { // invalid protocol version - abort @@ -244,7 +243,7 @@ * get absolute path and query of the request uri */ // TODO: check for '#' #72 - sstr_t absPath = http_request_get_abspath(request); + cxmutstr absPath = http_request_get_abspath(request); if(!absPath.ptr) { // TODO: error msg return 1; @@ -253,7 +252,7 @@ return 1; } - sstr_t query; + cxmutstr query; query.length = 0; for(int i=0;i<request->uri.length;i++) { @@ -277,7 +276,7 @@ } // Get abs_path part of request URI, and canonicalize the path - sstr_t orig_path = absPath; + cxmutstr orig_path = absPath; absPath.ptr = util_canonicalize_uri( pool, absPath.ptr, @@ -371,7 +370,7 @@ rq->port = request->connection->listener->port; if(rq->host) { - VirtualServer *vs = ucx_map_cstr_get(sn->config->host_vs, rq->host); + VirtualServer *vs = cxMapGet(sn->config->host_vs, cx_hash_key_str(rq->host)); if(vs) { rq->vs = vs; } else { @@ -454,7 +453,7 @@ -void header_add(HeaderArray *hd, sstr_t name, sstr_t value) { +void header_add(HeaderArray *hd, cxmutstr name, cxmutstr value) { while(hd->len >= hd->alloc) { if(hd->next == NULL) { HeaderArray *block = malloc(sizeof(HeaderArray)); @@ -648,9 +647,8 @@ void request_free_resources(NSAPISession *sn, NSAPIRequest *rq) { if(!rq->resources) return; - UcxMapIterator i = ucx_map_iterator(rq->resources); - ResourceData *resource; - UCX_MAP_FOREACH(key, resource, i) { + CxIterator i = cxMapIteratorValues(rq->resources); + cx_foreach(ResourceData *, resource, i) { resourcepool_free(&sn->sn, &rq->rq, resource); } } @@ -737,7 +735,7 @@ // if no function has set the ppath var, translate it to docroot if(ret == REQ_NOACTION && ppath == NULL) { - sstr_t docroot = rq->vs->document_root; + cxmutstr docroot = rq->vs->document_root; if(docroot.length < 1) { log_ereport( LOG_WARN, @@ -753,12 +751,12 @@ docroot.length--; } - sstr_t uri = sstr(pblock_findkeyval(pb_key_uri, rq->rq.reqpb)); + cxmutstr uri = cx_str(pblock_findkeyval(pb_key_uri, rq->rq.reqpb)); - sstr_t translated; + cxmutstr translated; translated.length = docroot.length + uri.length; translated.ptr = alloca(translated.length + 1); - translated = sstrncat(translated, 2, docroot, uri); + translated = cx_strncat(translated, 2, docroot, uri); pblock_kvinsert( pb_key_ppath, @@ -766,8 +764,8 @@ translated.length, rq->rq.vars); */ - sstr_t uri = sstr(pblock_findkeyval(pb_key_uri, rq->rq.reqpb)); - request_set_path(docroot, uri, rq->rq.vars); + cxstring uri = cx_str(pblock_findkeyval(pb_key_uri, rq->rq.reqpb)); + request_set_path(cx_strcast(docroot), uri, rq->rq.vars); } // TODO: remove ppath @@ -868,13 +866,13 @@ * 'internal/directory' so that 'index-common' can serve the content. * Otherwise we set the content type to text/plain */ - sstr_t path = sstr(pblock_findkeyval(pb_key_ppath, rq->rq.vars)); - sstr_t ct; + cxstring path = cx_str(pblock_findkeyval(pb_key_ppath, rq->rq.vars)); + cxstring ct; if(path.ptr[path.length - 1] == '/') { // directory - ct = sstrn("internal/directory", 18); + ct = (cxstring)CX_STR("internal/directory"); } else { - ct = sstrn("text/plain", 10); + ct = (cxstring)CX_STR("text/plain"); } pblock_kvinsert(pb_key_content_type, ct.ptr, ct.length, rq->rq.srvhdrs); @@ -911,7 +909,7 @@ rq->rq.srvhdrs); } // compare types - if(!contenttype_match(sstr(dtp), sstr(content_type))) { + if(!contenttype_match(cx_str(dtp), cx_str(content_type))) { continue; } } @@ -1079,7 +1077,7 @@ char *poolname = pblock_findkeyval(pb_key_pool, d->param); if(poolname) { - threadpool_t *pool = get_threadpool(sstr(poolname)); + threadpool_t *pool = get_threadpool(cx_str(poolname)); if(pool && pool != sn->currentpool) { // execute directive in different thread pool return nsapi_exec_tp(d, sn, rq, pool); @@ -1212,35 +1210,35 @@ * of types (also with wildcard support) * (type1|type2*) */ -int contenttype_match(sstr_t cmp, sstr_t ctype) { +int contenttype_match(cxstring cmp, cxstring ctype) { if(cmp.ptr[0] != '(') { if(cmp.ptr[0] == '*') { cmp.ptr++; cmp.length--; - return sstrsuffix(ctype, cmp); + return cx_strsuffix(ctype, cmp); } else if(cmp.ptr[cmp.length-1] == '*') { cmp.length--; - return sstrprefix(ctype, cmp); + return cx_strprefix(ctype, cmp); } else { - return !sstrcmp(cmp, ctype); + return !cx_strcmp(cmp, ctype); } } else if(cmp.ptr[0] == 0) { log_ereport(LOG_WARN, "Skipped service saf with empty type parameter"); return 0; } - cmp = sstrsubsl(cmp, 1, cmp.length - 2); + cmp = cx_strsubsl(cmp, 1, cmp.length - 2); int begin = 0; for(int i=0;i<cmp.length;i++) { if(cmp.ptr[i] == '|') { - if(contenttype_match(sstrsubsl(cmp, begin, i-begin), ctype)) { + if(contenttype_match(cx_strsubsl(cmp, begin, i-begin), ctype)) { return 1; } begin = i + 1; } } - return contenttype_match(sstrsubs(cmp, begin), ctype); + return contenttype_match(cx_strsubs(cmp, begin), ctype); } /*
--- a/src/server/daemon/httprequest.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/httprequest.h Sun Nov 06 15:53:32 2022 +0100 @@ -29,7 +29,7 @@ #ifndef HTTPREQUEST_H #define HTTPREQUEST_H -#include <ucx/string.h> +#include <cx/string.h> #include "sessionhandler.h" #include "../public/nsapi.h" @@ -47,18 +47,18 @@ struct _http_request { Connection *connection; - sstr_t request_line; - sstr_t method; - sstr_t uri; - sstr_t httpv; + cxmutstr request_line; + cxmutstr method; + cxmutstr uri; + cxmutstr httpv; HeaderArray *headers; netbuf *netbuf; time_t req_start; }; struct _header { - sstr_t name; - sstr_t value; + cxmutstr name; + cxmutstr value; }; struct _header_array { @@ -71,7 +71,7 @@ void http_request_init(HTTPRequest *req); void http_request_cleanup(HTTPRequest *req); -sstr_t http_request_get_abspath(HTTPRequest *req); +cxmutstr http_request_get_abspath(HTTPRequest *req); NSAPISession* nsapisession_create(pool_handle_t *pool); @@ -87,7 +87,7 @@ -void header_add(HeaderArray *hd, sstr_t name, sstr_t value); +void header_add(HeaderArray *hd, cxmutstr name, cxmutstr value); void header_array_free(HeaderArray *hd); int nsapi_handle_request(NSAPISession *sn, NSAPIRequest *rq); @@ -132,7 +132,7 @@ char *path); int method_match(char *cmp, char *method); -int contenttype_match(sstr_t cmp, sstr_t ctype); +int contenttype_match(cxstring cmp, cxstring ctype); /* request.h functions */ int request_initialize(
--- a/src/server/daemon/keyfile_auth.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/keyfile_auth.c Sun Nov 06 15:53:32 2022 +0100 @@ -41,28 +41,30 @@ #include "../util/atomic.h" #include "../util/util.h" +#include <cx/hash_map.h> + #include "keyfile_auth.h" -Keyfile* keyfile_new(UcxAllocator *a) { - Keyfile *keyfile = alcalloc(a, 1, sizeof(Keyfile)); +Keyfile* keyfile_new(CxAllocator *a) { + Keyfile *keyfile = cxCalloc(a, 1, sizeof(Keyfile)); if(!keyfile) { return NULL; } keyfile->authdb.get_user = keyfile_get_user; keyfile->authdb.use_cache = 0; - keyfile->users = ucx_map_new_a(a, 16); + keyfile->users = cxHashMapCreate(a, 16); return keyfile; } int keyfile_add_user( Keyfile *keyfile, - sstr_t name, + cxmutstr name, enum KeyfileHashType hash_type, - sstr_t hash, - sstr_t *groups, + cxmutstr hash, + cxmutstr *groups, size_t ngroups) { - UcxAllocator *a = keyfile->users->allocator; + CxAllocator *a = keyfile->users->allocator; if(hash.length < 12) { // hash too short @@ -70,8 +72,8 @@ return -1; } - KeyfileUser *user = almalloc(a, sizeof(KeyfileUser)); - user->user.name = sstrdup_a(a, name).ptr; + KeyfileUser *user = cxMalloc(a, sizeof(KeyfileUser)); + user->user.name = cx_strdup_a(a, cx_strcast(name)).ptr; user->user.uid = -1; user->user.gid = -1; user->user.verify_password = keyfile_user_verify_password; @@ -79,7 +81,7 @@ user->user.free = keyfile_user_free; user->hash_type = hash_type; - user->hash = almalloc(a, hash.length + 1); + user->hash = cxMalloc(a, hash.length + 1); if(!user->user.name || !user->hash) { return -1; @@ -88,12 +90,12 @@ user->hashlen = util_base64decode(hash.ptr, hash.length, user->hash); if(ngroups > 0) { - user->groups = alcalloc(a, ngroups, sizeof(sstr_t)); + user->groups = cxCalloc(a, ngroups, sizeof(cxmutstr)); if(!user->groups) { return -1; } for(int i=0;i<ngroups;i++) { - user->groups[i] = sstrdup_a(a, groups[i]); + user->groups[i] = cx_strdup_a(a, cx_strcast(groups[i])); } } else { @@ -101,26 +103,26 @@ } // add to keyfile - return ucx_map_sstr_put(keyfile->users, name, user); + return cxMapPut(keyfile->users, cx_hash_key(name.ptr, name.length), user); } // authdb functions -User* keyfile_get_user(AuthDB *db, char *user) { +User* keyfile_get_user(AuthDB *db, const char *user) { Keyfile *keyfile = (Keyfile*)db; - return ucx_map_cstr_get(keyfile->users, user); + return cxMapGet(keyfile->users, cx_hash_key_str(user)); } -int keyfile_user_verify_password(User *user, char *password) { +int keyfile_user_verify_password(User *user, const char *password) { KeyfileUser *usr = (KeyfileUser*)user; return ssha_verify(usr, password); } -int keyfile_user_check_group(User *user, char *group) { +int keyfile_user_check_group(User *user, const char *group) { KeyfileUser *usr = (KeyfileUser*)user; - sstr_t grp = sstr(group); + cxstring grp = cx_str(group); for(int i=0;i<usr->numgroups;i++) { - if(!sstrcmp(usr->groups[i], grp)) { + if(!cx_strcmp(cx_strcast(usr->groups[i]), grp)) { return 1; } } @@ -132,7 +134,7 @@ } -int ssha_verify(KeyfileUser *user, char *password) { +int ssha_verify(KeyfileUser *user, const char *password) { /* * SSHA: SHA(pw + salt) + salt * user->hash is already base64 decoded
--- a/src/server/daemon/keyfile_auth.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/keyfile_auth.h Sun Nov 06 15:53:32 2022 +0100 @@ -31,7 +31,7 @@ #include "../public/auth.h" -#include <ucx/map.h> +#include <cx/map.h> #include <inttypes.h> #ifdef __cplusplus @@ -49,35 +49,35 @@ struct keyfile { AuthDB authdb; - UcxMap *users; + CxMap *users; }; struct keyfile_user { User user; - sstr_t *groups; + cxmutstr *groups; size_t numgroups; enum KeyfileHashType hash_type; char *hash; size_t hashlen; }; -Keyfile* keyfile_new(UcxAllocator *a); +Keyfile* keyfile_new(CxAllocator *a); int keyfile_add_user( Keyfile *keyfile, - sstr_t user, + cxmutstr user, enum KeyfileHashType hash_type, - sstr_t hash, - sstr_t *groups, + cxmutstr hash, + cxmutstr *groups, size_t ngroups); -User* keyfile_get_user(AuthDB *db, char *user); -int keyfile_user_verify_password(User *user, char *password); -int keyfile_user_check_group(User *user, char *group); +User* keyfile_get_user(AuthDB *db, const char *user); +int keyfile_user_verify_password(User *user, const char *password); +int keyfile_user_check_group(User *user, const char *group); void keyfile_user_free(User *user); // SSHA password compare -int ssha_verify(KeyfileUser *user, char *password); +int ssha_verify(KeyfileUser *user, const char *password); #ifdef __cplusplus }
--- a/src/server/daemon/ldap_auth.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/ldap_auth.c Sun Nov 06 15:53:32 2022 +0100 @@ -35,7 +35,8 @@ #include <string.h> #include <sys/time.h> -#include <ucx/utils.h> +#include <cx/utils.h> +#include <cx/hash_map.h> #include "ldap_auth.h" @@ -48,7 +49,7 @@ } AuthDB* create_ldap_authdb(ServerConfiguration *cfg, const char *name, LDAPConfig *conf) { - LDAPAuthDB *authdb = almalloc(cfg->a, sizeof(LDAPAuthDB)); + LDAPAuthDB *authdb = cxMalloc(cfg->a, sizeof(LDAPAuthDB)); authdb->authdb.name = pool_strdup(cfg->pool, name); authdb->authdb.get_user = ldap_get_user; authdb->authdb.use_cache = 1; @@ -64,7 +65,7 @@ // initialize group cache authdb->groups.first = NULL; authdb->groups.last = NULL; - authdb->groups.map = ucx_map_new_a(cfg->a, 32); + authdb->groups.map = cxHashMapCreate(cfg->a, 32); return (AuthDB*) authdb; } @@ -113,7 +114,7 @@ return ld; } -User* ldap_get_user(AuthDB *db, char *username) { +User* ldap_get_user(AuthDB *db, const char *username) { LDAPAuthDB *authdb = (LDAPAuthDB*) db; LDAPConfig *config = &authdb->config; @@ -161,7 +162,7 @@ user->user.verify_password = ldap_user_verify_password; user->user.check_group = ldap_user_check_group; user->user.free = ldap_user_free; - user->user.name = username; // must not be freed + user->user.name = (char*)username; // must not be freed TODO: maybe copy // TODO: get uid/gid from ldap user->user.uid = -1; @@ -180,7 +181,7 @@ return NULL; } -LDAPGroup* ldap_get_group(LDAPAuthDB *authdb, char *group) { +LDAPGroup* ldap_get_group(LDAPAuthDB *authdb, const char *group) { printf("ldap_get_group: %s\n", group); LDAPConfig *config = &authdb->config; @@ -246,10 +247,10 @@ wsgroup->members = calloc(count, sizeof(LDAPMember)); wsgroup->nmembers = count; for(int i=0;i<count;i++) { - sstr_t member = sstrn( + cxstring member = cx_strn( values[i]->bv_val, values[i]->bv_len); - wsgroup->members[i].name = sstrdup(member).ptr; + wsgroup->members[i].name = cx_strdup(member).ptr; // TODO: uid? printf("added member: %.*s\n", (int)member.length, member.ptr); } @@ -271,12 +272,12 @@ return wsgroup; } -int ldap_user_verify_password(User *u, char *password) { +int ldap_user_verify_password(User *u, const char *password) { LDAPUser *user = (LDAPUser*)u; //int r = ldap_simple_bind_s(user->ldap, user->userdn, password); struct berval cred; - cred.bv_val = password; + cred.bv_val = (char*)password; cred.bv_len = strlen(password); struct berval *server_cred; int r = ldap_sasl_bind_s( @@ -296,7 +297,7 @@ } } -int ldap_user_check_group(User *u, char *group_str) { +int ldap_user_check_group(User *u, const char *group_str) { LDAPUser *user = (LDAPUser*)u; int ret = 0;
--- a/src/server/daemon/ldap_auth.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/ldap_auth.h Sun Nov 06 15:53:32 2022 +0100 @@ -32,7 +32,7 @@ #include "../public/auth.h" #include <sys/types.h> #include <ldap.h> -#include <ucx/map.h> +#include <cx/map.h> #include "config.h" @@ -61,7 +61,7 @@ struct ldap_group_cache { LDAPGroup *first; LDAPGroup *last; - UcxMap *map; + CxMap *map; }; struct ldap_auth_db { @@ -96,12 +96,12 @@ LDAP* get_ldap_session(LDAPAuthDB *authdb); -User* ldap_get_user(AuthDB *sb, char *username); +User* ldap_get_user(AuthDB *sb, const char *username); -LDAPGroup* ldap_get_group(LDAPAuthDB *authdb, char *group); +LDAPGroup* ldap_get_group(LDAPAuthDB *authdb, const char *group); -int ldap_user_verify_password(User *user, char *password); -int ldap_user_check_group(User *user, char *group); +int ldap_user_verify_password(User *user, const char *password); +int ldap_user_check_group(User *user, const char *group); void ldap_user_free(User *user); #ifdef __cplusplus
--- a/src/server/daemon/log.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/log.c Sun Nov 06 15:53:32 2022 +0100 @@ -43,8 +43,9 @@ #include "../util/io.h" #include "../util/atomic.h" -#include <ucx/map.h> -#include <ucx/list.h> +#include <cx/hash_map.h> +#include <cx/linked_list.h> +#include <cx/compare.h> static int is_initialized = 0; @@ -52,7 +53,7 @@ static int log_level = 0; static uint32_t log_dup_count = 0; -static UcxList *log_dup_list = NULL; +static CxList *log_dup_list = NULL; static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; WSBool main_is_daemon(void); @@ -65,7 +66,7 @@ /* * access log file map */ -static UcxMap *access_log_files; // map of LogFile* +static CxMap *access_log_files; // map of LogFile* static char *log_date_month[] = { @@ -110,6 +111,8 @@ return 0; } + log_dup_list = cxPointerLinkedListCreate(cxDefaultAllocator, cx_cmp_ptr); + /* open the log file */ mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; log_file_fd = open(cfg->file, O_WRONLY | O_CREAT | O_APPEND, mode); @@ -165,7 +168,7 @@ } } - sstr_t s; + cxstring s; s.ptr = str; s.length = len; @@ -195,8 +198,8 @@ msg[len] = '\n'; pthread_mutex_lock(&mutex); - UCX_FOREACH(elm, log_dup_list) { - LogDup *dup = elm->data; + CxIterator i = cxListIterator(log_dup_list, 0); + cx_foreach(LogDup *, dup, i) { dup->write(dup->cookie, msg, len + 1); } pthread_mutex_unlock(&mutex); @@ -205,10 +208,10 @@ } } -sstr_t log_get_prefix(int level) { +cxmutstr log_get_prefix(int level) { time_t t = time(NULL); - sstr_t d; + cxmutstr d; d.ptr = NULL; d.length = 0; @@ -238,21 +241,22 @@ void log_add_logdup(LogDup *dup) { pthread_mutex_lock(&mutex); - log_dup_list = ucx_list_append(log_dup_list, dup); + cxListAdd(log_dup_list, dup); ws_atomic_inc32(&log_dup_count); pthread_mutex_unlock(&mutex); } void log_remove_logdup(LogDup *ldup) { pthread_mutex_lock(&mutex); - UcxList *elm = log_dup_list; - while(elm) { - if(elm->data == ldup) { - log_dup_list = ucx_list_remove(log_dup_list, elm); + CxIterator i = cxListIterator(log_dup_list, 0); + WSBool finished = 0; + cx_foreach(LogDup *, dup, i) { + if(finished) break; + if(dup == ldup) { + i.remove = 1; + finished = 1; ws_atomic_dec32(&log_dup_count); - break; } - elm = elm->next; } pthread_mutex_unlock(&mutex); } @@ -278,18 +282,18 @@ return 0; } - sstr_t lmsg; + cxmutstr lmsg; lmsg.ptr = NULL; /* create log message prefix */ - sstr_t lpre = log_get_prefix(degree); + cxmutstr lpre = log_get_prefix(degree); /* format message */ int len = vasprintf(&lmsg.ptr, format, args); lmsg.length = len; /* create message string */ - sstr_t message = sstrcat(2, lpre, lmsg); + cxmutstr message = cx_strcat(2, lpre, lmsg); /* write message to the log file */ log_file_writeln(message.ptr, message.length); @@ -336,18 +340,19 @@ * This source file only manages access log files. IO is performed directly * by AddLog safs. */ -LogFile* get_access_log_file(scstr_t file) { +LogFile* get_access_log_file(cxstring file) { // TODO: this looks dubious if(!access_log_files) { - access_log_files = ucx_map_new(4); + access_log_files = cxHashMapCreate(cxDefaultAllocator, 4); } if(file.ptr == NULL || file.length == 0) { return NULL; } - LogFile *log = ucx_map_sstr_get(access_log_files, file); + CxHashKey key = cx_hash_key_bytes((unsigned const char*)file.ptr, file.length); + LogFile *log = cxMapGet(access_log_files, key); if(log != NULL) { ws_atomic_inc32(&log->ref); return log; @@ -366,7 +371,7 @@ log->ref = 1; // add access log to the map - ucx_map_sstr_put(access_log_files, file, log); + cxMapPut(access_log_files, key, log); return log; }
--- a/src/server/daemon/log.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/log.h Sun Nov 06 15:53:32 2022 +0100 @@ -30,8 +30,8 @@ #define LOG_H #include "../public/nsapi.h" -#include <ucx/string.h> -#include <ucx/utils.h> +#include <cx/string.h> +#include <cx/utils.h> #include <inttypes.h> @@ -52,8 +52,8 @@ } LogFile; typedef struct { - sstr_t file; - sstr_t format; // unused + cxmutstr file; + cxmutstr format; // unused LogFile *log; } AccessLog; @@ -69,13 +69,13 @@ void log_file_writeln(char *str, size_t len); -sstr_t log_get_prefix(int level); +cxmutstr log_get_prefix(int level); void log_add_logdup(LogDup *dup); void log_remove_logdup(LogDup *dup); // access logging -LogFile* get_access_log_file(scstr_t file); +LogFile* get_access_log_file(cxstring file); #ifdef __cplusplus }
--- a/src/server/daemon/main.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/main.c Sun Nov 06 15:53:32 2022 +0100 @@ -39,7 +39,7 @@ #include "../util/plist.h" #include "../util/date.h" -#include <ucx/string.h> +#include <cx/string.h> #include "webserver.h" #include "log.h" @@ -55,7 +55,7 @@ void test() { time_t t = time(NULL); pool_handle_t *pool = pool_create(); - sstr_t date = date_format_http(t, pool); + cxmutstr date = date_format_http(t, pool); printf("%s\n", date.ptr); } @@ -173,7 +173,7 @@ int status; status = webserver_init(); if(status != 0) { - log_ereport(LOG_FAILURE, "Cannot initialize server."); + log_ereport(LOG_FAILURE, "cannot initialize server."); return EXIT_FAILURE; }
--- a/src/server/daemon/protocol.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/protocol.c Sun Nov 06 15:53:32 2022 +0100 @@ -58,7 +58,7 @@ /* * http_status_message from Open Webserver (frame/http.cpp) - * TODO: replace, use sstr_t + * TODO: replace, use cxmutstr */ NSAPI_PUBLIC const char * protocol_status_message (int code) { @@ -379,7 +379,7 @@ int http_send_continue(Session *sn) { NSAPISession *s = (NSAPISession*)sn; - sstr_t msg = S("HTTP/1.1 100 Continue\r\n\r\n"); + cxstring msg = CX_STR("HTTP/1.1 100 Continue\r\n\r\n"); int w = s->connection->write(s->connection, msg.ptr, msg.length); if(w != msg.length) { return 1;
--- a/src/server/daemon/protocol.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/protocol.h Sun Nov 06 15:53:32 2022 +0100 @@ -57,7 +57,7 @@ char **host, uint16_t *port); -#define sbuf_write(out, buf, len) sbuf_append(out, sstrn(buf, len)) +#define sbuf_write(out, buf, len) sbuf_append(out, cx_strn(buf, len)) #ifdef __cplusplus }
--- a/src/server/daemon/request.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/request.c Sun Nov 06 15:53:32 2022 +0100 @@ -127,7 +127,7 @@ return s; } -int request_set_path(sstr_t root, sstr_t path, pblock *vars) { +int request_set_path(cxstring root, cxstring path, pblock *vars) { // TODO: maybe replace this code with request_set_path from req.cpp // concat path
--- a/src/server/daemon/request.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/request.h Sun Nov 06 15:53:32 2022 +0100 @@ -32,7 +32,7 @@ #include "../public/nsapi.h" #include "../util/object.h" -#include <ucx/map.h> +#include <cx/map.h> #ifdef __cplusplus extern "C" { @@ -48,7 +48,7 @@ uint16_t port; NSAPIContext context; void *jvm_context; - UcxMap *resources; + CxMap *resources; }; /* macros for context access */ @@ -57,7 +57,7 @@ #define REQ_HASHSIZE 10 -int request_set_path(sstr_t root, sstr_t path, pblock *vars); +int request_set_path(cxstring root, cxstring path, pblock *vars); #ifdef __cplusplus }
--- a/src/server/daemon/resourcepool.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/resourcepool.c Sun Nov 06 15:53:32 2022 +0100 @@ -36,15 +36,15 @@ #define RESOURCE_POOL_MAX_ALLOC 268435455 -static UcxMap *resource_pool_types; +static CxMap *resource_pool_types; int init_resource_pools(void) { - resource_pool_types = ucx_map_new(4); + resource_pool_types = cxHashMapCreate(cxDefaultAllocator, 4); return resource_pool_types ? 0 : 1; } int resourcepool_register_type(const char *type_name, ResourceType *type_info) { - if(ucx_map_cstr_put(resource_pool_types, type_name, type_info)) { + if(cxMapPut(resource_pool_types, cx_hash_key_str(type_name), type_info)) { log_ereport(LOG_CATASTROPHE, "resourcepool_register_type: OOM"); return 1; } @@ -53,10 +53,10 @@ -int resourcepool_new(ServerConfiguration *cfg, scstr_t type, scstr_t name, ConfigNode *node) { - ResourceType *restype = ucx_map_sstr_get(resource_pool_types, type); +int resourcepool_new(ServerConfiguration *cfg, cxstring type, cxstring name, ConfigNode *node) { + ResourceType *restype = cxMapGet(resource_pool_types, cx_hash_key_bytes((unsigned const char*)type.ptr, type.length)); if(!restype) { - log_ereport(LOG_MISCONFIG, "Unknown resource pool type: %s", type.ptr); + log_ereport(LOG_MISCONFIG, "unknown resource pool type: %s", type.ptr); return 1; } @@ -101,7 +101,7 @@ respool->resalloc = respool->max; respool->resources = pool_malloc(cfg->pool, respool->resalloc * sizeof(ResourceDataPrivate*)); - if(!respool->resources || ucx_map_sstr_put(cfg->resources, name, respool)) { + if(!respool->resources || cxMapPut(cfg->resources, cx_hash_key_bytes((unsigned const char*)name.ptr, name.length), respool)) { log_ereport(LOG_FAILURE, "Cannot add resource pool: OOM"); // the only cleanup we have to do restype->destroy(respool_data); @@ -121,13 +121,13 @@ // was this resource already used by this request? if(request && request->resources) { - resource = ucx_map_cstr_get(request->resources, name); + resource = cxMapGet(request->resources, cx_hash_key_str(name)); if(resource) { return &resource->data; } } - ResourcePool *respool = ucx_map_cstr_get(cfg->resources, name); + ResourcePool *respool = cxMapGet(cfg->resources, cx_hash_key_str(name)); if(!respool) return NULL; @@ -170,11 +170,11 @@ if(resource) { if(request && session) { if(!request->resources) { - request->resources = ucx_map_new_a(&session->allocator, 8); + request->resources = cxHashMapCreate(pool_allocator(session->sn.pool), 8); } if(request->resources) { - if(ucx_map_cstr_put(request->resources, name, resource)) { + if(cxMapPut(request->resources, cx_hash_key_str(name), resource)) { err = 1; } } else {
--- a/src/server/daemon/resourcepool.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/resourcepool.h Sun Nov 06 15:53:32 2022 +0100 @@ -111,7 +111,7 @@ int init_resource_pools(void); -int resourcepool_new(ServerConfiguration *cfg, scstr_t type, scstr_t name, ConfigNode *node); +int resourcepool_new(ServerConfiguration *cfg, cxstring type, cxstring name, ConfigNode *node); void resourcepool_destroy_resource(ResourceDataPrivate *res);
--- a/src/server/daemon/session.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/session.c Sun Nov 06 15:53:32 2022 +0100 @@ -41,7 +41,3 @@ return sn->config; } -NSAPI_PUBLIC void* session_get_allocator(Session *sn) { - return &((NSAPISession*)sn)->allocator; -} -
--- a/src/server/daemon/session.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/session.h Sun Nov 06 15:53:32 2022 +0100 @@ -51,8 +51,6 @@ int pos; int cursize; - UcxAllocator allocator; - ServerConfiguration *config; }; @@ -63,7 +61,6 @@ // get the server configuration of this session NSAPI_PUBLIC void* session_get_config(Session *s); -NSAPI_PUBLIC void* session_get_allocator(Session *sn); #ifdef __cplusplus }
--- a/src/server/daemon/sessionhandler.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/sessionhandler.c Sun Nov 06 15:53:32 2022 +0100 @@ -117,7 +117,7 @@ io = sslstream_new(pool, conn->ssl); *ssl = 1; } else { - io = sysstream_new(pool, conn->fd); + io = sycx_stream_new(pool, conn->fd); *ssl = 0; } return io;
--- a/src/server/daemon/srvctrl.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/srvctrl.c Sun Nov 06 15:53:32 2022 +0100 @@ -37,22 +37,23 @@ #include "../util/systhr.h" -#include <ucx/utils.h> -#include <ucx/buffer.h> +#include <cx/utils.h> +#include <cx/buffer.h> +#include <cx/printf.h> #include "srvctrl.h" #define SRVCTRL_THREAD_STACKSIZE 8192 static int srvctrl; -static sstr_t srvctrl_path; +static cxmutstr srvctrl_path; static WSBool srv_shutdown; int srvctrl_init(ServerConfiguration *cfg) { // create srvctrl unix domain socket // this socket is used for stop, reconfigure and other operations - srvctrl_path = ucx_sprintf("%s/private/srvctrl.sock", cfg->tmp.ptr); + srvctrl_path = cx_asprintf("%s/private/srvctrl.sock", cfg->tmp.ptr); struct sockaddr_un addr; if(srvctrl_path.length > sizeof(addr.sun_path)-1) { log_ereport( @@ -148,7 +149,8 @@ log_add_logdup(&log); char buf[64]; - UcxBuffer *line = ucx_buffer_new(NULL, 32, UCX_BUFFER_AUTOEXTEND); + CxBuffer line; + cxBufferInit(&line, NULL, 32, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS); ssize_t r; WSBool br = FALSE; @@ -156,13 +158,13 @@ for(int i=0;i<r;i++) { char c = buf[i]; if(c == '\n') { - sstr_t ln = sstrn(line->space, line->pos); + cxmutstr ln = cx_mutstrn(line.space, line.pos); if(srvctrl_handle_cmd(client, ln)) { br = TRUE; break; } } else { - ucx_buffer_putc(line, c); + cxBufferPut(&line, c); } } if(br) { @@ -171,25 +173,25 @@ } log_remove_logdup(&log); - ucx_buffer_free(line); + cxBufferDestroy(&line); close(client->fd); free(client); return NULL; } -int srvctrl_handle_cmd(SrvCtrlClient *client, sstr_t cmd) { - if(!sstrcmp(cmd, S("reconfig"))) { +int srvctrl_handle_cmd(SrvCtrlClient *client, cxmutstr cmd) { + if(!cx_strcmp(cx_strcast(cmd), cx_str("reconfig"))) { log_ereport(LOG_INFORM, "reconfigure server"); if(webserver_reconfig()) { log_ereport(LOG_FAILURE, "cannot reload config"); } else { log_ereport(LOG_INFORM, "reconfig: success"); } - } else if(!sstrcmp(cmd, S("shutdown"))) { + } else if(!cx_strcmp(cx_strcast(cmd), cx_str("shutdown"))) { webserver_shutdown(); - } else if(!sstrcmp(cmd, S("stat"))) { + } else if(!cx_strcmp(cx_strcast(cmd), cx_str("stat"))) { // TODO: implement - } else if(!sstrcmp(cmd, S("log"))) { + } else if(!cx_strcmp(cx_strcast(cmd), cx_str("log"))) { return 0; } else { log_ereport(
--- a/src/server/daemon/srvctrl.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/srvctrl.h Sun Nov 06 15:53:32 2022 +0100 @@ -31,7 +31,7 @@ #include "../public/nsapi.h" -#include <ucx/string.h> +#include <cx/string.h> #include "config.h" @@ -55,7 +55,7 @@ SrvCtrlClient* srvctrl_create_client(int fd); void* srvctrl_thread(SrvCtrlClient *client); -int srvctrl_handle_cmd(SrvCtrlClient *client, sstr_t cmd); +int srvctrl_handle_cmd(SrvCtrlClient *client, cxmutstr cmd); void srvctrl_log(SrvCtrlClient *client, char *msg, size_t len);
--- a/src/server/daemon/threadpools.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/threadpools.c Sun Nov 06 15:53:32 2022 +0100 @@ -29,14 +29,14 @@ #include <stdio.h> #include <stdlib.h> -#include <ucx/map.h> +#include <cx/hash_map.h> #include "threadpools.h" -static UcxMap *thread_pool_map; +static CxMap *thread_pool_map; static int num_thrpools; -static UcxMap *io_pool_map; +static CxMap *io_pool_map; static int num_iopools; static threadpool_t *default_thread_pool; @@ -45,12 +45,13 @@ static threadpool_t *default_io_pool; static threadpool_t *last_io_pool; -int create_threadpool(scstr_t name, ThreadPoolConfig *cfg) { +int create_threadpool(cxstring name, ThreadPoolConfig *cfg) { if(thread_pool_map == NULL) { - thread_pool_map = ucx_map_new(16); + thread_pool_map = cxHashMapCreate(cxDefaultAllocator, 16); } - threadpool_t *pool = ucx_map_sstr_get(thread_pool_map, name); + CxHashKey key = cx_hash_key_bytes((const unsigned char*)name.ptr, name.length); + threadpool_t *pool = cxMapGet(thread_pool_map, key); if(pool) { if(pool->min_threads > cfg->max_threads) { pool->min_threads = cfg->min_threads; @@ -65,7 +66,7 @@ int ret = 0; if(!threadpool_start(tp)) { - ret = ucx_map_sstr_put(thread_pool_map, name, tp); + ret = cxMapPut(thread_pool_map, key, tp); } else { ret = 1; } @@ -82,11 +83,12 @@ } } -int create_io_pool(scstr_t name, int numthreads) { +int create_io_pool(cxstring name, int numthreads) { if(io_pool_map == NULL) { - io_pool_map = ucx_map_new(4); + io_pool_map = cxHashMapCreate(cxDefaultAllocator, 4); } - threadpool_t *pool = ucx_map_sstr_get(io_pool_map, name); + CxHashKey key = cx_hash_key_bytes((const unsigned char*)name.ptr, name.length); + threadpool_t *pool = cxMapGet(io_pool_map, key); if(pool) { pool->min_threads = numthreads; pool->max_threads = numthreads; @@ -94,7 +96,7 @@ } else { threadpool_t *tp = threadpool_new(numthreads, numthreads); - int ret = ucx_map_sstr_put(io_pool_map, name, tp); + int ret = cxMapPut(io_pool_map, key, tp); if(ret == 0) { num_iopools++; @@ -115,13 +117,13 @@ cfg.max_threads = 8; cfg.queue_size = 64; cfg.stack_size = 262144; - if(create_threadpool(SC("default"), &cfg)) { + if(create_threadpool(cx_str("default"), &cfg)) { return 1; } } if(num_iopools == 0) { - if(create_io_pool(SC("default"), 8)) { + if(create_io_pool(cx_str("default"), 8)) { return 1; } } @@ -133,14 +135,14 @@ return default_thread_pool; } -threadpool_t* get_threadpool(sstr_t name) { - return ucx_map_sstr_get(thread_pool_map, name); +threadpool_t* get_threadpool(cxstring name) { + return cxMapGet(thread_pool_map, cx_hash_key_bytes((const unsigned char*)name.ptr, name.length)); } threadpool_t* get_default_iopool() { return default_io_pool; } -threadpool_t* get_iopool(sstr_t name) { - return ucx_map_sstr_get(io_pool_map, name); +threadpool_t* get_iopool(cxstring name) { + return cxMapGet(io_pool_map, cx_hash_key_bytes((const unsigned char*)name.ptr, name.length)); }
--- a/src/server/daemon/threadpools.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/threadpools.h Sun Nov 06 15:53:32 2022 +0100 @@ -29,7 +29,7 @@ #ifndef THREADPOOLS_H #define THREADPOOLS_H -#include <ucx/string.h> +#include <cx/string.h> #include "../public/nsapi.h" #include "../util/thrpool.h" @@ -45,14 +45,14 @@ int queue_size; } ThreadPoolConfig; -int create_threadpool(scstr_t name, ThreadPoolConfig *cfg); +int create_threadpool(cxstring name, ThreadPoolConfig *cfg); int check_thread_pool_cfg(); threadpool_t* get_default_threadpool(); -threadpool_t* get_threadpool(sstr_t name); +threadpool_t* get_threadpool(cxstring name); threadpool_t* get_default_iopool(); -threadpool_t* get_iopool(sstr_t name); +threadpool_t* get_iopool(cxstring name); #ifdef __cplusplus }
--- a/src/server/daemon/vfs.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/vfs.c Sun Nov 06 15:53:32 2022 +0100 @@ -33,7 +33,7 @@ #include <unistd.h> #include <sys/types.h> #include <aio.h> -#include <ucx/map.h> +#include <cx/hash_map.h> #include "../util/pool.h" #include "netsite.h" @@ -45,7 +45,7 @@ #define VFS_MALLOC(pool, size) pool ? pool_malloc(pool, size) : malloc(size) #define VFS_FREE(pool, ptr) pool ? pool_free(pool, ptr) : free(ptr) -static UcxMap *vfs_type_map; +static CxMap *vfs_type_map; static VFS sys_vfs = { sys_vfs_open, @@ -80,7 +80,7 @@ }; int vfs_init(void) { - vfs_type_map = ucx_map_new(16); + vfs_type_map = cxHashMapCreate(cxDefaultAllocator, 16); if(!vfs_type_map) { return -1; } @@ -103,11 +103,11 @@ vfsType->init = vfsInit; vfsType->create = vfsCreate; - return ucx_map_cstr_put(vfs_type_map, name, vfsType); + return cxMapPut(vfs_type_map, cx_hash_key_str(name), vfsType); } -VfsType* vfs_get_type(scstr_t vfs_class) { - return ucx_map_sstr_get(vfs_type_map, vfs_class); +VfsType* vfs_get_type(cxstring vfs_class) { + return cxMapGet(vfs_type_map, cx_hash_key_bytes((const unsigned char*)vfs_class.ptr, vfs_class.length)); } void* vfs_init_backend(ServerConfiguration *cfg, pool_handle_t *pool, VfsType *vfs_class, WSConfigNode *config, int *error) { @@ -124,7 +124,7 @@ } VFS* vfs_create(Session *sn, Request *rq, const char *vfs_class, pblock *pb, void *initData) { - VfsType *vfsType = ucx_map_cstr_get(vfs_type_map, vfs_class); + VfsType *vfsType = cxMapGet(vfs_type_map, cx_hash_key_str(vfs_class)); if(!vfsType) { log_ereport(LOG_MISCONFIG, "vfs_create: unkown VFS type %s", vfs_class); return NULL;
--- a/src/server/daemon/vfs.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/vfs.h Sun Nov 06 15:53:32 2022 +0100 @@ -32,7 +32,7 @@ #include "../public/vfs.h" #include "acl.h" -#include <ucx/string.h> +#include <cx/string.h> #ifdef __cplusplus extern "C" { @@ -55,7 +55,7 @@ typedef enum VFSAioOp VFSAioOp; int vfs_init(void); -VfsType* vfs_get_type(scstr_t vfs_class); +VfsType* vfs_get_type(cxstring vfs_class); void* vfs_init_backend(ServerConfiguration *cfg, pool_handle_t *pool, VfsType *vfs_class, WSConfigNode *config, int *error);
--- a/src/server/daemon/vserver.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/vserver.c Sun Nov 06 15:53:32 2022 +0100 @@ -31,7 +31,7 @@ VirtualServer* vs_new() { VirtualServer *vs = malloc(sizeof(VirtualServer)); vs->objects = NULL; - vs->document_root = sstr("docs"); + vs->document_root = cx_mutstr("docs"); vs->acls = NULL; vs->log = NULL; vs->ref = 1;
--- a/src/server/daemon/vserver.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/vserver.h Sun Nov 06 15:53:32 2022 +0100 @@ -29,7 +29,7 @@ #ifndef VSERVER_H #define VSERVER_H -#include <ucx/string.h> +#include <cx/string.h> #include "../util/object.h" #include "../public/nsapi.h" @@ -41,19 +41,32 @@ extern "C" { #endif +typedef struct VSLocation VSLocation; + +struct VSLocation { + VSLocation *child_begin; + VSLocation *child_end; + + VSLocation *next; +}; + struct VirtualServer { - sstr_t name; - sstr_t host; + cxmutstr name; + cxmutstr host; // TODO: list of listeners, check listener of vs - sstr_t objectfile; + cxmutstr objectfile; HTTPObjectConfig *objects; - sstr_t document_root; + cxmutstr document_root; ACLData *acls; AccessLog *log; + VSLocation *locations_begin; + VSLocation *locations_end; + + uint32_t ref; // reference counter };
--- a/src/server/daemon/webserver.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/webserver.c Sun Nov 06 15:53:32 2022 +0100 @@ -48,7 +48,9 @@ #include "../util/pblock.h" #include "../util/util.h" -#include <ucx/utils.h> +#include <cx/utils.h> +#include <cx/printf.h> +#include <cx/compare.h> #include "../safs/common.h" @@ -200,7 +202,7 @@ free(pid_file_path); // create unix domain socket for server control - sstr_t tmp_priv = ucx_sprintf("%s/private", cfg->tmp.ptr); + cxmutstr tmp_priv = cx_asprintf("%s/private", cfg->tmp.ptr); // TODO: remove existing private dir if(mkdir(tmp_priv.ptr, S_IRWXU)) { if(errno == EEXIST) {
--- a/src/server/daemon/websocket.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/daemon/websocket.c Sun Nov 06 15:53:32 2022 +0100 @@ -35,7 +35,7 @@ #include "../util/pblock.h" #include "../util/util.h" #include "../util/strbuf.h" -#include <ucx/string.h> +#include <cx/string.h> #define WS_BUFFER_LEN 2048 @@ -51,25 +51,25 @@ return REQ_NOACTION; } - if(sstrcasecmp(sstr(connection), S("upgrade"))) { + if(cx_strcasecmp(cx_str(connection), (cxstring)CX_STR("upgrade"))) { return REQ_NOACTION; } - if(sstrcasecmp(sstr(upgrade), S("websocket"))) { + if(cx_strcasecmp(cx_str(upgrade), (cxstring)CX_STR("websocket"))) { return REQ_NOACTION; } - sstr_t wsaccept = sstrcat(2, sstr(wskey), S("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")); + cxmutstr wsaccept = cx_strcat(2, cx_str(wskey), (cxstring)CX_STR("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")); unsigned char hash[20]; SHA1((const unsigned char*)wsaccept.ptr, wsaccept.length, hash); char *websocket_accept = util_base64encode((char*)hash, 20); sbuf_t *response = sbuf_new(512); - sbuf_append(response, S("HTTP/1.1 101 Switching Protocols\r\n")); - sbuf_append(response, S("Upgrade: websocket\r\n")); - sbuf_append(response, S("Connection: Upgrade\r\n")); - sbuf_append(response, S("Sec-WebSocket-Accept: ")); + sbuf_append(response, (cxstring)CX_STR("HTTP/1.1 101 Switching Protocols\r\n")); + sbuf_append(response, (cxstring)CX_STR("Upgrade: websocket\r\n")); + sbuf_append(response, (cxstring)CX_STR("Connection: Upgrade\r\n")); + sbuf_append(response, (cxstring)CX_STR("Sec-WebSocket-Accept: ")); sbuf_puts(response, websocket_accept); - sbuf_append(response, S("\r\n\r\n")); + sbuf_append(response, (cxstring)CX_STR("\r\n\r\n")); net_write(sn->csd, response->ptr, response->length); sbuf_free(response);
--- a/src/server/plugins/java/jvm.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/plugins/java/jvm.c Sun Nov 06 15:53:32 2022 +0100 @@ -30,7 +30,7 @@ #include <stdlib.h> #include <dlfcn.h> -#include <ucx/map.h> +#include <cx/map.h> #include "jvm.h" #include "daemon/request.h"
--- a/src/server/plugins/postgresql/config.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/plugins/postgresql/config.c Sun Nov 06 15:53:32 2022 +0100 @@ -87,18 +87,18 @@ } PgRepository* pg_init_repo(ServerConfiguration *cfg, pool_handle_t *pool, WSConfigNode *config) { - UcxAllocator a = util_pool_allocator(pool); + CxAllocator *a = pool_allocator(pool); - ConfigNode *pg = serverconfig_get_node(config, CONFIG_NODE_OBJECT, SC("Postgresql")); + ConfigNode *pg = serverconfig_get_node(config, CONFIG_NODE_OBJECT, cx_str("Postgresql")); if(!pg) { log_ereport(LOG_MISCONFIG, "pg_init_repo: missing postgresql config object"); return NULL; } - scstr_t cfg_respool = serverconfig_directive_value(pg, SC("ResourcePool")); - scstr_t cfg_rootid = serverconfig_directive_value(pg, SC("RootId")); - scstr_t cfg_rootnode = serverconfig_directive_value(pg, SC("RootNode")); - scstr_t cfg_dav = serverconfig_directive_value(pg, SC("PGDavConfig")); + cxstring cfg_respool = serverconfig_directive_value(pg, cx_str("ResourcePool")); + cxstring cfg_rootid = serverconfig_directive_value(pg, cx_str("RootId")); + cxstring cfg_rootnode = serverconfig_directive_value(pg, cx_str("RootNode")); + cxstring cfg_dav = serverconfig_directive_value(pg, cx_str("PGDavConfig")); // minimum requirement is a resource pool if(cfg_respool.length == 0) { @@ -140,7 +140,7 @@ PgRepository *repo = pool_malloc(pool, sizeof(PgRepository)); ZERO(repo, sizeof(PgRepository)); - repo->resourcepool = sstrdup_a(&a, cfg_respool); + repo->resourcepool = cx_strdup_a(a, cfg_respool); repo->root_resource_id = root_id; // check for extended pg dav config @@ -188,7 +188,7 @@ PgRepository *repo, const char *file_path) { - UcxAllocator a = util_pool_allocator(pool); + CxAllocator *a = pool_allocator(pool); // check if the file exists and can be accessed struct stat s; @@ -203,7 +203,7 @@ } // prepare PgRepository - repo->prop_ext = ucx_map_new_a(&a, 8); + repo->prop_ext = cxHashMapCreate(a, 8); if(!repo->prop_ext) { log_ereport(LOG_FAILURE, "pg: cannot load config file: OOM"); return 1; @@ -245,8 +245,8 @@ int ret = 0; PgExtParser parserData; - parserData.table_lookup = ucx_map_new(8); - parserData.tables = NULL; + parserData.table_lookup = cxHashMapCreate(cxDefaultAllocator, 8); + parserData.tables = cxLinkedListCreate(cxDefaultAllocator, cx_cmp_ptr, sizeof(PgExtTable)); while(node && !ret) { // currently, the only possible config element is <extension> @@ -260,13 +260,13 @@ // convert parserData if(!ret) { - size_t ntables = ucx_list_size(parserData.tables); + size_t ntables = parserData.tables->size; repo->ntables = ntables; repo->tables = pool_calloc(pool, ntables, sizeof(PgExtTable)); if(repo->tables) { int i = 0; - UCX_FOREACH(elm, parserData.tables) { - PgExtTable *tab = elm->data; + CxIterator iter = cxListIterator(parserData.tables, 0); + cx_foreach(PgExtTable *, tab, iter) { repo->tables[i++] = *tab; } } else { @@ -276,9 +276,8 @@ } // cleanup parser - ucx_list_free_content(parserData.tables, free); - ucx_list_free(parserData.tables); - ucx_map_free(parserData.table_lookup); + cxListDestroy(parserData.tables); + cxMapDestroy(parserData.table_lookup); return ret; } @@ -290,12 +289,12 @@ const char *file_path, xmlNode *ext_node) { - UcxAllocator a = util_pool_allocator(pool); + CxAllocator *a = pool_allocator(pool); xmlNode *node = ext_node->children; const char *table = NULL; - UcxList *properties = NULL; + CxList *properties = cxPointerLinkedListCreate(a, cx_cmp_ptr); while(node) { if(node->type == XML_ELEMENT_NODE) { @@ -325,7 +324,7 @@ log_ereport(LOG_MISCONFIG, "pg: config %s: no column specified for property %s", file_path, ps->name); return 1; } - properties = ucx_list_append_a(&a, properties, ps); + cxListAdd(properties, ps); } ps = ps->next; } @@ -345,7 +344,7 @@ } // check if the table was already specified - if(ucx_map_cstr_get(ext->table_lookup, table)) { + if(cxMapGet(ext->table_lookup, cx_hash_key_str(table))) { log_ereport(LOG_MISCONFIG, "pg: config %s: extension table %s not unique", file_path, table); return 1; } @@ -355,19 +354,18 @@ if(!tabname) return 1; // exttable is only used temporarily - PgExtTable *exttable = malloc(sizeof(PgExtTable)); - if(!exttable) return 1; - exttable->table = tabname; - exttable->isused = 0; // not relevant in config - int tableindex = (int)ucx_list_size(ext->tables); - ext->tables = ucx_list_append(ext->tables, exttable); + PgExtTable exttable; + exttable.table = tabname; + exttable.isused = 0; // not relevant in config + int tableindex = (int)ext->tables->size; + cxListAdd(ext->tables, &exttable); - if(ucx_map_cstr_put(ext->table_lookup, table, table)) { + if(cxMapPut(ext->table_lookup, cx_hash_key_str(table), (void*)table)) { return 1; } - UCX_FOREACH(elm, properties) { - xmlNode *ps = elm->data; + CxIterator iter = cxListIterator(properties, 0); + cx_foreach(xmlNode *, ps, iter) { const char *value = pg_util_xml_get_text(ps); PgPropertyStoreExt *ext_col = pool_malloc(pool, sizeof(PgPropertyStoreExt)); @@ -382,9 +380,9 @@ return 1; } - UcxKey key = webdav_property_key(ext_col->ns, ext_col->name); - int err = ucx_map_put(repo->prop_ext, key, ext_col); - free((void*)key.data); + CxHashKey key = webdav_property_key(ext_col->ns, ext_col->name); + int err = cxMapPut(repo->prop_ext, key, ext_col); + free(key.data.bytes); if(err) { return 1; }
--- a/src/server/plugins/postgresql/config.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/plugins/postgresql/config.h Sun Nov 06 15:53:32 2022 +0100 @@ -40,7 +40,10 @@ #include <libpq-fe.h> -#include <ucx/string.h> +#include <cx/string.h> +#include <cx/linked_list.h> +#include <cx/hash_map.h> +#include <cx/compare.h> #ifdef __cplusplus extern "C" { @@ -53,10 +56,10 @@ typedef struct PgRepository { int64_t root_resource_id; - sstr_t resourcepool; + cxmutstr resourcepool; PgExtTable *tables; size_t ntables; - UcxMap *prop_ext; + CxMap *prop_ext; } PgRepository; typedef struct { @@ -67,8 +70,8 @@ } PgPropertyStoreExt; typedef struct { - UcxMap *table_lookup; - UcxList *tables; + CxMap *table_lookup; + CxList *tables; } PgExtParser; int pg_lookup_root(ResourceData *res, const char *rootnode, int64_t *rootid);
--- a/src/server/plugins/postgresql/pgtest.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/plugins/postgresql/pgtest.c Sun Nov 06 15:53:32 2022 +0100 @@ -36,9 +36,9 @@ #include "../../public/webdav.h" #include "../../webdav/webdav.h" -#include <ucx/string.h> -#include <ucx/utils.h> -#include <ucx/buffer.h> +#include <cx/string.h> +#include <cx/utils.h> +#include <cx/buffer.h> #include "pgtest.h" #include "vfs.h" @@ -48,6 +48,9 @@ #define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b) +#define MAP_GET(map, key) cxMapGet(map, cx_hash_key_str(key)) +#define MAP_PUT(map, key, value) cxMapPut(map, cx_hash_key_str(key), value) + static char *pg_connstr = "postgresql://localhost/test1"; static int abort_pg_tests = 0; static PGconn *test_connection; @@ -118,11 +121,11 @@ static void parse_response_tag(TestMultistatus *ms, xmlNode *node) { // thanks to dav for some of this code - UcxAllocator *a = ms->mp->allocator; + CxAllocator *a = (CxAllocator*)ms->mp->allocator; node = node->children; - sstr_t href = {NULL, 0}; - UcxMap *properties = ucx_map_new_a(ms->mp->allocator, 16); + cxmutstr href = {NULL, 0}; + CxMap *properties = cxHashMapCreate(a, 16); while(node) { if(node->type == XML_ELEMENT_NODE) { @@ -131,7 +134,7 @@ if(href_node->type != XML_TEXT_NODE) { return; } - href = sstrdup_a(ms->mp->allocator, scstr((const char*)href_node->content)); + href = cx_strdup_a(a, cx_str((const char*)href_node->content)); } else if(xstreq(node->name, "propstat")) { xmlNode *n = node->children; xmlNode *prop_node = NULL; @@ -145,12 +148,12 @@ if(status_node->type != XML_TEXT_NODE) { return; } - sstr_t status_str = sstr((char*)status_node->content); + cxmutstr status_str = cx_mutstr((char*)status_node->content); if(status_str.length < 13) { return; } - status_str = sstrsubsl(status_str, 9, 3); - sstr_t status_s = sstrdup(status_str); + status_str = cx_strsubsl_m(status_str, 9, 3); + cxmutstr status_s = cx_strdup(cx_strcast(status_str)); status_code = atoi(status_s.ptr); free(status_s.ptr); } @@ -161,21 +164,21 @@ n = prop_node->children; while(n) { if(n->type == XML_ELEMENT_NODE) { - TestProperty *property = ucx_mempool_calloc(ms->mp, 1, sizeof(TestProperty)); + TestProperty *property = cxCalloc(ms->mp->allocator, 1, sizeof(TestProperty)); if(n->ns) { - property->prefix = n->ns->prefix ? sstrdup_a(a, scstr((const char*)n->ns->prefix)).ptr : NULL; - property->namespace = n->ns->href ? sstrdup_a(a, scstr((const char*)n->ns->href)).ptr : NULL; + property->prefix = n->ns->prefix ? cx_strdup_a(a, cx_str((const char*)n->ns->prefix)).ptr : NULL; + property->namespace = n->ns->href ? cx_strdup_a(a, cx_str((const char*)n->ns->href)).ptr : NULL; } - property->name = sstrdup_a(a, scstr((const char*)n->name)).ptr; + property->name = cx_strdup_a(a, cx_str((const char*)n->name)).ptr; property->node = n; property->status = status_code; xmlNode *value = n->children; if(value && value->type == XML_TEXT_NODE) { - property->value = sstrdup_a(a, scstr((const char*)value->content)).ptr; + property->value = cx_strdup_a(a, cx_str((const char*)value->content)).ptr; } if(property->namespace && property->name) { - sstr_t pname = sstrcat(2, sstr(property->namespace), sstr(property->name)); - ucx_map_sstr_put(properties, pname, property); + cxmutstr pname = cx_strcat(2, cx_str(property->namespace), cx_str(property->name)); + cxMapPut(properties, cx_hash_key(pname.ptr, pname.length), property); free(pname.ptr); } } @@ -186,11 +189,11 @@ node = node->next; } - TestResponse *resp =almalloc(a, sizeof(TestResponse)); + TestResponse *resp = cxMalloc(a, sizeof(TestResponse)); resp->href = href.ptr; resp->properties = properties; - ucx_map_sstr_put(ms->responses, href, resp); + cxMapPut(ms->responses, cx_hash_key(href.ptr, href.length), resp); } TestMultistatus* test_parse_multistatus(const char *space, size_t size) { @@ -199,11 +202,11 @@ return NULL; } - UcxMempool *mp = ucx_mempool_new(64); - TestMultistatus *ms = ucx_mempool_malloc(mp, sizeof(TestMultistatus)); + CxMempool *mp = cxBasicMempoolCreate(64); + TestMultistatus *ms = cxMalloc(mp->allocator, sizeof(TestMultistatus)); ms->doc = doc; ms->mp = mp; - ms->responses = ucx_map_new_a(mp->allocator, 8); + ms->responses = cxHashMapCreate((CxAllocator*)mp->allocator, 8); // parse response xmlNode *xml_root = xmlDocGetRootElement(doc); @@ -224,7 +227,7 @@ void test_multistatus_destroy(TestMultistatus *ms) { if(!ms) return; xmlFreeDoc(ms->doc); - ucx_mempool_destroy(ms->mp); + cxMempoolDestroy(ms->mp); } @@ -641,16 +644,16 @@ TestMultistatus *ms = test_parse_multistatus(st->buf->space, st->buf->size); UCX_TEST_ASSERT(ms, "propfind1: response is not valid xml"); - TestResponse *r1 = ucx_map_cstr_get(ms->responses, "/propfind/"); + TestResponse *r1 = MAP_GET(ms->responses, "/propfind/"); UCX_TEST_ASSERT(r1, "propfind1: missing /propfind/ response"); - UCX_TEST_ASSERT(ms->responses->count == 1, "propfind1: wrong response count"); + UCX_TEST_ASSERT(ms->responses->size == 1, "propfind1: wrong response count"); - TestProperty *p = ucx_map_cstr_get(r1->properties, "DAV:resourcetype"); + TestProperty *p = MAP_GET(r1->properties, "DAV:resourcetype"); UCX_TEST_ASSERT(p, "propfind1: missing property 'resourcetype'"); UCX_TEST_ASSERT(p->status == 200, "propfind1: wrong status code for property 'resourcetype'"); - p = ucx_map_cstr_get(r1->properties, "DAV:getlastmodified"); + p = MAP_GET(r1->properties, "DAV:getlastmodified"); UCX_TEST_ASSERT(p, "propfind1: missing property 'getlastmodified'"); UCX_TEST_ASSERT(p->status == 200, "propfind1: wrong status code for property 'getlastmodified'"); @@ -673,15 +676,15 @@ ms = test_parse_multistatus(st->buf->space, st->buf->size); UCX_TEST_ASSERT(ms, "propfind2: response is not valid xml"); - r1 = ucx_map_cstr_get(ms->responses, "/propfind/"); + r1 = MAP_GET(ms->responses, "/propfind/"); UCX_TEST_ASSERT(r1, "propfind2: missing /propfind/ response"); - UCX_TEST_ASSERT(ms->responses->count == 5, "propfind2: wrong response count"); + UCX_TEST_ASSERT(ms->responses->size == 5, "propfind2: wrong response count"); - r1 = ucx_map_cstr_get(ms->responses, "/propfind/res2"); + r1 = MAP_GET(ms->responses, "/propfind/res2"); UCX_TEST_ASSERT(r1, "propfind2: missing /propfind/res2 response"); - p = ucx_map_cstr_get(r1->properties, "http://example.com/test"); + p = MAP_GET(r1->properties, "http://example.com/test"); UCX_TEST_ASSERT(p, "propfind2: missing property 'test'"); UCX_TEST_ASSERT(p->status == 200, "propfind2: wrong status code for property 'test'"); UCX_TEST_ASSERT(!strcmp(p->value, "res2test"), "propfind2: wrong property value"); @@ -707,27 +710,27 @@ ms = test_parse_multistatus(st->buf->space, st->buf->size); UCX_TEST_ASSERT(ms, "propfind3: response is not valid xml"); - r1 = ucx_map_cstr_get(ms->responses, "/propfind/"); + r1 = MAP_GET(ms->responses, "/propfind/"); UCX_TEST_ASSERT(r1, "propfind3: missing /propfind/ response"); - UCX_TEST_ASSERT(ms->responses->count == 6, "propfind3: wrong response count"); + UCX_TEST_ASSERT(ms->responses->size == 6, "propfind3: wrong response count"); - r1 = ucx_map_cstr_get(ms->responses, "/propfind/res1"); + r1 = MAP_GET(ms->responses, "/propfind/res1"); UCX_TEST_ASSERT(r1, "propfind3: missing /propfind/sub/res1 response"); - p = ucx_map_cstr_get(r1->properties, "http://example.com/test"); + p = MAP_GET(r1->properties, "http://example.com/test"); UCX_TEST_ASSERT(p, "propfind3: missing property 'test'"); UCX_TEST_ASSERT(p->status == 200, "propfind3: wrong status code for property 'test'"); UCX_TEST_ASSERT(!strcmp(p->value, "testvalue"), "propfind3: wrong property value"); - p = ucx_map_cstr_get(r1->properties, "http://example.com/prop2"); + p = MAP_GET(r1->properties, "http://example.com/prop2"); UCX_TEST_ASSERT(p, "propfind3: missing property 'prop2'"); UCX_TEST_ASSERT(p->status == 200, "propfind3: wrong status code for property 'prop2'"); UCX_TEST_ASSERT(!strcmp(p->value, "value2"), "propfind3: wrong property value"); - r1 = ucx_map_cstr_get(ms->responses, "/propfind/sub/res4"); + r1 = MAP_GET(ms->responses, "/propfind/sub/res4"); UCX_TEST_ASSERT(r1, "propfind3: missing /propfind/sub/res4 response"); testutil_destroy_session(sn); @@ -770,11 +773,11 @@ TestMultistatus *ms = test_parse_multistatus(st->buf->space, st->buf->size); UCX_TEST_ASSERT(ms, "propfind1: response is not valid xml"); - r1 = ucx_map_cstr_get(ms->responses, "/propfind/"); + r1 = MAP_GET(ms->responses, "/propfind/"); UCX_TEST_ASSERT(r1, "propfind1: missing /propfind/ response"); - UCX_TEST_ASSERT(ms->responses->count == 1, "propfind1: wrong response count"); + UCX_TEST_ASSERT(ms->responses->size == 1, "propfind1: wrong response count"); - p = ucx_map_cstr_get(r1->properties, "DAV:resourcetype"); + p = MAP_GET(r1->properties, "DAV:resourcetype"); UCX_TEST_ASSERT(r1, "propfind1: missing resourcetype property"); testutil_destroy_session(sn); @@ -795,23 +798,23 @@ ms = test_parse_multistatus(st->buf->space, st->buf->size); UCX_TEST_ASSERT(ms, "propfind2: response is not valid xml"); - r1 = ucx_map_cstr_get(ms->responses, "/propfind/"); + r1 = MAP_GET(ms->responses, "/propfind/"); UCX_TEST_ASSERT(r1, "propfind2: missing /propfind/ response"); - UCX_TEST_ASSERT(ms->responses->count == 5, "propfind2: wrong response count"); + UCX_TEST_ASSERT(ms->responses->size == 5, "propfind2: wrong response count"); - r1 = ucx_map_cstr_get(ms->responses, "/propfind/res1"); + r1 = MAP_GET(ms->responses, "/propfind/res1"); UCX_TEST_ASSERT(r1, "propfind2: missing /propfind/res1 response"); - p = ucx_map_cstr_get(r1->properties, "DAV:resourcetype"); + p = MAP_GET(r1->properties, "DAV:resourcetype"); UCX_TEST_ASSERT(r1, "propfind2: missing resourcetype property"); - p = ucx_map_cstr_get(r1->properties, "http://example.com/test"); + p = MAP_GET(r1->properties, "http://example.com/test"); UCX_TEST_ASSERT(r1, "propfind2: missing test property"); - p = ucx_map_cstr_get(r1->properties, "http://example.com/prop2"); + p = MAP_GET(r1->properties, "http://example.com/prop2"); UCX_TEST_ASSERT(r1, "propfind2: missing prop2 property"); - UCX_TEST_ASSERT(ucx_map_cstr_get(ms->responses, "/propfind/res2"), "propfind2: missing /propfind/res2 response"); - UCX_TEST_ASSERT(ucx_map_cstr_get(ms->responses, "/propfind/res3"), "propfind2: missing /propfind/res3 response"); - UCX_TEST_ASSERT(ucx_map_cstr_get(ms->responses, "/propfind/sub/"), "propfind2: missing /propfind/sub response"); + UCX_TEST_ASSERT(MAP_GET(ms->responses, "/propfind/res2"), "propfind2: missing /propfind/res2 response"); + UCX_TEST_ASSERT(MAP_GET(ms->responses, "/propfind/res3"), "propfind2: missing /propfind/res3 response"); + UCX_TEST_ASSERT(MAP_GET(ms->responses, "/propfind/sub/"), "propfind2: missing /propfind/sub response"); testutil_destroy_session(sn); test_multistatus_destroy(ms); @@ -829,24 +832,24 @@ ms = test_parse_multistatus(st->buf->space, st->buf->size); UCX_TEST_ASSERT(ms, "propfind3: response is not valid xml"); - r1 = ucx_map_cstr_get(ms->responses, "/propfind/"); + r1 = MAP_GET(ms->responses, "/propfind/"); UCX_TEST_ASSERT(r1, "propfind3: missing /propfind/ response"); - UCX_TEST_ASSERT(ms->responses->count == 6, "propfind3: wrong response count"); + UCX_TEST_ASSERT(ms->responses->size == 6, "propfind3: wrong response count"); - r1 = ucx_map_cstr_get(ms->responses, "/propfind/res1"); + r1 = MAP_GET(ms->responses, "/propfind/res1"); UCX_TEST_ASSERT(r1, "propfind3: missing /propfind/res1 response"); - p = ucx_map_cstr_get(r1->properties, "DAV:resourcetype"); + p = MAP_GET(r1->properties, "DAV:resourcetype"); UCX_TEST_ASSERT(r1, "propfind3: missing resourcetype property"); - p = ucx_map_cstr_get(r1->properties, "http://example.com/test"); + p = MAP_GET(r1->properties, "http://example.com/test"); UCX_TEST_ASSERT(r1, "propfind3: missing test property"); - p = ucx_map_cstr_get(r1->properties, "http://example.com/prop2"); + p = MAP_GET(r1->properties, "http://example.com/prop2"); UCX_TEST_ASSERT(r1, "propfind3: missing prop2 property"); - UCX_TEST_ASSERT(ucx_map_cstr_get(ms->responses, "/propfind/res2"), "propfind3: missing /propfind/res2 response"); - UCX_TEST_ASSERT(ucx_map_cstr_get(ms->responses, "/propfind/res3"), "propfind3: missing /propfind/res3 response"); - UCX_TEST_ASSERT(ucx_map_cstr_get(ms->responses, "/propfind/sub/"), "propfind3: missing /propfind/sub response"); - UCX_TEST_ASSERT(ucx_map_cstr_get(ms->responses, "/propfind/sub/res4"), "propfind3: missing /propfind/sub/res4 response"); + UCX_TEST_ASSERT(MAP_GET(ms->responses, "/propfind/res2"), "propfind3: missing /propfind/res2 response"); + UCX_TEST_ASSERT(MAP_GET(ms->responses, "/propfind/res3"), "propfind3: missing /propfind/res3 response"); + UCX_TEST_ASSERT(MAP_GET(ms->responses, "/propfind/sub/"), "propfind3: missing /propfind/sub response"); + UCX_TEST_ASSERT(MAP_GET(ms->responses, "/propfind/sub/res4"), "propfind3: missing /propfind/sub/res4 response"); testutil_destroy_session(sn); test_multistatus_destroy(ms);
--- a/src/server/plugins/postgresql/pgtest.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/plugins/postgresql/pgtest.h Sun Nov 06 15:53:32 2022 +0100 @@ -14,8 +14,9 @@ #ifndef PGTEST_H #define PGTEST_H -#include <ucx/test.h> -#include <ucx/map.h> +#include "../../test/test.h" +#include <cx/map.h> +#include <cx/basic_mempool.h> #include <libxml/tree.h> #ifdef __cplusplus @@ -23,14 +24,14 @@ #endif typedef struct TestMultistatus { - UcxMempool *mp; + CxMempool *mp; xmlDoc *doc; - UcxMap *responses; + CxMap *responses; } TestMultistatus; typedef struct TestResponse { char *href; - UcxMap *properties; + CxMap *properties; } TestResponse; typedef struct TestProperty {
--- a/src/server/plugins/postgresql/webdav.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/plugins/postgresql/webdav.c Sun Nov 06 15:53:32 2022 +0100 @@ -35,8 +35,9 @@ #include "../../daemon/http.h" // etag -#include <ucx/buffer.h> -#include <ucx/utils.h> +#include <cx/buffer.h> +#include <cx/utils.h> +#include <cx/printf.h> #include <libxml/tree.h> @@ -238,14 +239,14 @@ * adds str to the buffer * some characters will be escaped: \,{} */ -static void buf_addstr_escaped(UcxBuffer *buf, const char *str) { +static void buf_addstr_escaped(CxBuffer *buf, const char *str) { size_t len = strlen(str); for(size_t i=0;i<len;i++) { char c = str[i]; if(c == '{' || c == '}' || c == ',' || c == '\\') { - ucx_buffer_putc(buf, '\\'); + cxBufferPut(buf, '\\'); } - ucx_buffer_putc(buf, c); + cxBufferPut(buf, c); } } @@ -257,23 +258,23 @@ * * returns 0 on success, 1 otherwise */ -int pg_create_property_param_arrays(WebdavPList *plist, UcxBuffer *xmlns, UcxBuffer *pname) { - ucx_buffer_putc(xmlns, '{'); - ucx_buffer_putc(pname, '{'); +int pg_create_property_param_arrays(WebdavPList *plist, CxBuffer *xmlns, CxBuffer *pname) { + cxBufferPut(xmlns, '{'); + cxBufferPut(pname, '{'); while(plist) { WebdavProperty *property = plist->property; if(property && property->namespace && property->namespace->href && property->name) { buf_addstr_escaped(xmlns, (const char*)property->namespace->href); buf_addstr_escaped(pname, (const char*)property->name); if(plist->next) { - ucx_buffer_putc(xmlns, ','); - ucx_buffer_putc(pname, ','); + cxBufferPut(xmlns, ','); + cxBufferPut(pname, ','); } } plist = plist->next; } - int r1 = ucx_buffer_write("}\0", 2, 1, xmlns) == 0; - int r2 = ucx_buffer_write("}\0", 2, 1, pname) == 0; + int r1 = cxBufferWrite("}\0", 2, 1, xmlns) == 0; + int r2 = cxBufferWrite("}\0", 2, 1, pname) == 0; return r1+r2 != 0; } @@ -295,7 +296,7 @@ WSBool iscollection, PgPropfindExtCol *ext, size_t numext, - UcxBuffer *sql) + CxBuffer *sql) { PgWebdavBackend *pgdav = rq->dav->instance; PgRepository *repo = pgdav->repository; @@ -318,47 +319,47 @@ // CTE if(depth == -1) { - ucx_buffer_puts(sql, sql_propfind_cte_recursive); + cxBufferPutString(sql, sql_propfind_cte_recursive); } // select - ucx_buffer_puts(sql, sql_propfind_select); + cxBufferPutString(sql, sql_propfind_select); // ppath switch(depth) { - case 0: ucx_buffer_puts(sql, sql_propfind_ppath_depth0); break; - case 1: ucx_buffer_puts(sql, sql_propfind_ppath_depth1); break; - case -1: ucx_buffer_puts(sql, sql_propfind_ppath_depth_infinity); break; + case 0: cxBufferPutString(sql, sql_propfind_ppath_depth0); break; + case 1: cxBufferPutString(sql, sql_propfind_ppath_depth1); break; + case -1: cxBufferPutString(sql, sql_propfind_ppath_depth_infinity); break; } // cols - ucx_buffer_puts(sql, sql_propfind_cols); + cxBufferPutString(sql, sql_propfind_cols); // ext_cols if(ext) { if(rq->allprop) { for(int i=0;i<repo->ntables;i++) { - ucx_bprintf(sql, ",x%d.*\n", i); + cx_bprintf(sql, ",x%d.*\n", i); } } else { for(int i=0;i<numext;i++) { PgPropfindExtCol e = ext[i]; - ucx_bprintf(sql, ",x%d.%s\n", e.ext->tableindex, e.ext->column); + cx_bprintf(sql, ",x%d.%s\n", e.ext->tableindex, e.ext->column); } } } // from - ucx_buffer_puts(sql, depth == -1 ? sql_propfind_from_cte : sql_propfind_from_table); + cxBufferPutString(sql, depth == -1 ? sql_propfind_from_cte : sql_propfind_from_table); // prop join - ucx_buffer_puts(sql, rq->allprop ? sql_propfind_propjoin_allprop : sql_propfind_propjoin_plist); + cxBufferPutString(sql, rq->allprop ? sql_propfind_propjoin_allprop : sql_propfind_propjoin_plist); // ext_join if(ext) { if(rq->allprop) { for(int i=0;i<repo->ntables;i++) { - ucx_bprintf(sql, "left join %s x%d on r.resource_id = x%d.resource_id\n", repo->tables[i].table, i, i); + cx_bprintf(sql, "left join %s x%d on r.resource_id = x%d.resource_id\n", repo->tables[i].table, i, i); } } else { int tab = -1; @@ -366,7 +367,7 @@ PgPropfindExtCol e = ext[i]; if(e.ext->tableindex != tab) { tab = e.ext->tableindex; - ucx_bprintf(sql, "left join %s x%d on r.resource_id = x%d.resource_id\n", repo->tables[tab].table, tab, tab); + cx_bprintf(sql, "left join %s x%d on r.resource_id = x%d.resource_id\n", repo->tables[tab].table, tab, tab); } } } @@ -375,20 +376,20 @@ // where if(depth == 0) { - ucx_buffer_puts(sql, sql_propfind_where_depth0); + cxBufferPutString(sql, sql_propfind_where_depth0); } else if(depth == 1) { - ucx_buffer_puts(sql, sql_propfind_where_depth1); + cxBufferPutString(sql, sql_propfind_where_depth1); } // order if(depth == 1) { - ucx_buffer_puts(sql, sql_propfind_order_depth1); + cxBufferPutString(sql, sql_propfind_order_depth1); } else if(depth == -1) { - ucx_buffer_puts(sql, sql_propfind_order_depth_infinity); + cxBufferPutString(sql, sql_propfind_order_depth_infinity); } // end - ucx_buffer_puts(sql, ";\0"); + cxBufferWrite(";\0", 1, 2, sql); return 0; } @@ -400,6 +401,7 @@ WebdavPList **outplist) { PgWebdavBackend *pgdav = rq->dav->instance; + CxAllocator *a = pool_allocator(rq->sn->pool); // first, check if the resource exists // if it doesn't exist, we can return immediately @@ -432,7 +434,7 @@ // like to use it char resource_id_str[32]; snprintf(resource_id_str, 32, "%" PRId64, resource_id); - pblock_nvinsert("resource_id",resource_id_str, rq->rq->vars); + pblock_nvinsert("resource_id", resource_id_str, rq->rq->vars); // create a list of requsted extended properties PgPropfindExtCol *ext; @@ -442,16 +444,15 @@ ext = NULL; numext = 0; } else { - numext = pgdav->repository->prop_ext->count; + numext = pgdav->repository->prop_ext->size; ext = pool_calloc(rq->sn->pool, numext, sizeof(PgPropfindExtCol)); if(rq->allprop) { // the map pgdav->repository->prop_ext contains all property extensions // we can just convert the map to an array - UcxMapIterator i = ucx_map_iterator(pgdav->repository->prop_ext); - PgPropertyStoreExt *cfg_ext; + CxIterator i = cxMapIteratorValues(pgdav->repository->prop_ext); int j = 0; - UCX_MAP_FOREACH(key, cfg_ext, i) { + cx_foreach(PgPropertyStoreExt *, cfg_ext, i) { PgPropfindExtCol extcol; extcol.ext = cfg_ext; extcol.field_num = -1; // get the field_num after the PQexec @@ -464,9 +465,9 @@ while(webdav_plist_iterator_next(&i, &cur)) { WSNamespace *ns = cur->property->namespace; if(ns) { - UcxKey pkey = webdav_property_key((const char*)ns->href, cur->property->name); - PgPropertyStoreExt *cfg_ext = ucx_map_get(pgdav->repository->prop_ext, pkey); - free((void*)pkey.data); + CxHashKey pkey = webdav_property_key((const char*)ns->href, cur->property->name); + PgPropertyStoreExt *cfg_ext = cxMapGet(pgdav->repository->prop_ext, pkey); + free(pkey.data.bytes); if(cfg_ext) { PgPropfindExtCol extcol; extcol.ext = cfg_ext; @@ -485,11 +486,15 @@ // create sql query const char *query = NULL; - UcxBuffer *sql = ucx_buffer_new(NULL, 2048, UCX_BUFFER_AUTOEXTEND); - if(pg_create_propfind_query(rq, iscollection, ext, numext, sql)) { + CxBuffer sql; + if(cxBufferInit(&sql, NULL, 2048, a, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS)) { return 1; } - query = sql->space; + + if(pg_create_propfind_query(rq, iscollection, ext, numext, &sql)) { + return 1; + } + query = sql.space; // get all resources and properties size_t href_len = strlen(href); @@ -501,32 +506,34 @@ href_param[href_len] = '\0'; // if allprop is false, create array pair for xmlns/property names - UcxBuffer *xmlns_buf = NULL; - UcxBuffer *pname_buf = NULL; + CxBuffer xmlns_buf; + CxBuffer pname_buf; + WSBool buf_initialized = FALSE; char *xmlns_param = NULL; char *pname_param = NULL; int nparam = 2; if(!rq->allprop) { size_t bufsize = rq->propcount < 200 ? 8 + rq->propcount * 32 : 4096; - xmlns_buf = ucx_buffer_new(NULL, bufsize, UCX_BUFFER_AUTOEXTEND); - if(!xmlns_buf) { + if(cxBufferInit(&xmlns_buf, NULL, bufsize, a, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS)) { return 1; } - pname_buf = ucx_buffer_new(NULL, bufsize, UCX_BUFFER_AUTOEXTEND); - if(!pname_buf) { - ucx_buffer_free(xmlns_buf); + if(cxBufferInit(&pname_buf, NULL, bufsize, a, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS)) { + cxBufferDestroy(&xmlns_buf); return 1; } - if(pg_create_property_param_arrays(*outplist, xmlns_buf, pname_buf)) { - ucx_buffer_free(xmlns_buf); - ucx_buffer_free(pname_buf); + if(pg_create_property_param_arrays(*outplist, &xmlns_buf, &pname_buf)) { + cxBufferDestroy(&xmlns_buf); + cxBufferDestroy(&pname_buf); return 1; } - xmlns_param = xmlns_buf->space; - pname_param = pname_buf->space; + buf_initialized = TRUE; + xmlns_param = xmlns_buf.space; + pname_param = pname_buf.space; nparam = 4; } + + const char* params[4] = { resource_id_str, href_param, xmlns_param, pname_param }; PGresult *result = PQexecParams( pgdav->connection, @@ -539,9 +546,9 @@ 0); // 0: result in text format int nrows = PQntuples(result); pool_free(rq->sn->pool, href_param); - if(xmlns_buf) { - ucx_buffer_free(xmlns_buf); - ucx_buffer_free(pname_buf); + if(buf_initialized) { + cxBufferDestroy(&xmlns_buf); + cxBufferDestroy(&pname_buf); } if(nrows < 1) { // we resolved the path, so the resource exists and nrows should @@ -899,9 +906,9 @@ static PgPropertyStoreExt* pg_proppatch_prop_get_ext(PgWebdavBackend *pgdav, WebdavProperty *property) { - UcxKey pkey = webdav_property_key((const char*)property->namespace->href, property->name); - PgPropertyStoreExt *ext = ucx_map_get(pgdav->repository->prop_ext, pkey); - free((void*)pkey.data); + CxHashKey pkey = webdav_property_key((const char*)property->namespace->href, property->name); + PgPropertyStoreExt *ext = cxMapGet(pgdav->repository->prop_ext, pkey); + free(pkey.data.bytes); return ext; } @@ -923,14 +930,21 @@ ext_prop->column = ext; ext_prop->property = property; - UcxAllocator a = util_pool_allocator(pool); + CxAllocator *a = pool_allocator(pool); proppatch->ext[ext->tableindex].isused = TRUE; - UcxList **list = proppatch_op == PG_PROPPATCH_EXT_REMOVE - ? &proppatch->ext[ext->tableindex].remove - : &proppatch->ext[ext->tableindex].set; - *list = ucx_list_append_a(&a, *list, ext_prop); - + PgProppatchExtProp **list_begin; + PgProppatchExtProp **list_end; + if(proppatch_op == PG_PROPPATCH_EXT_SET) { + list_begin = &proppatch->ext[ext->tableindex].set_begin; + list_end = &proppatch->ext[ext->tableindex].set_end; + } else { + list_begin = &proppatch->ext[ext->tableindex].remove_begin; + list_end = &proppatch->ext[ext->tableindex].remove_end; + } + + cx_linked_list_add((void**)list_begin, (void**)list_end, -1, offsetof(PgProppatchExtProp, next), ext_prop); + proppatch->extensions_used = TRUE; return 0; @@ -1053,33 +1067,36 @@ * * Query: insert into <table> (resource_id, col1, ...) values ($1, $2 ...); */ -static UcxBuffer* ext_row_create_insert_query(WebdavProppatchRequest *request, PgProppatchExt *ext, PgExtTable *table, char *** params, size_t *nparams) { +static CxBuffer* ext_row_create_insert_query(WebdavProppatchRequest *request, PgProppatchExt *ext, PgExtTable *table, char *** params, size_t *nparams) { pool_handle_t *pool = request->sn->pool; - UcxBuffer *sql = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND); + CxBuffer *sql = pool_malloc(pool, sizeof(CxBuffer)); if(!sql) { return NULL; } + if(cxBufferInit(sql, NULL, 1024, pool_allocator(pool), CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS)) { + pool_free(pool, sql); + return NULL; + } - size_t pg_nparams = ucx_list_size(ext->set) + 1; + size_t pg_nparams = cx_linked_list_size(ext->set_begin, offsetof(PgProppatchExtProp, next)) + 1; char** pg_params = pool_calloc(pool, pg_nparams, sizeof(char*)); if(!pg_params) { - ucx_buffer_free(sql); + cxBufferDestroy(sql); + pool_free(pool, sql); return NULL; } - ucx_buffer_puts(sql, "insert into "); - ucx_buffer_puts(sql, table->table); - ucx_buffer_puts(sql, "(resource_id"); - UCX_FOREACH(elm, ext->set) { - PgProppatchExtProp *prop = elm->data; - ucx_bprintf(sql, ",%s", prop->column->name); + cxBufferPutString(sql, "insert into "); + cxBufferPutString(sql, table->table); + cxBufferPutString(sql, "(resource_id"); + for(PgProppatchExtProp *prop=ext->set_begin;prop;prop=prop->next) { + cx_bprintf(sql, ",%s", prop->column->name); } - ucx_buffer_puts(sql, ") values ($1\n"); + cxBufferPutString(sql, ") values ($1\n"); int i = 1; - UCX_FOREACH(elm, ext->set) { - PgProppatchExtProp *prop = elm->data; + for(PgProppatchExtProp *prop=ext->set_begin;prop;prop=prop->next) { WebdavProperty *property = prop->property; // convert the property value to WSXmlData // property->vtype == WS_VALUE_XML_NODE should always be true @@ -1091,15 +1108,15 @@ if(property_value->namespaces) { // currently only text data is supported pool_free(pool, params); - ucx_buffer_free(sql); + cxBufferDestroy(sql); return NULL; } } pg_params[i] = value_str; - ucx_bprintf(sql, ",$%d", ++i); + cx_bprintf(sql, ",$%d", ++i); } - ucx_buffer_puts(sql, ");"); + cxBufferPutString(sql, ");"); //printf("\n\n%.*s\n\n", (int)sql->size, sql->space); @@ -1122,28 +1139,32 @@ * ... * where resource_id = $1 ; */ -static UcxBuffer* ext_row_create_update_query(WebdavProppatchRequest *request, PgProppatchExt *ext, PgExtTable *table, char *** params, size_t *nparams) { +static CxBuffer* ext_row_create_update_query(WebdavProppatchRequest *request, PgProppatchExt *ext, PgExtTable *table, char *** params, size_t *nparams) { pool_handle_t *pool = request->sn->pool; - UcxBuffer *sql = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND); + CxBuffer *sql = pool_malloc(pool, sizeof(CxBuffer)); if(!sql) { return NULL; } + if(cxBufferInit(sql, NULL, 1024, pool_allocator(pool), CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS)) { + pool_free(pool, sql); + return NULL; + } - ucx_buffer_puts(sql, "update "); - ucx_buffer_puts(sql, table->table); - ucx_buffer_puts(sql, " set\n"); + cxBufferPutString(sql, "update "); + cxBufferPutString(sql, table->table); + cxBufferPutString(sql, " set\n"); - size_t pg_nparams = ucx_list_size(ext->set) + 1; + size_t pg_nparams = cx_linked_list_size(ext->set_begin, offsetof(PgProppatchExtProp, next)) + 1; char** pg_params = pool_calloc(pool, pg_nparams, sizeof(char*)); if(!pg_params) { - ucx_buffer_free(sql); + cxBufferDestroy(sql); + pool_free(pool, sql); return NULL; } int i = 1; - UCX_FOREACH(elm, ext->set) { - PgProppatchExtProp *prop = elm->data; + for(PgProppatchExtProp *prop=ext->set_begin;prop;prop=prop->next) { WebdavProperty *property = prop->property; // convert the property value to WSXmlData // property->vtype == WS_VALUE_XML_NODE should always be true @@ -1155,23 +1176,22 @@ if(property_value->namespaces) { // currently only text data is supported pool_free(pool, params); - ucx_buffer_free(sql); + cxBufferDestroy(sql); return NULL; } } pg_params[i] = value_str; - ucx_bprintf(sql, " %s = $%d,\n", prop->column->name, ++i); + cx_bprintf(sql, " %s = $%d,\n", prop->column->name, ++i); } - UCX_FOREACH(elm, ext->remove) { - PgProppatchExtProp *prop = elm->data; - ucx_bprintf(sql, " %s = NULL,\n", prop->column->name); + for(PgProppatchExtProp *prop=ext->remove_begin;prop;prop=prop->next) { + cx_bprintf(sql, " %s = NULL,\n", prop->column->name); } // check if any write worked if(sql->pos == 0) { - ucx_buffer_free(sql); + cxBufferDestroy(sql); pool_free(pool, pg_params); return NULL; } @@ -1182,7 +1202,8 @@ sql->space[sql->pos-2] = ' '; } - ucx_bprintf(sql, "where resource_id = $1 ;\0"); + cxBufferPutString(sql, "where resource_id = $1 ;"); + cxBufferPut(sql, '\0'); //printf("\n\n%.*s\n\n", (int)sql->size, sql->space); //fflush(stdout); @@ -1203,7 +1224,7 @@ char **params; size_t nparam; - UcxBuffer *sql = ext_row_create_insert_query(request, ext, table, ¶ms, &nparam); + CxBuffer *sql = ext_row_create_insert_query(request, ext, table, ¶ms, &nparam); if(!sql) { return 1; } @@ -1222,7 +1243,7 @@ NULL, 0); // 0: result in text format - ucx_buffer_free(sql); + cxBufferDestroy(sql); int ret = 1; if(PQresultStatus(result) == PGRES_COMMAND_OK) { @@ -1252,7 +1273,7 @@ char **params; size_t nparam; - UcxBuffer *sql = ext_row_create_update_query(request, ext, table, ¶ms, &nparam); + CxBuffer *sql = ext_row_create_update_query(request, ext, table, ¶ms, &nparam); if(!sql) { return 1; } @@ -1271,7 +1292,7 @@ NULL, 0); // 0: result in text format - ucx_buffer_free(sql); + cxBufferDestroy(sql); int ret = 1; if(PQresultStatus(result) == PGRES_COMMAND_OK) {
--- a/src/server/plugins/postgresql/webdav.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/plugins/postgresql/webdav.h Sun Nov 06 15:53:32 2022 +0100 @@ -35,7 +35,7 @@ #include "config.h" #include <libpq-fe.h> -#include <ucx/buffer.h> +#include <cx/buffer.h> #ifdef __cplusplus extern "C" { @@ -71,14 +71,18 @@ int nrows; } PgPropfind; -typedef struct { +typedef struct PgProppatchExtProp PgProppatchExtProp; +struct PgProppatchExtProp { PgPropertyStoreExt *column; WebdavProperty *property; -} PgProppatchExtProp; + PgProppatchExtProp *next; +}; typedef struct { - UcxList *set; /* list of PgProppatchExtProp* */ - UcxList *remove; /* list of PgProppatchExtProp* */ + PgProppatchExtProp *set_begin; + PgProppatchExtProp *set_end; + PgProppatchExtProp *remove_begin; + PgProppatchExtProp *remove_end; WSBool isused; } PgProppatchExt; @@ -95,7 +99,7 @@ WebdavBackend* pg_webdav_prop_create(Session *sn, Request *rq, pblock *pb); -int pg_create_property_param_arrays(WebdavPList *plist, UcxBuffer *xmlns, UcxBuffer *pname); +int pg_create_property_param_arrays(WebdavPList *plist, CxBuffer *xmlns, CxBuffer *pname); /* ----------------- webdav backend functions ----------------- */ int pg_dav_propfind_init(
--- a/src/server/public/auth.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/public/auth.h Sun Nov 06 15:53:32 2022 +0100 @@ -49,7 +49,7 @@ * param1: authentication database * param2: user */ -typedef User*(*authdb_get_user_f)(AuthDB*, char*); +typedef User*(*authdb_get_user_f)(AuthDB*, const char*); struct auth_db { char *name; @@ -65,7 +65,7 @@ * param1: user * param2: password */ -typedef int(*user_verify_passwd_f)(User*, char*); +typedef int(*user_verify_passwd_f)(User*, const char*); /* * check if the user is a member of a given group @@ -73,7 +73,7 @@ * param1: user * param2: group */ -typedef int(*user_check_group_f)(User*, char*); +typedef int(*user_check_group_f)(User*, const char*); /* * free the user object @@ -93,8 +93,8 @@ }; -User* authdb_get_user(AuthDB *db, char *user); -User* authdb_get_and_verify(AuthDB *db, char *user, char *password, int *pw); +User* authdb_get_user(AuthDB *db, const char *user); +User* authdb_get_and_verify(AuthDB *db, const char *user, const char *password, int *pw); #ifdef __cplusplus }
--- a/src/server/safs/addlog.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/safs/addlog.c Sun Nov 06 15:53:32 2022 +0100 @@ -72,11 +72,10 @@ } // remove trailing line feed - sstr_t tmstr = sstr(time); + cxstring tmstr = cx_str(time); if(tmstr.ptr[tmstr.length-1] == '\n') { tmstr.length--; } - tmstr = sstrdup_pool(sn->pool, tmstr); if(combined) { char *referer = pblock_findval("referer", rq->headers); @@ -93,9 +92,10 @@ } fprintf( log->log->file, - "%s - %s [%s] \"%s\" %d %s %.*s%s%.*s %.*s%s%.*s\n", + "%s - %s [%.*s] \"%s\" %d %s %.*s%s%.*s %.*s%s%.*s\n", ip, user, + (int)tmstr.length, tmstr.ptr, req, rq->status_num, @@ -114,9 +114,10 @@ } else { fprintf( log->log->file, - "%s - %s [%s] \"%s\" %d %s\n", + "%s - %s [%.*s] \"%s\" %d %s\n", ip, user, + (int)tmstr.length, tmstr.ptr, req, rq->status_num,
--- a/src/server/safs/auth.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/safs/auth.c Sun Nov 06 15:53:32 2022 +0100 @@ -243,8 +243,7 @@ // get auth db ServerConfiguration *config = session_get_config(sn); - sstr_t dbname = sstr(db); - AuthDB *authdb = ucx_map_sstr_get(config->authdbs, dbname); + AuthDB *authdb = cxMapGet(config->authdbs, cx_hash_key_str(db)); User *auth_user = authdb->get_user(authdb, user); if(auth_user && !auth_user->verify_password(auth_user, pw)) {
--- a/src/server/safs/cgi.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/safs/cgi.c Sun Nov 06 15:53:32 2022 +0100 @@ -36,7 +36,7 @@ #include <signal.h> #include <sys/wait.h> -#include <ucx/string.h> +#include <cx/string.h> #include "../util/util.h" #include "../util/pblock.h" @@ -216,13 +216,13 @@ // child // get script directory and script name - sstr_t script = sstr(path); - sstr_t parent; + cxstring script = cx_str(path); + cxmutstr parent; int len = strlen(path); for(int i=len-1;i>=0;i--) { if(path[i] == '/') { - script = sstrn(path + i + 1, len - i); - parent = sstrdup(sstrn(path, i)); + script = cx_strn(path + i + 1, len - i); + parent = cx_strdup(cx_strn(path, i)); if(chdir(parent.ptr)) { perror("cgi_start: chdir"); free(parent.ptr); @@ -281,15 +281,15 @@ CGIResponseParser* parser = pool_malloc(sn->pool, sizeof(CGIResponseParser)); parser->sn = sn; parser->rq = rq; - parser->tmp = ucx_buffer_new(NULL, 64, UCX_BUFFER_AUTOEXTEND); parser->status = 0; parser->msg = NULL; + cxBufferInit(&parser->tmp, NULL, 64, pool_allocator(sn->pool), CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS); return parser; } void cgi_parser_free(CGIResponseParser *parser) { - if(parser->tmp) { - ucx_buffer_free(parser->tmp); + if(parser->tmp.space) { + cxBufferDestroy(&parser->tmp); } pool_free(parser->sn->pool, parser); } @@ -302,9 +302,9 @@ * -1: error */ static int parse_lines(CGIResponseParser *parser, char *buf, size_t len, int *pos) { - UcxAllocator a = util_pool_allocator(parser->sn->pool); - sstr_t name; - sstr_t value; + CxAllocator *a = pool_allocator(parser->sn->pool); + cxmutstr name; + cxmutstr value; WSBool space = TRUE; int i; @@ -313,7 +313,7 @@ for(i=0;i<len;i++) { char c = buf[i]; if(value_begin == line_begin && c == ':') { - name = sstrn(buf + line_begin, i - line_begin); + name = cx_mutstrn(buf + line_begin, i - line_begin); value_begin = i + 1; } else if(c == '\n') { if(value_begin == line_begin) { @@ -325,17 +325,18 @@ return -1; } } - value = sstrn(buf + value_begin, i - value_begin); + value = cx_mutstrn(buf + value_begin, i - value_begin); - name = sstrlower_a(&a, sstrtrim(name)); - value = sstrtrim(value); + cx_strlower(name); + name = cx_strdup_a(a, cx_strtrim((cxstring){name.ptr, name.length})); + value = cx_strtrim_m(value); if(name.length == 0 || value.length == 0) { return -1; } - if(!sstrcmp(name, S("status"))) { - sstr_t codestr = value; + if(!cx_strcmp((cxstring){name.ptr, name.length}, (cxstring)CX_STR("status"))) { + cxmutstr codestr = value; int j; for(j=0;j<codestr.length;j++) { if(!isdigit(codestr.ptr[j])) { @@ -351,10 +352,10 @@ util_strtoint(codestr.ptr, &s); parser->status = (int)s; - sstr_t msg = sstrtrim(sstrsubs(value, j + 1)); + cxmutstr msg = cx_strtrim_m(cx_strsubs_m(value, j + 1)); if(msg.length > 0) { - parser->msg = sstrdup_pool(parser->sn->pool, msg).ptr; + parser->msg = cx_strdup_pool(parser->sn->pool, msg).ptr; } } else { pblock_nvlinsert( @@ -388,7 +389,7 @@ int cgi_parse_response(CGIResponseParser *parser, char *buf, size_t len, size_t *bpos) { *bpos = 0; int pos = 0; - if(parser->tmp->pos > 0) { + if(parser->tmp.pos > 0) { // the tmp buffer contains an unfinished line // fill up the buffer until the line is complete WSBool nb = FALSE; @@ -398,12 +399,12 @@ break; } } - ucx_buffer_write(buf, 1, pos, parser->tmp); + cxBufferWrite(buf, 1, pos, &parser->tmp); if(nb) { // line complete int npos; - int r = parse_lines(parser, parser->tmp->space, parser->tmp->pos, &npos); + int r = parse_lines(parser, parser->tmp.space, parser->tmp.pos, &npos); switch(r) { case -1: return -1; case 0: return -1; @@ -414,9 +415,9 @@ } } // reset tmp buffer - parser->tmp->pos = 0; + parser->tmp.pos = 0; } else { - if(parser->tmp->pos > CGI_RESPONSE_MAX_LINE_LENGTH) { + if(parser->tmp.pos > CGI_RESPONSE_MAX_LINE_LENGTH) { return -1; } } @@ -430,7 +431,7 @@ case 1: { int newlen = len - npos; if(npos > 0) { - ucx_buffer_write(buf + npos, 1, newlen, parser->tmp); + cxBufferWrite(buf + npos, 1, newlen, &parser->tmp); } return 0; }
--- a/src/server/safs/cgi.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/safs/cgi.h Sun Nov 06 15:53:32 2022 +0100 @@ -30,7 +30,7 @@ #define CGI_H #include "../public/nsapi.h" -#include <ucx/buffer.h> +#include <cx/buffer.h> #ifdef __cplusplus extern "C" { @@ -45,7 +45,7 @@ typedef struct { Session *sn; Request *rq; - UcxBuffer *tmp; + CxBuffer tmp; int status; char *msg; } CGIResponseParser;
--- a/src/server/safs/cgiutils.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/safs/cgiutils.c Sun Nov 06 15:53:32 2022 +0100 @@ -310,7 +310,7 @@ env[x++] = util_env_str("SERVER_SOFTWARE", PRODUCT_HEADER_ID"/"PRODUCT_VERSION_ID); */ // TODO: enable - //NSString srvName, portStr; + //Ncx_string srvName, portStr; //char buf1[256], buf2[64]; //srvName.useStatic(buf1, sizeof(buf1), 0); //portStr.useStatic(buf2, sizeof(buf2), 0);
--- a/src/server/safs/common.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/safs/common.c Sun Nov 06 15:53:32 2022 +0100 @@ -33,9 +33,10 @@ #include "../util/pblock.h" #include "../util/util.h" -#include <ucx/map.h> +#include <cx/map.h> +#include <cx/hash_map.h> -static UcxMap *var_names; +static CxMap *var_names; enum SAFVarNames { COMMONSAF_INSERT_CLIENT = 1, @@ -74,35 +75,35 @@ #define COMMONSAF_RET_ERROR -2 void common_saf_init() { - var_names = ucx_map_new(32); + var_names = cxHashMapCreate(cxDefaultAllocator, 32); - ucx_map_cstr_put(var_names, "insert-client", (intptr_t)COMMONSAF_INSERT_CLIENT); - ucx_map_cstr_put(var_names, "insert-vars", (intptr_t)COMMONSAF_INSERT_VARS); - ucx_map_cstr_put(var_names, "insert-reqpb", (intptr_t)COMMONSAF_INSERT_REQPB); - ucx_map_cstr_put(var_names, "insert-headers", (intptr_t)COMMONSAF_INSERT_HEADERS); - ucx_map_cstr_put(var_names, "insert-srvhdrs", (intptr_t)COMMONSAF_INSERT_SRVHDRS); + cxMapPut(var_names, cx_hash_key_str("insert-client"), (void*)(intptr_t)COMMONSAF_INSERT_CLIENT); + cxMapPut(var_names, cx_hash_key_str("insert-vars"), (void*)(intptr_t)COMMONSAF_INSERT_VARS); + cxMapPut(var_names, cx_hash_key_str("insert-reqpb"), (void*)(intptr_t)COMMONSAF_INSERT_REQPB); + cxMapPut(var_names, cx_hash_key_str("insert-headers"), (void*)(intptr_t)COMMONSAF_INSERT_HEADERS); + cxMapPut(var_names, cx_hash_key_str("insert-srvhdrs"), (void*)(intptr_t)COMMONSAF_INSERT_SRVHDRS); - ucx_map_cstr_put(var_names, "set-client", (intptr_t)COMMONSAF_SET_CLIENT); - ucx_map_cstr_put(var_names, "set-vars", (intptr_t)COMMONSAF_SET_VARS); - ucx_map_cstr_put(var_names, "set-reqpb", (intptr_t)COMMONSAF_SET_REQPB); - ucx_map_cstr_put(var_names, "set-headers", (intptr_t)COMMONSAF_SET_HEADERS); - ucx_map_cstr_put(var_names, "set-srvhdrs", (intptr_t)COMMONSAF_SET_SRVHDRS); + cxMapPut(var_names, cx_hash_key_str("set-client"), (void*)(intptr_t)COMMONSAF_SET_CLIENT); + cxMapPut(var_names, cx_hash_key_str("set-vars"), (void*)(intptr_t)COMMONSAF_SET_VARS); + cxMapPut(var_names, cx_hash_key_str("set-reqpb"), (void*)(intptr_t)COMMONSAF_SET_REQPB); + cxMapPut(var_names, cx_hash_key_str("set-headers"), (void*)(intptr_t)COMMONSAF_SET_HEADERS); + cxMapPut(var_names, cx_hash_key_str("set-srvhdrs"), (void*)(intptr_t)COMMONSAF_SET_SRVHDRS); - ucx_map_cstr_put(var_names, "remove-client", (intptr_t)COMMONSAF_REMOVE_CLIENT); - ucx_map_cstr_put(var_names, "remove-vars", (intptr_t)COMMONSAF_REMOVE_VARS); - ucx_map_cstr_put(var_names, "remove-reqpb", (intptr_t)COMMONSAF_REMOVE_REQPB); - ucx_map_cstr_put(var_names, "remove-headers", (intptr_t)COMMONSAF_REMOVE_HEADERS); - ucx_map_cstr_put(var_names, "remove-srvhdrs", (intptr_t)COMMONSAF_REMOVE_SRVHDRS); + cxMapPut(var_names, cx_hash_key_str("remove-client"), (void*)(intptr_t)COMMONSAF_REMOVE_CLIENT); + cxMapPut(var_names, cx_hash_key_str("remove-vars"), (void*)(intptr_t)COMMONSAF_REMOVE_VARS); + cxMapPut(var_names, cx_hash_key_str("remove-reqpb"), (void*)(intptr_t)COMMONSAF_REMOVE_REQPB); + cxMapPut(var_names, cx_hash_key_str("remove-headers"), (void*)(intptr_t)COMMONSAF_REMOVE_HEADERS); + cxMapPut(var_names, cx_hash_key_str("remove-srvhdrs"), (void*)(intptr_t)COMMONSAF_REMOVE_SRVHDRS); - ucx_map_cstr_put(var_names, "abort", (intptr_t)COMMONSAF_ABORT); - ucx_map_cstr_put(var_names, "noaction", (intptr_t)COMMONSAF_NOACTION); - ucx_map_cstr_put(var_names, "error", (intptr_t)COMMONSAF_ERROR); - ucx_map_cstr_put(var_names, "escape", (intptr_t)COMMONSAF_ESCAPE); - ucx_map_cstr_put(var_names, "find-pathinfo-forward", (intptr_t)COMMONSAF_FIND_PATHINFO_FORWARD); - ucx_map_cstr_put(var_names, "http-downgrade", (intptr_t)COMMONSAF_HTTP_DOWNGRADE); - ucx_map_cstr_put(var_names, "http-upgrade", (intptr_t)COMMONSAF_HTTP_UPGRADE); - ucx_map_cstr_put(var_names, "keep-alive", (intptr_t)COMMONSAF_KEEP_ALIVE); - ucx_map_cstr_put(var_names, "name", (intptr_t)COMMONSAF_NAME); + cxMapPut(var_names, cx_hash_key_str("abort"), (void*)(intptr_t)COMMONSAF_ABORT); + cxMapPut(var_names, cx_hash_key_str("noaction"), (void*)(intptr_t)COMMONSAF_NOACTION); + cxMapPut(var_names, cx_hash_key_str("error"), (void*)(intptr_t)COMMONSAF_ERROR); + cxMapPut(var_names, cx_hash_key_str("escape"), (void*)(intptr_t)COMMONSAF_ESCAPE); + cxMapPut(var_names, cx_hash_key_str("find-pathinfo-forward"), (void*)(intptr_t)COMMONSAF_FIND_PATHINFO_FORWARD); + cxMapPut(var_names, cx_hash_key_str("http-downgrade"), (void*)(intptr_t)COMMONSAF_HTTP_DOWNGRADE); + cxMapPut(var_names, cx_hash_key_str("http-upgrade"), (void*)(intptr_t)COMMONSAF_HTTP_UPGRADE); + cxMapPut(var_names, cx_hash_key_str("keep-alive"), (void*)(intptr_t)COMMONSAF_KEEP_ALIVE); + cxMapPut(var_names, cx_hash_key_str("name"), (void*)(intptr_t)COMMONSAF_NAME); } int print_message(pblock *pb, Session *sn, Request *rq) { @@ -115,8 +116,8 @@ } static void var_set(char *value, pblock *pb, WSBool insert) { - sstr_t n; - sstr_t v; + cxstring n; + cxstring v; v.ptr = NULL; n.ptr = value; @@ -125,7 +126,7 @@ for(i=1;i<len;i++) { if(value[i] == '=') { n.length = i; - v = sstrsubs(sstrn(value, len), i + 1); + v = cx_strsubs(cx_strn(value, len), i + 1); break; } } @@ -143,8 +144,8 @@ pblock_nvlinsert(n.ptr, n.length, v.ptr, v.length, pb); } -static int set_var(Session *sn, Request *rq, char *var, char *value) { - intptr_t v = (intptr_t)ucx_map_cstr_get(var_names, var); +static int set_var(Session *sn, Request *rq, const char *var, char *value) { + intptr_t v = (intptr_t)cxMapGet(var_names, cx_hash_key_str(var)); switch(v) { default: break; case COMMONSAF_INSERT_CLIENT: var_set(value, sn->client, TRUE); break; @@ -184,7 +185,7 @@ return COMMONSAF_RET_ERROR; } - char *msg = isnum ? NULL : sstrtrim(sstr(value + i)).ptr; + const char *msg = isnum ? NULL : cx_strtrim(cx_str(value + i)).ptr; protocol_status(sn, rq, (int)status, msg); return COMMONSAF_REQ_ABORTED;
--- a/src/server/safs/init.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/safs/init.c Sun Nov 06 15:53:32 2022 +0100 @@ -27,7 +27,7 @@ */ #include <dlfcn.h> -#include <ucx/string.h> +#include <cx/string.h> #include "../daemon/func.h" #include "../daemon/log.h" @@ -94,7 +94,7 @@ struct FuncStruct fc; ZERO(&fc, sizeof(struct FuncStruct)); fc.func = (FuncPtr)sym; - fc.name = sstrdup(sstr(funcs)).ptr; + fc.name = cx_strdup(cx_str(funcs)).ptr; add_function(&fc); if(b) {
--- a/src/server/safs/nametrans.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/safs/nametrans.c Sun Nov 06 15:53:32 2022 +0100 @@ -48,22 +48,15 @@ rq->vfs = vfs; } - WebdavBackend *backend_first = NULL; - WebdavBackend *backend_last = NULL; - UCX_FOREACH(elm, repo->davBackends) { - WebdavBackendInitData *davInit = elm->data; + void *backend_first = NULL; + void *backend_last = NULL; + CxIterator i = cxListIterator(repo->davBackends, 0); + cx_foreach(WebdavBackendInitData *, davInit, i) { WebdavBackend *backend = davInit->davType->create(sn, rq, pb, davInit->davInitData); if(!backend) { return REQ_ABORTED; } - - if(backend_last) { - backend_last->next = backend; - backend_last = backend; - } else { - backend_first = backend; - backend_last = backend; - } + cx_linked_list_add(&backend_first, &backend_last, -1, offsetof(WebdavBackend, next), backend); } rq->davCollection = backend_first; @@ -75,7 +68,7 @@ if(!dav) return 0; ServerConfiguration *config = session_get_config(sn); - WebdavRepository *repo = ucx_map_cstr_get(config->dav, dav); + WebdavRepository *repo = cxMapGet(config->dav, cx_hash_key_str(dav)); if(!repo) { log_ereport(LOG_MISCONFIG, "nametrans: unknown dav repository '%s'", dav); @@ -181,8 +174,8 @@ return REQ_ABORTED; } - sstr_t root_str = sstr(root); - sstr_t uri_str = sstr(pblock_findkeyval(pb_key_uri, rq->reqpb)); + cxstring root_str = cx_str(root); + cxstring uri_str = cx_str(pblock_findkeyval(pb_key_uri, rq->reqpb)); request_set_path(root_str, uri_str, rq->vars); @@ -255,7 +248,7 @@ return REQ_ABORTED; } - request_set_path(sstr(dir), sstr(uri), rq->vars); + request_set_path(cx_str(dir), cx_str(uri), rq->vars); if(name) { // add object to rq->vars @@ -306,13 +299,13 @@ } char *uri = pblock_findkeyval(pb_key_uri, rq->reqpb); - sstr_t u = sstr(uri); - sstr_t f = sstr(from); - if(sstrprefix(u, f)) { - sstr_t suf = sstrsubs(u, f.length); - sstr_t ppath = sstrcat(2, sstr(path), suf); + cxstring u = cx_str(uri); + cxstring f = cx_str(from); + if(cx_strprefix(u, f)) { + cxstring suf = cx_strsubs(u, f.length); + cxmutstr ppath = cx_strcat(2, cx_str(path), suf); - request_set_path(sstr(root), ppath, rq->vars); + request_set_path(cx_str(root), (cxstring){ppath.ptr, ppath.length}, rq->vars); free(ppath.ptr); if(name) {
--- a/src/server/safs/objecttype.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/safs/objecttype.c Sun Nov 06 15:53:32 2022 +0100 @@ -26,8 +26,8 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include <ucx/string.h> -#include <ucx/map.h> +#include <cx/string.h> +#include <cx/map.h> #include "objecttype.h" #include "../util/pblock.h" @@ -36,15 +36,15 @@ #include "../daemon/session.h" int object_type_by_extension(pblock *pb, Session *sn, Request *rq) { - sstr_t path = sstr(pblock_findkeyval(pb_key_path, rq->vars)); + cxstring path = cx_str(pblock_findkeyval(pb_key_path, rq->vars)); //printf("\nobject_type_by_extension: {%s}[%d]\n\n", path); - sstr_t ct; + cxstring ct; if(path.ptr[path.length - 1] == '/') { // directory - ct = sstrn("internal/directory", 18); + ct = (cxstring)CX_STR("internal/directory"); } else { - sstr_t ext; + cxstring ext; ext.length = 0; for(int i=path.length - 1;i>=0;i--) { if(path.ptr[i] == '.') { @@ -66,7 +66,7 @@ WS_ASSERT(config->mimetypes); WS_ASSERT(config->mimetypes->map); - char *type = ucx_map_sstr_get(config->mimetypes->map, ext); + char *type = cxMapGet(config->mimetypes->map, cx_hash_key_bytes((const unsigned char*)ext.ptr, ext.length)); if(!type) { return REQ_NOACTION;
--- a/src/server/safs/pathcheck.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/safs/pathcheck.c Sun Nov 06 15:53:32 2022 +0100 @@ -26,7 +26,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include <ucx/string.h> +#include <cx/string.h> #include "pathcheck.h" @@ -41,6 +41,8 @@ #include "../config/acl.h" +#define PATHCHECK_MAX_TOKENS 2048 + int require_auth(pblock *pb, Session *sn, Request *rq) { char *user = pblock_findkeyval(pb_key_auth_user, rq->vars); @@ -74,14 +76,13 @@ } uint32_t access_mask = 0; - ssize_t n = 0; - sstr_t *rights = sstrsplit(sstr(mask_str), sstrn(",", 1), &n); + cxstring *rights = NULL; + ssize_t n = cx_strsplit_a(pool_allocator(sn->pool), cx_str(mask_str), (cxstring){",", 1}, PATHCHECK_MAX_TOKENS, &rights); for(int i=0;i<n;i++) { - sstr_t right = rights[i]; + cxstring right = rights[i]; access_mask = access_mask | accstr2int(right); - free(right.ptr); } - free(rights); + pool_free(sn->pool, rights); rq->aclreqaccess = access_mask; @@ -132,8 +133,8 @@ return REQ_ABORTED; } - ssize_t ni = 0; - sstr_t *names = sstrsplit(sstr(inames), S(","), &ni); + cxstring *names = NULL; + ssize_t ni = cx_strsplit_a(pool_allocator(sn->pool), cx_str(inames), (cxstring)CX_STR(","), PATHCHECK_MAX_TOKENS, &names); if(ni <= 0) { log_ereport( LOG_MISCONFIG, @@ -154,10 +155,10 @@ char *path = pblock_findkeyval(pb_key_path, rq->vars); size_t pathlen = strlen(path); - sstr_t p = sstrn(path, pathlen); + cxstring p = cx_strn(path, pathlen); if(path[pathlen-1] == '/') { for(int i=0;i<ni;i++) { - sstr_t newpath = sstrcat(2, p, sstrtrim(names[i])); + cxmutstr newpath = cx_strcat(2, p, cx_strtrim(names[i])); struct stat s; if(!vfs_stat(vfs, newpath.ptr, &s)) { pblock_kvinsert( @@ -173,10 +174,7 @@ } } - for(int i=0;i<ni;i++) { - free(names[i].ptr); - } - free(names); + pool_free(sn->pool, names); return ret; }
--- a/src/server/safs/service.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/safs/service.c Sun Nov 06 15:53:32 2022 +0100 @@ -38,8 +38,9 @@ #include "../daemon/vfs.h" #include "../util/strbuf.h" -#include <ucx/string.h> -#include <ucx/utils.h> +#include <cx/string.h> +#include <cx/utils.h> +#include <cx/printf.h> #include <errno.h> @@ -112,14 +113,14 @@ static HttpRange* parse_range(Session *sn, char *header, int *status) { *status = PROTOCOL_OK; - sstr_t range = sstrtrim(sstr(header)); - if(!sstrprefix(range, S("bytes="))) { + cxstring range = cx_strtrim(cx_str(header)); + if(!cx_strprefix(range, (cxstring)CX_STR("bytes="))) { // unknown range unit - ignore range header return NULL; } // get byte-range-set - range = sstrsubs(range, 6); + range = cx_strsubs(range, 6); if(range.length < 1) { return NULL; } @@ -132,7 +133,7 @@ for(int i=0;i<=range.length;i++) { char c = range.ptr[i]; if(c == '-') { - sstr_t num = sstrsubsl(range, start, i-start); + cxstring num = cx_strsubsl(range, start, i-start); if(num.length == 0) { // empty string before '-' is legal hasbegin = 1; @@ -152,7 +153,7 @@ return NULL; } } else if(c == ',' || c == '\0') { - sstr_t num = sstrsubsl(range, start, i-start); + cxstring num = cx_strsubsl(range, start, i-start); if(hasbegin) { long long n; if(num.length == 0) { @@ -495,24 +496,26 @@ } struct multi_range_elm { - sstr_t header; + cxmutstr header; off_t offset; off_t length; }; static int send_multi_range(Session *sn, Request *rq, SYS_FILE fd, off_t filelen, HttpRange *range) { + CxAllocator *a = pool_allocator(sn->pool); + pb_param *content_type = pblock_remove("content-type", rq->srvhdrs); char sep[64]; int seplen = util_mime_separator(sep); - sstr_t newct = ucx_sprintf("multipart/byteranges; boundary=%s", sep+4); + cxmutstr newct = cx_asprintf_a(a, "multipart/byteranges; boundary=%s", sep+4); pblock_kvinsert( pb_key_content_type, newct.ptr, newct.length, rq->srvhdrs); - free(newct.ptr); + cxFree(a, newct.ptr); // calculate content-length off_t response_len = 0; @@ -524,12 +527,13 @@ rangeelm = rangeelm->next; } - struct multi_range_elm *r = calloc(nrange, sizeof(struct multi_range_elm)); + struct multi_range_elm *r = pool_calloc(sn->pool, nrange, sizeof(struct multi_range_elm)); rangeelm = range; int i=0; while(rangeelm) { range2off(rangeelm, filelen, &(r[i].offset), &(r[i].length)); - r[i].header = ucx_sprintf( + r[i].header = cx_asprintf_a( + a, "%s\r\nContent-Type: %s\r\nContent-Range: bytes %lld-%lld/%lld\r\n\r\n", sep, content_type->value, @@ -565,7 +569,7 @@ } net_printf(sn->csd, "%s--\r\n", sep); - free(r); + pool_free(sn->pool, r); return 0; } @@ -616,7 +620,7 @@ (long long)length, rq->srvhdrs); - sstr_t content_range = ucx_sprintf( + cxmutstr content_range = cx_asprintf( "%lld-%lld/%lld", (long long)offset, (long long)offset+length - 1, @@ -696,7 +700,7 @@ char *path = pblock_findkeyval(pb_key_path, rq->vars); char *uri = pblock_findkeyval(pb_key_uri, rq->reqpb); - sstr_t r_uri = sstr(uri); + cxstring r_uri = cx_str(uri); // open the file VFSContext *vfs = vfs_request_context(sn, rq); @@ -717,7 +721,7 @@ // list directory VFS_ENTRY f; while(vfs_readdir(dir, &f)) { - sstr_t filename = sstr(f.name); + cxstring filename = cx_str(f.name); sbuf_puts(out, "<a href=\""); sbuf_append(out, r_uri);
--- a/src/server/test/main.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/test/main.c Sun Nov 06 15:53:32 2022 +0100 @@ -40,7 +40,7 @@ #include "../util/date.h" #include "../daemon/vfs.h" -#include <ucx/test.h> +#include "test.h" #include "vfs.h" #include "writer.h"
--- a/src/server/test/objs.mk Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/test/objs.mk Sun Nov 06 15:53:32 2022 +0100 @@ -30,7 +30,8 @@ TEST_OBJPRE = $(OBJ_DIR)$(TEST_SRC_DIR) -TESTOBJ = main.o +TESTOBJ = test.o +TESTOBJ += main.o TESTOBJ += testutils.o TESTOBJ += webdav.o TESTOBJ += vfs.o
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/test/test.c Sun Nov 06 15:53:32 2022 +0100 @@ -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); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/test/test.h Sun Nov 06 15:53:32 2022 +0100 @@ -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: **** + * + * <pre> + * UCX_TEST(function_name); + * UCX_TEST_SUBROUTINE(subroutine_name, paramlist); // optional + * </pre> + * + * **** IN SOURCE FILE: **** + * <pre> + * 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 + * } + * </pre> + * + * <b>Note:</b> if a test fails, a longjump is performed + * back to the #UCX_TEST_BEGIN macro! + * + * <b>Attention:</b> 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <setjmp.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef __FUNCTION__ + +/** + * Alias for the <code>__func__</code> preprocessor macro. + * Some compilers use <code>__func__</code> and others use __FUNCTION__. + * We use __FUNCTION__ so we define it for those compilers which use + * <code>__func__</code>. + */ +#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 <code>EXIT_SUCCESS</code> on success or + * <code>EXIT_FAILURE</code> 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. + * <b>Note:</b> Any UCX_TEST_ASSERT() calls must be performed <b>after</b> + * #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. + * + * <b>Note:</b> You may <b>only</b> 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. + * <b>Note:</b> Any UCX_TEST_ASSERT() calls must be performed <b>before</b> + * #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 */ +
--- a/src/server/test/testutils.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/test/testutils.c Sun Nov 06 15:53:32 2022 +0100 @@ -29,8 +29,9 @@ #include <stdio.h> #include <stdlib.h> -#include <ucx/string.h> -#include <ucx/utils.h> +#include <cx/string.h> +#include <cx/utils.h> +#include <cx/printf.h> #include "../util/pblock.h" @@ -54,7 +55,7 @@ ZERO(&httprequest, sizeof(HTTPRequest)); request_initialize(pool, &httprequest, rq); - sstr_t clf = ucx_sprintf("%s %s HTTP/1.1", method, uri); + cxmutstr clf = cx_asprintf("%s %s HTTP/1.1", method, uri); pblock_kvinsert( pb_key_clf_request, clf.ptr, @@ -100,7 +101,7 @@ } void testutil_request_body(Session *sn, Request *rq, const char *body, size_t len) { - sstr_t cl = ucx_sprintf("%d", (int)len); + cxmutstr cl = cx_asprintf("%d", (int)len); pblock_nvreplace("content-length", cl.ptr, rq->headers); free(cl.ptr); @@ -122,7 +123,7 @@ static ssize_t test_io_write(IOStream *io, void *buf, size_t size) { TestIOStream *st = (TestIOStream*)io; - return ucx_buffer_write(buf, 1, size, st->buf); + return cxBufferWrite(buf, 1, size, st->buf); } static ssize_t test_io_writev(IOStream *io, struct iovec *iovec, int iovctn) { @@ -153,9 +154,10 @@ TestIOStream *stream = calloc(1, sizeof(TestIOStream)); int flags = 0; if(autoextend) { - flags = UCX_BUFFER_AUTOEXTEND; + flags = CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS; } - stream->buf = ucx_buffer_new(NULL, size, flags); + stream->buf = malloc(sizeof(CxBuffer)); + cxBufferInit(stream->buf, NULL, size, cxDefaultAllocator, flags); stream->io.st.write = test_io_write; stream->io.st.writev = test_io_writev; @@ -166,6 +168,7 @@ } void testutil_iostream_destroy(TestIOStream *stream) { - ucx_buffer_free(stream->buf); + cxBufferDestroy(stream->buf); + free(stream->buf); free(stream); }
--- a/src/server/test/testutils.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/test/testutils.h Sun Nov 06 15:53:32 2022 +0100 @@ -29,11 +29,13 @@ #ifndef TESTUTILS_H #define TESTUTILS_H +#include "test.h" + #include "../public/nsapi.h" #include "../daemon/httprequest.h" #include "../util/io.h" -#include <ucx/buffer.h> +#include <cx/buffer.h> #ifdef __cplusplus extern "C" { @@ -41,7 +43,7 @@ typedef struct TestIOStream { HttpStream io; - UcxBuffer *buf; + CxBuffer *buf; } TestIOStream; Session* testutil_session(void);
--- a/src/server/test/uri.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/test/uri.h Sun Nov 06 15:53:32 2022 +0100 @@ -32,7 +32,7 @@ #include "../public/nsapi.h" -#include <ucx/test.h> +#include "test.h" #ifdef __cplusplus extern "C" {
--- a/src/server/test/vfs.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/test/vfs.c Sun Nov 06 15:53:32 2022 +0100 @@ -30,9 +30,9 @@ #include <stdlib.h> #include <errno.h> -#include <ucx/string.h> -#include <ucx/list.h> -#include <ucx/map.h> +#include <cx/string.h> +#include <cx/list.h> +#include <cx/map.h> #include "../daemon/session.h" @@ -41,33 +41,33 @@ #include "vfs.h" typedef struct TestVFS { - UcxMap *files; + CxMap *files; int count_unlink; int count_rmdir; } TestVFS; typedef struct TestVFSFile { VFSFile file; - sstr_t path; + cxmutstr path; int isdir; - UcxBuffer *content; + CxBuffer content; } TestVFSFile; typedef struct TestVFSDir { VFSDir dir; TestVFSFile *file; - UcxMapIterator i; - sstr_t name; + CxIterator i; + cxmutstr name; } TestVFSDir; /* dir io */ -static char* test_resource_name(char *url) { - sstr_t urlstr = sstr(url); +static const char* test_resource_name(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 { @@ -79,29 +79,28 @@ TestVFS *vfs = dir->ctx->vfs->instance; TestVFSDir *vfsdir = (TestVFSDir*)dir; - sstr_t prefix = sstrcat(2, vfsdir->file->path, S("/")); + cxmutstr prefix = cx_strcat(2, vfsdir->file->path, cx_str("/")); + // not the most efficient file system implementation ever, but it is only + // for testing + // check every entry in vfs->files and return, if the parent path + // matches the open directory TestVFSFile *file = NULL; - UCX_MAP_FOREACH(key, file, vfsdir->i) { - sstr_t file_path = sstrcat( + cx_foreach(TestVFSFile *, entry, vfsdir->i) { + if(file) break; + cxmutstr file_path = cx_strcat( 2, prefix, - sstr(test_resource_name(file->path.ptr))); - void *m = ucx_map_get(vfs->files, ucx_key(file_path.ptr, file_path.length)); - // don't ask why alfree and not free() - alfree(ucx_default_allocator(), file_path.ptr); - if(m) { - break; - } else { - file = NULL; - } + cx_str(test_resource_name(entry->path.ptr))); + file = cxMapGet(vfs->files, cx_hash_key(file_path.ptr, file_path.length)); + free(file_path.ptr); } free(prefix.ptr); if(file) { - vfsdir->name = sstrdup_a( - session_get_allocator(dir->ctx->sn), - sstr(test_resource_name(file->path.ptr))); + vfsdir->name = cx_strdup_a( + pool_allocator(dir->ctx->sn->pool), + cx_str(test_resource_name(file->path.ptr))); ZERO(entry, sizeof(VFS_ENTRY)); entry->name = vfsdir->name.ptr; @@ -126,35 +125,35 @@ ssize_t testvfs_read(SYS_FILE fd, void *buf, size_t nbyte) { TestVFSFile *file = (TestVFSFile*)fd; - return (ssize_t)ucx_buffer_read(buf, 1, nbyte, file->content); + return (ssize_t)cxBufferRead(buf, 1, nbyte, &file->content); } ssize_t testvfs_write(SYS_FILE fd, const void *buf, size_t nbyte) { TestVFSFile *file = (TestVFSFile*)fd; - return (ssize_t)ucx_buffer_write(buf, 1, nbyte, file->content); + return (ssize_t)cxBufferWrite(buf, 1, nbyte, &file->content); } ssize_t testvfs_pread(SYS_FILE fd, void *buf, size_t nbyte, off_t offset) { TestVFSFile *file = (TestVFSFile*)fd; - file->content->pos = (size_t)offset; + file->content.pos = (size_t)offset; return testvfs_read(fd, buf, nbyte); } ssize_t testvfs_pwrite(SYS_FILE fd, const void *buf, size_t nbyte, off_t offset) { TestVFSFile *file = (TestVFSFile*)fd; - file->content->pos = (size_t)offset; + file->content.pos = (size_t)offset; return testvfs_write(fd, buf, nbyte); } off_t testvfs_seek(SYS_FILE fd, off_t offset, int whence) { TestVFSFile *file = (TestVFSFile*)fd; - ucx_buffer_seek(file->content, offset, whence); - return (off_t)file->content->pos; + cxBufferSeek(&file->content, offset, whence); + return (off_t)file->content.pos; } void testvfs_close(SYS_FILE fd) { TestVFSFile *file = (TestVFSFile*)fd; - file->content->pos = 0; + file->content.pos = 0; } VFS_IO test_file_io = { @@ -180,28 +179,23 @@ TestVFS *vfs = ctx->vfs->instance; TestVFSFile *file = NULL; - sstr_t s_path = sstr((char*)path); - if(sstrsuffix(s_path, S("/"))) { + cxstring s_path = cx_str((char*)path); + if(cx_strsuffix(s_path, cx_str("/"))) { s_path.length--; } - file = ucx_map_sstr_get(vfs->files, s_path); + file = cxMapGet(vfs->files, cx_hash_key_bytes((const unsigned char*)s_path.ptr, s_path.length)); if(!file) { if((oflags & O_CREAT) == O_CREAT) { file = pool_malloc(ctx->sn->pool, sizeof(TestVFSFile)); ZERO(file, sizeof(TestVFSFile)); file->file.ctx = ctx; - file->path = sstrdup_a(session_get_allocator(ctx->sn), s_path); + file->path = cx_strdup_a(pool_allocator(ctx->sn->pool), s_path); file->file.io = &test_file_io; - file->content = pool_calloc(ctx->sn->pool, 1, sizeof(UcxBuffer)); - file->content->capacity = 2048; - file->content->space = pool_malloc(ctx->sn->pool, file->content->capacity); - file->content->flags = 0; - file->content->pos = 0; - file->content->size = 0; + cxBufferInit(&file->content, pool_malloc(ctx->sn->pool, 2048), 2048, pool_allocator(ctx->sn->pool), 0); - ucx_map_sstr_put(vfs->files, s_path, file); + cxMapPut(vfs->files, cx_hash_key((void*)s_path.ptr, s_path.length), file); } else { ctx->vfs_errno = ENOENT; } @@ -214,12 +208,12 @@ TestVFS *vfs = ctx->vfs->instance; TestVFSFile *file = NULL; - sstr_t s_path = sstr((char*)path); - if(sstrsuffix(s_path, S("/"))) { + cxstring s_path = cx_str((char*)path); + if(cx_strsuffix(s_path, cx_str("/"))) { s_path.length--; } - file = ucx_map_sstr_get(vfs->files, s_path); + file = cxMapGet(vfs->files, cx_hash_key((void*)s_path.ptr, s_path.length)); if(!file) { ctx->vfs_errno = ENOENT; return 1; @@ -241,12 +235,12 @@ TestVFS *vfs = ctx->vfs->instance; TestVFSFile *file = NULL; - sstr_t s_path = sstr((char*)path); - if(sstrsuffix(s_path, S("/"))) { + cxstring s_path = cx_str((char*)path); + if(cx_strsuffix(s_path, cx_str("/"))) { s_path.length--; } - file = ucx_map_sstr_get(vfs->files, s_path); + file = cxMapGet(vfs->files, cx_hash_key((void*)s_path.ptr, s_path.length)); if(!file) { ctx->vfs_errno = ENOENT; return NULL; @@ -259,7 +253,7 @@ TestVFSDir *dir = pool_malloc(ctx->sn->pool, sizeof(TestVFSDir)); ZERO(dir, sizeof(TestVFSDir)); dir->file = file; - dir->i = ucx_map_iterator(vfs->files); + dir->i = cxMapIteratorValues(vfs->files); dir->dir.ctx = ctx; dir->dir.io = &test_dir_io; @@ -277,7 +271,7 @@ TestVFSDir *dir = pool_malloc(ctx->sn->pool, sizeof(TestVFSDir)); ZERO(dir, sizeof(TestVFSDir)); dir->file = file; - dir->i = ucx_map_iterator(vfs->files); + dir->i = cxMapIteratorValues(vfs->files); dir->dir.ctx = ctx; dir->dir.io = &test_dir_io; @@ -299,7 +293,8 @@ int testvfs_unlink(VFSContext *ctx, const char *path) { TestVFS *vfs = ctx->vfs->instance; - TestVFSFile *file = ucx_map_cstr_get(vfs->files, path); + CxHashKey path_key = cx_hash_key_str(path); + TestVFSFile *file = cxMapGet(vfs->files, path_key); if(!file) { return 1; } @@ -308,14 +303,15 @@ return 1; } - ucx_map_cstr_remove(vfs->files, path); + (void)cxMapRemove(vfs->files, path_key); vfs->count_unlink++; return 0; } int testvfs_rmdir(VFSContext *ctx, const char *path) { TestVFS *vfs = ctx->vfs->instance; - TestVFSFile *dir = ucx_map_cstr_get(vfs->files, path); + CxHashKey path_key = cx_hash_key_str(path); + TestVFSFile *dir = cxMapGet(vfs->files, path_key); if(!dir) { ctx->vfs_errno = ENOENT; return 1; @@ -325,15 +321,14 @@ return 1; } - UcxMapIterator i = ucx_map_iterator(vfs->files); - TestVFSFile *f; - UCX_MAP_FOREACH(key, f, i) { - if(f->path.length > dir->path.length && sstrprefix(f->path, dir->path)){ + CxIterator i = cxMapIteratorValues(vfs->files); + cx_foreach(TestVFSFile *, f, i) { + if(f->path.length > dir->path.length && cx_strprefix(cx_strcast(f->path), cx_strcast(dir->path))){ return 1; // dir not empty } } - ucx_map_cstr_remove(vfs->files, path); + (void)cxMapRemove(vfs->files, path_key); vfs->count_rmdir++; return 0; } @@ -356,7 +351,7 @@ TestVFS *vfs = pool_malloc(sn->pool, sizeof(TestVFS)); vfs->count_unlink = 0; vfs->count_rmdir = 0; - vfs->files = ucx_map_new_a(session_get_allocator(sn), 64); + vfs->files = cxHashMapCreate(pool_allocator(sn->pool), 64); testVFSClass.instance = vfs; return &testVFSClass; @@ -452,20 +447,20 @@ VFSDir *dir = vfs_opendir(vfs, "/dir"); UCX_TEST_ASSERT(dir, "dir not opened"); - UcxMap *files = ucx_map_new(8); + CxMap *files = cxHashMapCreate(cxDefaultAllocator, 8); VFSEntry entry; while(vfs_readdir(dir, &entry)) { - ucx_map_cstr_put(files, entry.name, dir); + cxMapPut(files, cx_hash_key_str(entry.name), dir); } - UCX_TEST_ASSERT(files->count == 4, "wrong files count"); - UCX_TEST_ASSERT(ucx_map_cstr_get(files, "file1"), "file1 missing"); - UCX_TEST_ASSERT(ucx_map_cstr_get(files, "file2"), "file2 missing"); - UCX_TEST_ASSERT(ucx_map_cstr_get(files, "file3"), "file3 missing"); - UCX_TEST_ASSERT(ucx_map_cstr_get(files, "file4"), "file4 missing"); + UCX_TEST_ASSERT(files->size == 4, "wrong files count"); + UCX_TEST_ASSERT(cxMapGet(files, cx_hash_key_str("file1")), "file1 missing"); + UCX_TEST_ASSERT(cxMapGet(files, cx_hash_key_str("file2")), "file2 missing"); + UCX_TEST_ASSERT(cxMapGet(files, cx_hash_key_str("file3")), "file3 missing"); + UCX_TEST_ASSERT(cxMapGet(files, cx_hash_key_str("file4")), "file4 missing"); - ucx_map_free(files); + cxMapDestroy(files); UCX_TEST_END;
--- a/src/server/test/vfs.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/test/vfs.h Sun Nov 06 15:53:32 2022 +0100 @@ -32,7 +32,7 @@ #include "../public/nsapi.h" #include "../public/vfs.h" -#include <ucx/test.h> +#include "test.h" #ifdef __cplusplus extern "C" {
--- a/src/server/test/webdav.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/test/webdav.c Sun Nov 06 15:53:32 2022 +0100 @@ -312,7 +312,7 @@ // for adding resources to the response WebdavResponse *response = (WebdavResponse*)ms; - UcxList *requests = NULL; + WebdavPropfindRequestList *requests = NULL; // Initialize all Webdav Backends if(webdav_propfind_init(&backend1, propfind, "/", "/", &requests)) { @@ -703,11 +703,12 @@ rq = testutil_request(sn->pool, "PUT", "/"); testutil_request_body(sn, rq, "Hello World!", 12); - UcxBuffer *b1 = rqbody2buffer(sn, rq); - UCX_TEST_ASSERT(b1->size == 12, "b1: wrong size"); - UCX_TEST_ASSERT(!memcmp(b1->space,"Hello World!",12), "b1: wrong content"); + CxBuffer b1; + rqbody2buffer(sn, rq, &b1); + UCX_TEST_ASSERT(b1.size == 12, "b1: wrong size"); + UCX_TEST_ASSERT(!memcmp(b1.space,"Hello World!",12), "b1: wrong content"); - ucx_buffer_free(b1); + cxBufferDestroy(&b1); testutil_destroy_session(sn); // @@ -721,11 +722,12 @@ rq = testutil_request(sn->pool, "PUT", "/"); testutil_request_body(sn, rq, (char*)body1, len1); - UcxBuffer *b2 = rqbody2buffer(sn, rq); - UCX_TEST_ASSERT(b2->size == len1, "b2: wrong size"); - UCX_TEST_ASSERT(!memcmp(b2->space, body1, len1), "b2: wrong content"); + CxBuffer b2; + rqbody2buffer(sn, rq, &b2); + UCX_TEST_ASSERT(b2.size == len1, "b2: wrong size"); + UCX_TEST_ASSERT(!memcmp(b2.space, body1, len1), "b2: wrong content"); - ucx_buffer_free(b2); + cxBufferDestroy(&b2); testutil_destroy_session(sn); UCX_TEST_END; @@ -997,15 +999,15 @@ UCX_TEST_BEGIN; UCX_TEST_ASSERT(!test_init(&sn, &rq, &propfind, TEST_PROPFIND1), "init failed"); - UcxList *requests = NULL; + WebdavPropfindRequestList *requests = NULL; int err = webdav_propfind_init(&backend1, propfind, "/", "/", &requests); UCX_TEST_ASSERT(!err, "webdav_propfind_init failed"); UCX_TEST_ASSERT(requests, "request list is empty"); - UCX_TEST_ASSERT(ucx_list_size(requests), "request list has wrong size"); + UCX_TEST_ASSERT(cx_linked_list_size(requests, offsetof(WebdavPropfindRequestList, next)), "request list has wrong size"); - WebdavPropfindRequest *p1 = requests->data; - WebdavPropfindRequest *p2 = requests->next->data; + WebdavPropfindRequest *p1 = requests->propfind; + WebdavPropfindRequest *p2 = requests->next->propfind; // backend1 removes the first property from the plist // backend2 should have one property less
--- a/src/server/test/webdav.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/test/webdav.h Sun Nov 06 15:53:32 2022 +0100 @@ -34,7 +34,7 @@ #include "testutils.h" -#include <ucx/test.h> +#include "test.h" #ifdef __cplusplus extern "C" {
--- a/src/server/test/writer.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/test/writer.c Sun Nov 06 15:53:32 2022 +0100 @@ -31,7 +31,7 @@ #include "../util/writer.h" -#include <ucx/buffer.h> +#include <cx/buffer.h> #include "writer.h" #include "testutils.h" @@ -39,7 +39,7 @@ UCX_TEST(test_writer_putc) { Session *sn = testutil_session(); TestIOStream *st = testutil_iostream(2048, TRUE); - UcxBuffer *buf = st->buf; + CxBuffer *buf = st->buf; UCX_TEST_BEGIN; @@ -76,7 +76,7 @@ UCX_TEST(test_writer_flush) { Session *sn = testutil_session(); TestIOStream *st = testutil_iostream(2048, TRUE); - UcxBuffer *buf = st->buf; + CxBuffer *buf = st->buf; UCX_TEST_BEGIN; @@ -106,7 +106,7 @@ UCX_TEST(test_writer_put) { Session *sn = testutil_session(); TestIOStream *st = testutil_iostream(2048, TRUE); - UcxBuffer *buf = st->buf; + CxBuffer *buf = st->buf; UCX_TEST_BEGIN; @@ -135,7 +135,7 @@ UCX_TEST_ASSERT(!memcmp(buf->space, "abcdefgh12345678", 16), "4: wrong UcxBuffer content"); UCX_TEST_ASSERT(buf->pos == 16, "4: wrong UcxBuffer pos"); - writer_puts(out, S("345678abcdefgh12345678end.")); + writer_puts(out, cx_str("345678abcdefgh12345678end.")); UCX_TEST_ASSERT(!memcmp(wbuf, "end.", 4), "5: wrong content"); UCX_TEST_ASSERT(writer.pos == 4, "5: wrong pos"); UCX_TEST_ASSERT(!memcmp(
--- a/src/server/test/writer.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/test/writer.h Sun Nov 06 15:53:32 2022 +0100 @@ -29,7 +29,7 @@ #ifndef TEST_WRITER_H #define TEST_WRITER_H -#include <ucx/test.h> +#include "test.h" #ifdef __cplusplus extern "C" {
--- a/src/server/test/xml.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/test/xml.h Sun Nov 06 15:53:32 2022 +0100 @@ -30,7 +30,7 @@ #define TEST_XML_H #include "../public/nsapi.h" -#include <ucx/test.h> +#include "test.h" #ifdef __cplusplus extern "C" {
--- a/src/server/util/date.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/util/date.c Sun Nov 06 15:53:32 2022 +0100 @@ -56,8 +56,8 @@ "Dec" }; -sstr_t date_format_http(time_t t, pool_handle_t *pool) { - sstr_t d; +cxmutstr date_format_http(time_t t, pool_handle_t *pool) { + cxmutstr d; d.ptr = NULL; d.length = 0; @@ -83,8 +83,8 @@ return d; } -sstr_t date_format_iso8601(time_t t, pool_handle_t *pool) { - sstr_t d; +cxmutstr date_format_iso8601(time_t t, pool_handle_t *pool) { + cxmutstr d; d.ptr = NULL; d.length = 0;
--- a/src/server/util/date.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/util/date.h Sun Nov 06 15:53:32 2022 +0100 @@ -33,15 +33,15 @@ #include <time.h> -#include <ucx/string.h> +#include <cx/string.h> #ifdef __cplusplus extern "C" { #endif -sstr_t date_format_http(time_t t, pool_handle_t *pool); +cxmutstr date_format_http(time_t t, pool_handle_t *pool); -sstr_t date_format_iso8601(time_t t, pool_handle_t *pool); +cxmutstr date_format_iso8601(time_t t, pool_handle_t *pool); #ifdef __cplusplus
--- a/src/server/util/io.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/util/io.c Sun Nov 06 15:53:32 2022 +0100 @@ -66,7 +66,8 @@ #include "pool.h" #include "../daemon/netsite.h" #include "../daemon/event.h" -#include "ucx/utils.h" +#include "cx/utils.h" +#include <cx/printf.h> IOStream native_io_funcs = { (io_write_f)net_sys_write, @@ -106,31 +107,31 @@ /* - * SysStream implementation + * Sycx_stream implementation */ -IOStream* sysstream_new(pool_handle_t *pool, SYS_SOCKET fd) { - SysStream *st = pool_malloc(pool, sizeof(SysStream)); +IOStream* sycx_stream_new(pool_handle_t *pool, SYS_SOCKET fd) { + Sycx_stream *st = pool_malloc(pool, sizeof(Sycx_stream)); st->st = native_io_funcs; st->fd = fd; return (IOStream*)st; } #ifdef XP_UNIX -ssize_t net_sys_write(SysStream *st, void *buf, size_t nbytes) { +ssize_t net_sys_write(Sycx_stream *st, void *buf, size_t nbytes) { return write(st->fd, buf, nbytes); } -ssize_t net_sys_writev(SysStream *st, struct iovec *iovec, int iovcnt) { +ssize_t net_sys_writev(Sycx_stream *st, struct iovec *iovec, int iovcnt) { return writev(st->fd, iovec, iovcnt); } -ssize_t net_sys_read(SysStream *st, void *buf, size_t nbytes) { +ssize_t net_sys_read(Sycx_stream *st, void *buf, size_t nbytes) { return read(st->fd, buf, nbytes); } #ifdef WS_SENDFILE -ssize_t net_sys_sendfile(SysStream *st, sendfiledata *sfd) { +ssize_t net_sys_sendfile(Sycx_stream *st, sendfiledata *sfd) { ssize_t ret = 0; off_t fileoffset = sfd->offset; if(sfd->fd->fd != -1) { @@ -177,11 +178,11 @@ } #endif -void net_sys_close(SysStream *st) { +void net_sys_close(Sycx_stream *st) { system_close(st->fd); } -void net_sys_setmode(SysStream *st, int mode) { +void net_sys_setmode(Sycx_stream *st, int mode) { int flags; if (-1 == (flags = fcntl(st->fd, F_GETFL, 0))) { flags = 0; @@ -199,7 +200,7 @@ } } -int net_sys_poll(SysStream *st, EventHandler *ev, int events, Event *cb) { +int net_sys_poll(Sycx_stream *st, EventHandler *ev, int events, Event *cb) { switch(events) { default: return -1; case IO_POLL_NONE: return ev_remove_poll(ev, st->fd); @@ -211,7 +212,7 @@ #elif defined(XP_WIN32) -ssize_t net_sys_write(SysStream *st, void *buf, size_t nbytes) { +ssize_t net_sys_write(Sycx_stream *st, void *buf, size_t nbytes) { int ret = send(st->fd, buf, nbytes, 0); if(ret == SOCKET_ERROR) { return IO_ERROR; @@ -219,11 +220,11 @@ return ret; } -ssize_t net_sys_writev(SysStream *st, struct iovec *iovec, int iovcnt) { +ssize_t net_sys_writev(Sycx_stream *st, struct iovec *iovec, int iovcnt) { // TODO } -ssize_t net_sys_read(SysStream *st, void *buf, size_t nbytes) { +ssize_t net_sys_read(Sycx_stream *st, void *buf, size_t nbytes) { int ret = recv(st->fd, buf, nbytes, 0); if(ret == SOCKET_ERROR) { return IO_ERROR; @@ -231,11 +232,11 @@ return ret; } -ssize_t net_sys_sendfile(SysStream *st, sendfiledata *sfd) { +ssize_t net_sys_sendfile(Sycx_stream *st, sendfiledata *sfd) { // TODO } -void net_sys_close(SysStream *st) { +void net_sys_close(Sycx_stream *st) { closesocket(st->fd); } @@ -250,6 +251,7 @@ HttpStream *st = pool_malloc(pool, sizeof(HttpStream)); st->st = http_io_funcs; st->fd = fd; + st->written = 0; st->max_read = 0; st->read = 0; st->read_total = 0; @@ -747,7 +749,7 @@ ssize_t net_printf(SYS_NETFD fd, char *format, ...) { va_list arg; va_start(arg, format); - sstr_t buf = ucx_vasprintf(ucx_default_allocator(), format, arg); + cxmutstr buf = cx_vasprintf_a(cxDefaultAllocator, format, arg); ssize_t r = buf.length > 0 ? net_write(fd, buf.ptr, buf.length) : 0; free(buf.ptr); va_end(arg);
--- a/src/server/util/io.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/util/io.h Sun Nov 06 15:53:32 2022 +0100 @@ -56,7 +56,7 @@ #define IO_POLL_OUT 2 typedef struct IOStream IOStream; -typedef struct SysStream SysStream; +typedef struct Sycx_stream Sycx_stream; typedef struct HttpStream HttpStream; typedef ssize_t(*io_write_f)(IOStream *, void *, size_t); @@ -80,7 +80,7 @@ int io_errno; }; -struct SysStream { +struct Sycx_stream { IOStream st; #ifdef XP_UNIX int fd; @@ -154,15 +154,15 @@ /* system stream */ -IOStream* sysstream_new(pool_handle_t *pool, SYS_SOCKET fd); +IOStream* sycx_stream_new(pool_handle_t *pool, SYS_SOCKET fd); -ssize_t net_sys_write(SysStream *st, void *buf, size_t nbytes); -ssize_t net_sys_writev(SysStream *st, struct iovec *iovec, int iovcnt); -ssize_t net_sys_read(SysStream *st, void *buf, size_t nbytes); -ssize_t net_sys_sendfile(SysStream *st, sendfiledata *sfd); -void net_sys_close(SysStream *st); -void net_sys_setmode(SysStream *st, int mode); -int net_sys_poll(SysStream *st, EventHandler *ev, int events, Event *cb); +ssize_t net_sys_write(Sycx_stream *st, void *buf, size_t nbytes); +ssize_t net_sys_writev(Sycx_stream *st, struct iovec *iovec, int iovcnt); +ssize_t net_sys_read(Sycx_stream *st, void *buf, size_t nbytes); +ssize_t net_sys_sendfile(Sycx_stream *st, sendfiledata *sfd); +void net_sys_close(Sycx_stream *st); +void net_sys_setmode(Sycx_stream *st, int mode); +int net_sys_poll(Sycx_stream *st, EventHandler *ev, int events, Event *cb); /* http stream */ IOStream* httpstream_new(pool_handle_t *pool, IOStream *fd);
--- a/src/server/util/object.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/util/object.c Sun Nov 06 15:53:32 2022 +0100 @@ -26,7 +26,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include <ucx/string.h> +#include <cx/string.h> #include "../public/nsapi.h"
--- a/src/server/util/pool.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/util/pool.c Sun Nov 06 15:53:32 2022 +0100 @@ -78,6 +78,14 @@ /* Pool global statistics */ static pool_global_stats_t pool_global_stats; +/* ucx allocator pool class */ +static cx_allocator_class pool_allocator_class = { + (void *(*)(void *,size_t )) pool_malloc, + (void *(*)(void *,void *, size_t )) pool_realloc, + (void *(*)(void *,size_t ,size_t )) pool_calloc, + (void (*)(void *, void *))pool_free +}; + static int pool_internal_init() { @@ -134,6 +142,10 @@ return REQ_PROCEED; } +CxAllocator* pool_allocator(pool_handle_t *pool) { + return &((pool_t *)pool)->allocator; +} + static block_t * _create_block(pool_t *pool, int size) { @@ -250,7 +262,10 @@ //if (pool_global_stats.lock == NULL) { // TODO: remove // pool_internal_init(); //} - + + newpool->allocator.cl = &pool_allocator_class; + newpool->allocator.data = newpool; + newpool->used_blocks = NULL; newpool->free_blocks = NULL; newpool->free_size = 0; @@ -718,8 +733,8 @@ #endif /* PER_POOL_STATISTICS */ // new -sstr_t sstrdup_pool(pool_handle_t *pool, sstr_t s) { - sstr_t newstring; +cxmutstr cx_strdup_pool(pool_handle_t *pool, cxmutstr s) { + cxmutstr newstring; newstring.ptr = (char*)pool_malloc(pool, s.length + 1); if (newstring.ptr != NULL) { newstring.length = s.length;
--- a/src/server/util/pool.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/util/pool.h Sun Nov 06 15:53:32 2022 +0100 @@ -53,7 +53,7 @@ */ // new -#include <ucx/string.h> +#include <cx/string.h> #ifndef NETSITE_H //include "netsite.h" @@ -83,7 +83,9 @@ NSAPI_PUBLIC int INTpool_init(pblock *pb, Session *sn, Request *rq); -sstr_t sstrdup_pool(pool_handle_t *pool, sstr_t s); +cxmutstr cx_strdup_pool(pool_handle_t *pool, cxmutstr s); + +CxAllocator* pool_allocator(pool_handle_t *pool); #ifdef DEBUG_CACHES NSAPI_PUBLIC int INTpool_service_debug(pblock *pb, Session *sn, Request *rq);
--- a/src/server/util/pool_pvt.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/util/pool_pvt.h Sun Nov 06 15:53:32 2022 +0100 @@ -37,6 +37,7 @@ #ifndef BASE_POOL_H #include "pool.h" #include <inttypes.h> +#include <cx/allocator.h> #endif /* BASE_POOL_H */ /* @@ -147,6 +148,7 @@ */ typedef struct pool_t pool_t; struct pool_t { + CxAllocator allocator; /* ucx allocator interface */ block_t *curr_block; /* current block being used */ block_t *used_blocks; /* blocks that are all used up */ block_t *free_blocks; /* blocks that are free */
--- a/src/server/util/strbuf.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/util/strbuf.c Sun Nov 06 15:53:32 2022 +0100 @@ -30,7 +30,7 @@ #include <stdlib.h> #include <string.h> -#include <ucx/string.h> +#include <cx/string.h> #include "strbuf.h" @@ -47,14 +47,14 @@ } void sbuf_puts(sbuf_t *buf, char *str) { - sbuf_append(buf, sstr(str)); + sbuf_append(buf, cx_str(str)); } void sbuf_put(sbuf_t *buf, char chr) { - sbuf_append(buf, sstrn(&chr, 1)); + sbuf_append(buf, (cxstring){&chr, 1}); } -void sbuf_append(sbuf_t *buf, sstr_t str) { +void sbuf_append(sbuf_t *buf, cxstring str) { if (buf->length + str.length >= buf->size) { buf->size *= 2; buf->ptr = realloc(buf->ptr, buf->size);
--- a/src/server/util/strbuf.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/util/strbuf.h Sun Nov 06 15:53:32 2022 +0100 @@ -29,7 +29,7 @@ #ifndef STRBUF_H #define STRBUF_H -#include <ucx/string.h> +#include <cx/string.h> #ifdef __cplusplus extern "C" { @@ -48,7 +48,7 @@ void sbuf_put(sbuf_t *buf, char chr); -void sbuf_append(sbuf_t *buf, sstr_t str); +void sbuf_append(sbuf_t *buf, cxstring str); void sbuf_free(sbuf_t *buf);
--- a/src/server/util/util.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/util/util.c Sun Nov 06 15:53:32 2022 +0100 @@ -53,9 +53,10 @@ #include "../daemon/netsite.h" #include "../public/nsapi.h" -#include <ucx/string.h> -#include <ucx/mempool.h> -#include <ucx/utils.h> +#include <cx/string.h> +#include <cx/mempool.h> +#include <cx/utils.h> +#include <cx/printf.h> #include "pblock.h" #include "util.h" @@ -292,10 +293,10 @@ NSAPI_PUBLIC int util_vasprintf(pool_handle_t *pool, char **s, const char *fmt, va_list args) { - UcxAllocator a = util_pool_allocator(pool); + CxAllocator *a = pool_allocator(pool); va_list ap; va_copy(ap, args); - sstr_t str = ucx_vasprintf(&a, fmt, ap); + cxmutstr str = cx_vasprintf_a(a, fmt, ap); *s = str.ptr; return str.length; } @@ -413,7 +414,7 @@ return def; } -int util_getboolean_s(scstr_t s, int def) { +int util_getboolean_s(cxstring s, int def) { if(s.length == 0) { return def; } @@ -439,11 +440,11 @@ } NSAPI_PUBLIC const char* util_resource_name(const char *url) { - scstr_t urlstr = scstr(url); + cxstring urlstr = cx_str(url); if(urlstr.ptr[urlstr.length-1] == '/') { urlstr.length--; } - scstr_t resname = scstrrchr(urlstr, '/'); + cxstring resname = cx_strrchr(urlstr, '/'); if(resname.length > 1) { return resname.ptr+1; } else { @@ -582,22 +583,22 @@ NSAPI_PUBLIC -sstr_t util_path_append(pool_handle_t *pool, char *path, char *ch) { - sstr_t parent = sstr(path); - sstr_t child = sstr(ch); - sstr_t newstr; +cxmutstr util_path_append(pool_handle_t *pool, char *path, char *ch) { + cxmutstr parent = cx_mutstr(path); + cxmutstr child = cx_mutstr(ch); + cxmutstr newstr; - UcxAllocator a = util_pool_allocator(pool); + CxAllocator *a = pool_allocator(pool); if(parent.ptr[parent.length-1] == '/') { - newstr = sstrcat_a(&a, 2, parent, child); + newstr = cx_strcat_a(a, 2, parent, child); } else { - newstr = sstrcat_a(&a, 3, parent, S("/"), child); + newstr = cx_strcat_a(a, 3, parent, cx_str("/"), child); } return newstr; } -sstr_t util_path_remove_last(sstr_t path) { +cxmutstr util_path_remove_last(cxmutstr path) { int i; for(i=path.length-1;i>=0;i--) { char c = path.ptr[i]; @@ -614,7 +615,7 @@ return path; } -void util_add_ppath(sstr_t root, sstr_t path, pblock *vars) { +void util_add_ppath(cxmutstr root, cxmutstr path, pblock *vars) { // concat path size_t length = root.length + path.length; char *translated_path = alloca(length); @@ -632,17 +633,6 @@ } -UcxAllocator util_pool_allocator(pool_handle_t *pool) { - UcxAllocator a; - a.malloc = (ucx_allocator_malloc)pool_malloc; - a.calloc = (ucx_allocator_calloc)pool_calloc; - a.realloc = (ucx_allocator_realloc)pool_realloc; - a.free = (ucx_allocator_free)pool_free; - a.pool = pool; - return a; -} - - // new - code from params.cpp NSAPI_PUBLIC pblock* util_parse_param(pool_handle_t *pool, char *query) { pblock *pb = pblock_create_pool(pool, 32); @@ -701,21 +691,6 @@ return pb; } -// TODO: remove -sstr_t sstrdup_mp(UcxMempool *pool, sstr_t s) { - sstr_t newstring; - newstring.ptr = (char*)ucx_mempool_malloc(pool, s.length + 1); - if (newstring.ptr != NULL) { - newstring.length = s.length; - newstring.ptr[newstring.length] = 0; - - memcpy(newstring.ptr, s.ptr, s.length); - } - - return newstring; -} - - /* ---------------------------- util_mstr2num ----------------------------- */ @@ -1020,9 +995,9 @@ return gmtime_r(clock, res); } -int util_isdate(char *str) { - sstr_t datestr = sstr(str); - sstr_t example = S("Sun, 06 Nov 1994 08:49:37 GMT"); +int util_isdate(const char *str) { + cxstring datestr = cx_str(str); + cxstring example = cx_str("Sun, 06 Nov 1994 08:49:37 GMT"); if(datestr.length != example.length) { return 0; @@ -1049,7 +1024,7 @@ } } - if(!sstrsuffix(datestr, S("GMT"))) { + if(!cx_strsuffix(datestr, cx_str("GMT"))) { return 0; }
--- a/src/server/util/util.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/util/util.h Sun Nov 06 15:53:32 2022 +0100 @@ -35,7 +35,7 @@ #define BASE_UTIL_H #include "../daemon/netsite.h" -#include <ucx/string.h> +#include <cx/string.h> #include "pool.h" #ifdef XP_UNIX @@ -202,7 +202,7 @@ NSAPI_PUBLIC PRBool INTutil_format_http_version(const char *v, int *protv_num, char *buffer, int size); NSAPI_PUBLIC int INTutil_getboolean(const char *v, int def); -int util_getboolean_s(scstr_t s, int def); +int util_getboolean_s(cxstring s, int def); // new NSAPI_PUBLIC int util_strtoint(const char *str, int64_t *value); @@ -241,16 +241,14 @@ /* path utils */ NSAPI_PUBLIC -sstr_t util_path_append(pool_handle_t *pool, char *path, char *child); +cxmutstr util_path_append(pool_handle_t *pool, char *path, char *child); NSAPI_PUBLIC -sstr_t util_path_remove_last(sstr_t path); +cxmutstr util_path_remove_last(cxmutstr path); NSAPI_PUBLIC -void util_add_ppath(sstr_t root, sstr_t path, pblock *vars); +void util_add_ppath(cxmutstr root, cxmutstr path, pblock *vars); -/* ucx utils */ -UcxAllocator util_pool_allocator(pool_handle_t *pool); -int util_isdate(char *str); +int util_isdate(const char *str); /* --- End common function prototypes --- */
--- a/src/server/util/writer.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/util/writer.c Sun Nov 06 15:53:32 2022 +0100 @@ -104,7 +104,7 @@ } } -int writer_puts(Writer *w, sstr_t s) { +int writer_puts(Writer *w, cxstring s) { return writer_put(w, s.ptr, s.length); }
--- a/src/server/util/writer.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/util/writer.h Sun Nov 06 15:53:32 2022 +0100 @@ -30,7 +30,7 @@ #define WRITER_H #include "../public/nsapi.h" -#include <ucx/string.h> +#include <cx/string.h> #ifdef __cplusplus extern "C" { @@ -47,7 +47,8 @@ int error; } Writer; - +#define writer_put_lit(w, str) writer_put(w, str, sizeof(str)-1) +#define writer_put_str(w, str) writer_put(w, str, strlen(str)) void writer_init(Writer *w, SYS_NETFD fd, char *buf, size_t len); @@ -57,7 +58,7 @@ int writer_put(Writer *w, const char *s, size_t len); -int writer_puts(Writer *w, sstr_t s); +int writer_puts(Writer *w, cxstring s); int writer_putc(Writer *w, char c);
--- a/src/server/webdav/multistatus.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/webdav/multistatus.c Sun Nov 06 15:53:32 2022 +0100 @@ -33,7 +33,8 @@ #include "../daemon/protocol.h" #include "../util/platform.h" -#include <ucx/string.h> +#include <cx/string.h> +#include <cx/hash_map.h> #include "multistatus.h" @@ -51,48 +52,48 @@ ms->response.addresource = multistatus_addresource; ms->sn = sn; ms->rq = rq; - ms->namespaces = ucx_map_new_a(session_get_allocator(ms->sn), 8); + ms->namespaces = cxHashMapCreate(pool_allocator(sn->pool), 8); ms->proppatch = FALSE; if(!ms->namespaces) { return NULL; } - if(ucx_map_cstr_put(ms->namespaces, "D", webdav_dav_namespace())) { + if(cxMapPut(ms->namespaces, cx_hash_key_str("D"), webdav_dav_namespace())) { return NULL; } return ms; } static int send_xml_root(Multistatus *ms, Writer *out) { - writer_puts(out, S("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" - "<D:multistatus")); + writer_put_lit(out, "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" + "<D:multistatus"); // write the namespaces definitions // key is the namespace prefix // the map always contains the "DAV:" namespace with the prefix "D" - UcxMapIterator i = ucx_map_iterator(ms->namespaces); - WSNamespace *ns; - UCX_MAP_FOREACH(key, ns, i) { - writer_puts(out, S(" xmlns:")); - writer_put(out, key.data, key.len); - writer_puts(out, S("=\"")); - writer_puts(out, sstr((char*)ns->href)); - writer_puts(out, S("\"")); + CxIterator i = cxMapIterator(ms->namespaces); + cx_foreach(CxMapEntry*, entry, i) { + WSNamespace *ns = entry->value; + writer_put_lit(out, " xmlns:"); + writer_put (out, entry->key->data.str, entry->key->len); + writer_put_lit(out, "=\""); + writer_put_str(out, (char*)ns->href); + writer_put_lit(out, "\""); } - writer_puts(out, S(">\n")); + writer_put_lit(out, ">\n"); return out->error; } static void send_nsdef(WSNamespace *ns, Writer *out) { - writer_puts(out, S(" xmlns:")); - writer_puts(out, sstr((char*)ns->prefix)); - writer_puts(out, S("=\"")); - writer_puts(out, sstr((char*)ns->href)); - writer_putc(out, '\"'); + writer_put_lit(out, " xmlns:"); + writer_put_str(out, (char*)ns->prefix); + writer_put_lit(out, "=\""); + writer_put_str(out, (char*)ns->href); + writer_putc (out, '\"'); } -static void send_string_escaped(Writer *out, sstr_t str) { +static void send_string_escaped(Writer *out, cxmutstr str) { char *begin = str.ptr; char *end = begin; char *escape = NULL; @@ -150,10 +151,10 @@ Writer *out) { // write: "<prefix:name" - writer_putc(out, '<'); - writer_puts(out, sstr((char*)property->namespace->prefix)); - writer_putc(out, ':'); - writer_puts(out, sstr((char*)property->name)); + writer_putc (out, '<'); + writer_put_str(out, (char*)property->namespace->prefix); + writer_putc (out, ':'); + writer_put_str(out, (char*)property->name); // send additional namespace definitions required for the value WebdavNSList *def = nsdef; @@ -164,9 +165,9 @@ // send xml lang attribute if(property->lang) { - writer_puts(out, S(" xml:lang=\"")); - writer_puts(out, sstr((char*)property->lang)); - writer_putc(out, '\"'); + writer_put_lit(out, " xml:lang=\""); + writer_put_str(out, (char*)property->lang); + writer_putc (out, '\"'); } // end property tag and write content @@ -202,55 +203,54 @@ } // end tag - writer_puts(out, S("</")); - writer_puts(out, sstr((char*)property->namespace->prefix)); - writer_putc(out, ':'); - writer_puts(out, sstr((char*)property->name)); - writer_putc(out, '>'); + writer_put_lit(out, "</"); + writer_put_str(out, (char*)property->namespace->prefix); + writer_putc (out, ':'); + writer_put_str(out, (char*)property->name); + writer_putc (out, '>'); } else { - writer_puts(out, S("/>")); + writer_put_lit(out, "/>"); } return out->error; } static int send_response_tag(Multistatus *ms, MSResponse *rp, Writer *out) { - writer_puts(out, S(" <D:response>\n" - " <D:href>")); - //writer_puts(out, sstr(rp->resource.href)); - send_string_escaped(out, sstr(rp->resource.href)); - writer_puts(out, S("</D:href>\n")); + writer_put_lit(out, " <D:response>\n" + " <D:href>"); + send_string_escaped(out, cx_mutstr(rp->resource.href)); + writer_put_lit(out, "</D:href>\n"); WSBool writeContent = ms->proppatch ? FALSE : TRUE; if(rp->plist_begin) { - writer_puts(out, S(" <D:propstat>\n" - " <D:prop>\n")); + writer_put_lit(out, " <D:propstat>\n" + " <D:prop>\n"); // send properties PropertyOkList *p = rp->plist_begin; while(p) { - writer_puts(out, S(" ")); + writer_put_lit(out, " "); if(send_property(ms, p->property, p->nsdef, writeContent, out)) { return out->error; } - writer_puts(out, S("\n")); + writer_put_lit(out, "\n"); p = p->next; } - writer_puts(out, S(" </D:prop>\n" - " <D:status>HTTP/1.1 200 OK</D:status>\n" - " </D:propstat>\n")); + writer_put_lit(out, " </D:prop>\n" + " <D:status>HTTP/1.1 200 OK</D:status>\n" + " </D:propstat>\n"); } // send error properties PropertyErrorList *error = rp->errors; while(error) { - writer_puts(out, S(" <D:propstat>\n" - " <D:prop>\n")); + writer_put_lit(out, " <D:propstat>\n" + " <D:prop>\n"); WebdavPList *errprop = error->begin; while(errprop) { - writer_puts(out, S(" ")); + writer_put_lit(out, " "); if(send_property(ms, errprop->property, NULL, FALSE, out)) { return out->error; } @@ -267,24 +267,24 @@ statuscode[3] = ' '; sclen = 4; } - writer_puts(out, S(" </D:prop>\n" - " <D:status>HTTP/1.1 ")); + writer_put_lit(out, " </D:prop>\n" + " <D:status>HTTP/1.1 "); writer_put(out, statuscode, sclen); const char *status_msg = protocol_status_message(error->status); if(status_msg) { writer_put(out, status_msg, strlen(status_msg)); } else { - writer_puts(out, S("Server Error")); + writer_put_lit(out, "Server Error"); } - writer_puts(out, S("</D:status>\n" - " </D:propstat>\n")); + writer_put_lit(out, "</D:status>\n" + " </D:propstat>\n"); error = error->next; } // end response tag - writer_puts(out, S(" </D:response>\n")); + writer_put_lit(out, " </D:response>\n"); return out->error; } @@ -322,7 +322,7 @@ } // end multistatus - writer_puts(out, S("</D:multistatus>\n")); + writer_put_lit(out, "</D:multistatus>\n"); //printf("\n\n"); //fflush(stdout); @@ -355,7 +355,7 @@ res->resource.addproperty = msresponse_addproperty; res->resource.close = msresponse_close; - res->properties = ucx_map_new_a(session_get_allocator(ms->sn), 32); + res->properties = cxHashMapCreate(pool_allocator(ms->sn->pool), 32); if(!res->properties) { return NULL; } @@ -414,7 +414,6 @@ int statuscode) { pool_handle_t *pool = response->multistatus->sn->pool; - UcxAllocator *a = session_get_allocator(response->multistatus->sn); response->resource.err++; @@ -459,6 +458,15 @@ return 0; } +static CxHashKey ms_property_key( + CxAllocator *a, + const xmlChar *href, + const char *property_name) +{ + cxmutstr key_data = cx_strcat_a(a, 3, cx_str((const char*)href), (cxstring){ "\0", 1 }, cx_str(property_name)); + return cx_hash_key_bytes((unsigned char*)key_data.ptr, key_data.length); +} + int msresponse_addproperty( WebdavResource *res, WebdavProperty *property, @@ -486,21 +494,16 @@ } // check if the property was already added to the resource - UcxAllocator *a = session_get_allocator(sn); - sstr_t key = sstrcat_a( - a, - 3, - sstr((char*)property->namespace->href), - S("\0"), - sstr((char*)property->name)); - if(ucx_map_sstr_get(response->properties, key)) { - a->free(a->pool, key.ptr); + CxAllocator *a = pool_allocator(sn->pool); + CxHashKey key = ms_property_key(a, property->namespace->href, property->name); + if(cxMapGet(response->properties, key)) { + cxFree(a, key.data.bytes); return 0; } - if(ucx_map_sstr_put(response->properties, key, property)) { + if(cxMapPut(response->properties, key, property)) { return 1; // OOM } - a->free(a->pool, key.ptr); + cxFree(a, key.data.bytes); // list of namespace definitions for this property WebdavNSList *nsdef_begin = NULL; @@ -509,14 +512,14 @@ // add namespace of this property to the namespace map // the namespace map will be used for global namespace definitions if(property->namespace->prefix) { - WSNamespace *ns = ucx_map_cstr_get( + WSNamespace *ns = cxMapGet( response->multistatus->namespaces, - (const char*)property->namespace->prefix); + cx_hash_key_str((const char*)property->namespace->prefix)); if(!ns) { // prefix is not in use -> we can add the namespace to the ns map - int err = ucx_map_cstr_put( + int err = cxMapPut( response->multistatus->namespaces, - (const char*)property->namespace->prefix, + cx_hash_key_str((const char*)property->namespace->prefix), property->namespace); if(err) { return 1; // OOM @@ -622,16 +625,11 @@ } // add missing properties with status code 404 - UcxAllocator *a = session_get_allocator(ms->sn); + CxAllocator *a = pool_allocator(ms->sn->pool); WebdavPList *pl = ms->response.op->reqprops; while(pl) { - sstr_t key = sstrcat_a( - a, - 3, - sstr((char*)pl->property->namespace->href), - S("\0"), - sstr((char*)pl->property->name)); - if(!ucx_map_sstr_get(response->properties, key)) { + CxHashKey key = ms_property_key(a, pl->property->namespace->href, pl->property->name); + if(!cxMapGet(response->properties, key)) { // property was not added to this response if(ms->proppatch) { if(msresponse_addproperty(res, pl->property, 424)) { @@ -668,7 +666,7 @@ } // we don't need the properties anymore - ucx_map_free(response->properties); + cxMapDestroy(response->properties); response->resource.isclosed = TRUE; return ret;
--- a/src/server/webdav/multistatus.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/webdav/multistatus.h Sun Nov 06 15:53:32 2022 +0100 @@ -30,8 +30,8 @@ #include "../public/webdav.h" -#include <ucx/map.h> -#include <ucx/buffer.h> +#include <cx/map.h> +#include <cx/buffer.h> #include <libxml/tree.h> #include "../util/writer.h" @@ -61,7 +61,7 @@ * key: (char*) namespace prefix * value: WSNamespace* */ - UcxMap *namespaces; + CxMap *namespaces; /* * Is this a proppatch request? @@ -84,7 +84,7 @@ * key: <href> null-byte <name> * value: WebdavProperty* */ - UcxMap *properties; + CxMap *properties; /* * All properties with status != 200
--- a/src/server/webdav/operation.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/webdav/operation.c Sun Nov 06 15:53:32 2022 +0100 @@ -30,7 +30,7 @@ #include <stdlib.h> #include <errno.h> -#include <ucx/list.h> +#include <cx/list.h> #include "../daemon/session.h" #include "../util/pblock.h" @@ -60,7 +60,7 @@ Request *rq, WebdavBackend *dav, WebdavPList *reqprops, - UcxList *requests, + WebdavPropfindRequestList *requests, WebdavResponse *response) { WebdavOperation *op = pool_malloc(sn->pool, sizeof(WebdavOperation)); @@ -94,7 +94,7 @@ op->parent = parent; // get first propfind object - WebdavPropfindRequest *propfind = op->requests->data; + WebdavPropfindRequest *propfind = op->requests->propfind; // execute propfind_do of the first backend for the first resource int ret = REQ_PROCEED; @@ -117,6 +117,7 @@ char *path; size_t hreflen; size_t pathlen; + struct PathSearchElm *next; } PathSearchElm; /* @@ -187,7 +188,7 @@ const char *href, const char *path) { - WebdavPropfindRequest *request = op->requests->data; + WebdavPropfindRequest *request = op->requests->propfind; return webdav_op_iterate_children( vfs, request->depth, href, path, propfind_child_cb, op); } @@ -199,13 +200,13 @@ // start with second backend and request, because // the first one was already called by webdav_op_propfind_begin WebdavBackend *dav = op->dav->next; - UcxList *request = op->requests->next; + WebdavPropfindRequestList *request = op->requests->next; // call propfind_do of all remaining backends int ret = REQ_PROCEED; while(dav && request) { if(dav->propfind_do( - request->data, + request->propfind, op->response, op->parent, resource, @@ -225,11 +226,11 @@ */ int webdav_op_propfind_finish(WebdavOperation *op) { WebdavBackend *dav = op->dav; - UcxList *requests = op->requests; + WebdavPropfindRequestList *requests = op->requests; int ret = REQ_PROCEED; while(dav && requests) { - if(dav->propfind_finish(requests->data)) { + if(dav->propfind_finish(requests->propfind)) { ret = REQ_ABORTED; } @@ -274,7 +275,7 @@ const char *path) { WebdavProppatchRequest *orig_request = op->proppatch; - UcxAllocator *a = session_get_allocator(op->sn); + CxAllocator *a = pool_allocator(op->sn->pool); // create WebdavResource object for the requested resource WebdavResource *resource = op->response->addresource(op->response, href); @@ -499,7 +500,6 @@ vfs_op_child_func func, void *userdata) { - UcxAllocator *a = session_get_allocator(vfs->sn); pool_handle_t *pool = vfs->sn->pool; PathSearchElm *start_elm = pool_malloc(pool, sizeof(PathSearchElm)); @@ -507,12 +507,10 @@ start_elm->path = pool_strdup(pool, path ? path : ""); start_elm->hreflen = href ? strlen(href) : 0; start_elm->pathlen = path ? strlen(path) : 0; + start_elm->next = NULL; - UcxList *stack = ucx_list_prepend_a(a, NULL, start_elm); - UcxList *stack_end = stack; - if(!stack) { - return 1; - } + PathSearchElm *stack = start_elm; + PathSearchElm *stack_end = start_elm; // reusable buffer for full child path and href char *newpath = pool_malloc(pool, 256); @@ -523,7 +521,7 @@ int err = 0; while(stack && !err) { - PathSearchElm *cur_elm = stack->data; + PathSearchElm *cur_elm = stack; // when newpath is initialized with the parent path // set path_buf_init to TRUE @@ -596,38 +594,41 @@ PathSearchElm *new_elm = pool_malloc(pool, sizeof(PathSearchElm)); + if(!new_elm) { + err = 1; + break; + } new_elm->href = hrefcp; new_elm->path = pathcp; new_elm->hreflen = childhreflen; new_elm->pathlen = childpathlen; + new_elm->next = NULL; // add the new_elm to the stack - // stack_end is always not NULL here, because we remove + // stack_end is always not NULL here, because the loop is + // running as long as we have a stack and we remove // the first stack element at the end of the loop - UcxList *newlistelm = ucx_list_append_a(a, stack_end, new_elm); - if(!newlistelm) { - err = 1; - break; - } - stack_end = newlistelm; + stack_end->next = new_elm; + stack_end = new_elm; } } vfs_closedir(dir); + stack = stack->next; + pool_free(pool, cur_elm->path); pool_free(pool, cur_elm->href); pool_free(pool, cur_elm); - - stack = ucx_list_remove_a(a, stack, stack); } // in case of an error, we have to free all remaining stack elements - UCX_FOREACH(elm, stack) { - char *data = elm->data; - if(data != path) { - pool_free(pool, data); - } + for(PathSearchElm *elm=stack;elm;) { + PathSearchElm *next_elm = elm->next; + pool_free(pool, elm->path); + pool_free(pool, elm->href); + pool_free(pool, elm); + elm = next_elm; } return err;
--- a/src/server/webdav/operation.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/webdav/operation.h Sun Nov 06 15:53:32 2022 +0100 @@ -30,8 +30,8 @@ #define OPERATION_H #include "../public/webdav.h" -#include <ucx/list.h> -#include <ucx/map.h> +#include <cx/list.h> +#include <cx/map.h> #ifdef __cplusplus extern "C" { @@ -45,20 +45,20 @@ typedef struct CopyResource CopyResource; struct WebdavOperation { - WebdavBackend *dav; - Request *rq; - Session *sn; + WebdavBackend *dav; + Request *rq; + Session *sn; - WebdavProppatchRequest *proppatch; /* proppatch request or NULL */ - WebdavPList *reqprops; /* requested properties */ - UcxList *requests; /* backend specific request objects */ + WebdavProppatchRequest *proppatch; /* proppatch request or NULL */ + WebdavPList *reqprops; /* requested properties */ + WebdavPropfindRequestList *requests; /* backend specific request objects */ - WebdavResponse *response; + WebdavResponse *response; - response_close_func response_close; + response_close_func response_close; - VFS_DIR parent; /* current directory */ - struct stat *stat; /* current stat object */ + VFS_DIR parent; /* current directory */ + struct stat *stat; /* current stat object */ }; struct WebdavVFSOperation { @@ -87,7 +87,7 @@ struct CopyResource { WebdavResource resource; - UcxMap *properties; + CxMap *properties; }; enum WebdavVFSOpType { @@ -118,7 +118,7 @@ Request *rq, WebdavBackend *dav, WebdavPList *reqprops, - UcxList *requests, + WebdavPropfindRequestList *requests, WebdavResponse *response); int webdav_op_propfind_begin(
--- a/src/server/webdav/requestparser.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/webdav/requestparser.c Sun Nov 06 15:53:32 2022 +0100 @@ -30,13 +30,16 @@ #include <stdlib.h> #include <string.h> -#include <ucx/string.h> -#include <ucx/utils.h> -#include <ucx/map.h> +#include <cx/string.h> +#include <cx/utils.h> +#include <cx/map.h> +#include <cx/hash_map.h> #include "requestparser.h" #include "webdav.h" +#include "../util/pool.h" + #define xstreq(a, b) !strcmp((const char*)a, (const char*)b) void proplist_free(pool_handle_t *pool, WebdavPList *list) { @@ -63,7 +66,7 @@ static int parse_prop( Session *sn, xmlNode *node, - UcxMap *propmap, + CxMap *propmap, WebdavPList **plist_begin, WebdavPList **plist_end, size_t *propcount, @@ -80,14 +83,14 @@ const char* name = (const char*)pnode->name; // check for prop duplicates - UcxKey k = webdav_property_key((const char*)ns, (const char*)name); - if(!k.data) { + CxHashKey k = webdav_property_key((const char*)ns, (const char*)name); + if(!k.data.bytes) { *error = proppatch ? PROPPATCH_PARSER_OOM : PROPFIND_PARSER_OOM; return 1; } - void *c = ucx_map_get(propmap, k); + void *c = cxMapGet(propmap, k); if(!c) { - if(ucx_map_put(propmap, k, (void*)1)) { + if(cxMapPut(propmap, k, (void*)1)) { *error = proppatch ? PROPPATCH_PARSER_OOM : PROPFIND_PARSER_OOM; } @@ -111,7 +114,7 @@ *error = PROPPATCH_PARSER_DUPLICATE; } - free((void*)k.data); + free((void*)k.data.str); if(*error) { return 1; } @@ -156,7 +159,8 @@ return NULL; } - UcxMap *propmap = ucx_map_new(32); + CxAllocator *a = pool_allocator(sn->pool); + CxMap *propmap = cxHashMapCreate(a, 32); // value: intptr_t if(!propmap) { *error = PROPFIND_PARSER_OOM; xmlFreeDoc(doc); @@ -190,7 +194,7 @@ node = node->next; } - ucx_map_free(propmap); // no allocated content must be freed + cxMapDestroy(propmap); // no allocated content must be freed if(ret) { // parse_prop failed @@ -295,7 +299,10 @@ // ret vars *error = 0; - UcxMap *propmap = ucx_map_new(32); // map for duplicate checking + CxAllocator *a = pool_allocator(sn->pool); + // map for duplicate checking + // value type: intptr_t + CxMap *propmap = cxHashMapCreate(a, 32); if(!propmap) { *error = PROPPATCH_PARSER_OOM; xmlFreeDoc(doc); @@ -350,7 +357,7 @@ node = node->next; } - ucx_map_free(propmap); // allocated content must not be freed + cxMapDestroy(propmap); // allocated content must not be freed if(set_count + remove_count == 0) { *error = PROPPATCH_PARSER_NO_PROPERTIES;
--- a/src/server/webdav/requestparser.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/webdav/requestparser.h Sun Nov 06 15:53:32 2022 +0100 @@ -32,7 +32,7 @@ #include "../public/webdav.h" #include <libxml/tree.h> -#include <ucx/buffer.h> +#include <cx/buffer.h> #ifdef __cplusplus extern "C" {
--- a/src/server/webdav/webdav.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/webdav/webdav.c Sun Nov 06 15:53:32 2022 +0100 @@ -30,8 +30,11 @@ #include <stdlib.h> #include <string.h> -#include <ucx/buffer.h> -#include <ucx/list.h> +#include <cx/buffer.h> +#include <cx/list.h> +#include <cx/linked_list.h> +#include <cx/hash_map.h> +#include <cx/printf.h> #include "webdav.h" @@ -53,14 +56,14 @@ * key: http method name (string) * value: SAF fptr */ -static UcxMap *method_handler_map; +static CxMap *method_handler_map; /* * webdav backend types * key: backend name (string) * value: WebdavBackend* */ -static UcxMap *webdav_type_map; +static CxMap *webdav_type_map; static WebdavBackend default_backend; @@ -89,11 +92,11 @@ WebdavType *webdavType = malloc(sizeof(WebdavType)); webdavType->init = webdavInit; webdavType->create = webdavCreate; - return ucx_map_cstr_put(webdav_type_map, name, webdavType); + return cxMapPut(webdav_type_map, cx_hash_key_str(name), webdavType); } -WebdavType* webdav_get_type(scstr_t dav_class) { - return ucx_map_sstr_get(webdav_type_map, dav_class); +WebdavType* webdav_get_type(cxstring dav_class) { + return cxMapGet(webdav_type_map, cx_hash_key_bytes((unsigned const char *)dav_class.ptr, dav_class.length)); } void* webdav_init_backend(ServerConfiguration *cfg, pool_handle_t *pool, WebdavType *dav_class, WSConfigNode *config, int *error) { @@ -110,7 +113,7 @@ } WebdavBackend* webdav_create(Session *sn, Request *rq, const char *dav_class, pblock *pb, void *initData) { - WebdavType *webdavType = ucx_map_cstr_get(webdav_type_map, dav_class); + WebdavType *webdavType = cxMapGet(webdav_type_map, cx_hash_key_str(dav_class)); if(!webdavType) { log_ereport(LOG_MISCONFIG, "webdav_create: unkown dav type %s", dav_class); return NULL; @@ -127,43 +130,43 @@ } webdav_is_initialized = TRUE; - webdav_type_map = ucx_map_new(8); + webdav_type_map = cxHashMapCreate(cxDefaultAllocator, 8); if(!webdav_type_map) { return REQ_ABORTED; } - method_handler_map = ucx_map_new(64); + method_handler_map = cxHashMapCreate(cxDefaultAllocator, 64); if(!method_handler_map) { return REQ_ABORTED; } init_default_backend(); - ucx_map_cstr_put(webdav_type_map, "default", &default_backend); + cxMapPut(webdav_type_map, cx_hash_key_str("default"), &default_backend); - ucx_map_cstr_put(method_handler_map, "OPTIONS", webdav_options); - ucx_map_cstr_put(method_handler_map, "PROPFIND", webdav_propfind); - ucx_map_cstr_put(method_handler_map, "PROPPATCH", webdav_proppatch); - ucx_map_cstr_put(method_handler_map, "MKCOL", webdav_mkcol); - ucx_map_cstr_put(method_handler_map, "POST", webdav_post); - ucx_map_cstr_put(method_handler_map, "DELETE", webdav_delete); - ucx_map_cstr_put(method_handler_map, "PUT", webdav_put); - ucx_map_cstr_put(method_handler_map, "COPY", webdav_copy); - ucx_map_cstr_put(method_handler_map, "MOVE", webdav_move); - ucx_map_cstr_put(method_handler_map, "LOCK", webdav_lock); - ucx_map_cstr_put(method_handler_map, "UNLOCK", webdav_unlock); - ucx_map_cstr_put(method_handler_map, "REPORT", webdav_report); - ucx_map_cstr_put(method_handler_map, "ACL", webdav_acl); + cxMapPut(method_handler_map, cx_hash_key_str("OPTIONS"), webdav_options); + cxMapPut(method_handler_map, cx_hash_key_str("PROPFIND"), webdav_propfind); + cxMapPut(method_handler_map, cx_hash_key_str("PROPPATCH"), webdav_proppatch); + cxMapPut(method_handler_map, cx_hash_key_str("MKCOL"), webdav_mkcol); + cxMapPut(method_handler_map, cx_hash_key_str("POST"), webdav_post); + cxMapPut(method_handler_map, cx_hash_key_str("DELETE"), webdav_delete); + cxMapPut(method_handler_map, cx_hash_key_str("PUT"), webdav_put); + cxMapPut(method_handler_map, cx_hash_key_str("COPY"), webdav_copy); + cxMapPut(method_handler_map, cx_hash_key_str("MOVE"), webdav_move); + cxMapPut(method_handler_map, cx_hash_key_str("LOCK"), webdav_lock); + cxMapPut(method_handler_map, cx_hash_key_str("UNLOCK"), webdav_unlock); + cxMapPut(method_handler_map, cx_hash_key_str("REPORT"), webdav_report); + cxMapPut(method_handler_map, cx_hash_key_str("ACL"), webdav_acl); - ucx_map_cstr_put(method_handler_map, "SEARCH", webdav_search); + cxMapPut(method_handler_map, cx_hash_key_str("SEARCH"), webdav_search); - ucx_map_cstr_put(method_handler_map, "VERSION-CONTROL", webdav_version_control); - ucx_map_cstr_put(method_handler_map, "CHECKOUT", webdav_checkout); - ucx_map_cstr_put(method_handler_map, "CHECKIN", webdav_checkin); - ucx_map_cstr_put(method_handler_map, "UNCHECKOUT", webdav_uncheckout); - ucx_map_cstr_put(method_handler_map, "MKWORKSPACE", webdav_mkworkspace); - ucx_map_cstr_put(method_handler_map, "UPDATE", webdav_update); - ucx_map_cstr_put(method_handler_map, "LABEL", webdav_label); - ucx_map_cstr_put(method_handler_map, "MERGE", webdav_merge); + cxMapPut(method_handler_map, cx_hash_key_str("VERSION-CONTROL"), webdav_version_control); + cxMapPut(method_handler_map, cx_hash_key_str("CHECKOUT"), webdav_checkout); + cxMapPut(method_handler_map, cx_hash_key_str("CHECKIN"), webdav_checkin); + cxMapPut(method_handler_map, cx_hash_key_str("UNCHECKOUT"), webdav_uncheckout); + cxMapPut(method_handler_map, cx_hash_key_str("MKWORKSPACE"), webdav_mkworkspace); + cxMapPut(method_handler_map, cx_hash_key_str("UPDATE"), webdav_update); + cxMapPut(method_handler_map, cx_hash_key_str("LABEL"), webdav_label); + cxMapPut(method_handler_map, cx_hash_key_str("MERGE"), webdav_merge); dav_namespace.href = (xmlChar*)"DAV:"; dav_namespace.prefix = (xmlChar*)"D"; @@ -191,7 +194,7 @@ } char *method = pblock_findkeyval(pb_key_method, rq->reqpb); - FuncPtr saf = (FuncPtr)ucx_map_cstr_get(method_handler_map, method); + FuncPtr saf = (FuncPtr)cxMapGet(method_handler_map, cx_hash_key_str(method)); if(!saf) { return REQ_NOACTION; } @@ -199,33 +202,30 @@ return saf(pb, sn, rq); } -UcxBuffer* rqbody2buffer(Session *sn, Request *rq) { +int rqbody2buffer(Session *sn, Request *rq, CxBuffer *buf) { if(!sn->inbuf) { //request body required, set http response code protocol_status(sn, rq, 400, NULL); - return NULL; + return 1; } - UcxBuffer *buf = ucx_buffer_new( - NULL, - sn->inbuf->maxsize, - UCX_BUFFER_AUTOEXTEND); - if(!buf) { + CxAllocator *a = pool_allocator(sn->pool); + if(cxBufferInit(buf, NULL, sn->inbuf->maxsize, a, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS)) { protocol_status(sn, rq, 500, NULL); - return NULL; + return 1; } char in[2048]; int r; while((r = netbuf_getbytes(sn->inbuf, in, 2048)) > 0) { - if(ucx_buffer_write(in, 1, r, buf) != r) { + if(cxBufferWrite(in, 1, r, buf) != r) { protocol_status(sn, rq, 500, NULL); - ucx_buffer_free(buf); - return NULL; + cxBufferDestroy(buf); + return 1; } } - return buf; + return 0; } int webdav_options(pblock *pb, Session *sn, Request *rq) { @@ -254,8 +254,8 @@ } } - UcxBuffer *reqbody = rqbody2buffer(sn, rq); - if(!reqbody) { + CxBuffer reqbody; + if(rqbody2buffer(sn, rq, &reqbody)) { return REQ_ABORTED; } @@ -263,10 +263,10 @@ WebdavPropfindRequest *propfind = propfind_parse( sn, rq, - reqbody->space, - reqbody->size, + reqbody.space, + reqbody.size, &error); - ucx_buffer_free(reqbody); + cxBufferDestroy(&reqbody); if(!propfind) { log_ereport(LOG_FAILURE, "webdav-propfind: %s", propfind_error2str(error)); return REQ_ABORTED; @@ -318,13 +318,14 @@ WebdavPropfindRequest *propfind, const char *path, const char *uri, - UcxList **out_req) + WebdavPropfindRequestList **out_req) { pool_handle_t *pool = propfind->sn->pool; - UcxAllocator *a = session_get_allocator(propfind->sn); + CxAllocator *a = pool_allocator(pool); // list of individual WebdavPropfindRequest objects for each Backend - UcxList *requestObjects = NULL; + WebdavPropfindRequestList *requestObjectsBegin = NULL; + WebdavPropfindRequestList *requestObjectsEnd = NULL; // new properties after init, start with clone of original plist WebdavPList *newProp = webdav_plist_clone(pool, propfind->properties); @@ -347,10 +348,18 @@ pReq->dav = davList; // add new WebdavPropfindRequest object to list for later use - requestObjects = ucx_list_append_a(a, requestObjects, pReq); - if(!requestObjects) { + WebdavPropfindRequestList *reqListElm = pool_malloc(pool, sizeof(WebdavPropfindRequestList)); + if(!reqListElm) { return REQ_ABORTED; // OOM } + reqListElm->propfind = pReq; + reqListElm->next = NULL; + cx_linked_list_add( + (void**)&requestObjectsBegin, + (void**)&requestObjectsEnd, + -1, + offsetof(WebdavPropfindRequestList, next), + reqListElm); // create plist copy as out-plist for init newProp = webdav_plist_clone(pool, newProp); @@ -366,7 +375,7 @@ davList = davList->next; } - *out_req = requestObjects; + *out_req = requestObjectsBegin; return REQ_PROCEED; } @@ -385,7 +394,7 @@ uint32_t settings = dav->settings; // list of individual WebdavPropfindRequest objects for each Backend - UcxList *requestObjects = NULL; + WebdavPropfindRequestList *requestObjects = NULL; // Initialize all Webdav Backends if(webdav_propfind_init(dav, propfind, path, uri, &requestObjects)) { @@ -480,8 +489,8 @@ } } - UcxBuffer *reqbody = rqbody2buffer(sn, rq); - if(!reqbody) { + CxBuffer reqbody; + if(rqbody2buffer(sn, rq, &reqbody)) { // most likely OOM return REQ_ABORTED; } @@ -490,10 +499,10 @@ WebdavProppatchRequest *proppatch = proppatch_parse( sn, rq, - reqbody->space, - reqbody->size, + reqbody.space, + reqbody.size, &error); - ucx_buffer_free(reqbody); + cxBufferDestroy(&reqbody); if(!proppatch) { log_ereport(LOG_FAILURE, "webdav-proppatch: %s", proppatch_error2str(error)); return REQ_ABORTED; @@ -587,14 +596,16 @@ typedef struct DeleteFile { char *path; struct stat s; + struct DeleteFile *prev; + struct DeleteFile *next; } DeleteFile; typedef struct DeleteLists { - UcxAllocator *a; - UcxList *dirs_begin; - UcxList *dirs_end; - UcxList *files_begin; - UcxList *files_end; + CxAllocator *a; + DeleteFile *dirs_begin; + DeleteFile *dirs_end; + DeleteFile *files_begin; + DeleteFile *files_end; } DeleteOp; static int deletelist_add( @@ -608,19 +619,20 @@ DeleteOp *op = userdata; // create object for this file - DeleteFile *file = almalloc(op->a, sizeof(DeleteFile)); + DeleteFile *file = cxMalloc(op->a, sizeof(DeleteFile)); if(!file) { return 1; } - file->path = sstrdup_a(op->a, sstr((char*)path)).ptr; + file->path = cx_strdup_a(op->a, cx_str((char*)path)).ptr; if(!file->path) { return 1; } file->s = *s; + file->next = NULL; // determine which list to use - UcxList **begin; - UcxList **end; + DeleteFile **begin; + DeleteFile **end; if(S_ISDIR(s->st_mode)) { begin = &op->dirs_begin; end = &op->dirs_end; @@ -630,19 +642,10 @@ } // add file to list - UcxList *elm = ucx_list_append_a(op->a, NULL, file); - if(!elm) { - alfree(op->a, file->path); // at least do some cleanup, although it - alfree(op->a, file); // isn't really necessary - return 1; - } - if(*begin == NULL) { - *begin = elm; - *end = elm; - } else { - ucx_list_concat(*end, elm); - *end = elm; - } + cx_linked_list_add( + (void**)begin, (void**)end, + offsetof(DeleteFile, prev), offsetof(DeleteFile, next), + file); return 0; } @@ -651,7 +654,7 @@ { DeleteOp del; ZERO(&del, sizeof(DeleteOp)); - del.a = session_get_allocator(op->sn); + del.a = pool_allocator(op->sn->pool); // get a list of all files if(webdav_op_iterate_children(op->vfs, -1, NULL, op->path, @@ -664,22 +667,18 @@ DeleteFile root; root.path = op->path; root.s = *op->stat; - UcxList root_elm; - root_elm.data = &root; - root_elm.prev = NULL; - root_elm.next = del.dirs_begin; + root.prev = NULL; + root.next = del.dirs_begin; if(del.dirs_begin) { - del.dirs_begin->prev = &root_elm; - del.dirs_begin = &root_elm; + del.dirs_begin->prev = &root; } else { - del.dirs_begin = &root_elm; - del.dirs_end = &root_elm; + del.dirs_end = &root; } + del.dirs_begin = &root; // delete files first - UCX_FOREACH(elm, del.files_begin) { - DeleteFile *file = elm->data; + for(DeleteFile *file=del.files_begin;file;file=file->next) { WebdavVFSOperation sub = webdav_vfs_sub_op(op, file->path, &file->s); if(webdav_vfs_op_do(&sub, WEBDAV_VFS_DELETE)) { return 1; @@ -687,8 +686,7 @@ } // delete directories, reverse order - for(UcxList *elm=del.dirs_end;elm;elm=elm->prev) { - DeleteFile *file = elm->data; + for(DeleteFile *file=del.dirs_end;file;file=file->prev) { WebdavVFSOperation sub = webdav_vfs_sub_op(op, file->path, &file->s); if(webdav_vfs_op_do(&sub, WEBDAV_VFS_DELETE)) { return 1; @@ -913,18 +911,16 @@ /* ------------------------------ Utils ------------------------------ */ -UcxKey webdav_property_key(const char *ns, const char *name) { - UcxKey key; - sstr_t data = ucx_sprintf("%s\n%s", name, ns); - key.data = data.ptr; +CxHashKey webdav_property_key(const char *ns, const char *name) { + CxHashKey key; + cxmutstr data = cx_asprintf("%s\n%s", name, ns); + key.data.str = data.ptr; key.len = data.length; - key.hash = ucx_hash(data.ptr, data.length); + cx_hash_murmur(&key); return key; } - - /* ------------------------------ public API ------------------------------ */ int webdav_getdepth(Request *rq) {
--- a/src/server/webdav/webdav.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/webdav/webdav.h Sun Nov 06 15:53:32 2022 +0100 @@ -31,9 +31,10 @@ #include "../public/webdav.h" -#include <ucx/map.h> -#include <ucx/list.h> -#include <ucx/buffer.h> +#include <cx/map.h> +#include <cx/list.h> +#include <cx/buffer.h> +#include <cx/string.h> #ifdef __cplusplus extern "C" { @@ -47,8 +48,14 @@ typedef struct DefaultWebdavData { WebdavVFSProperties vfsproperties; } DefaultWebdavData; + +typedef struct WebdavPropfindRequestList WebdavPropfindRequestList; +struct WebdavPropfindRequestList { + WebdavPropfindRequest *propfind; + WebdavPropfindRequestList *next; +}; -WebdavType* webdav_get_type(scstr_t dav_class); +WebdavType* webdav_get_type(cxstring dav_class); void* webdav_init_backend(ServerConfiguration *cfg, pool_handle_t *pool, WebdavType *dav_class, WSConfigNode *config, int *error); @@ -62,7 +69,7 @@ * this function sets an http response code in case of an error * or missing request body */ -UcxBuffer* rqbody2buffer(Session *sn, Request *rq); +int rqbody2buffer(Session *sn, Request *rq, CxBuffer *buf); int webdav_options(pblock *pb, Session *sn, Request *rq); @@ -74,7 +81,7 @@ WebdavPropfindRequest *propfind, const char *path, const char *uri, - UcxList **out_req); + WebdavPropfindRequestList **out_req); int webdav_propfind_do( WebdavBackend *dav, @@ -123,7 +130,7 @@ VFSFile *file, WSBool commit); -UcxKey webdav_property_key(const char *ns, const char *name); +CxHashKey webdav_property_key(const char *ns, const char *name); #ifdef __cplusplus }
--- a/src/server/webdav/xml.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/webdav/xml.c Sun Nov 06 15:53:32 2022 +0100 @@ -30,9 +30,11 @@ #include <stdlib.h> #include <string.h> -#include <ucx/string.h> -#include <ucx/map.h> -#include <ucx/buffer.h> +#include <cx/string.h> +#include <cx/map.h> +#include <cx/hash_map.h> +#include <cx/buffer.h> +#include <cx/linked_list.h> #include "../util/util.h" #include "../util/pool.h" @@ -47,12 +49,12 @@ * generates a string key for an xml namespace * format: prefix '\0' href */ -static sstr_t xml_namespace_key(UcxAllocator *a, WSNamespace *ns) { - sstr_t key = sstrcat_a(a, 3, - ns->prefix ? sstr((char*)ns->prefix) : S("\0"), - S("\0"), - sstr((char*)ns->href)); - return key; +static CxHashKey xml_namespace_key(CxAllocator *a, WSNamespace *ns) { + cxmutstr key_data = cx_strcat_a(a, 3, + ns->prefix ? cx_str((char*)ns->prefix) : cx_strn("\0", 1), + cx_strn("\0", 1), + cx_str((char*)ns->href)); + return cx_hash_key_bytes((unsigned char*)key_data.ptr, key_data.length); } @@ -195,8 +197,8 @@ /* ------------------- wsxml_get_required_namespaces ------------------- */ typedef struct WSNsCollector { - UcxAllocator *a; - UcxMap *nsmap; + CxAllocator *a; + CxMap *nsmap; WebdavNSList *def; int error; } WSNsCollector; @@ -207,12 +209,12 @@ if(node->type == XML_ELEMENT_NODE && node->ns) { // we create a list of unique prefix-href namespaces by putting // all namespaces in a map - sstr_t nskey = xml_namespace_key(col->a, node->ns); - if(!nskey.ptr) { + CxHashKey nskey = xml_namespace_key(col->a, node->ns); + if(!nskey.data.bytes) { col->error = 1; return 1; } - if(ucx_map_sstr_put(col->nsmap, nskey, node->ns)) { + if(cxMapPut(col->nsmap, nskey, node->ns)) { col->error = 1; return 1; } @@ -221,8 +223,7 @@ // from col->nsmap later WSNamespace *def = node->nsDef; while(def) { - WebdavNSList *newdef = col->a->malloc( - col->a->pool, sizeof(WebdavNSList)); + WebdavNSList *newdef = cxMalloc(col->a, sizeof(WebdavNSList)); if(!newdef) { col->error = 1; return 1; @@ -255,15 +256,15 @@ { if(error) *error = 0; - UcxAllocator a = util_pool_allocator(pool); - UcxMap *nsmap = ucx_map_new_a(&a, 16); + CxAllocator *a = pool_allocator(pool); + CxMap *nsmap = cxHashMapCreate(a, 16); if(!nsmap) { if(error) *error = 1; return NULL; } WSNsCollector col; - col.a = &a; + col.a = a; col.nsmap = nsmap; col.def = NULL; @@ -279,19 +280,19 @@ // what we get is a map that contains all missing namespace definitions WebdavNSList *def = col.def; while(def) { - sstr_t nskey = xml_namespace_key(&a, def->namespace); - if(!nskey.ptr) { + CxHashKey nskey = xml_namespace_key(a, def->namespace); + if(!nskey.data.bytes) { if(error) *error = 1; break; } - ucx_map_sstr_remove(nsmap, nskey); + (void)cxMapRemove(nsmap, nskey); def = def->next; } // convert nsmap to a list - UcxMapIterator i = ucx_map_iterator(nsmap); + CxIterator i = cxMapIteratorValues(nsmap); WSNamespace *ns; - UCX_MAP_FOREACH(key, ns, i) { + cx_foreach(WSNamespace *, ns, i) { WebdavNSList *newelm = pool_malloc(pool, sizeof(WebdavNSList)); if(!newelm) { if(error) *error = 1; @@ -300,23 +301,18 @@ } newelm->namespace = ns; newelm->next = NULL; - newelm->prev = end; // NULL or the end of list - if(end) { - end->next = newelm; // append new element - } else { - list = newelm; // start new list - } - end = newelm; + newelm->prev = NULL; + cx_linked_list_add((void**)&list, (void**)&end, offsetof(WebdavNSList, prev), offsetof(WebdavNSList, next), newelm); } } - ucx_map_free(nsmap); + cxMapDestroy(nsmap); return list; } static ssize_t buf_writefunc(void *buf, const char *s, size_t len) { - int w = ucx_buffer_write(s, 1, len, buf); + int w = cxBufferWrite(s, 1, len, buf); return w == 0 ? IO_ERROR : w; } @@ -324,8 +320,8 @@ pool_handle_t *pool, WSXmlNode *node) { - UcxBuffer *buf = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND); - if(!buf) { + CxBuffer buf; + if(cxBufferInit(&buf, NULL, 1024, pool_allocator(pool), CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS)) { return NULL; } @@ -337,23 +333,23 @@ Writer writer; char buffer[512]; - writer_init_with_stream(&writer, buf, buf_writefunc, buffer, 512); + writer_init_with_stream(&writer, &buf, buf_writefunc, buffer, 512); WSXmlData *data = NULL; if(!wsxml_write_nodes(pool, &writer, NULL, node) && !writer_flush(&writer)) { data = pool_malloc(pool, sizeof(WSXmlData)); if(data) { - data->data = pool_malloc(pool, buf->size + 1); + data->data = pool_malloc(pool, buf.size + 1); if(data->data) { - memcpy(data->data, buf->space, buf->size); - data->data[buf->size] = '\0'; - data->length = buf->size; + memcpy(data->data, buf.space, buf.size); + data->data[buf.size] = '\0'; + data->length = buf.size; data->namespaces = nslist; } } } - ucx_buffer_free(buf); + cxBufferDestroy(&buf); return data; } @@ -439,8 +435,8 @@ break; } memset(ns, 0, sizeof(WSNamespace)); - ns->prefix = prefix_len > 0 ? (xmlChar*)sstrdup_pool(pool, sstrn(prefix, prefix_len)).ptr : NULL; - ns->href = (xmlChar*)sstrdup_pool(pool, sstrn(href, i-href_start)).ptr; + ns->prefix = prefix_len > 0 ? (xmlChar*)cx_strdup_pool(pool, cx_mutstrn(prefix, prefix_len)).ptr : NULL; + ns->href = (xmlChar*)cx_strdup_pool(pool, cx_mutstrn(href, i-href_start)).ptr; if(list_current) { list_current->next = elm; } else { @@ -503,7 +499,7 @@ * key: (char*) namespace prefix * value: WSNamespace* */ - UcxMap *namespaces; + CxMap *namespaces; /* * Should namespace definitions be created @@ -519,22 +515,22 @@ static void xml_ser_text(Writer *out, int type, const char *text) { size_t start = 0; size_t i; - sstr_t entityref = { NULL, 0 }; + cxstring entityref = { NULL, 0 }; for(i=0;text[i]!='\0';i++) { char c = text[i]; if(c == '&') { - entityref = S("&"); + entityref = (cxstring)CX_STR("&"); } else if(type == 0) { if(c == '<') { - entityref = S("<"); + entityref = (cxstring)CX_STR("<"); } else if(c == '>') { - entityref = S(">"); + entityref = (cxstring)CX_STR(">"); } } else { if(c == '\"') { - entityref = S("""); + entityref = (cxstring)CX_STR("""); } else if(c == '\'') { - entityref = S("'"); + entityref = (cxstring)CX_STR("'"); } } @@ -564,12 +560,12 @@ // write prefix and ':' if(node->ns && node->ns->prefix) { - writer_puts(out, sstr((char*)node->ns->prefix)); + writer_puts(out, cx_str((char*)node->ns->prefix)); writer_putc(out, ':'); } // node name - writer_puts(out, sstr((char*)node->name)); + writer_puts(out, cx_str((char*)node->name)); // namespace definitions if(xw->define_namespaces) { @@ -580,19 +576,19 @@ // xw->namespaces contains all namespace, that were defined // before xml serialization if(!nsdef->prefix) { - writer_puts(out, S(" xmlns=\"")); - writer_puts(out, sstr((char*)nsdef->href)); - writer_putc(out, '"'); + writer_put_lit(out, " xmlns=\""); + writer_put_str(out, (char*)nsdef->href); + writer_putc (out, '"'); } else { WSNamespace *n = xw->namespaces ? - ucx_map_cstr_get(xw->namespaces, (char*)nsdef->prefix) : + cxMapGet(xw->namespaces, cx_hash_key_str((const char*)nsdef->prefix)) : NULL; if(!n) { - writer_puts(out, S(" xmlns:")); - writer_puts(out, sstr((char*)nsdef->prefix)); - writer_puts(out, S("=\"")); - writer_puts(out, sstr((char*)nsdef->href)); - writer_putc(out, '"'); + writer_put_lit(out, " xmlns:"); + writer_put_str(out, (const char*)nsdef->prefix); + writer_put_lit(out, "=\""); + writer_put_str(out, (const char*)nsdef->href); + writer_putc (out, '"'); } } @@ -607,12 +603,12 @@ writer_putc(out, ' '); // optional namespace if(attr->ns && attr->ns->prefix) { - writer_puts(out, sstr((char*)attr->ns->prefix)); + writer_puts(out, cx_str((char*)attr->ns->prefix)); writer_putc(out, ':'); } // <name>=" - writer_puts(out, sstr((char*)attr->name)); - writer_puts(out, S("=\"")); + writer_put_str(out, (char*)attr->name); + writer_put_lit(out, "=\""); // value xmlNode *value = attr->children; while(value) { @@ -630,7 +626,7 @@ if(node->children) { writer_putc(out, '>'); } else { - writer_puts(out, S("/>")); + writer_put_lit(out, "/>"); } } @@ -672,14 +668,14 @@ Writer *out = xw->out; if(node->type == XML_ELEMENT_NODE) { if(node->children) { - writer_puts(xw->out, S("</")); + writer_put_lit(xw->out, "</"); // write prefix and ':' if(node->ns && node->ns->prefix) { - writer_puts(out, sstr((char*)node->ns->prefix)); + writer_puts(out, cx_str((char*)node->ns->prefix)); writer_putc(out, ':'); } // name and close tag - writer_puts(out, sstr((char*)node->name)); + writer_puts(out, cx_str((char*)node->name)); writer_putc(out, '>'); } // element was already closed in xml_ser_node_begin @@ -691,7 +687,7 @@ static int xml_write_nodes( pool_handle_t *pool, Writer *out, - UcxMap *nsdefs, + CxMap *nsdefs, WSBool createdefs, xmlNode *node) { @@ -719,7 +715,7 @@ int wsxml_write_nodes( pool_handle_t *pool, Writer *out, - UcxMap *nsdefs, + CxMap *nsdefs, xmlNode *node) { return xml_write_nodes(pool, out, nsdefs, TRUE, node);
--- a/src/server/webdav/xml.h Wed Nov 02 19:19:01 2022 +0100 +++ b/src/server/webdav/xml.h Sun Nov 06 15:53:32 2022 +0100 @@ -32,8 +32,7 @@ #include "../public/webdav.h" #include <libxml/tree.h> -#include <ucx/map.h> -#include <ucx/ucx.h> +#include <cx/map.h> #include "../util/writer.h" @@ -47,7 +46,7 @@ int wsxml_write_nodes( pool_handle_t *pool, Writer *out, - UcxMap *nsdefs, + CxMap *nsdefs, xmlNode *node); /*
--- a/src/tools/Makefile Wed Nov 02 19:19:01 2022 +0100 +++ b/src/tools/Makefile Sun Nov 06 15:53:32 2022 +0100 @@ -31,7 +31,7 @@ include $(BUILD_ROOT)/config.mk CFLAGS += -I../ucx/ -LDFLAGS += -L../../build/lib -lucx -lwscfg +LDFLAGS += -lucx -lwscfg # list of source files WSTOOL_SRC = wstool.c @@ -42,7 +42,7 @@ all: $(BUILD_ROOT)/build/tools $(BUILD_ROOT)/build/bin/wstool $(BUILD_ROOT)/build/bin/wstool: $(BUILD_ROOT)/build/tools $(WSTOOL_OBJ) - $(CC) -o $@ $(WSTOOL_OBJ) $(LDFLAGS) + $(CC) -o $@ $(WSTOOL_OBJ) -L$(BUILD_ROOT)/build/lib $(LDFLAGS) $(BUILD_ROOT)/build/tools/%$(OBJ_EXT): %.c $(CC) $(CFLAGS) -c -o $@ $<
--- a/src/tools/wstool.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/tools/wstool.c Sun Nov 06 15:53:32 2022 +0100 @@ -62,19 +62,19 @@ int tool_get_tmpdir(char *configfile) { ServerConfig *serverconf = serverconfig_load(configfile); - UcxList *list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("Runtime")); + CxList *list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, cx_str("Runtime")); if(!list) { fprintf(stderr, "Error: No Runtime element in %s\n", configfile); return -1; } - if(ucx_list_size(list) != 1) { + if(list->size != 1) { fprintf(stderr, "Error: Multiple Runtime elements in %s\n", configfile); return -1; } - ConfigNode *runtime = list->data; - scstr_t tmp = serverconfig_directive_value(runtime, SC("Temp")); + ConfigNode *runtime = cxListAt(list, 0); + cxstring tmp = serverconfig_directive_value(runtime, cx_str("Temp")); - ucx_list_free(list); + cxListDestroy(list); if(!tmp.ptr) { fprintf(stderr, "Error: No Temp directive in Runtime Object\n");
--- a/src/ucx/Makefile Wed Nov 02 19:19:01 2022 +0100 +++ b/src/ucx/Makefile Sun Nov 06 15:53:32 2022 +0100 @@ -31,20 +31,18 @@ include $(BUILD_ROOT)/config.mk # list of source files -SRC = ucx.c -SRC += utils.c +SRC = allocator.c +SRC += basic_mempool.c +SRC += buffer.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 += string.c -SRC += test.c -SRC += allocator.c -SRC += logging.c -SRC += buffer.c -SRC += stack.c -SRC += array.c +SRC += tree.c +SRC += utils.c +SRC += printf.c +SRC += compare.c OBJ = $(SRC:%.c=$(BUILD_ROOT)/build/ucx/%$(OBJ_EXT))
--- a/src/ucx/allocator.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/ucx/allocator.c Sun Nov 06 15:53:32 2022 +0100 @@ -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,99 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include "ucx/allocator.h" +#include "cx/allocator.h" #include <stdlib.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); +}
--- a/src/ucx/array.c Wed Nov 02 19:19:01 2022 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,467 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2019 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. - */ - -#define _GNU_SOURCE /* we want to use qsort_r(), if available */ -#define __STDC_WANT_LIB_EXT1__ 1 /* use qsort_s, if available */ - - -#include "ucx/array.h" -#include "ucx/utils.h" - -#include <string.h> -#include <stdlib.h> -#include <errno.h> - -#ifndef UCX_ARRAY_DISABLE_QSORT -#ifdef __GLIBC__ -#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8) -#define ucx_array_sort_impl qsort_r -#endif /* glibc version >= 2.8 */ -#elif /* not __GLIBC__ */ defined(__APPLE__) || defined(__FreeBSD__) -#define ucx_array_sort_impl ucx_qsort_r -#define USE_UCX_QSORT_R -#elif /* not (__APPLE || __FreeBSD__) */ defined(__sun) -#if __STDC_VERSION__ >= 201112L -#define ucx_array_sort_impl qsort_s -#endif -#endif /* __GLIBC__, __APLE__, __FreeBSD__, __sun */ -#endif /* UCX_ARRAY_DISABLE_QSORT */ - -#ifndef ucx_array_sort_impl -#define ucx_array_sort_impl ucx_mergesort -#endif - -static int ucx_array_ensurecap(UcxArray *array, size_t reqcap) { - size_t required_capacity = array->capacity; - while (reqcap > required_capacity) { - if (required_capacity * 2 < required_capacity) - return 1; - required_capacity <<= 1; - } - if (ucx_array_reserve(array, required_capacity)) { - return 1; - } - return 0; -} - -int ucx_array_util_set_a(UcxAllocator* alloc, void** array, size_t* capacity, - size_t elmsize, size_t index, void* data) { - - if(!alloc || !capacity || !array) { - errno = EINVAL; - return 1; - } - - size_t newcapacity = *capacity; - while(index >= newcapacity) { - if(ucx_szmul(newcapacity, 2, &newcapacity)) { - errno = EOVERFLOW; - return 1; - } - } - - size_t memlen, offset; - if(ucx_szmul(newcapacity, elmsize, &memlen)) { - errno = EOVERFLOW; - return 1; - } - /* we don't need to check index*elmsize - it is smaller than memlen */ - - - void* newptr = alrealloc(alloc, *array, memlen); - if(newptr == NULL) { - errno = ENOMEM; /* we cannot assume that every allocator sets this */ - return 1; - } - *array = newptr; - *capacity = newcapacity; - - - char* dest = *array; - dest += elmsize*index; - memcpy(dest, data, elmsize); - - return 0; -} - -int ucx_array_util_setptr_a(UcxAllocator* alloc, void** array, size_t* capacity, - size_t index, void* data) { - - return ucx_array_util_set_a(alloc, array, capacity, sizeof(void*), - index, &data); -} - -UcxArray* ucx_array_new(size_t capacity, size_t elemsize) { - return ucx_array_new_a(capacity, elemsize, ucx_default_allocator()); -} - -UcxArray* ucx_array_new_a(size_t capacity, size_t elemsize, - UcxAllocator* allocator) { - UcxArray* array = almalloc(allocator, sizeof(UcxArray)); - if(array) { - ucx_array_init_a(array, capacity, elemsize, allocator); - } - return array; -} - -void ucx_array_init(UcxArray* array, size_t capacity, size_t elemsize) { - ucx_array_init_a(array, capacity, elemsize, ucx_default_allocator()); -} - -void ucx_array_init_a(UcxArray* array, size_t capacity, size_t elemsize, - UcxAllocator* allocator) { - - array->allocator = allocator; - array->elemsize = elemsize; - array->size = 0; - array->data = alcalloc(allocator, capacity, elemsize); - - if (array->data) { - array->capacity = capacity; - } else { - array->capacity = 0; - } -} - -int ucx_array_clone(UcxArray* dest, UcxArray const* src) { - if (ucx_array_ensurecap(dest, src->capacity)) { - return 1; - } - - dest->elemsize = src->elemsize; - dest->size = src->size; - - if (dest->data) { - memcpy(dest->data, src->data, src->size*src->elemsize); - } - - return 0; -} - -int ucx_array_equals(UcxArray const *array1, UcxArray const *array2, - cmp_func cmpfnc, void* data) { - - if (array1->size != array2->size || array1->elemsize != array2->elemsize) { - return 0; - } else { - if (array1->size == 0) - return 1; - - size_t elemsize; - if (cmpfnc == NULL) { - cmpfnc = ucx_cmp_mem; - elemsize = array1->elemsize; - data = &elemsize; - } - - for (size_t i = 0 ; i < array1->size ; i++) { - int r = cmpfnc( - ucx_array_at(array1, i), - ucx_array_at(array2, i), - data); - if (r != 0) - return 0; - } - return 1; - } -} - -void ucx_array_destroy(UcxArray *array) { - if(array->data) - alfree(array->allocator, array->data); - array->data = NULL; - array->capacity = array->size = 0; -} - -void ucx_array_free(UcxArray *array) { - ucx_array_destroy(array); - alfree(array->allocator, array); -} - -int ucx_array_append_from(UcxArray *array, void *data, size_t count) { - if (ucx_array_ensurecap(array, array->size + count)) - return 1; - - void* dest = ucx_array_at(array, array->size); - if (data) { - memcpy(dest, data, array->elemsize*count); - } else { - memset(dest, 0, array->elemsize*count); - } - array->size += count; - - return 0; -} - -int ucx_array_prepend_from(UcxArray *array, void *data, size_t count) { - if (ucx_array_ensurecap(array, array->size + count)) - return 1; - - if (array->size > 0) { - void *dest = ucx_array_at(array, count); - memmove(dest, array->data, array->elemsize*array->size); - } - - if (data) { - memcpy(array->data, data, array->elemsize*count); - } else { - memset(array->data, 0, array->elemsize*count); - } - array->size += count; - - return 0; -} - -int ucx_array_set_from(UcxArray *array, size_t index, - void *data, size_t count) { - if (ucx_array_ensurecap(array, index + count)) - return 1; - - if (index+count > array->size) { - array->size = index+count; - } - - void *dest = ucx_array_at(array, index); - if (data) { - memcpy(dest, data, array->elemsize*count); - } else { - memset(dest, 0, array->elemsize*count); - } - - return 0; -} - -int ucx_array_concat(UcxArray *array1, const UcxArray *array2) { - - if (array1->elemsize != array2->elemsize) - return 1; - - size_t capacity = array1->capacity+array2->capacity; - - if (array1->capacity < capacity) { - if (ucx_array_reserve(array1, capacity)) { - return 1; - } - } - - void* dest = ucx_array_at(array1, array1->size); - memcpy(dest, array2->data, array2->size*array2->elemsize); - - array1->size += array2->size; - - return 0; -} - -void *ucx_array_at(UcxArray const *array, size_t index) { - char* memory = array->data; - char* loc = memory + index*array->elemsize; - return loc; -} - -size_t ucx_array_find(UcxArray const *array, void *elem, - cmp_func cmpfnc, void *data) { - - size_t elemsize; - if (cmpfnc == NULL) { - cmpfnc = ucx_cmp_mem; - elemsize = array->elemsize; - data = &elemsize; - } - - if (array->size > 0) { - for (size_t i = 0 ; i < array->size ; i++) { - void* ptr = ucx_array_at(array, i); - if (cmpfnc(ptr, elem, data) == 0) { - return i; - } - } - return array->size; - } else { - return 0; - } -} - -int ucx_array_contains(UcxArray const *array, void *elem, - cmp_func cmpfnc, void *data) { - return ucx_array_find(array, elem, cmpfnc, data) != array->size; -} - -static void ucx_mergesort_merge(void *arrdata,size_t elemsize, - cmp_func cmpfnc, void *data, - size_t start, size_t mid, size_t end) { - - char* array = arrdata; - - size_t rightstart = mid + 1; - - if (cmpfnc(array + mid*elemsize, - array + rightstart*elemsize, data) <= 0) { - /* already sorted */ - return; - } - - /* we need memory for one element */ - void *value = malloc(elemsize); - - while (start <= mid && rightstart <= end) { - if (cmpfnc(array + start*elemsize, - array + rightstart*elemsize, data) <= 0) { - start++; - } else { - /* save the value from the right */ - memcpy(value, array + rightstart*elemsize, elemsize); - - /* shift all left elements one element to the right */ - size_t shiftcount = rightstart-start; - void *startptr = array + start*elemsize; - void *dest = array + (start+1)*elemsize; - memmove(dest, startptr, shiftcount*elemsize); - - /* bring the first value from the right to the left */ - memcpy(startptr, value, elemsize); - - start++; - mid++; - rightstart++; - } - } - - /* free the temporary memory */ - free(value); -} - -static void ucx_mergesort_impl(void *arrdata, size_t elemsize, - cmp_func cmpfnc, void *data, size_t l, size_t r) { - if (l < r) { - size_t m = l + (r - l) / 2; - - ucx_mergesort_impl(arrdata, elemsize, cmpfnc, data, l, m); - ucx_mergesort_impl(arrdata, elemsize, cmpfnc, data, m + 1, r); - ucx_mergesort_merge(arrdata, elemsize, cmpfnc, data, l, m, r); - } -} - -static void ucx_mergesort(void *arrdata, size_t count, size_t elemsize, - cmp_func cmpfnc, void *data) { - - ucx_mergesort_impl(arrdata, elemsize, cmpfnc, data, 0, count-1); -} - -#ifdef USE_UCX_QSORT_R -struct cmpfnc_swapargs_info { - cmp_func func; - void *data; -}; - -static int cmp_func_swap_args(void *data, const void *x, const void *y) { - struct cmpfnc_swapargs_info* info = data; - return info->func(x, y, info->data); -} - -static void ucx_qsort_r(void *array, size_t count, size_t elemsize, - cmp_func cmpfnc, void *data) { - struct cmpfnc_swapargs_info info; - info.func = cmpfnc; - info.data = data; - qsort_r(array, count, elemsize, &info, cmp_func_swap_args); -} -#endif /* USE_UCX_QSORT_R */ - -void ucx_array_sort(UcxArray* array, cmp_func cmpfnc, void *data) { - ucx_array_sort_impl(array->data, array->size, array->elemsize, - cmpfnc, data); -} - -void ucx_array_remove(UcxArray *array, size_t index) { - array->size--; - if (index < array->size) { - void* dest = ucx_array_at(array, index); - void* src = ucx_array_at(array, index+1); - memmove(dest, src, (array->size - index)*array->elemsize); - } -} - -void ucx_array_remove_fast(UcxArray *array, size_t index) { - array->size--; - if (index < array->size) { - void* dest = ucx_array_at(array, index); - void* src = ucx_array_at(array, array->size); - memcpy(dest, src, array->elemsize); - } -} - -int ucx_array_shrink(UcxArray* array) { - void* newptr = alrealloc(array->allocator, array->data, - array->size*array->elemsize); - if (newptr) { - array->data = newptr; - array->capacity = array->size; - return 0; - } else { - return 1; - } -} - -int ucx_array_resize(UcxArray* array, size_t capacity) { - if (array->capacity >= capacity) { - void* newptr = alrealloc(array->allocator, array->data, - capacity*array->elemsize); - if (newptr) { - array->data = newptr; - array->capacity = capacity; - if (array->size > array->capacity) { - array->size = array->capacity; - } - return 0; - } else { - return 1; - } - } else { - return ucx_array_reserve(array, capacity); - } -} - -int ucx_array_reserve(UcxArray* array, size_t capacity) { - if (array->capacity > capacity) { - return 0; - } else { - void* newptr = alrealloc(array->allocator, array->data, - capacity*array->elemsize); - if (newptr) { - array->data = newptr; - array->capacity = capacity; - return 0; - } else { - return 1; - } - } -} - -int ucx_array_grow(UcxArray* array, size_t count) { - return ucx_array_reserve(array, array->size+count); -}
--- a/src/ucx/avl.c Wed Nov 02 19:19:01 2022 +0100 +++ /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 <limits.h> - -#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; - } -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ucx/basic_mempool.c Sun Nov 06 15:53:32 2022 +0100 @@ -0,0 +1,236 @@ +/* + * 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 <stdint.h> +#include <string.h> + +#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; +}
--- a/src/ucx/buffer.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/ucx/buffer.c Sun Nov 06 15:53:32 2022 +0100 @@ -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,78 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include "ucx/buffer.h" +#include "cx/buffer.h" +#include "cx/utils.h" -#include <stdarg.h> #include <stdlib.h> +#include <stdio.h> #include <string.h> +#include <stdint.h> -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 +) { + 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) - { - return NULL; +void cxBufferDestroy(CxBuffer *buffer) { + if ((buffer->flags & CX_BUFFER_FREE_CONTENTS) == CX_BUFFER_FREE_CONTENTS) { + cxFree(buffer->allocator, buffer->bytes); } - - 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); - } - return dst; } -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 +107,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 +347,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 +367,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; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ucx/compare.c Sun Nov 06 15:53:32 2022 +0100 @@ -0,0 +1,182 @@ +/* + * 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 <stdint.h> +#include <math.h> + +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_ptr(void const *ptr1, void const *ptr2) { + 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; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ucx/cx/allocator.h Sun Nov 06 15:53:32 2022 +0100 @@ -0,0 +1,260 @@ +/* + * 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))); + +/** + * Structure holding an advanced destructor function and the desired payload. + * Invocations of func should use data as first argument. + */ +typedef struct { + /** + * A pointer to the data that SHALL be used to invoke func. + */ + void *data; + /** + * A pointer to the function to invoke. + */ + cx_destructor_func2 func; +} cx_advanced_destructor; + +/** + * Specifies the type of destructor to use. + */ +enum cx_destructor_type { + /** + * Do not use a destructor function. + */ + CX_DESTRUCTOR_NONE, + /** + * Use a simple destructor. + * @see cx_destructor_func + */ + CX_DESTRUCTOR_SIMPLE, + /** + * Use an advanced destructor. + * @see cx_advanced_destructor + */ + CX_DESTRUCTOR_ADVANCED +}; + +/** + * 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 */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ucx/cx/basic_mempool.h Sun Nov 06 15:53:32 2022 +0100 @@ -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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ucx/cx/buffer.h Sun Nov 06 15:53:32 2022 +0100 @@ -0,0 +1,414 @@ +/* + * 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 + * @param flags buffer features (see cx_buffer_s.flags) + * @return zero on success, non-zero if a required allocation failed + */ +__attribute__((__nonnull__(1, 4))) +int cxBufferInit( + CxBuffer *buffer, + 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. + * + * @param buffer the buffer which contents shall be destroyed + */ +__attribute__((__nonnull__)) +void cxBufferDestroy(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 + * <code>memset(buffer->bytes, 0, shift)</code> for a right shift or + * <code>memset(buffer->bytes + buffer->size, 0, buffer->capacity - buffer->size)</code> + * 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 */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ucx/cx/common.h Sun Nov 06 15:53:32 2022 +0100 @@ -0,0 +1,127 @@ +/* + * 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. + * <p> + * Latest available source:<br> + * <a href="https://sourceforge.net/projects/ucx/files/">https://sourceforge.net/projects/ucx/files/</a> + * </p> + * + * <p> + * Repositories:<br> + * <a href="https://sourceforge.net/p/ucx/code">https://sourceforge.net/p/ucx/code</a> + * - or - + * <a href="https://develop.uap-core.de/hg/ucx">https://develop.uap-core.de/hg/ucx</a> + * </p> + * + * <h2>LICENCE</h2> + * + * 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) + +#include <stdlib.h> +#include <stddef.h> +#include <stdbool.h> + +/** + * Function pointer compatible with fwrite-like functions. + */ +typedef size_t (*cx_write_func)( + void const *, + size_t, + size_t, + void * +); + +#ifdef _WIN32 +#ifndef __WORDSIZE +#ifdef _WIN64 +#define __WORDSIZE 64 +#else +#define __WORDSIZE 32 +#endif +#endif /* __WORDSIZE */ +#else /* !_WIN32 */ + +#include <sys/types.h> + +#endif /* _WIN32 */ + +#ifndef __GNUC__ +/** + * Removes GNU C attributes where they are not supported. + */ +#define __attribute__(x) +#endif + +#endif /* UCX_COMMON_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ucx/cx/compare.h Sun Nov 06 15:53:32 2022 +0100 @@ -0,0 +1,199 @@ +/* + * 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 + +#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 two pointers. + * + * @param ptr1 pointer one + * @param ptr2 pointer two + * @return -1 if ptr1 is less than ptr2, 0 if both are equal, + * 1 if ptr1 is greater than ptr2 + */ +int cx_cmp_ptr(void const *ptr1, void const *ptr2); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif //UCX_COMPARE_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ucx/cx/hash_key.h Sun Nov 06 15:53:32 2022 +0100 @@ -0,0 +1,125 @@ +/* + * 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 "stddef.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Internal structure for a key within a hash map. */ +struct cx_hash_key_s { + /** The key data. */ + union { + unsigned char *bytes; + unsigned char const *cbytes; + char const *cstr; + char *str; + void *obj; + } 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 murmur hash-2. + * + * You need to initialize data and len in the key struct. + * The hash is then directly written to that struct. + * + * @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 *obj, + size_t len +); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* UCX_HASH_KEY_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ucx/cx/hash_map.h Sun Nov 06 15:53:32 2022 +0100 @@ -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 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 { + /** The value data. */ + void *data; + + /** A pointer to the next element in the current bucket. */ + struct cx_hash_map_element_s *next; + + /** The corresponding key. */ + CxHashKey key; +}; + +/** + * 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. + * + * @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 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 *allocator, + 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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ucx/cx/iterator.h Sun Nov 06 15:53:32 2022 +0100 @@ -0,0 +1,167 @@ +/* + * 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" + +/** + * Internal iterator struct - use CxIterator. + */ +struct cx_iterator_s { + /** + * True iff the iterator points to valid data. + */ + __attribute__ ((__nonnull__)) + bool (*valid)(struct cx_iterator_s const *); + + /** + * Returns a pointer to the current element. + */ + __attribute__ ((__nonnull__)) + void *(*current)(struct cx_iterator_s const *); + + /** + * Advances the iterator. + */ + __attribute__ ((__nonnull__)) + void (*next)(struct cx_iterator_s *); + + /** + * 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 *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; + + /** + * Users may set this to true, if the current element shall be removed from the underlying collection + * when the iterator advances. + * Has no effect on iterators that are not based on a collection. + */ + bool remove; +}; + +/** + * 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 (elements added or removed), + * the iterator becomes invalid (regardless of what cxIteratorValid() returns) and MUST be re-obtained + * from the collection. + */ +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 a pointer to the iterator + * @return true iff the iterator points to valid data + */ +__attribute__ ((__nonnull__)) +static inline bool cxIteratorValid(CxIterator const *iter) { + return iter->valid(iter); +} + +/** + * Returns a pointer to the current element. + * + * The behavior is undefined if this iterator is invalid. + * + * @param iter a pointer to the iterator + * @return a pointer to the current element + */ +__attribute__ ((__nonnull__)) +static inline void *cxIteratorCurrent(CxIterator const *iter) { + return iter->current(iter); +} + +/** + * Advances the iterator to the next element. + * + * @param iter a pointer to the iterator + */ +__attribute__ ((__nonnull__)) +static inline void cxIteratorNext(CxIterator *iter) { + iter->next(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)) // NOLINT(bugprone-macro-parentheses) + +#endif /* UCX_ITERATOR_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ucx/cx/linked_list.h Sun Nov 06 15:53:32 2022 +0100 @@ -0,0 +1,450 @@ +/* + * 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 + +/** + * Allocates a linked list for storing elements with \p item_size bytes each. + * + * @remark Elements added to the list are copied, therefore a possible destructor + * MUST NOT free the memory pointed to by its argument. + * + * @param allocator the allocator for allocating the list nodes + * @param comparator the comparator for the elements + * @param item_size the size of each element in bytes + * @return the created list + */ +CxList *cxLinkedListCreate( + CxAllocator const *allocator, + CxListComparator comparator, + size_t item_size +) __attribute__((__nonnull__)); + +/** + * Allocates a linked list for storing pointers. + * + * If you want to store the elements directly in this list, use cxLinkedListCreate(). + * + * @remark Since only pointers are stored in this list, a possible destructor + * MAY free the memory pointed to by its argument in order to prevent memory leaks. + * + * @param allocator the allocator for allocating the list nodes + * @param comparator the comparator for the elements + * @return the created list + */ +CxList *cxPointerLinkedListCreate( + CxAllocator const *allocator, + CxListComparator comparator +) __attribute__((__nonnull__)); + +/** + * Creates a linked list using the data from an array. + * + * @remark Elements added to the list are copied, therefore a possible destructor + * MUST NOT free the memory pointed to by its argument. + * + * @param allocator the allocator for allocating the list nodes + * @param comparator the comparator for the elements + * @param item_size the size of one item in the array + * @param num_items the number of items + * @param array the array data + * @return the created list + */ +CxList *cxLinkedListFromArray( + CxAllocator const *allocator, + CxListComparator comparator, + size_t item_size, + size_t num_items, + void const *array +) __attribute__((__nonnull__)); + +/** + * 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 follow_ptr \c false if the pointer denoted by \p loc_data shall be passed to the \p cmp_func. + * If \c true, the data at \p loc_data is assumed to be a pointer, dereferenced, and then passed to \p cmp_func. + * @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, + bool follow_ptr, + CxListComparator 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 structures: + * \code + * typedef struct node node; + * struct node { + * node* prev; + * node* next; + * my_payload data; // in this case set follow_ptr = 0 + * } + * + * typedef struct ptr_node ptr_node; + * struct ptr_node { + * ptr_node* prev; + * ptr_node* next; + * my_payload* data; // in this case set follow_ptr = 1 + * } + * \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 follow_ptr \c false if the pointer denoted by \p loc_data shall be passed to the \p cmp_func. + * If \c true, the data at \p loc_data is assumed to be a pointer, dereferenced, and then passed to \p cmp_func. + * @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, + bool follow_ptr, + CxListComparator cmp_func +) __attribute__((__nonnull__(1, 7))); + + +/** + * Compares two lists element wise. + * + * The \c follow_ptr flags have the following meaning: if \c false, the pointer denoted by \p loc_data shall + * directly be passed to the \p cmp_func. + * If \c true, the data at \p loc_data is assumed to be a pointer, dereferenced, and then passed to \p cmp_func. + * + * \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 follow_ptr_left indicates whether pointers in the left list shall be dereferenced + * @param follow_ptr_right indicates whether pointers in the right list shall be dereferenced + * @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, + bool follow_ptr_left, + bool follow_ptr_right, + CxListComparator cmp_func +) __attribute__((__nonnull__(7))); + +/** + * 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 */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ucx/cx/list.h Sun Nov 06 15:53:32 2022 +0100 @@ -0,0 +1,425 @@ +/* + * 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 "allocator.h" +#include "iterator.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * A comparator function comparing two list elements. + */ +typedef int(*CxListComparator)( + void const *left, + void const *right +); + +/** + * 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 { + /** + * The list class definition. + */ + cx_list_class *cl; + /** + * The allocator to use. + */ + CxAllocator const *allocator; + /** + * The comparator function for the elements. + */ + CxListComparator cmpfunc; + /** + * The size of each element (payload only). + */ + size_t itemsize; + /** + * The size of the list (number of currently stored elements). + */ + size_t size; + /** + * The capacity of the list (maximum number of elements). + */ + size_t capacity; + union { + /** + * An optional simple destructor for the list contents that admits the free() interface. + * + * @remark Set content_destructor_type to #CX_DESTRUCTOR_SIMPLE. + * + * @attention Read the documentation of the particular list 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 list contents providing additional data. + * + * @remark Set content_destructor_type to #CX_DESTRUCTOR_ADVANCED. + * + * @attention Read the documentation of the particular list implementation + * whether this destructor shall only destroy the contents or also free the memory. + */ + cx_advanced_destructor advanced_destructor; + }; + /** + * The type of destructor to use. + */ + enum cx_destructor_type content_destructor_type; +}; + +/** + * The class definition for arbitrary lists. + */ +struct cx_list_class_s { + /** + * Destructor function. + */ + void (*destructor)(struct cx_list_s *list); + + /** + * Member function for adding an element. + */ + int (*add)( + struct cx_list_s *list, + void const *elem + ); + + /** + * Member function for inserting an element. + */ + int (*insert)( + struct cx_list_s *list, + size_t index, + void const *elem + ); + + /** + * Member function for inserting an element relative to an iterator position. + */ + int (*insert_iter)( + struct cx_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 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); + + /** + * Returns an iterator pointing to the specified index. + */ + struct cx_iterator_s (*iterator)( + struct cx_list_s *list, + size_t index + ); +}; + +/** + * Common type for all list implementations. + */ +typedef struct cx_list_s CxList; + +/** + * 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 + */ +__attribute__((__nonnull__)) +static inline int cxListAdd( + CxList *list, + void const *elem +) { + return list->cl->add(list, elem); +} + +/** + * 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(list, index, elem); +} + +/** + * 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( + CxIterator *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( + CxIterator *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. + * @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); +} + +/** + * 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 cxListIterator( + CxList *list, + size_t index +) { + return list->cl->iterator(list, 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 cxListBegin(CxList *list) { + return list->cl->iterator(list, 0); +} + +/** + * 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 *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, zero if the first list is larger + */ +__attribute__((__nonnull__)) +static inline int cxListCompare( + CxList *list, + CxList *other +) { + return list->cl->compare(list, 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 */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ucx/cx/map.h Sun Nov 06 15:53:32 2022 +0100 @@ -0,0 +1,270 @@ +/* + * 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 "allocator.h" +#include "iterator.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 { + /** The map class definition. */ + cx_map_class *cl; + /** An allocator that is used for the map elements. */ + CxAllocator *allocator; + /** The number of elements currently stored. */ + size_t size; + // TODO: elemsize + a flag if values shall be copied to the map +}; + +/** + * 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__, __warn_unused_result__)) + void *(*remove)( + CxMap *map, + CxHashKey key + ); + + /** + * Iterator over the key/value pairs. + */ + __attribute__((__nonnull__, __warn_unused_result__)) + CxIterator (*iterator)(CxMap *map); + + /** + * Iterator over the keys. + */ + __attribute__((__nonnull__, __warn_unused_result__)) + CxIterator (*iterator_keys)(CxMap *map); + + /** + * Iterator over the values. + */ + __attribute__((__nonnull__, __warn_unused_result__)) + CxIterator (*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; +}; + + +/** + * Deallocates the memory of the specified map. + * + * @param map the map to be destroyed + */ +__attribute__((__nonnull__)) +static inline void cxMapDestroy(CxMap *map) { + // TODO: likely to add auto-free feature for contents in the future + 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); +} + +/** + * 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 key, + void *value +) { + return map->cl->put(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 *cxMapGet( + CxMap const *map, + CxHashKey key +) { + return map->cl->get(map, 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 + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cxMapRemove( + CxMap *map, + CxHashKey key +) { + return map->cl->remove(map, key); +} + +// 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); +} + +#ifdef __cplusplus +} +#endif + +#endif // UCX_MAP_H \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ucx/cx/mempool.h Sun Nov 06 15:53:32 2022 +0100 @@ -0,0 +1,121 @@ +/* + * 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 "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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ucx/cx/printf.h Sun Nov 06 15:53:32 2022 +0100 @@ -0,0 +1,144 @@ +/* + * 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 <stdarg.h> + +#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 + */ +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() + */ +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() + */ +cxmutstr cx_asprintf_a(CxAllocator *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() + */ +cxmutstr cx_vasprintf_a(CxAllocator *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 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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ucx/cx/string.h Sun Nov 06 15:53:32 2022 +0100 @@ -0,0 +1,835 @@ +/* + * 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; + +/** + * 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) {literal, sizeof(literal) - 1} + +#ifdef __cplusplus +extern "C" { +#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 *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 *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 *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 *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 +); + + +/** + * 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 *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 *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) + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif //UCX_STRING_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ucx/cx/tree.h Sun Nov 06 15:53:32 2022 +0100 @@ -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 */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ucx/cx/utils.h Sun Nov 06 15:53:32 2022 +0100 @@ -0,0 +1,125 @@ +/* + * 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)++) + +/* ---------------------- + * cx_szmul() definition. + * ---------------------- */ + +#if (__GNUC__ >= 5 || defined(__clang__)) && !defined(CX_NO_SZMUL_BUILTIN) +#define CX_SZMUL_BUILTIN + +#if __WORDSIZE == 32 +/** + * Alias for \c __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 cx_szmul(a, b, result) __builtin_umul_overflow(a, b, result) +#else /* __WORDSIZE != 32 */ +/** + * Alias for \c __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 cx_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. + * + * @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 + +#ifdef __cplusplus +} +#endif + +#endif /* UCX_UTILS_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ucx/hash_key.c Sun Nov 06 15:53:32 2022 +0100 @@ -0,0 +1,107 @@ +/* + * 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 <string.h> + +void cx_hash_murmur(CxHashKey *key) { + size_t len = key->len; + unsigned char const *data = key->data.cbytes; + + 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.cstr = 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.cbytes = bytes; + key.len = len; + cx_hash_murmur(&key); + return key; +} + +CxHashKey cx_hash_key( + void *obj, + size_t len +) { + CxHashKey key; + key.data.obj = obj; + key.len = len; + cx_hash_murmur(&key); + return key; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ucx/hash_map.c Sun Nov 06 15:53:32 2022 +0100 @@ -0,0 +1,402 @@ +/* + * 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 <string.h> +#include "cx/hash_map.h" +#include "cx/utils.h" + +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; + // free the key data + cxFree(map->allocator, elem->key.data.obj); + // 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 *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.obj, key.data.obj, key.len) == 0) { + // overwrite existing element + elm->data = value; + } else { + // allocate new element + struct cx_hash_map_element_s *e = cxMalloc(allocator, sizeof(struct cx_hash_map_element_s)); + if (e == NULL) { + return -1; + } + + // write the value + // TODO: depending on future map features, we may want to copy here + e->data = value; + + // copy the key + void *kd = cxMalloc(allocator, key.len); + if (kd == NULL) { + return -1; + } + memcpy(kd, key.data.obj, key.len); + e->key.data.obj = 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, elm->key.data.obj); + 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 + * @return the value corresponding to the key or \c NULL + */ +static void *cx_hash_map_get_remove( + CxMap *map, + CxHashKey key, + bool remove +) { + 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.obj, key.data.obj, key.len) == 0) { + void *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 when remove=false, the map stays untouched + return cx_hash_map_get_remove((CxMap *) map, key, false); +} + +static void *cx_hash_map_remove( + CxMap *map, + CxHashKey key +) { + return cx_hash_map_get_remove(map, key, true); +} + +static void *cx_hash_map_iter_current_entry(CxIterator const *iter) { + // struct has to have a compatible signature + return (struct cx_map_entry_s *) &(iter->kv_data); +} + +static void *cx_hash_map_iter_current_key(CxIterator const *iter) { + struct cx_hash_map_element_s *elm = iter->elem_handle; + return &elm->key; +} + +static void *cx_hash_map_iter_current_value(CxIterator const *iter) { + struct cx_hash_map_element_s *elm = iter->elem_handle; + // TODO: return a pointer to data if this map is storing copies + return elm->data; +} + +static bool cx_hash_map_iter_valid(CxIterator const *iter) { + return iter->elem_handle != NULL; +} + +static void cx_hash_map_iter_next(CxIterator *iter) { + struct cx_hash_map_s *map = iter->src_handle; + struct cx_hash_map_element_s *elm = iter->elem_handle; + + // remove current element, if asked + if (iter->remove) { + // clear the flag + iter->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; + } + } + + // 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 + 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; + // TODO: pointer to data if this map is storing copies + iter->kv_data.value = elm->data; + } +} + +static CxIterator cx_hash_map_iterator(CxMap *map) { + CxIterator iter; + + iter.src_handle = map; + iter.valid = cx_hash_map_iter_valid; + iter.next = cx_hash_map_iter_next; + iter.current = cx_hash_map_iter_current_entry; + + iter.slot = 0; + iter.index = 0; + iter.remove = false; + + 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]; + for (; elm == NULL; iter.slot++) { + elm = hash_map->buckets[iter.slot]; + } + iter.elem_handle = elm; + iter.kv_data.key = &elm->key; + // TODO: pointer to data if this map is storing copies + 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 *map) { + CxIterator iter = cx_hash_map_iterator(map); + iter.current = cx_hash_map_iter_current_key; + return iter; +} + +static CxIterator cx_hash_map_iterator_values(CxMap *map) { + CxIterator iter = cx_hash_map_iterator(map); + iter.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, +}; + +CxMap *cxHashMapCreate( + CxAllocator *allocator, + size_t buckets +) { + if (buckets == 0) { + // implementation defined default + buckets = 16; + } + + struct cx_hash_map_s *map = cxMalloc(allocator, 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; + map->base.size = 0; + + 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; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ucx/linked_list.c Sun Nov 06 15:53:32 2022 +0100 @@ -0,0 +1,807 @@ +/* + * 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 <stdint.h> +#include <string.h> +#include <assert.h> + +/* 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_f(node, follow_ptr) ((follow_ptr)?CX_LL_PTR(node, loc_data):(((char*)(node))+loc_data)) +#define ll_data(node) ll_data_f(node,follow_ptr) + +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, + bool follow_ptr, + CxListComparator 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; +} + +static void *cx_linked_list_sort_merge( + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + ptrdiff_t loc_data, + bool follow_ptr, + size_t length, + void *ls, + void *le, + void *re, + CxListComparator cmp_func +) { + const size_t sbo_len = 1024; + void *sbo[sbo_len]; + void **sorted = (length >= sbo_len) ? 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, + bool follow_ptr, + CxListComparator 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, follow_ptr, + 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, follow_ptr, cmp_func); + + // merge sorted list with (also sorted) remainder + *begin = cx_linked_list_sort_merge(loc_prev, loc_next, loc_data, follow_ptr, + 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, + bool follow_ptr_left, + bool follow_ptr_right, + CxListComparator cmp_func +) { + void const *left = begin_left, *right = begin_right; + + while (left != NULL && right != NULL) { + void const *left_data = ll_data_f(left, follow_ptr_left); + void const *right_data = ll_data_f(right, follow_ptr_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 */ + +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; + bool follow_ptr; +} 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->itemsize); + + // 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->itemsize); + + // 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 int cx_ll_insert( + struct cx_list_s *list, + size_t index, + void const *elem +) { + // out-of bounds check + if (index > list->size) return 1; + + // find position efficiently + cx_linked_list_node *node = index == 0 ? NULL : cx_ll_node_at((cx_linked_list *) list, index - 1); + + // perform insert + return cx_ll_insert_at(list, node, elem); +} + +static int cx_ll_add( + struct cx_list_s *list, + void const *elem +) { + return cx_ll_insert(list, list->size, elem); +} + +static int cx_pll_insert( + struct cx_list_s *list, + size_t index, + void const *elem +) { + return cx_ll_insert(list, index, &elem); +} + +static int cx_pll_add( + struct cx_list_s *list, + void const *elem +) { + return cx_ll_insert(list, list->size, &elem); +} + +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; + + // 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_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 void *cx_pll_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 : *(void **) node->payload; +} + +static size_t cx_ll_find( + struct cx_list_s const *list, + void const *elem +) { + cx_linked_list *ll = (cx_linked_list *) list; + return cx_linked_list_find(((cx_linked_list *) list)->begin, + CX_LL_LOC_NEXT, CX_LL_LOC_DATA, + ll->follow_ptr, 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, + ll->follow_ptr, 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, + left->follow_ptr, right->follow_ptr, list->cmpfunc); +} + +static bool cx_ll_iter_valid(CxIterator const *iter) { + return iter->elem_handle != NULL; +} + +static void cx_ll_iter_next(CxIterator *iter) { + if (iter->remove) { + iter->remove = false; + cx_linked_list *ll = iter->src_handle; + cx_linked_list_node *node = iter->elem_handle; + iter->elem_handle = node->next; + cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end, + CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node); + ll->base.size--; + cxFree(ll->base.allocator, node); + } else { + iter->index++; + cx_linked_list_node *node = iter->elem_handle; + iter->elem_handle = node->next; + } +} + +static void *cx_ll_iter_current(CxIterator const *iter) { + cx_linked_list_node *node = iter->elem_handle; + return node->payload; +} + +static void *cx_pll_iter_current(CxIterator const *iter) { + cx_linked_list_node *node = iter->elem_handle; + return *(void **) node->payload; +} + +static CxIterator cx_ll_iterator( + struct cx_list_s *list, + size_t index +) { + CxIterator iter; + iter.index = index; + iter.src_handle = list; + iter.elem_handle = cx_ll_node_at((cx_linked_list const *) list, index); + iter.valid = cx_ll_iter_valid; + iter.current = cx_ll_iter_current; + iter.next = cx_ll_iter_next; + iter.remove = false; + return iter; +} + +static CxIterator cx_pll_iterator( + struct cx_list_s *list, + size_t index +) { + CxIterator iter = cx_ll_iterator(list, index); + iter.current = cx_pll_iter_current; + return iter; +} + +static int cx_ll_insert_iter( + CxIterator *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(list, list->size, elem); + iter->index = list->size; + return result; + } +} + +static int cx_pll_insert_iter( + CxIterator *iter, + void const *elem, + int prepend +) { + return cx_ll_insert_iter(iter, &elem, prepend); +} + +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_add, + cx_ll_insert, + cx_ll_insert_iter, + cx_ll_remove, + cx_ll_at, + cx_ll_find, + cx_ll_sort, + cx_ll_compare, + cx_ll_reverse, + cx_ll_iterator +}; + +static cx_list_class cx_pointer_linked_list_class = { + cx_ll_destructor, + cx_pll_add, + cx_pll_insert, + cx_pll_insert_iter, + cx_ll_remove, + cx_pll_at, + cx_ll_find, + cx_ll_sort, + cx_ll_compare, + cx_ll_reverse, + cx_pll_iterator, +}; + +CxList *cxLinkedListCreate( + CxAllocator const *allocator, + CxListComparator comparator, + size_t item_size +) { + cx_linked_list *list = cxCalloc(allocator, 1, sizeof(cx_linked_list)); + if (list == NULL) return NULL; + + list->follow_ptr = false; + list->base.cl = &cx_linked_list_class; + list->base.allocator = allocator; + list->base.cmpfunc = comparator; + list->base.itemsize = item_size; + list->base.capacity = SIZE_MAX; + + return (CxList *) list; +} + +CxList *cxPointerLinkedListCreate( + CxAllocator const *allocator, + CxListComparator comparator +) { + cx_linked_list *list = cxCalloc(allocator, 1, sizeof(cx_linked_list)); + if (list == NULL) return NULL; + + list->follow_ptr = true; + list->base.cl = &cx_pointer_linked_list_class; + list->base.allocator = allocator; + list->base.cmpfunc = comparator; + list->base.itemsize = sizeof(void *); + list->base.capacity = SIZE_MAX; + + return (CxList *) list; +} + +CxList *cxLinkedListFromArray( + CxAllocator const *allocator, + CxListComparator comparator, + size_t item_size, + size_t num_items, + void const *array +) { + CxList *list = cxLinkedListCreate(allocator, comparator, item_size); + if (list == NULL) return NULL; + cx_for_n (i, num_items) { + if (0 != cxListAdd(list, ((const unsigned char *) array) + i * item_size)) { + cx_ll_destructor(list); + cxFree(allocator, list); + return NULL; + } + } + return list; +}
--- a/src/ucx/list.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/ucx/list.c Sun Nov 06 15:53:32 2022 +0100 @@ -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,403 +26,28 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include "ucx/list.h" - -UcxList *ucx_list_clone(const UcxList *l, copy_func fnc, void *data) { - return ucx_list_clone_a(ucx_default_allocator(), l, fnc, data); -} - -UcxList *ucx_list_clone_a(UcxAllocator *alloc, const 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; - } - 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); -} - -void ucx_list_free(UcxList *l) { - ucx_list_free_a(ucx_default_allocator(), l); -} - -void ucx_list_free_a(UcxAllocator *alloc, UcxList *l) { - UcxList *e = l, *f; - while (e != NULL) { - f = e; - e = e->next; - alfree(alloc, f); - } -} - -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); -} - -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); -} +#include "cx/list.h" -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; -} - -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; - } -} - -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; +void cxListDestroy(CxList *list) { + switch (list->content_destructor_type) { + case CX_DESTRUCTOR_SIMPLE: { + CxIterator iter = cxListBegin(list); + cx_foreach(void*, elem, iter) { + list->simple_destructor(elem); + } + break; } - list = list->next; - index++; - } - return -1; -} - -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(const 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; + case CX_DESTRUCTOR_ADVANCED: { + CxIterator iter = cxListBegin(list); + cx_foreach(void*, elem, iter) { + list->advanced_destructor.func(list->advanced_destructor.data, elem); } - } else { - if (elem == e->data) { - return index; - } + break; } - index++; - } - return -1; -} - -int ucx_list_contains(const 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++; + case CX_DESTRUCTOR_NONE: + break; // nothing } - return s; -} - -static UcxList *ucx_list_sort_merge(size_t 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; - size_t n = 0; - while (lc && lc != le && rc != re) { - if (fnc(lc->data, rc->data, data) <= 0) { - sorted[n] = lc; - lc = lc->next; - } else { - sorted[n] = rc; - rc = rc->next; - } - 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; - size_t 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; - size_t 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; - } -} - -UcxList *ucx_list_first(const UcxList *l) { - if (!l) { - return NULL; - } - - const UcxList *e = l; - while (e->prev) { - e = e->prev; - } - return (UcxList *)e; + list->cl->destructor(list); + cxFree(list->allocator, list); } - -UcxList *ucx_list_remove(UcxList *l, UcxList *e) { - return ucx_list_remove_a(ucx_default_allocator(), l, e); -} - -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; -} - - -static UcxList* ucx_list_setoperation_a(UcxAllocator *allocator, - UcxList const *left, UcxList const *right, - cmp_func cmpfnc, void* cmpdata, - copy_func cpfnc, void* cpdata, - int op) { - - UcxList *res = NULL; - UcxList *cur = NULL; - const UcxList *src = left; - - do { - UCX_FOREACH(node, src) { - void* elem = node->data; - if ( - (op == 0 && !ucx_list_contains(res, elem, cmpfnc, cmpdata)) || - (op == 1 && ucx_list_contains(right, elem, cmpfnc, cmpdata)) || - (op == 2 && !ucx_list_contains(right, elem, cmpfnc, cmpdata))) { - UcxList *nl = almalloc(allocator, sizeof(UcxList)); - nl->prev = cur; - nl->next = NULL; - if (cpfnc) { - nl->data = cpfnc(elem, cpdata); - } else { - nl->data = elem; - } - if (cur != NULL) - cur->next = nl; - cur = nl; - if (res == NULL) - res = cur; - } - } - if (op == 0 && src == left) - src = right; - else - src = NULL; - } while (src != NULL); - - return res; -} - -UcxList* ucx_list_union(UcxList const *left, UcxList const *right, - cmp_func cmpfnc, void* cmpdata, - copy_func cpfnc, void* cpdata) { - return ucx_list_union_a(ucx_default_allocator(), - left, right, cmpfnc, cmpdata, cpfnc, cpdata); -} - -UcxList* ucx_list_union_a(UcxAllocator *allocator, - UcxList const *left, UcxList const *right, - cmp_func cmpfnc, void* cmpdata, - copy_func cpfnc, void* cpdata) { - - return ucx_list_setoperation_a(allocator, left, right, - cmpfnc, cmpdata, cpfnc, cpdata, 0); -} - -UcxList* ucx_list_intersection(UcxList const *left, UcxList const *right, - cmp_func cmpfnc, void* cmpdata, - copy_func cpfnc, void* cpdata) { - return ucx_list_intersection_a(ucx_default_allocator(), left, right, - cmpfnc, cmpdata, cpfnc, cpdata); -} - -UcxList* ucx_list_intersection_a(UcxAllocator *allocator, - UcxList const *left, UcxList const *right, - cmp_func cmpfnc, void* cmpdata, - copy_func cpfnc, void* cpdata) { - - return ucx_list_setoperation_a(allocator, left, right, - cmpfnc, cmpdata, cpfnc, cpdata, 1); -} - -UcxList* ucx_list_difference(UcxList const *left, UcxList const *right, - cmp_func cmpfnc, void* cmpdata, - copy_func cpfnc, void* cpdata) { - return ucx_list_difference_a(ucx_default_allocator(), left, right, - cmpfnc, cmpdata, cpfnc, cpdata); -} - -UcxList* ucx_list_difference_a(UcxAllocator *allocator, - UcxList const *left, UcxList const *right, - cmp_func cmpfnc, void* cmpdata, - copy_func cpfnc, void* cpdata) { - - return ucx_list_setoperation_a(allocator, left, right, - cmpfnc, cmpdata, cpfnc, cpdata, 2); -}
--- a/src/ucx/logging.c Wed Nov 02 19:19:01 2022 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,117 +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 <stdlib.h> -#include <string.h> -#include <stdarg.h> -#include <time.h> - -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) { - char *fpart = strrchr(file, '/'); - if (fpart) file = fpart+1; - fpart = strrchr(file, '\\'); - if (fpart) file = fpart+1; - 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); - } -}
--- a/src/ucx/map.c Wed Nov 02 19:19:01 2022 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,402 +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 <stdlib.h> -#include <string.h> - -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 const *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 const *map, copy_func fnc, void *data) { - return ucx_map_clone_a(ucx_default_allocator(), map, fnc, data); -} - -UcxMap *ucx_map_clone_a(UcxAllocator *allocator, - UcxMap const *map, copy_func fnc, void *data) { - size_t bs = (map->count * 5) >> 1; - UcxMap *newmap = ucx_map_new_a(allocator, 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 const *map, UcxKey key) { - return ucx_map_get_and_remove((UcxMap *)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 const *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; -} - -UcxMap* ucx_map_union(const UcxMap *first, const UcxMap *second, - copy_func cpfnc, void* cpdata) { - return ucx_map_union_a(ucx_default_allocator(), - first, second, cpfnc, cpdata); -} - -UcxMap* ucx_map_union_a(UcxAllocator *allocator, - const UcxMap *first, const UcxMap *second, - copy_func cpfnc, void* cpdata) { - UcxMap* result = ucx_map_clone_a(allocator, first, cpfnc, cpdata); - ucx_map_copy(second, result, cpfnc, cpdata); - return result; -} - -UcxMap* ucx_map_intersection(const UcxMap *first, const UcxMap *second, - copy_func cpfnc, void* cpdata) { - return ucx_map_intersection_a(ucx_default_allocator(), - first, second, cpfnc, cpdata); -} - -UcxMap* ucx_map_intersection_a(UcxAllocator *allocator, - const UcxMap *first, const UcxMap *second, - copy_func cpfnc, void* cpdata) { - UcxMap *result = ucx_map_new_a(allocator, first->size < second->size ? - first->size : second->size); - - UcxMapIterator iter = ucx_map_iterator(first); - void* value; - UCX_MAP_FOREACH(key, value, iter) { - if (ucx_map_get(second, key)) { - ucx_map_put(result, key, cpfnc ? cpfnc(value, cpdata) : value); - } - } - - return result; -} - -UcxMap* ucx_map_difference(const UcxMap *first, const UcxMap *second, - copy_func cpfnc, void* cpdata) { - return ucx_map_difference_a(ucx_default_allocator(), - first, second, cpfnc, cpdata); -} - -UcxMap* ucx_map_difference_a(UcxAllocator *allocator, - const UcxMap *first, const UcxMap *second, - copy_func cpfnc, void* cpdata) { - - UcxMap *result = ucx_map_new_a(allocator, first->size - second->count); - - UcxMapIterator iter = ucx_map_iterator(first); - void* value; - UCX_MAP_FOREACH(key, value, iter) { - if (!ucx_map_get(second, key)) { - ucx_map_put(result, key, cpfnc ? cpfnc(value, cpdata) : value); - } - } - - ucx_map_rehash(result); - return result; -} \ No newline at end of file
--- a/src/ucx/mempool.c Wed Nov 02 19:19:01 2022 +0100 +++ /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 <stdlib.h> -#include <string.h> -#include <stdio.h> -#ifdef __cplusplus -#define __STDC_FORMAT_MACROS -#endif -#include <inttypes.h> - -/** 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 <code>&c</code> 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 ; i<pool->ndata ; 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 ; i<pool->ndata ; 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); -} -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ucx/printf.c Sun Nov 06 15:53:32 2022 +0100 @@ -0,0 +1,109 @@ +/* + * 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 <stdio.h> +#include <string.h> + +#define UCX_PRINTF_BUFSIZE 256 + +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[UCX_PRINTF_BUFSIZE]; + 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 { + 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 *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 *a, char const *fmt, va_list ap) { + cxmutstr s; + s.ptr = NULL; + s.length = 0; + char buf[UCX_PRINTF_BUFSIZE]; + 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 = 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; +} +
--- a/src/ucx/properties.c Wed Nov 02 19:19:01 2022 +0100 +++ /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 <stdio.h> -#include <stdlib.h> -#include <string.h> - -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(;i<len;i++) { - c = buf[i]; - if(c == comment1 || c == comment2 || c == comment3) { - if(comment_index == 0) { - comment_index = i; - has_comment = 1; - } - } else if(c == delimiter) { - if(delimiter_index == 0 && !has_comment) { - delimiter_index = i; - } - } else if(c == '\n') { - break; - } - } - - if(c != '\n') { - // we don't have enough data for a line - // store remaining bytes in temporary buffer for next round - parser->tmpcap = 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; -} -
--- a/src/ucx/stack.c Wed Nov 02 19:19:01 2022 +0100 +++ /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 <string.h> - -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; -}
--- a/src/ucx/string.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/ucx/string.c Sun Nov 06 15:53:32 2022 +0100 @@ -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,63 +26,71 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include "ucx/string.h" +#include "cx/string.h" +#include "cx/utils.h" -#include "ucx/allocator.h" - -#include <stdlib.h> #include <string.h> #include <stdarg.h> -#include <stdint.h> #include <ctype.h> #ifndef _WIN32 + #include <strings.h> /* for strncasecmp() */ + #endif /* _WIN32 */ -sstr_t sstr(char *cstring) { - sstr_t string; - string.ptr = cstring; - string.length = strlen(cstring); - return string; +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)}; +} + +cxstring cx_strn( + const char *cstring, + size_t length +) { + return (cxstring) {cstring, length}; } -scstr_t scstr(const char *cstring) { - scstr_t string; - string.ptr = cstring; - string.length = strlen(cstring); - return string; +cxstring cx_strcast(cxmutstr str) { + return (cxstring) {str.ptr, str.length}; } -scstr_t scstrn(const char *cstring, size_t length) { - scstr_t string; - string.ptr = cstring; - string.length = length; - return string; +void cx_strfree(cxmutstr *str) { + free(str->ptr); + str->ptr = NULL; + str->length = 0; } +void cx_strfree_a( + CxAllocator *alloc, + cxmutstr *str +) { + cxFree(alloc, str->ptr); + str->ptr = NULL; + str->length = 0; +} -size_t scstrnlen(size_t n, ...) { - if (n == 0) return 0; - +size_t cx_strlen( + size_t count, + ... +) { + if (count == 0) return 0; + va_list ap; - va_start(ap, n); - + va_start(ap, count); size_t size = 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; - } + cx_for_n(i, count) { + cxstring str = va_arg(ap, cxstring); size += str.length; } va_end(ap); @@ -90,257 +98,196 @@ return size; } -static sstr_t sstrvcat_a( - UcxAllocator *a, +cxmutstr cx_strcat_a( + CxAllocator *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<count;i++) { - scstr_t s = va_arg (ap, scstr_t); + size_t slen = 0; + cx_for_n(i, count) { + cxstring s = va_arg (ap, cxstring); strings[i] = s; - if(((size_t)-1) - s.length < slen) { - error = 1; - break; - } slen += s.length; } - if(error) { - free(strings); - return str; - } - + // create new string - str.ptr = (char*) almalloc(a, slen + 1); - str.length = slen; - if(!str.ptr) { - free(strings); - str.length = 0; - return str; - } - + cxmutstr result; + result.ptr = cxMalloc(alloc, slen + 1); + result.length = slen; + if (result.ptr == NULL) abort(); + // concatenate strings size_t pos = 0; - for (size_t i=0;i<count;i++) { - scstr_t s = strings[i]; - memcpy(str.ptr + pos, s.ptr, s.length); + cx_for_n(i, count) { + cxstring s = strings[i]; + memcpy(result.ptr + pos, s.ptr, s.length); pos += s.length; } - - str.ptr[str.length] = '\0'; - + + // terminate string + result.ptr[result.length] = '\0'; + + // free temporary array free(strings); - - return str; + + return result; } -sstr_t scstrcat(size_t count, scstr_t s1, ...) { - va_list ap; - va_start(ap, s1); - sstr_t s = sstrvcat_a(ucx_default_allocator(), count, s1, ap); - va_end(ap); - return s; +cxstring cx_strsubs( + cxstring string, + size_t start +) { + return cx_strsubsl(string, start, string.length - start); } -sstr_t scstrcat_a(UcxAllocator *a, size_t count, scstr_t s1, ...) { - va_list ap; - va_start(ap, s1); - sstr_t s = sstrvcat_a(a, count, s1, ap); - va_end(ap); - return s; +cxmutstr cx_strsubs_m( + cxmutstr string, + size_t start +) { + return cx_strsubsl_m(string, start, string.length - start); } -static int ucx_substring( - size_t str_length, +cxstring cx_strsubsl( + cxstring string, size_t start, - size_t length, - size_t *newlen, - size_t *newpos) -{ - *newlen = 0; - *newpos = 0; - - if(start > 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); -} - -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; - } - return ret; +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}; } -scstr_t scstrsubs(scstr_t string, size_t start) { - return scstrsubsl(string, start, string.length-start); +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 (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; -} - - -static int ucx_strchr(const char *str, size_t length, int chr, size_t *pos) { - for(size_t i=0;i<length;i++) { - if(str[i] == chr) { - *pos = i; - return 1; - } - } - return 0; +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_strrchr(const char *str, size_t length, int chr, size_t *pos) { - if(length > 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; + return (cxstring) {NULL, 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); -} - -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); +cxmutstr cx_strrchr_m( + cxmutstr string, + int chr +) { + cxstring result = cx_strrchr(cx_strcast(string), chr); + return (cxmutstr) {(char *) result.ptr, result.length}; } -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); -} - -#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); +#define STRSTR_SBO_BUFLEN 512 +cxstring cx_strstr( + cxstring haystack, + cxstring needle +) { + if (needle.length == 0) { + return haystack; + } -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; + /* optimize for single-char needles */ + if (needle.length == 1) { + return cx_strchr(haystack, *needle.ptr); } - - const char *result = NULL; - size_t resultlen = 0; - + /* * 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 */ + + /* local prefix table */ + size_t s_prefix_table[STRSTR_SBO_BUFLEN]; + + /* check needle 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; - + bool useheap = needle.length >= STRSTR_SBO_BUFLEN; + 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); + 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); + 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; } } @@ -349,151 +296,132 @@ 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 *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 *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) { @@ -503,7 +431,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); @@ -517,173 +448,123 @@ } } -sstr_t scstrdup(scstr_t s) { - return sstrdup_a(ucx_default_allocator(), s); +cxmutstr cx_strdup_a( + CxAllocator *allocator, + cxstring string +) { + cxmutstr result = { + cxMalloc(allocator, string.length + 1), + string.length + }; + if (result.ptr == NULL) { + result.length = 0; + return result; + } + memcpy(result.ptr, string.ptr, string.length); + result.ptr[string.length] = '\0'; + return result; } -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; +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--; } - - return newstring; + 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; +} -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_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; } -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_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 } -scstr_t scstrtrim(scstr_t string) { - scstr_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 } -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; +void cx_strlower(cxmutstr string) { + cx_for_n(i, string.length) { + string.ptr[i] = (char) tolower(string.ptr[i]); } } -int scstrsuffix(scstr_t string, scstr_t suffix) { - if (string.length == 0) { - return suffix.length == 0; - } - if (suffix.length == 0) { - return 1; - } - - if (suffix.length > string.length) { - return 0; - } else { - return memcmp(string.ptr+string.length-suffix.length, - suffix.ptr, suffix.length) == 0; - } -} - -int scstrcaseprefix(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 { - scstr_t subs = scstrsubsl(string, 0, prefix.length); - return scstrcasecmp(subs, prefix) == 0; - } -} - -int scstrcasesuffix(scstr_t string, scstr_t suffix) { - if (string.length == 0) { - return suffix.length == 0; - } - if (suffix.length == 0) { - return 1; +void cx_strupper(cxmutstr string) { + cx_for_n(i, string.length) { + string.ptr[i] = (char) toupper(string.ptr[i]); } - - if (suffix.length > string.length) { - return 0; - } else { - scstr_t subs = scstrsubs(string, string.length-suffix.length); - return scstrcasecmp(subs, suffix) == 0; - } -} - -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; -} - -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; -} - -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; -} - -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]); - } - return ret; } #define REPLACE_INDEX_BUFFER_MAX 100 -struct scstrreplace_ibuf { - size_t* buf; - unsigned int len; /* small indices */ - struct scstrreplace_ibuf* next; +struct cx_strreplace_ibuf { + size_t *buf; + struct cx_strreplace_ibuf *next; + unsigned int len; }; -static void scstrrepl_free_ibuf(struct scstrreplace_ibuf *buf) { +static void cx_strrepl_free_ibuf(struct cx_strreplace_ibuf *buf) { while (buf) { - struct scstrreplace_ibuf *next = buf->next; + struct cx_strreplace_ibuf *next = buf->next; free(buf->buf); free(buf); buf = next; } } -sstr_t scstrreplacen_a(UcxAllocator *allocator, scstr_t str, - scstr_t pattern, scstr_t replacement, size_t replmax) { +cxmutstr cx_strreplacen_a( + CxAllocator *allocator, + cxstring str, + cxstring pattern, + cxstring replacement, + size_t replmax +) { if (pattern.length == 0 || pattern.length > str.length || replmax == 0) - return sstrdup(str); + return cx_strdup_a(allocator, str); /* Compute expected buffer length */ size_t ibufmax = str.length / pattern.length; @@ -693,34 +574,34 @@ } /* Allocate first index buffer */ - struct scstrreplace_ibuf *firstbuf, *curbuf; - firstbuf = curbuf = calloc(1, sizeof(struct scstrreplace_ibuf)); - if (!firstbuf) return sstrn(NULL, 0); + 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 sstrn(NULL, 0); + return cx_mutstrn(NULL, 0); } /* Search occurrences */ - scstr_t searchstr = str; + cxstring searchstr = str; size_t found = 0; do { - scstr_t match = scstrscstr(searchstr, pattern); + cxstring match = cx_strstr(searchstr, pattern); if (match.length > 0) { /* Allocate next buffer in chain, if required */ if (curbuf->len == ibuflen) { - struct scstrreplace_ibuf *nextbuf = - calloc(1, sizeof(struct scstrreplace_ibuf)); + struct cx_strreplace_ibuf *nextbuf = + calloc(1, sizeof(struct cx_strreplace_ibuf)); if (!nextbuf) { - scstrrepl_free_ibuf(firstbuf); - return sstrn(NULL, 0); + cx_strrepl_free_ibuf(firstbuf); + return cx_mutstrn(NULL, 0); } nextbuf->buf = calloc(ibuflen, sizeof(size_t)); if (!nextbuf->buf) { free(nextbuf); - scstrrepl_free_ibuf(firstbuf); - return sstrn(NULL, 0); + cx_strrepl_free_ibuf(firstbuf); + return cx_mutstrn(NULL, 0); } curbuf->next = nextbuf; curbuf = nextbuf; @@ -738,7 +619,7 @@ } while (searchstr.length > 0 && found < replmax); /* Allocate result string */ - sstr_t result; + cxmutstr result; { ssize_t adjlen = (ssize_t) replacement.length - (ssize_t) pattern.length; size_t rcount = 0; @@ -748,24 +629,24 @@ curbuf = curbuf->next; } while (curbuf); result.length = str.length + rcount * adjlen; - result.ptr = almalloc(allocator, result.length); + result.ptr = cxMalloc(allocator, result.length + 1); if (!result.ptr) { - scstrrepl_free_ibuf(firstbuf); - return sstrn(NULL, 0); + cx_strrepl_free_ibuf(firstbuf); + return cx_mutstrn(NULL, 0); } } /* Build result string */ curbuf = firstbuf; size_t srcidx = 0; - char* destptr = result.ptr; + 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); + memcpy(destptr, str.ptr + srcidx, srclen); destptr += srclen; srcidx += srclen; } @@ -777,31 +658,15 @@ } curbuf = curbuf->next; } while (curbuf); - memcpy(destptr, str.ptr+srcidx, str.length-srcidx); + memcpy(destptr, str.ptr + srcidx, str.length - srcidx); + + /* Result is guaranteed to be zero-terminated */ + result.ptr[result.length] = '\0'; /* Free index buffer */ - scstrrepl_free_ibuf(firstbuf); + cx_strrepl_free_ibuf(firstbuf); return result; } -sstr_t scstrreplacen(scstr_t str, scstr_t pattern, - scstr_t replacement, size_t replmax) { - return scstrreplacen_a(ucx_default_allocator(), - str, pattern, replacement, replmax); -} - -// type adjustment functions -scstr_t ucx_sc2sc(scstr_t str) { - return str; -} -scstr_t ucx_ss2sc(sstr_t str) { - scstr_t cs; - cs.ptr = str.ptr; - cs.length = str.length; - return cs; -} -scstr_t ucx_ss2c_s(scstr_t c) { - return c; -}
--- a/src/ucx/test.c Wed Nov 02 19:19:01 2022 +0100 +++ /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); -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ucx/tree.c Sun Nov 06 15:53:32 2022 +0100 @@ -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; + } +}
--- a/src/ucx/ucx.c Wed Nov 02 19:19:01 2022 +0100 +++ /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. - * <p> - * Latest available source:<br> - * <a href="https://sourceforge.net/projects/ucx/files/"> - * https://sourceforge.net/projects/ucx/files/</a> - * </p> - * - * <p> - * Repositories:<br> - * <a href="https://sourceforge.net/p/ucx/code"> - * https://sourceforge.net/p/ucx/code</a> - * - or - - * <a href="https://develop.uap-core.de/hg/ucx"> - * https://develop.uap-core.de/hg/ucx</a> - * </p> - * - * <h2>LICENCE</h2> - * - * 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; - } -} -
--- a/src/ucx/ucx/allocator.h Wed Nov 02 19:19:01 2022 +0100 +++ /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 <code>malloc(), calloc(), realloc()</code> and <code>free()</code>. - * - * 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 <code>malloc()</code> function. - * @see UcxAllocator - */ -typedef void*(*ucx_allocator_malloc)(void *pool, size_t n); - -/** - * A function pointer to the allocators <code>calloc()</code> function. - * @see UcxAllocator - */ -typedef void*(*ucx_allocator_calloc)(void *pool, size_t n, size_t size); - -/** - * A function pointer to the allocators <code>realloc()</code> function. - * @see UcxAllocator - */ -typedef void*(*ucx_allocator_realloc)(void *pool, void *data, size_t n); - -/** - * A function pointer to the allocators <code>free()</code> 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 <code>malloc()</code> function for this allocator. - */ - ucx_allocator_malloc malloc; - /** - * The <code>calloc()</code> function for this allocator. - */ - ucx_allocator_calloc calloc; - /** - * The <code>realloc()</code> function for this allocator. - */ - ucx_allocator_realloc realloc; - /** - * The <code>free()</code> 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 <code>malloc()</code> function. - * @param ignore ignored (may be used by allocators for pooled memory) - * @param n argument passed to <code>malloc()</code> - * @return return value of <code>malloc()</code> - */ -void *ucx_default_malloc(void *ignore, size_t n); -/** - * A wrapper for the standard libc <code>calloc()</code> function. - * @param ignore ignored (may be used by allocators for pooled memory) - * @param n argument passed to <code>calloc()</code> - * @param size argument passed to <code>calloc()</code> - * @return return value of <code>calloc()</code> - */ -void *ucx_default_calloc(void *ignore, size_t n, size_t size); -/** - * A wrapper for the standard libc <code>realloc()</code> function. - * @param ignore ignored (may be used by allocators for pooled memory) - * @param data argumend passed to <code>realloc()</code> - * @param n argument passed to <code>realloc()</code> - * @return return value of <code>realloc()</code> - */ -void *ucx_default_realloc(void *ignore, void *data, size_t n); -/** - * A wrapper for the standard libc <code>free()</code> function. - * @param ignore ignored (may be used by allocators for pooled memory) - * @param data argument passed to <code>free()</code> - */ -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 <code>struct</code> 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 */ -
--- a/src/ucx/ucx/array.h Wed Nov 02 19:19:01 2022 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,460 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2019 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. - */ -/** - * Dynamically allocated array implementation. - * - * @file array.h - * @author Mike Becker - * @author Olaf Wintermann - */ - -#ifndef UCX_ARRAY_H -#define UCX_ARRAY_H - -#include "ucx.h" -#include "allocator.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * UCX array type. - */ -typedef struct { - /** - * The current capacity of the array. - */ - size_t capacity; - /** - * The actual number of elements in the array. - */ - size_t size; - /** - * The size of an individual element in bytes. - */ - size_t elemsize; - /** - * A pointer to the data. - */ - void* data; - /** - * The allocator used for the data. - */ - UcxAllocator* allocator; -} UcxArray; - -/** - * Sets an element in an arbitrary user defined array. - * The data is copied from the specified data location. - * - * If the capacity is insufficient, the array is automatically reallocated and - * the possibly new pointer is stored in the <code>array</code> argument. - * - * On reallocation the capacity of the array is doubled until it is sufficient. - * The new capacity is stored back to <code>capacity</code>. - * - * @param array a pointer to location of the array pointer - * @param capacity a pointer to the capacity - * @param elmsize the size of each element - * @param idx the index of the element to set - * @param data a pointer to the element data - * @return zero on success or non-zero on error (errno will be set) - */ -#define ucx_array_util_set(array, capacity, elmsize, idx, data) \ - ucx_array_util_set_a(ucx_default_allocator(), (void**)(array), capacity, \ - elmsize, idx, data) - -/** - * Sets an element in an arbitrary user defined array. - * The data is copied from the specified data location. - * - * If the capacity is insufficient, the array is automatically reallocated - * using the specified allocator and the possibly new pointer is stored in - * the <code>array</code> argument. - * - * On reallocation the capacity of the array is doubled until it is sufficient. - * The new capacity is stored back to <code>capacity</code>. - * - * @param alloc the allocator that shall be used to reallocate the array - * @param array a pointer to location of the array pointer - * @param capacity a pointer to the capacity - * @param elmsize the size of each element - * @param idx the index of the element to set - * @param data a pointer to the element data - * @return zero on success or non-zero on error (errno will be set) - */ -int ucx_array_util_set_a(UcxAllocator* alloc, void** array, size_t* capacity, - size_t elmsize, size_t idx, void* data); - -/** - * Stores a pointer in an arbitrary user defined array. - * The element size of the array must be sizeof(void*). - * - * If the capacity is insufficient, the array is automatically reallocated and - * the possibly new pointer is stored in the <code>array</code> argument. - * - * On reallocation the capacity of the array is doubled until it is sufficient. - * The new capacity is stored back to <code>capacity</code>. - * - * @param array a pointer to location of the array pointer - * @param capacity a pointer to the capacity - * @param idx the index of the element to set - * @param ptr the pointer to store - * @return zero on success or non-zero on error (errno will be set) - */ -#define ucx_array_util_setptr(array, capacity, idx, ptr) \ - ucx_array_util_setptr_a(ucx_default_allocator(), (void**)(array), \ - capacity, idx, ptr) - -/** - * Stores a pointer in an arbitrary user defined array. - * The element size of the array must be sizeof(void*). - * - * If the capacity is insufficient, the array is automatically reallocated - * using the specified allocator and the possibly new pointer is stored in - * the <code>array</code> argument. - * - * On reallocation the capacity of the array is doubled until it is sufficient. - * The new capacity is stored back to <code>capacity</code>. - * - * @param alloc the allocator that shall be used to reallocate the array - * @param array a pointer to location of the array pointer - * @param capacity a pointer to the capacity - * @param idx the index of the element to set - * @param ptr the pointer to store - * @return zero on success or non-zero on error (errno will be set) - */ -int ucx_array_util_setptr_a(UcxAllocator* alloc, void** array, size_t* capacity, - size_t idx, void* ptr); - - -/** - * Creates a new UCX array with the given capacity and element size. - * @param capacity the initial capacity - * @param elemsize the element size - * @return a pointer to a new UCX array structure - */ -UcxArray* ucx_array_new(size_t capacity, size_t elemsize); - -/** - * Creates a new UCX array using the specified allocator. - * - * @param capacity the initial capacity - * @param elemsize the element size - * @param allocator the allocator to use - * @return a pointer to new UCX array structure - */ -UcxArray* ucx_array_new_a(size_t capacity, size_t elemsize, - UcxAllocator* allocator); - -/** - * Initializes a UCX array structure with the given capacity and element size. - * The structure must be uninitialized as the data pointer will be overwritten. - * - * @param array the structure to initialize - * @param capacity the initial capacity - * @param elemsize the element size - */ -void ucx_array_init(UcxArray* array, size_t capacity, size_t elemsize); - -/** - * Initializes a UCX array structure using the specified allocator. - * The structure must be uninitialized as the data pointer will be overwritten. - * - * @param array the structure to initialize - * @param capacity the initial capacity - * @param elemsize the element size - * @param allocator the allocator to use - */ -void ucx_array_init_a(UcxArray* array, size_t capacity, size_t elemsize, - UcxAllocator* allocator); - -/** - * Creates an shallow copy of an array. - * - * This function clones the specified array by using memcpy(). - * If the destination capacity is insufficient, an automatic reallocation is - * attempted. - * - * Note: if the destination array is uninitialized, the behavior is undefined. - * - * @param dest the array to copy to - * @param src the array to copy from - * @return zero on success, non-zero on reallocation failure. - */ -int ucx_array_clone(UcxArray* dest, UcxArray const* src); - - -/** - * Compares two UCX arrays element-wise by using a compare function. - * - * Elements of the two specified arrays 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. - * - * This function always returns zero, if the element sizes of the arrays do - * not match and performs no comparisons in this case. - * - * @param array1 the first array - * @param array2 the second array - * @param cmpfnc the compare function - * @param data additional data for the compare function - * @return 1, if and only if the two arrays equal element-wise, 0 otherwise - */ -int ucx_array_equals(UcxArray const *array1, UcxArray const *array2, - cmp_func cmpfnc, void* data); - -/** - * Destroys the array. - * - * The data is freed and both capacity and count are reset to zero. - * If the array structure itself has been dynamically allocated, it has to be - * freed separately. - * - * @param array the array to destroy - */ -void ucx_array_destroy(UcxArray *array); - -/** - * Destroys and frees the array. - * - * @param array the array to free - */ -void ucx_array_free(UcxArray *array); - -/** - * Inserts elements at the end of the array. - * - * This is an O(1) operation. - * The array will automatically grow, if the capacity is exceeded. - * If a pointer to data is provided, the data is copied into the array with - * memcpy(). Otherwise the new elements are completely zeroed. - * - * @param array a pointer the array where to append the data - * @param data a pointer to the data to insert (may be <code>NULL</code>) - * @param count number of elements to copy from data (if data is - * <code>NULL</code>, zeroed elements are appended) - * @return zero on success, non-zero if a reallocation was necessary but failed - * @see ucx_array_set_from() - * @see ucx_array_append() - */ -int ucx_array_append_from(UcxArray *array, void *data, size_t count); - - -/** - * Inserts elements at the beginning of the array. - * - * This is an expensive operation, because the contents must be moved. - * If there is no particular reason to prepend data, you should use - * ucx_array_append_from() instead. - * - * @param array a pointer the array where to prepend the data - * @param data a pointer to the data to insert (may be <code>NULL</code>) - * @param count number of elements to copy from data (if data is - * <code>NULL</code>, zeroed elements are inserted) - * @return zero on success, non-zero if a reallocation was necessary but failed - * @see ucx_array_append_from() - * @see ucx_array_set_from() - * @see ucx_array_prepend() - */ -int ucx_array_prepend_from(UcxArray *array, void *data, size_t count); - - -/** - * Sets elements starting at the specified index. - * - * If the any index is out of bounds, the array automatically grows. - * The pointer to the data may be NULL, in which case the elements are zeroed. - * - * @param array a pointer the array where to set the data - * @param index the index of the element to set - * @param data a pointer to the data to insert (may be <code>NULL</code>) - * @param count number of elements to copy from data (if data is - * <code>NULL</code>, the memory in the array is zeroed) - * @return zero on success, non-zero if a reallocation was necessary but failed - * @see ucx_array_append_from() - * @see ucx_array_set() - */ -int ucx_array_set_from(UcxArray *array, size_t index, void *data, size_t count); - -/** - * Concatenates two arrays. - * - * The contents of the second array are appended to the first array in one - * single operation. The second array is otherwise left untouched. - * - * The first array may grow automatically. If this fails, both arrays remain - * unmodified. - * - * @param array1 first array - * @param array2 second array - * @return zero on success, non-zero if reallocation was necessary but failed - * or the element size does not match - */ -int ucx_array_concat(UcxArray *array1, const UcxArray *array2); - -/** - * Returns a pointer to the array element at the specified index. - * - * @param array the array to retrieve the element from - * @param index index of the element to return - * @return a pointer to the element at the specified index or <code>NULL</code>, - * if the index is greater than the array size - */ -void *ucx_array_at(UcxArray const* array, size_t index); - -/** - * 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, memcmp() is used. - * - * If the array contains the data more than once, the index of the first - * occurrence is returned. - * If the array does not contain the data, the size of array is returned. - * - * @param array the array 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 the size of - * the array, if the data is not found in this array - */ -size_t ucx_array_find(UcxArray const *array, void *elem, - cmp_func cmpfnc, void *data); - -/** - * Checks, if an array contains a specific element. - * - * An element is found, if ucx_array_find() returns a value less than the size. - * - * @param array the array 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 array contains the specified element data - * @see ucx_array_find() - */ -int ucx_array_contains(UcxArray const *array, void *elem, - cmp_func cmpfnc, void *data); - -/** - * Sorts a UcxArray with the best available sort algorithm. - * - * The qsort_r() function is used, if available (glibc, FreeBSD or MacOS). - * The order of arguments is automatically adjusted for the FreeBSD and MacOS - * version of qsort_r(). - * - * If qsort_r() is not available, a merge sort algorithm is used, which is - * guaranteed to use no more additional memory than for exactly one element. - * - * @param array the array to sort - * @param cmpfnc the function that shall be used to compare the element data - * @param data additional data for the cmp_func() or <code>NULL</code> - */ -void ucx_array_sort(UcxArray* array, cmp_func cmpfnc, void *data); - -/** - * Removes an element from the array. - * - * This is in general an expensive operation, because several elements may - * be moved. If the order of the elements is not relevant, use - * ucx_array_remove_fast() instead. - * - * @param array pointer to the array from which the element shall be removed - * @param index the index of the element to remove - */ -void ucx_array_remove(UcxArray *array, size_t index); - -/** - * Removes an element from the array. - * - * This is an O(1) operation, but does not maintain the order of the elements. - * The last element in the array is moved to the location of the removed - * element. - * - * @param array pointer to the array from which the element shall be removed - * @param index the index of the element to remove - */ -void ucx_array_remove_fast(UcxArray *array, size_t index); - -/** - * Shrinks the memory to exactly fit the contents. - * - * After this operation, the capacity equals the size. - * - * @param array a pointer to the array - * @return zero on success, non-zero if reallocation failed - */ -int ucx_array_shrink(UcxArray* array); - -/** - * Sets the capacity of the array. - * - * If the new capacity is smaller than the size of the array, the elements - * are removed and the size is adjusted accordingly. - * - * @param array a pointer to the array - * @param capacity the new capacity - * @return zero on success, non-zero if reallocation failed - */ -int ucx_array_resize(UcxArray* array, size_t capacity); - -/** - * Resizes the array only, if the capacity is insufficient. - * - * If the requested capacity is smaller than the current capacity, this - * function does nothing. - * - * @param array a pointer to the array - * @param capacity the guaranteed capacity - * @return zero on success, non-zero if reallocation failed - */ -int ucx_array_reserve(UcxArray* array, size_t capacity); - -/** - * Resizes the capacity, if the specified number of elements would not fit. - * - * A call to ucx_array_grow(array, count) is effectively the same as - * ucx_array_reserve(array, array->size+count). - * - * @param array a pointer to the array - * @param count the number of elements that should additionally fit - * into the array - * @return zero on success, non-zero if reallocation failed - */ -int ucx_array_grow(UcxArray* array, size_t count); - - -#ifdef __cplusplus -} -#endif - -#endif /* UCX_ARRAY_H */ -
--- a/src/ucx/ucx/avl.h Wed Nov 02 19:19:01 2022 +0100 +++ /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 <inttypes.h> - -#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 (<code>NULL</code>), 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 <code>NULL</code>, 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 <code>NULL</code>, 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 <code>NULL</code> on - * empty trees. - */ -#define UCX_AVL_FIND_CLOSEST 3 - -/** - * Finds a node within the tree. The following modes are supported: - * <ul> - * <li>#UCX_AVL_FIND_EXACT: the same behavior as #ucx_avl_get_node()</li> - * <li>#UCX_AVL_FIND_LOWER_BOUNDED: finds the node whose key is at least - * as large as the specified key</li> - * <li>#UCX_AVL_FIND_UPPER_BOUNDED: finds the node whose key is at most - * as large as the specified key</li> - * <li>#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 <code>NULL</code> on - * empty trees.</li> - * </ul> - * - * 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 <code>NULL</code>, 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 <code>NULL</code>, 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 <code>NULL</code> 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 <code>NULL</code> if - * the given node is the in-order maximum - */ -UcxAVLNode* ucx_avl_succ(UcxAVLNode* node); - -#ifdef __cplusplus -} -#endif - -#endif /* UCX_AVL_H */ -
--- a/src/ucx/ucx/buffer.h Wed Nov 02 19:19:01 2022 +0100 +++ /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 <sys/types.h> -#include <stdio.h> - -#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. - * - * <b>Note:</b> you may provide <code>NULL</code> as argument for - * <code>space</code>. Then this function will allocate the space and enforce - * the #UCX_BUFFER_AUTOFREE flag. - * - * @param space pointer to the memory area, or <code>NULL</code> 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. - * - * <b>Note:</b> 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 <code>shift</code> 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. - * - * <b>Security note:</b> the shifting operation does <em>not</em> erase the - * previously occupied memory cells. You can easily do that manually, e.g. by - * calling <code>memset(buffer->space, 0, shift)</code> for a right shift or - * <code>memset(buffer->size, 0, buffer->capacity-buffer->size)</code> - * 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 <code>whence</code> 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 <code>whence</code> - * @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 <code>memset()</code>. - * - * @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. - * - * <b>Note:</b> 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. - * - * <b>Attention:</b> the argument provided is the number of <i>additional</i> - * bytes the buffer shall hold. It is <b>NOT</b> 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 - * <i>at least</i> 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, <code>EOF</code> 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 <code>int</code> value - * @return the byte that has bean written as <code>int</code> value or - * <code>EOF</code> 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 <code>int</code> value or <code>EOF</code>, 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 <code>sstrn()</code> 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 */ -
--- a/src/ucx/ucx/list.h Wed Nov 02 19:19:01 2022 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,512 +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 <code>UCX_FOREACH</code> 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 = (UcxList*) 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 <code>NULL</code>, if this is the - * last element. - */ - UcxList *next; - /** - * Pointer to the previous list element or <code>NULL</code>, 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 - * <code>NULL</code>) - * @param data additional data for the copy_func() - * @return a pointer to the copy - */ -UcxList *ucx_list_clone(const 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 <code>data</code> 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 - * <code>NULL</code>) - * @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, const 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. - * - * <b>Caution:</b> the argument <b>MUST</b> 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 (<code>NULL</code>), 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 <code>NULL</code> to - * create a new list - * @param data the data to insert - * @return <code>list</code>, if it is not <code>NULL</code> 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 <code>NULL</code> to - * create a new list - * @param data the data to insert - * @return <code>list</code>, if it is not <code>NULL</code> 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 <i>should</i> overwrite the old list pointer by calling - * <code>mylist = ucx_list_prepend(mylist, mydata);</code>. 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 <code>NULL</code> 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 <code>NULL</code> 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 <code>NULL</code>. - * - * This function modifies the references to the next/previous element of - * the last/first element of <code>list1</code>/<code> - * list2</code>. - * - * @param list1 first list - * @param list2 second list - * @return if <code>list1</code> is <code>NULL</code>, <code>list2</code> is - * returned, otherwise <code>list1</code> 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 <code>NULL</code>, 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(const 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(const 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 <b>MUST</b> call this function - * as follows: <code>mylist = ucx_list_sort(mylist, mycmpfnc, mydata);</code>. - * - * @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 - * <i>highly recommended</i> to <i>always</i> update the pointer by calling - * <code>mylist = ucx_list_remove(mylist, myelem);</code>. - * - * @param list the list from which the element shall be removed - * @param element the element to remove - * @return returns the updated list pointer or <code>NULL</code>, 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 <code>NULL</code>, if the list - * @see ucx_list_remove() - */ -UcxList *ucx_list_remove_a(UcxAllocator *allocator, UcxList *list, - UcxList *element); - -/** - * Returns the union of two lists. - * - * The union is a list of unique elements regarding cmpfnc obtained from - * both source lists. - * - * @param left the left source list - * @param right the right source list - * @param cmpfnc a function to compare elements - * @param cmpdata additional data for the compare function - * @param cpfnc a function to copy the elements - * @param cpdata additional data for the copy function - * @return a new list containing the union - */ -UcxList* ucx_list_union(const UcxList *left, const UcxList *right, - cmp_func cmpfnc, void* cmpdata, - copy_func cpfnc, void* cpdata); - -/** - * Returns the union of two lists. - * - * The union is a list of unique elements regarding cmpfnc obtained from - * both source lists. - * - * @param allocator allocates the new list elements - * @param left the left source list - * @param right the right source list - * @param cmpfnc a function to compare elements - * @param cmpdata additional data for the compare function - * @param cpfnc a function to copy the elements - * @param cpdata additional data for the copy function - * @return a new list containing the union - */ -UcxList* ucx_list_union_a(UcxAllocator *allocator, - const UcxList *left, const UcxList *right, - cmp_func cmpfnc, void* cmpdata, - copy_func cpfnc, void* cpdata); - -/** - * Returns the intersection of two lists. - * - * The intersection contains all elements of the left list - * (including duplicates) that can be found in the right list. - * - * @param left the left source list - * @param right the right source list - * @param cmpfnc a function to compare elements - * @param cmpdata additional data for the compare function - * @param cpfnc a function to copy the elements - * @param cpdata additional data for the copy function - * @return a new list containing the intersection - */ -UcxList* ucx_list_intersection(const UcxList *left, const UcxList *right, - cmp_func cmpfnc, void* cmpdata, - copy_func cpfnc, void* cpdata); - -/** - * Returns the intersection of two lists. - * - * The intersection contains all elements of the left list - * (including duplicates) that can be found in the right list. - * - * @param allocator allocates the new list elements - * @param left the left source list - * @param right the right source list - * @param cmpfnc a function to compare elements - * @param cmpdata additional data for the compare function - * @param cpfnc a function to copy the elements - * @param cpdata additional data for the copy function - * @return a new list containing the intersection - */ -UcxList* ucx_list_intersection_a(UcxAllocator *allocator, - const UcxList *left, const UcxList *right, - cmp_func cmpfnc, void* cmpdata, - copy_func cpfnc, void* cpdata); - -/** - * Returns the difference of two lists. - * - * The difference contains all elements of the left list - * (including duplicates) that are not equal to any element of the right list. - * - * @param left the left source list - * @param right the right source list - * @param cmpfnc a function to compare elements - * @param cmpdata additional data for the compare function - * @param cpfnc a function to copy the elements - * @param cpdata additional data for the copy function - * @return a new list containing the difference - */ -UcxList* ucx_list_difference(const UcxList *left, const UcxList *right, - cmp_func cmpfnc, void* cmpdata, - copy_func cpfnc, void* cpdata); - -/** - * Returns the difference of two lists. - * - * The difference contains all elements of the left list - * (including duplicates) that are not equal to any element of the right list. - * - * @param allocator allocates the new list elements - * @param left the left source list - * @param right the right source list - * @param cmpfnc a function to compare elements - * @param cmpdata additional data for the compare function - * @param cpfnc a function to copy the elements - * @param cpdata additional data for the copy function - * @return a new list containing the difference - */ -UcxList* ucx_list_difference_a(UcxAllocator *allocator, - const UcxList *left, const UcxList *right, - cmp_func cmpfnc, void* cmpdata, - copy_func cpfnc, void* cpdata); - -#ifdef __cplusplus -} -#endif - -#endif /* UCX_LIST_H */ -
--- a/src/ucx/ucx/logging.h Wed Nov 02 19:19:01 2022 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,253 +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 <stdio.h> - -#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: <code>"%F %T %z "</code>). - * @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 <code>unsigned int</code> for the key and - * <code>const char*</code> 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 <code>format</code> 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: - * - * <code>[LEVEL] [TIMESTAMP] [SOURCEFILE]:[LINENO] message</code> - * - * The source file name is reduced to the actual file name. This is necessary to - * get consistent behavior over different definitions of the __FILE__ macro. - * - * <b>Attention:</b> 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 */
--- a/src/ucx/ucx/map.h Wed Nov 02 19:19:01 2022 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,549 +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 <stdio.h> - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Loop statement for UCX maps. - * - * The <code>key</code> variable is implicitly defined, but the - * <code>value</code> 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 const *map; - - /** The current map element. */ - UcxMapElement *cur; - - /** - * The current index of the element list array. - * <b>Attention: </b> this is <b>NOT</b> the element index! Do <b>NOT</b> - * 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. - * - * <b>Note:</b> the contents are <b>not</b> 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 (<code>NULL</code>), 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. - * - * <b>Note:</b> the contents are <b>not</b> 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. - * - * <b>Note:</b> 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 <code>NULL</code> 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 const *from, UcxMap *to, copy_func fnc, void *data); - -/** - * Clones the map and rehashes if necessary. - * - * <b>Note:</b> In contrast to ucx_map_rehash() the load factor is irrelevant. - * This function <i>always</i> 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 <code>NULL</code> 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 const *map, copy_func fnc, void *data); - -/** - * Clones the map and rehashes if necessary. - * - * <b>Note:</b> In contrast to ucx_map_rehash() the load factor is irrelevant. - * This function <i>always</i> ensures a new UcxMap.size of at least - * 2.5*UcxMap.count. - * - * @param allocator the allocator to use for the cloned map - * @param map the map to clone - * @param fnc the copy function to use or <code>NULL</code> 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_a(UcxAllocator *allocator, - UcxMap const *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 const *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. - * - * <b>Note:</b> 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 <b>NOT</b> rely on - * the iteration order. - * - * <b>Note:</b> The iterator is <b>NOT</b> 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 const *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); - -/** - * Returns the union of two maps. - * - * The union is a fresh map which is filled by two successive calls of - * ucx_map_copy() on the two input maps. - * - * @param first the first source map - * @param second the second source map - * @param cpfnc a function to copy the elements - * @param cpdata additional data for the copy function - * @return a new map containing the union - */ -UcxMap* ucx_map_union(const UcxMap *first, const UcxMap *second, - copy_func cpfnc, void* cpdata); - -/** - * Returns the union of two maps. - * - * The union is a fresh map which is filled by two successive calls of - * ucx_map_copy() on the two input maps. - * - * @param allocator the allocator that shall be used by the new map - * @param first the first source map - * @param second the second source map - * @param cpfnc a function to copy the elements - * @param cpdata additional data for the copy function - * @return a new map containing the union - */ -UcxMap* ucx_map_union_a(UcxAllocator *allocator, - const UcxMap *first, const UcxMap *second, - copy_func cpfnc, void* cpdata); - -/** - * Returns the intersection of two maps. - * - * The intersection is defined as a copy of the first map with every element - * removed that has no valid key in the second map. - * - * @param first the first source map - * @param second the second source map - * @param cpfnc a function to copy the elements - * @param cpdata additional data for the copy function - * @return a new map containing the intersection - */ -UcxMap* ucx_map_intersection(const UcxMap *first, const UcxMap *second, - copy_func cpfnc, void* cpdata); - -/** - * Returns the intersection of two maps. - * - * The intersection is defined as a copy of the first map with every element - * removed that has no valid key in the second map. - * - * @param allocator the allocator that shall be used by the new map - * @param first the first source map - * @param second the second source map - * @param cpfnc a function to copy the elements - * @param cpdata additional data for the copy function - * @return a new map containing the intersection - */ -UcxMap* ucx_map_intersection_a(UcxAllocator *allocator, - const UcxMap *first, const UcxMap *second, - copy_func cpfnc, void* cpdata); - -/** - * Returns the difference of two maps. - * - * The difference contains a copy of all elements of the first map - * for which the corresponding keys cannot be found in the second map. - * - * @param first the first source map - * @param second the second source map - * @param cpfnc a function to copy the elements - * @param cpdata additional data for the copy function - * @return a new list containing the difference - */ -UcxMap* ucx_map_difference(const UcxMap *first, const UcxMap *second, - copy_func cpfnc, void* cpdata); - -/** - * Returns the difference of two maps. - * - * The difference contains a copy of all elements of the first map - * for which the corresponding keys cannot be found in the second map. - * - * @param allocator the allocator that shall be used by the new map - * @param first the first source map - * @param second the second source map - * @param cpfnc a function to copy the elements - * @param cpdata additional data for the copy function - * @return a new list containing the difference - */ -UcxMap* ucx_map_difference_a(UcxAllocator *allocator, - const UcxMap *first, const UcxMap *second, - copy_func cpfnc, void* cpdata); - - -#ifdef __cplusplus -} -#endif - -#endif /* UCX_MAP_H */ -
--- a/src/ucx/ucx/mempool.h Wed Nov 02 19:19:01 2022 +0100 +++ /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 <stddef.h> - -#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 <code>abort()</code>. - * - * @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 <b>MUST NOT</b> 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 <b>MUST</b> 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. - * <b>However, this does not cause the memory to be freed automatically!</b>. - * If you want to use this function, make the memory pool free non-pooled - * memory, the specified destructor function must call <code>free()</code> - * 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 */ -
--- a/src/ucx/ucx/properties.h Wed Nov 02 19:19:01 2022 +0100 +++ /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(). - * - * <b>Attention:</b> 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 <code>FILE*</code> 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: - * - * <code>[key] = [value]\\n</code> - * - * @param map the map to store - * @param file the <code>FILE*</code> 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 */ -
--- a/src/ucx/ucx/stack.h Wed Nov 02 19:19:01 2022 +0100 +++ /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 (<code>NULL</code> 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 <code>NULL</code> 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 <code> - * dest</code>, if specified. - * - * Use #ucx_stack_topsize()# to get the amount of memory that must be available - * at the location of <code>dest</code>. - * - * @param stack a pointer to the stack - * @param dest the location where the contents shall be written to, or <code> - * NULL</code>, 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 <code> - * dest</code>. - * - * This function copies at most <code>n</code> bytes to the destination, but - * the element is always freed as a whole. - * If the element was larger than <code>n</code>, 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 <code>dest</code> - * @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 */ -
--- a/src/ucx/ucx/string.h Wed Nov 02 19:19:01 2022 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1201 +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 (<code>sstr_t</code>) provide an alternative to C strings. - * The main difference to C strings is, that <code>sstr_t</code> does <b>not - * need to be <code>NULL</code>-terminated</b>. Instead the length is stored - * within the structure. - * - * When using <code>sstr_t</code>, developers must be full aware of what type - * of string (<code>NULL</code>-terminated) or not) they are using, when - * accessing the <code>char* ptr</code> directly. - * - * The UCX string module provides some common string functions, known from - * standard libc, working with <code>sstr_t</code>. - * - * @file string.h - * @author Mike Becker - * @author Olaf Wintermann - */ - -#ifndef UCX_STRING_H -#define UCX_STRING_H - -#include "ucx.h" -#include "allocator.h" -#include <stddef.h> - -/* - * Use this macro to disable the shortcuts if you experience macro collision. - */ -#ifndef UCX_NO_SSTR_SHORTCUTS -/** - * Shortcut for a <code>sstr_t struct</code> - * or <code>scstr_t struct</code> literal. - */ -#define ST(s) { s, sizeof(s)-1 } - -/** Shortcut for the conversion of a C string to a <code>sstr_t</code>. */ -#define S(s) sstrn(s, sizeof(s)-1) - -/** Shortcut for the conversion of a C string to a <code>scstr_t</code>. */ -#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 - * (<b>not necessarily <code>NULL</code>-terminated</b>) */ - 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 - * (<b>not necessarily <code>NULL</code>-terminated</b>) */ - 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 an scstr_t. - * - * Used <b>internally</b> to convert a UCX string to an immutable UCX string. - * - * <b>Do not use this function manually.</b> - * - * @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 an scstr_t. - * - * Used <b>internally</b> 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. - * - * <b>Do not use this function manually.</b> - * - * @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 an immutable version of the provided string - */ -#define SCSTR(s) s2scstr(s) -#else - -/** - * One of two type adjustment functions that return an scstr_t. - * - * Used <b>internally</b> 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. - * - * <b>Do not use this function manually.</b> - * - * @param str some scstr_t - * @return the argument itself - */ -scstr_t ucx_sc2sc(scstr_t str); - -/** - * One of two type adjustment functions that return an scstr_t. - * - * Used <b>internally</b> to convert a UCX string to an immutable UCX string. - * - * <b>Do not use this function manually.</b> - * - * @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 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 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 <b>internal</b> function (ab)uses the C standard an expects one single - * argument which is then implicitly converted to scstr_t without a warning. - * - * <b>Do not use this function manually.</b> - * - * @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 <code>strlen()</code>. - * - * <b>Note:</b> the sstr_t will share the specified pointer 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. - * - * <b>Note:</b> the sstr_t will share the specified pointer 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 <code>strlen()</code>. - * - * <b>Note:</b> the scstr_t will share the specified pointer 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. - * - * <b>Note:</b> the scstr_t will share the specified pointer 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 accumulated length of all specified strings. - * - * <b>Attention:</b> if the count argument is larger than the count 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 - */ -size_t scstrnlen(size_t count, ...); - -/** - * Returns the accumulated length of all specified strings. - * - * <b>Attention:</b> if the count argument is larger than the count of the - * specified strings, the behavior is undefined. - * - * @param count the total number of specified strings - * @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 <code>malloc()</code>. - * So developers <b>MUST</b> pass the sstr_t.ptr to <code>free()</code>. - * - * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>- - * 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, ...); - -/** - * Concatenates two or more strings. - * - * The resulting string will be allocated by standard <code>malloc()</code>. - * So developers <b>MUST</b> pass the sstr_t.ptr to <code>free()</code>. - * - * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>- - * terminated. - * - * @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. - * - * The resulting string must be freed by the allocators <code>free()</code> - * implementation. - * - * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>- - * terminated. - * - * @param alloc 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 - * - * @see scstrcat() - */ -sstr_t scstrcat_a(UcxAllocator *alloc, size_t count, scstr_t s1, ...); - -/** - * Concatenates two or more strings using a UcxAllocator. - * - * The resulting string must be freed by the allocators <code>free()</code> - * implementation. - * - * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>- - * terminated. - * - * @param alloc 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 - * - * @see sstrcat() - */ -#define sstrcat_a(alloc, count, s1, ...) \ - scstrcat_a(alloc, count, SCSTR(s1), __VA_ARGS__) - -/** - * Returns a substring starting at the specified location. - * - * <b>Attention:</b> the new string references the same memory area as the - * input string and is <b>NOT</b> required to be <code>NULL</code>-terminated. - * Use sstrdup() to get a copy. - * - * @param string input string - * @param start start location of the substring - * @return a substring of <code>string</code> starting at <code>start</code> - * - * @see sstrsubsl() - * @see sstrchr() - */ -sstr_t sstrsubs(sstr_t string, size_t start); - -/** - * Returns a substring with the given length starting at the specified location. - * - * <b>Attention:</b> the new string references the same memory area as the - * input string and is <b>NOT</b> required to be <code>NULL</code>-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 <code>string</code> starting at <code>start</code> - * with a maximum length of <code>length</code> - * - * @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. - * - * <b>Attention:</b> the new string references the same memory area as the -* input string and is <b>NOT</b> required to be <code>NULL</code>-terminated. - * Use scstrdup() to get a copy. - * - * @param string input string - * @param start start location of the substring - * @return a substring of <code>string</code> starting at <code>start</code> - * - * @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. - * - * <b>Attention:</b> the new string references the same memory area as the - * input string and is <b>NOT</b> required to be <code>NULL</code>-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 <code>string</code> starting at <code>start</code> - * with a maximum length of <code>length</code> - * - * @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 <code>chr</code> - * - * @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 <code>chr</code> - * - * @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 <code>chr</code> - * - * @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 <code>chr</code> - * - * @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 <code>match</code> is an empty string, the complete <code>string</code> 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 - * <code>match</code>, or an empty string, if the sequence is not - * present in <code>string</code> - */ -sstr_t scstrsstr(sstr_t string, scstr_t match); - -/** - * 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 <code>match</code> is an empty string, the complete <code>string</code> 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 - * <code>match</code>, or an empty string, if the sequence is not - * present in <code>string</code> - */ -#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 <code>match</code> is an empty string, the complete <code>string</code> 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 - * <code>match</code>, or an empty string, if the sequence is not - * present in <code>string</code> - */ -scstr_t scstrscstr(scstr_t string, scstr_t 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 <code>match</code> is an empty string, the complete <code>string</code> 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 - * <code>match</code>, or an empty string, if the sequence is not - * present in <code>string</code> - */ -#define sstrscstr(string, match) scstrscstr(string, SCSTR(match)) - -/** - * Splits a string into parts by using a delimiter string. - * - * This function will return <code>NULL</code>, if one of the following happens: - * <ul> - * <li>the string length is zero</li> - * <li>the delimeter length is zero</li> - * <li>the string equals the delimeter</li> - * <li>memory allocation fails</li> - * </ul> - * - * The integer referenced by <code>count</code> 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 <code>count</code> is also used as output and is - * set to - * <ul> - * <li>-2, on memory allocation errors</li> - * <li>-1, if either the string or the delimiter is an empty string</li> - * <li>0, if the string equals the delimiter</li> - * <li>1, if the string does not contain the delimiter</li> - * <li>the count of array items, otherwise</li> - * </ul> - * - * 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, <i>including</i> the terminating - * delimiter. - * - * <b>Attention:</b> The array pointer <b>AND</b> all sstr_t.ptr of the array - * items must be manually passed to <code>free()</code>. Use scstrsplit_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 - * <code>NULL</code> on error - * - * @see scstrsplit_a() - */ -sstr_t* scstrsplit(scstr_t string, scstr_t delim, ssize_t *count); - -/** - * Splits a string into parts by using a delimiter string. - * - * This function will return <code>NULL</code>, if one of the following happens: - * <ul> - * <li>the string length is zero</li> - * <li>the delimeter length is zero</li> - * <li>the string equals the delimeter</li> - * <li>memory allocation fails</li> - * </ul> - * - * The integer referenced by <code>count</code> 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 <code>count</code> is also used as output and is - * set to - * <ul> - * <li>-2, on memory allocation errors</li> - * <li>-1, if either the string or the delimiter is an empty string</li> - * <li>0, if the string equals the delimiter</li> - * <li>1, if the string does not contain the delimiter</li> - * <li>the count of array items, otherwise</li> - * </ul> - * - * 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, <i>including</i> the terminating - * delimiter. - * - * <b>Attention:</b> The array pointer <b>AND</b> all sstr_t.ptr of the array - * items must be manually passed to <code>free()</code>. 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 - * <code>NULL</code> on error - * - * @see sstrsplit_a() - */ -#define sstrsplit(string, delim, count) \ - scstrsplit(SCSTR(string), SCSTR(delim), count) - -/** - * Performing scstrsplit() using a UcxAllocator. - * - * <i>Read the description of scstrsplit() for details.</i> - * - * 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. - * - * @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 - * <code>NULL</code> on error - * - * @see scstrsplit() - */ -sstr_t* scstrsplit_a(UcxAllocator *allocator, scstr_t string, scstr_t delim, - ssize_t *count); - -/** - * Performing sstrsplit() using a UcxAllocator. - * - * <i>Read the description of sstrsplit() for details.</i> - * - * 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. - * - * @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 - * <code>NULL</code> 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 <code>memcmp()</code>. - * - * At first it compares the scstr_t.length attribute of the two strings. The - * <code>memcmp()</code> 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 - * <code>memcmp()</code> otherwise (i.e. 0 if the strings match) - */ -int scstrcmp(scstr_t s1, scstr_t s2); - -/** - * Compares two UCX strings with standard <code>memcmp()</code>. - * - * At first it compares the sstr_t.length attribute of the two strings. The - * <code>memcmp()</code> 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 - * <code>memcmp()</code> 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); - -/** - * Compares two UCX strings ignoring the case. - * - * At first it compares the sstr_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. - */ -#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 - * <code>malloc()</code>. So developers <b>MUST</b> pass the sstr_t.ptr to - * <code>free()</code>. - * - * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>- - * 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); - -/** - * Creates a duplicate of the specified string. - * - * The new sstr_t will contain a copy allocated by standard - * <code>malloc()</code>. So developers <b>MUST</b> pass the sstr_t.ptr to - * <code>free()</code>. - * - * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>- - * terminated, regardless of 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 <i>always</i> be <code>NULL</code>- - * 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); - -/** - * 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 <i>always</i> be <code>NULL</code>- - * terminated, 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() - */ -#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. - * - * <b>Note:</b> the new sstr_t references the same memory, thus you - * <b>MUST NOT</b> pass the sstr_t.ptr of the return value to - * <code>free()</code>. It is also highly recommended to avoid assignments like - * <code>mystr = sstrtrim(mystr);</code> 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. - * - * <b>Note:</b> the new scstr_t references the same memory, thus you - * <b>MUST NOT</b> pass the scstr_t.ptr of the return value to - * <code>free()</code>. It is also highly recommended to avoid assignments like - * <code>mystr = scstrtrim(mystr);</code> 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); - -/** - * 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 - */ -#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); - -/** - * 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 - */ -#define sstrsuffix(string, suffix) scstrsuffix(SCSTR(string), SCSTR(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 1, if and only if the string has the specified prefix, 0 otherwise - */ -int scstrcaseprefix(scstr_t string, scstr_t prefix); - -/** - * 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 1, if and only if the string has the specified prefix, 0 otherwise - */ -#define sstrcaseprefix(string, prefix) \ - scstrcaseprefix(SCSTR(string), SCSTR(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 1, if and only if the string has the specified suffix, 0 otherwise - */ -int scstrcasesuffix(scstr_t string, scstr_t suffix); - -/** - * 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 1, if and only if the string has the specified suffix, 0 otherwise - */ -#define sstrcasesuffix(string, suffix) \ - scstrcasesuffix(SCSTR(string), SCSTR(suffix)) - -/** - * Returns a lower case version of a string. - * - * This function creates a duplicate of the input string, first - * (see scstrdup()). - * - * @param string the input string - * @return the resulting lower case string - * @see scstrdup() - */ -sstr_t scstrlower(scstr_t string); - -/** - * Returns a lower case version of a string. - * - * This function creates a duplicate of the input string, first - * (see sstrdup()). - * - * @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 scstrdup_a()). - * - * @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); - - -/** - * Returns a lower case version of a string. - * - * This function creates a duplicate of the input string, first - * (see sstrdup_a()). - * - * @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 scstrdup()). - * - * @param string the input string - * @return the resulting upper case string - * @see scstrdup() - */ -sstr_t scstrupper(scstr_t string); - -/** - * Returns a upper case version of a string. - * - * This function creates a duplicate of the input string, first - * (see sstrdup()). - * - * @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 scstrdup_a()). - * - * @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); - -/** - * Returns a upper case version of a string. - * - * This function creates a duplicate of the input string, first - * (see sstrdup_a()). - * - * @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) - - -/** - * Replaces a pattern in a string with another string. - * - * The pattern is taken literally and is no regular expression. - * Replaces at most <code>replmax</code> occurrences. - * - * The resulting string is allocated by the specified allocator. I.e. it - * depends on the used allocator, whether the sstr_t.ptr must be freed - * manually. - * - * If allocation fails, the sstr_t.ptr of the return value is NULL. - * - * @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 - */ -sstr_t scstrreplacen_a(UcxAllocator *allocator, scstr_t str, - scstr_t pattern, scstr_t 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 <code>replmax</code> occurrences. - * - * The sstr_t.ptr of the resulting string must be freed manually. - * - * If allocation fails, the sstr_t.ptr of the return value is NULL. - * - * @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 - */ -sstr_t scstrreplacen(scstr_t str, scstr_t pattern, - scstr_t 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 <code>replmax</code> occurrences. - * - * The resulting string is allocated by the specified allocator. I.e. it - * depends on the used allocator, whether the sstr_t.ptr must be freed - * manually. - * - * @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 - */ -#define sstrreplacen_a(allocator, str, pattern, replacement, replmax) \ - scstrreplacen_a(allocator, SCSTR(str), SCSTR(pattern), \ - SCSTR(replacement), replmax) - -/** - * Replaces a pattern in a string with another string. - * - * The pattern is taken literally and is no regular expression. - * Replaces at most <code>replmax</code> occurrences. - * - * The sstr_t.ptr of the resulting string must be freed manually. - * - * If allocation fails, the sstr_t.ptr of the return value is NULL. - * - * @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 sstrreplacen(str, pattern, replacement, replmax) \ - scstrreplacen(SCSTR(str), SCSTR(pattern), SCSTR(replacement), replmax) - -/** - * Replaces a pattern in a string with another string. - * - * The pattern is taken literally and is no regular expression. - * Replaces at most <code>replmax</code> occurrences. - * - * The resulting string is allocated by the specified allocator. I.e. it - * depends on the used allocator, whether the sstr_t.ptr must be freed - * manually. - * - * If allocation fails, the sstr_t.ptr of the return value is NULL. - * - * @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 sstrreplace_a(allocator, str, pattern, replacement) \ - scstrreplacen_a(allocator, SCSTR(str), SCSTR(pattern), \ - SCSTR(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 <code>replmax</code> occurrences. - * - * The sstr_t.ptr of the resulting string must be freed manually. - * - * If allocation fails, the sstr_t.ptr of the return value is NULL. - * - * @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 sstrreplace(str, pattern, replacement) \ - scstrreplacen(SCSTR(str), SCSTR(pattern), SCSTR(replacement), SIZE_MAX) - -#ifdef __cplusplus -} -#endif - -#endif /* UCX_STRING_H */
--- a/src/ucx/ucx/test.h Wed Nov 02 19:19:01 2022 +0100 +++ /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: **** - * - * <pre> - * UCX_TEST(function_name); - * UCX_TEST_SUBROUTINE(subroutine_name, paramlist); // optional - * </pre> - * - * **** IN SOURCE FILE: **** - * <pre> - * 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 - * } - * </pre> - * - * <b>Note:</b> if a test fails, a longjump is performed - * back to the #UCX_TEST_BEGIN macro! - * - * <b>Attention:</b> 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 <stdio.h> -#include <string.h> -#include <setjmp.h> - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef __FUNCTION__ - -/** - * Alias for the <code>__func__</code> preprocessor macro. - * Some compilers use <code>__func__</code> and others use __FUNCTION__. - * We use __FUNCTION__ so we define it for those compilers which use - * <code>__func__</code>. - */ -#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 <code>EXIT_SUCCESS</code> on success or - * <code>EXIT_FAILURE</code> 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. - * <b>Note:</b> Any UCX_TEST_ASSERT() calls must be performed <b>after</b> - * #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. - * - * <b>Note:</b> You may <b>only</b> 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. - * <b>Note:</b> Any UCX_TEST_ASSERT() calls must be performed <b>before</b> - * #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 */ -
--- a/src/ucx/ucx/ucx.h Wed Nov 02 19:19:01 2022 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,204 +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 1 - -/** Version constant which ensures to increase monotonically. */ -#define UCX_VERSION (((UCX_VERSION_MAJOR)<<16)|UCX_VERSION_MINOR) - -#include <stdlib.h> -#include <stdint.h> - -#ifdef _WIN32 -#if !(defined __ssize_t_defined || defined _SSIZE_T_) -#include <BaseTsd.h> -typedef SSIZE_T ssize_t; -#define __ssize_t_defined -#define _SSIZE_T_ -#endif /* __ssize_t_defined and _SSIZE_T */ -#else /* !_WIN32 */ -#include <sys/types.h> -#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 - * <code>NULL</code>, 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 - * <code>NULL</code>, it shall be ignored. - - * <b>Attention:</b> if pointers returned by functions of this type may be - * passed to <code>free()</code> depends on the implementation of the - * respective <code>copy_func</code>. - */ -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 <code>fwrite</code>, 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 <code>fread</code>, 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 <code>__builtin_umul_overflow</code>. - * - * 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 <code>__builtin_umull_overflow</code>. - * - * 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. - * - * @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) - -/** - * 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 ucx_szmul_impl(size_t a, size_t b, size_t *result); - -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* UCX_H */ -
--- a/src/ucx/ucx/utils.h Wed Nov 02 19:19:01 2022 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,508 +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 <inttypes.h> -#include <string.h> -#include <stdarg.h> - -#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 <code>NULL</code> if a buffer - * shall be implicitly created on the heap - * @param bufsize the size of the copy buffer - if <code>NULL</code> was - * provided for <code>buf</code>, 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 <code>NULL</code> if a buffer - * shall be implicitly created on the heap - * @param bufsize the size of the copy buffer - if <code>NULL</code> was - * provided for <code>buf</code>, 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); - -/** - * Compares two integers of type long long. - * @param i1 pointer to long long one - * @param i2 pointer to long long 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_longlong(const void *i1, const void *i2, void *data); - -/** - * Compares two integers of type int16_t. - * @param i1 pointer to int16_t one - * @param i2 pointer to int16_t 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_int16(const void *i1, const void *i2, void *data); - -/** - * Compares two integers of type int32_t. - * @param i1 pointer to int32_t one - * @param i2 pointer to int32_t 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_int32(const void *i1, const void *i2, void *data); - -/** - * Compares two integers of type int64_t. - * @param i1 pointer to int64_t one - * @param i2 pointer to int64_t 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_int64(const void *i1, const void *i2, void *data); - -/** - * Compares two integers of type unsigned int. - * @param i1 pointer to unsigned integer one - * @param i2 pointer to unsigned 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_uint(const void *i1, const void *i2, void *data); - -/** - * Compares two integers of type unsigned long int. - * @param i1 pointer to unsigned long integer one - * @param i2 pointer to unsigned 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_ulongint(const void *i1, const void *i2, void *data); - -/** - * Compares two integers of type unsigned long long. - * @param i1 pointer to unsigned long long one - * @param i2 pointer to unsigned long long 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_ulonglong(const void *i1, const void *i2, void *data); - -/** - * Compares two integers of type uint16_t. - * @param i1 pointer to uint16_t one - * @param i2 pointer to uint16_t 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_uint16(const void *i1, const void *i2, void *data); - -/** - * Compares two integers of type uint32_t. - * @param i1 pointer to uint32_t one - * @param i2 pointer to uint32_t 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_uint32(const void *i1, const void *i2, void *data); - -/** - * Compares two integers of type uint64_t. - * @param i1 pointer to uint64_t one - * @param i2 pointer to uint64_t 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_uint64(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); - -/** - * Distance function for integers of type long long. - * @param i1 pointer to long long one - * @param i2 pointer to long long two - * @param data omitted - * @return i1 minus i2 - */ -intmax_t ucx_dist_longlong(const void *i1, const void *i2, void *data); - -/** - * Distance function for integers of type int16_t. - * @param i1 pointer to int16_t one - * @param i2 pointer to int16_t two - * @param data omitted - * @return i1 minus i2 - */ -intmax_t ucx_dist_int16(const void *i1, const void *i2, void *data); - -/** - * Distance function for integers of type int32_t. - * @param i1 pointer to int32_t one - * @param i2 pointer to int32_t two - * @param data omitted - * @return i1 minus i2 - */ -intmax_t ucx_dist_int32(const void *i1, const void *i2, void *data); - -/** - * Distance function for integers of type int64_t. - * @param i1 pointer to int64_t one - * @param i2 pointer to int64_t two - * @param data omitted - * @return i1 minus i2 - */ -intmax_t ucx_dist_int64(const void *i1, const void *i2, void *data); - -/** - * Distance function for integers of type unsigned int. - * @param i1 pointer to unsigned integer one - * @param i2 pointer to unsigned integer two - * @param data omitted - * @return i1 minus i2 - */ -intmax_t ucx_dist_uint(const void *i1, const void *i2, void *data); - -/** - * Distance function for integers of type unsigned long int. - * @param i1 pointer to unsigned long integer one - * @param i2 pointer to unsigned long integer two - * @param data omitted - * @return i1 minus i2 - */ -intmax_t ucx_dist_ulongint(const void *i1, const void *i2, void *data); - -/** - * Distance function for integers of type unsigned long long. - * @param i1 pointer to unsigned long long one - * @param i2 pointer to unsigned long long two - * @param data omitted - * @return i1 minus i2 - */ -intmax_t ucx_dist_ulonglong(const void *i1, const void *i2, void *data); - -/** - * Distance function for integers of type uint16_t. - * @param i1 pointer to uint16_t one - * @param i2 pointer to uint16_t two - * @param data omitted - * @return i1 minus i2 - */ -intmax_t ucx_dist_uint16(const void *i1, const void *i2, void *data); - -/** - * Distance function for integers of type uint32_t. - * @param i1 pointer to uint32_t one - * @param i2 pointer to uint32_t two - * @param data omitted - * @return i1 minus i2 - */ -intmax_t ucx_dist_uint32(const void *i1, const void *i2, void *data); - -/** - * Distance function for integers of type uint64_t. - * @param i1 pointer to uint64_t one - * @param i2 pointer to uint64_t two - * @param data omitted - * @return i1 minus i2 - */ -intmax_t ucx_dist_uint64(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 <code>printf()</code> 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, ...); - -/** - * <code>va_list</code> 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 <code>printf()</code> like function which allocates space for a sstr_t - * the result is written to. - * - * <b>Attention</b>: 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. - * - * <b>Note</b>: The sstr_t.ptr of the return value will <i>always</i> be - * <code>NULL</code>-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, ...); - -/** - * <code>va_list</code> 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 <code>printf()</code> 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 */ -
--- a/src/ucx/utils.c Wed Nov 02 19:19:01 2022 +0100 +++ b/src/ucx/utils.c Sun Nov 06 15:53:32 2022 +0100 @@ -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,423 +26,21 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include "ucx/utils.h" - -#include <math.h> -#include <stdio.h> -#include <limits.h> -#include <errno.h> +#include "cx/utils.h" -/* 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) { +#ifndef CX_SZMUL_BUILTIN +int cx_szmul_impl(size_t a, size_t b, size_t *result) { + if(a == 0 || b == 0) { + *result = 0; return 0; } - - char *lbuf; - size_t ncp = 0; - - if(buf) { - lbuf = buf; - } else { - lbuf = (char*)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); - ncp += r; - n -= r; - rn = bufsize > n ? n : bufsize; - 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) { - 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 ucx_cmp_longlong(const void *i1, const void *i2, void *data) { - 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 ucx_cmp_int16(const void *i1, const void *i2, void *data) { - 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 ucx_cmp_int32(const void *i1, const void *i2, void *data) { - 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 ucx_cmp_int64(const void *i1, const void *i2, void *data) { - 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 ucx_cmp_uint(const void *i1, const void *i2, void *data) { - 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 ucx_cmp_ulongint(const void *i1, const void *i2, void *data) { - 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 ucx_cmp_ulonglong(const void *i1, const void *i2, void *data) { - 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 ucx_cmp_uint16(const void *i1, const void *i2, void *data) { - 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 ucx_cmp_uint32(const void *i1, const void *i2, void *data) { - 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 ucx_cmp_uint64(const void *i1, const void *i2, void *data) { - 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; - } -} - -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; -} - -intmax_t ucx_dist_longlong(const void *i1, const void *i2, void *data) { - intmax_t a = *((const long long*) i1); - intmax_t b = *((const long long*) i2); - return a - b; -} - -intmax_t ucx_dist_int16(const void *i1, const void *i2, void *data) { - intmax_t a = *((const int16_t*) i1); - intmax_t b = *((const int16_t*) i2); - return a - b; -} - -intmax_t ucx_dist_int32(const void *i1, const void *i2, void *data) { - intmax_t a = *((const int32_t*) i1); - intmax_t b = *((const int32_t*) i2); - return a - b; -} - -intmax_t ucx_dist_int64(const void *i1, const void *i2, void *data) { - intmax_t a = *((const int64_t*) i1); - intmax_t b = *((const int64_t*) i2); - return a - b; -} - -intmax_t ucx_dist_uint(const void *i1, const void *i2, void *data) { - uintmax_t a = *((const unsigned int*) i1); - uintmax_t b = *((const unsigned int*) i2); - return a > b ? (intmax_t)(a - b) : -(intmax_t)(b - a); -} - -intmax_t ucx_dist_ulongint(const void *i1, const void *i2, void *data) { - uintmax_t a = *((const unsigned long int*) i1); - uintmax_t b = *((const unsigned long int*) i2); - return a > b ? (intmax_t)(a - b) : -(intmax_t)(b - a); -} - -intmax_t ucx_dist_ulonglong(const void *i1, const void *i2, void *data) { - uintmax_t a = *((const unsigned long long*) i1); - uintmax_t b = *((const unsigned long long*) i2); - return a > b ? (intmax_t)(a - b) : -(intmax_t)(b - a); -} - -intmax_t ucx_dist_uint16(const void *i1, const void *i2, void *data) { - uintmax_t a = *((const uint16_t*) i1); - uintmax_t b = *((const uint16_t*) i2); - return a > b ? (intmax_t)(a - b) : -(intmax_t)(b - a); -} - -intmax_t ucx_dist_uint32(const void *i1, const void *i2, void *data) { - uintmax_t a = *((const uint32_t*) i1); - uintmax_t b = *((const uint32_t*) i2); - return a > b ? (intmax_t)(a - b) : -(intmax_t)(b - a); -} - -intmax_t ucx_dist_uint64(const void *i1, const void *i2, void *data) { - uintmax_t a = *((const uint64_t*) i1); - uintmax_t b = *((const uint64_t*) i2); - return a > b ? (intmax_t)(a - b) : -(intmax_t)(b - a); -} - -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) { + size_t r = a * b; + if(r / b == a) { + *result = r; return 0; } else { - return p1 < p2 ? -1 : 1; + *result = 0; + return 1; } } - -int ucx_cmp_mem(const void *ptr1, const void *ptr2, void *n) { - return memcmp(ptr1, ptr2, *((size_t*)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 #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; -}