return NULL;
}
- CxBuffer *buf = cxBufferCreate(NULL, textLen, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS);
+ CxBuffer *buf = cxBufferCreate(cxDefaultAllocator, NULL, textLen, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS);
cxBufferWrite(xmlText, 1, textLen, buf);
xmlFree(xmlText);
return buf;
path->length = 0;
int s;
- if(cx_strprefix(url, CX_STR("http://"))) {
+ if(cx_strprefix(url, cx_str("http://"))) {
s = 7;
- } else if(cx_strprefix(url, CX_STR("https://"))) {
+ } else if(cx_strprefix(url, cx_str("https://"))) {
s = 8;
} else {
s = 1;
// TODO: who is responsible for freeing this repository?
// how can the callee know, if he has to call free()?
repo = dav_repository_new(config);
- repo->name.value = cx_strdup_a(config->mp->allocator, CX_STR(""));
+ repo->name.value = cx_strdup_a(config->mp->allocator, cx_str(""));
if(url.ptr[url.length-1] == '/') {
repo->url.value = cx_strdup_a(config->mp->allocator, url);
- *path = cx_strdup(CX_STR("/"));
+ *path = cx_strdup(cx_str("/"));
} else if (cx_strchr(url, '/').length > 0) {
// TODO: fix the following workaround after
// fixing the inconsistent behavior of util_url_*()
*path = cx_strdup(util_url_path_s(url));
} else {
repo->url.value = cx_strdup(url);
- *path = cx_strdup(CX_STR("/"));
+ *path = cx_strdup(cx_str("/"));
}
}
SHA256_Init(ctx);
}
-void dav_sha256_update(DAV_SHA_CTX *ctx, const void *data, size_t length) {
+void dav_sha256_update(DAV_SHA_CTX *ctx, const char *data, size_t length) {
SHA256_Update(ctx, data, length);
}
-void dav_sha256_final(char *md, DAV_SHA_CTX *ctx) {
- SHA256_Final(md, ctx);
+void dav_sha256_final(DAV_SHA_CTX *ctx, unsigned char *buf) {
+ SHA256_Final(buf, ctx);
}
#else
return ctx;
}
+void dav_sha256_init(DAV_SHA_CTX *ctx) {
+ CC_SHA256_Init(ctx);
+}
+
void dav_sha256_update(DAV_SHA_CTX *ctx, const char *data, size_t len) {
CC_SHA256_Update(ctx, data, len);
}
void dav_sha256_final(DAV_SHA_CTX *ctx, unsigned char *buf) {
CC_SHA256_Final(buf, ctx);
- free(ctx);
}
DavKey* dav_pw2key(const char *password, const unsigned char *salt, int saltlen, int pwfunc, int enc) {
DAV_SHA_CTX *ctx = dav_sha256_create();
if(ctx) {
dav_sha256_update(ctx, data, len);
- dav_sha256_final(ctx, hash);
+ dav_sha256_final_free(ctx, hash);
}
return util_hexstr(hash, DAV_SHA256_DIGEST_LENGTH);
}
// cleanup
cng_cleanup(ctx->hAlg, NULL, ctx->hHash, ctx->pbHashObject);
- free(ctx);
}
DavKey* dav_pw2key(const char *password, const unsigned char *salt, int saltlen, int pwfunc, int enc) {
}
#endif
-
+void dav_sha256_final_free(DAV_SHA_CTX *ctx, unsigned char *buf) {
+ dav_sha256_final(ctx, buf);
+ free(ctx);
+}
CxBuffer* aes_encrypt_buffer(CxBuffer *in, DavKey *key) {
- CxBuffer *encbuf = cxBufferCreate(NULL, in->size, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ CxBuffer *encbuf = cxBufferCreate(cxDefaultAllocator, NULL, in->size, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
if(!encbuf) {
return NULL;
}
}
CxBuffer* aes_decrypt_buffer(CxBuffer *in, DavKey *key) {
- CxBuffer *decbuf = cxBufferCreate(NULL, in->size, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ CxBuffer *decbuf = cxBufferCreate(cxDefaultAllocator, NULL, in->size, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
if(!decbuf) {
return NULL;
}
DAV_SHA_CTX* dav_sha256_create(void);
void dav_sha256_update(DAV_SHA_CTX *ctx, const char *data, size_t len);
void dav_sha256_final(DAV_SHA_CTX *ctx, unsigned char *buf);
+void dav_sha256_final_free(DAV_SHA_CTX *ctx, unsigned char *buf);
DavKey* dav_pw2key(const char *password, const unsigned char *salt, int saltlen, int pwfunc, int enc);
#include <string.h>
#include <inttypes.h>
-#include <cx/utils.h>
#include <cx/map.h>
#include <cx/hash_map.h>
#include <cx/printf.h>
cxmutstr dav_format_string(const CxAllocator *a, cxstring fstr, DavQLArgList *ap, davqlerror_t *error) {
CxBuffer buf;
- cxBufferInit(&buf, NULL, 128, a, CX_BUFFER_AUTO_EXTEND);
+ cxBufferInit(&buf, a, NULL, 128, CX_BUFFER_AUTO_EXTEND);
int placeholder = 0;
for(int i=0;i<fstr.length;i++) {
property->name = name;
property->value = NULL;
- cxMapPut(map, cx_hash_key(expression->srctext.ptr, expression->srctext.length), property);
+ cxMapPut(map, expression->srctext, property);
}
if(expression->left) {
CxIterator i = cxListIterator(fields);
cx_foreach(DavQLField*, field, i) {
- if(!cx_strcmp(field->name, CX_STR("*"))) {
+ if(!cx_strcmp(field->name, cx_str("*"))) {
cxMapFree(properties);
*isallprop = 1;
return create_allprop_propfind_request();
- } else if(!cx_strcmp(field->name, CX_STR("-"))) {
+ } else if(!cx_strcmp(field->name, cx_str("-"))) {
cxMapFree(properties);
return create_propfind_request(sn, NULL, "propfind", 0);
} else {
}
CxMapIterator mi = cxMapIteratorValues(properties);
- CxList *list = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+ CxList *list = cxLinkedListCreate(NULL, CX_STORE_POINTERS);
cx_foreach(DavProperty*, value, mi) {
cxListAdd(list, value);
}
cxMempoolRegister(mp, rqbuf, (cx_destructor_func)cxBufferFree);
// compile field list
- CxList *cfieldlist = cxLinkedListCreate(mp->allocator, NULL, CX_STORE_POINTERS);
+ CxList *cfieldlist = cxLinkedListCreate(mp->allocator, CX_STORE_POINTERS);
if(st->fields) {
CxIterator i = cxListIterator(st->fields);
cx_foreach(DavQLField*, field, i) {
- if(cx_strcmp(field->name, CX_STR("*")) && cx_strcmp(field->name, CX_STR("-"))) {
+ if(cx_strcmp(field->name, cx_str("*")) && cx_strcmp(field->name, cx_str("-"))) {
// compile field expression
CxBuffer *code = dav_compile_expr(
sn->context,
// compile order criterion
CxList *ordercr = NULL;
if(st->orderby) {
- ordercr = cxLinkedListCreate(mp->allocator, NULL, sizeof(DavOrderCriterion));
+ ordercr = cxLinkedListCreate(mp->allocator, sizeof(DavOrderCriterion));
CxIterator i = cxListIterator(st->orderby);
cx_foreach(DavQLOrderCriterion*, oc, i) {
DavQLExpression *column = oc->column;
DavResource *selroot = dav_resource_new(sn, path.ptr);
- CxList *stack = cxLinkedListCreateSimple(sizeof(DavQLRes));
+ CxList *stack = cxLinkedListCreate(NULL, sizeof(DavQLRes));
// initialize the stack with the requested resource
DavQLRes res;
res.resource = selroot;
cxListInsert(stack, 0, &res);
// reuseable response buffer
- CxBuffer *rpbuf = cxBufferCreate(NULL, 4096, mp->allocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ CxBuffer *rpbuf = cxBufferCreate(mp->allocator, NULL, 4096, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
if(!rpbuf) {
// TODO: cleanup
cxMempoolFree(mp);
}
int dav_identifier2resprop(cxstring src, davqlresprop_t *prop) {
- if(!cx_strcmp(src, CX_STR("name"))) {
+ if(!cx_strcmp(src, cx_str("name"))) {
*prop = DAVQL_RES_NAME;
- } else if(!cx_strcmp(src, CX_STR("path"))) {
+ } else if(!cx_strcmp(src, cx_str("path"))) {
*prop = DAVQL_RES_PATH;
- } else if(!cx_strcmp(src, CX_STR("href"))) {
+ } else if(!cx_strcmp(src, cx_str("href"))) {
*prop = DAVQL_RES_HREF;
- } else if(!cx_strcmp(src, CX_STR("contentlength"))) {
+ } else if(!cx_strcmp(src, cx_str("contentlength"))) {
*prop = DAVQL_RES_CONTENTLENGTH;
- } else if(!cx_strcmp(src, CX_STR("contenttype"))) {
+ } else if(!cx_strcmp(src, cx_str("contenttype"))) {
*prop = DAVQL_RES_CONTENTTYPE;
- } else if(!cx_strcmp(src, CX_STR("creationdate"))) {
+ } else if(!cx_strcmp(src, cx_str("creationdate"))) {
*prop = DAVQL_RES_CREATIONDATE;
- } else if(!cx_strcmp(src, CX_STR("lastmodified"))) {
+ } else if(!cx_strcmp(src, cx_str("lastmodified"))) {
*prop = DAVQL_RES_LASTMODIFIED;
- } else if(!cx_strcmp(src, CX_STR("iscollection"))) {
+ } else if(!cx_strcmp(src, cx_str("iscollection"))) {
*prop = DAVQL_RES_ISCOLLECTION;
} else {
return 0;
return -1;
}
} else if(!dav_identifier2resprop(src, &cmd.data.resprop)) {
- if(!cx_strcmp(src, CX_STR("true"))) {
+ if(!cx_strcmp(src, cx_str("true"))) {
cmd.type = DAVQL_CMD_INT;
cmd.data.integer = 1;
- } else if(!cx_strcmp(src, CX_STR("false"))) {
+ } else if(!cx_strcmp(src, cx_str("false"))) {
cmd.type = DAVQL_CMD_INT;
cmd.data.integer = 0;
} else {
}
CxBuffer* dav_compile_expr(DavContext *ctx, const CxAllocator *a, DavQLExpression *lexpr, DavQLArgList *ap) {
- CxBuffer *bcode = cxBufferCreate(NULL, 512, a, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ CxBuffer *bcode = cxBufferCreate(a, NULL, 512, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
if(!bcode) {
return NULL;
}
*/
#include "davqlparser.h"
-#include <cx/utils.h>
#include <cx/linked_list.h>
#include <cx/printf.h>
#include <string.h>
static void dav_debug_ql_expr_print(DavQLExpression *expr) {
if (dav_debug_ql_expr_selected(expr)) {
- cxstring empty = CX_STR("(empty)");
+ cxstring empty = cx_str("(empty)");
printf(
"Text: %.*s\nType: %s\nOperator: %s\n",
sfmtarg(expr->srctext),
// we try to achieve two things: get as many information as possible
// and recover the concrete source string (and not the token strings)
- cxstring emptystring = CX_STR("");
+ cxstring emptystring = cx_str("");
cxstring prev = token->prev ? (token->prev->prev ?
token_sstr(token->prev->prev) : token_sstr(token->prev))
: emptystring;
static const char *special_token_symbols = ",()+-*/&|^~=!<>";
static _Bool iskeyword(DavQLToken *token) {
- cxstring keywords[] ={CX_STR("select"), CX_STR("set"), CX_STR("from"), CX_STR("at"), CX_STR("as"),
- CX_STR("where"), CX_STR("anywhere"), CX_STR("like"), CX_STR("unlike"), CX_STR("and"),
- CX_STR("or"), CX_STR("not"), CX_STR("xor"), CX_STR("with"), CX_STR("infinity"),
- CX_STR("order"), CX_STR("by"), CX_STR("asc"), CX_STR("desc")
+ cxstring keywords[] ={cx_str("select"), cx_str("set"), cx_str("from"), cx_str("at"), cx_str("as"),
+ cx_str("where"), cx_str("anywhere"), cx_str("like"), cx_str("unlike"), cx_str("and"),
+ cx_str("or"), cx_str("not"), cx_str("xor"), cx_str("with"), cx_str("infinity"),
+ cx_str("order"), cx_str("by"), cx_str("asc"), cx_str("desc")
};
for (int i = 0 ; i < sizeof(keywords)/sizeof(cxstring) ; i++) {
if (!cx_strcasecmp(token->value, keywords[i])) {
}
static _Bool islongoperator(DavQLToken *token) {
- cxstring operators[] = {CX_STR("and"), CX_STR("or"), CX_STR("not"), CX_STR("xor"),
- CX_STR("like"), CX_STR("unlike")
+ cxstring operators[] = {cx_str("and"), cx_str("or"), cx_str("not"), cx_str("xor"),
+ cx_str("like"), cx_str("unlike")
};
for (int i = 0 ; i < sizeof(operators)/sizeof(cxstring) ; i++) {
if (!cx_strcasecmp(token->value, operators[i])) {
static int dav_stmt_add_field(DavQLStatement *stmt, DavQLField *field) {
if(!stmt->fields) {
- stmt->fields = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+ stmt->fields = cxLinkedListCreate(NULL, CX_STORE_POINTERS);
if(!stmt->fields) {
stmt->errorcode = DAVQL_ERROR_OUT_OF_MEMORY;
return 1;
alloc_token();
token->tokenclass = DAVQL_TOKEN_END;
- token->value = CX_STR("");
+ token->value = cx_str("");
cx_linked_list_add((void**)&tokens_begin, (void**)&tokens_end, offsetof(DavQLToken, prev), offsetof(DavQLToken, next), token);
return tokens_begin;
static void fmt_args_add(DavQLStatement *stmt, void *data) {
if(!stmt->args) {
- stmt->args = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+ stmt->args = cxLinkedListCreate(NULL, CX_STORE_POINTERS);
}
cxListAdd(stmt->args, data);
}
DavQLOrderCriterion crit;
if(!stmt->orderby) {
- stmt->orderby = cxLinkedListCreateSimple(sizeof(DavQLOrderCriterion));
+ stmt->orderby = cxLinkedListCreate(NULL, sizeof(DavQLOrderCriterion));
if(!stmt->orderby) {
return 0;
}
void dav_free_statement(DavQLStatement *stmt) {
if(stmt->fields) {
- cxDefineDestructor(stmt->fields, dav_free_field);
+ cxSetDestructor(stmt->fields, dav_free_field);
cxListFree(stmt->fields);
}
}
if(stmt->orderby) {
- cxDefineDestructor(stmt->orderby, dav_free_order_criterion);
+ cxSetDestructor(stmt->orderby, dav_free_order_criterion);
cxListFree(stmt->orderby);
}
if(stmt->args) {
#include "session.h"
#include "xml.h"
-#include <cx/utils.h>
#include <cx/printf.h>
#include <cx/hash_map.h>
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, cxBufferWrite);
curl_easy_setopt(handle, CURLOPT_WRITEDATA, response);
CxMap *respheaders = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 32);
- cxDefineDestructor(respheaders, free);
+ cxSetDestructor(respheaders, free);
util_capture_header(handle, respheaders);
for(int i=0;i<maxretry;i++) {
* => the server handled our request and we can stop requesting
*/
char *msdavexterror;
- msdavexterror = cxMapGet(respheaders, cx_hash_key_str("x-msdavext_error"));
+ msdavexterror = cxMapGet(respheaders, "x-msdavext_error");
int iishack = depth == 1 &&
msdavexterror && !strncmp(msdavexterror, "589831;", 7);
}
CxBuffer* create_allprop_propfind_request(void) {
- CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ CxBuffer *buf = cxBufferCreate(cxDefaultAllocator, NULL, 512, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
cxstring s;
- s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
+ s = cx_str("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
cxBufferWrite(s.ptr, 1, s.length, buf);
- s = CX_STR("<D:propfind xmlns:D=\"DAV:\">\n");
+ s = cx_str("<D:propfind xmlns:D=\"DAV:\">\n");
cxBufferWrite(s.ptr, 1, s.length, buf);
- s = CX_STR("<D:allprop/></D:propfind>\n");
+ s = cx_str("<D:allprop/></D:propfind>\n");
cxBufferWrite(s.ptr, 1, s.length, buf);
return buf;
}
CxBuffer* create_cryptoprop_propfind_request(void) {
- CxBuffer *buf = cxBufferCreate(NULL, 256, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ CxBuffer *buf = cxBufferCreate(cxDefaultAllocator, NULL, 256, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
cxstring s;
- s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
+ s = cx_str("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
cxBufferWrite(s.ptr, 1, s.length, buf);
- s = CX_STR("<D:propfind xmlns:D=\"DAV:\" xmlns:idav=\"" DAV_NS "\">\n");
+ s = cx_str("<D:propfind xmlns:D=\"DAV:\" xmlns:idav=\"" DAV_NS "\">\n");
cxBufferWrite(s.ptr, 1, s.length, buf);
- s = CX_STR("<D:prop><idav:crypto-prop/></D:prop></D:propfind>\n");
+ s = cx_str("<D:prop><idav:crypto-prop/></D:prop></D:propfind>\n");
cxBufferWrite(s.ptr, 1, s.length, buf);
return buf;
}
CxBuffer* create_propfind_request(DavSession *sn, CxList *properties, char *rootelm, DavBool nocrypt) {
- CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ CxBuffer *buf = cxBufferCreate(cxDefaultAllocator, NULL, 512, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
cxstring s;
int add_crypto_name = 1;
CxIterator i = cxListIterator(properties);
cx_foreach(DavProperty*, p, i) {
if(strcmp(p->ns->name, "DAV:")) {
- cxMapPut(namespaces, cx_hash_key_str(p->ns->prefix), p->ns);
+ cxMapPut(namespaces, p->ns->prefix, p->ns);
}
// if the properties list contains the idav properties crypto-name
if(add_crypto_name && add_crypto_key && DAV_CRYPTO(sn) && !nocrypt) {
idav_ns.prefix = "idav";
idav_ns.name = DAV_NS;
- cxMapPut(namespaces, cx_hash_key_str("idav"), &idav_ns);
+ cxMapPut(namespaces, "idav", &idav_ns);
}
- s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
// write root element and namespaces
cx_bprintf(buf, "<D:%s xmlns:D=\"DAV:\"", rootelm);
CxMapIterator mapi = cxMapIteratorValues(namespaces);
cx_foreach(DavNamespace*, ns, mapi) {
- s = CX_STR(" xmlns:");
- cxBufferWrite(s.ptr, 1, s.length, buf);
- s = cx_str(ns->prefix);
- cxBufferWrite(s.ptr, 1, s.length, buf);
- s = CX_STR("=\"");
- cxBufferWrite(s.ptr, 1, s.length, buf);
- s = cx_str(ns->name);
- cxBufferWrite(s.ptr, 1, s.length, buf);
- s = CX_STR("\"");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, " xmlns:");
+ cxBufferPutString(buf, ns->prefix);
+ cxBufferPutString(buf, "=\"");
+ cxBufferPutString(buf, ns->name);
+ cxBufferPutString(buf, "\"");
}
- s = CX_STR(">\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, ">\n");
// default properties
- s = CX_STR("<D:prop>\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
-
- s = CX_STR("<D:creationdate />\n<D:getlastmodified />\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
-
- s = CX_STR("<D:getcontentlength />\n<D:getcontenttype />\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, "<D:prop>\n");
+ cxBufferPutString(buf, "<D:creationdate />\n<D:getlastmodified />\n");
+ cxBufferPutString(buf, "<D:getcontentlength />\n<D:getcontenttype />\n");
- s = CX_STR("<D:resourcetype />\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, "<D:resourcetype />\n");
// crypto properties
if(DAV_CRYPTO(sn) && !nocrypt) {
if(add_crypto_name) {
cxBufferPut(buf, '<');
cxBufferPutString(buf, crypto_ns);
- s = CX_STR(":crypto-name />\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, ":crypto-name />\n");
}
if(add_crypto_key) {
cxBufferPut(buf, '<');
cxBufferPutString(buf, crypto_ns);
- s = CX_STR(":crypto-key />\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, ":crypto-key />\n");
}
if(add_crypto_hash) {
cxBufferPut(buf, '<');
cxBufferPutString(buf, crypto_ns);
- s = CX_STR(":crypto-hash />\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, ":crypto-hash />\n");
}
}
if(properties) {
CxIterator i = cxListIterator(properties);
cx_foreach(DavProperty*, prop, i) {
- s = CX_STR("<");
- cxBufferWrite(s.ptr, 1, s.length, buf);
- s = cx_str(prop->ns->prefix);
- cxBufferWrite(s.ptr, 1, s.length, buf);
- s = CX_STR(":");
- cxBufferWrite(s.ptr, 1, s.length, buf);
- s = cx_str(prop->name);
- cxBufferWrite(s.ptr, 1, s.length, buf);
- s = CX_STR(" />\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, "<");
+ cxBufferPutString(buf, prop->ns->prefix);
+ cxBufferPutString(buf, ":");
+ cxBufferPutString(buf, prop->name);
+ cxBufferPutString(buf, " />\n");
}
}
}
CxBuffer* create_basic_propfind_request(void) {
- CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ CxBuffer *buf = cxBufferCreate(cxDefaultAllocator, NULL, 512, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
cxstring s;
- s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
- s = CX_STR("<D:propfind xmlns:D=\"DAV:\" xmlns:i=\"");
- cxBufferWrite(s.ptr, 1, s.length, buf);
- s = CX_STR(DAV_NS);
- cxBufferWrite(s.ptr, 1, s.length, buf);
- s = CX_STR("\" >\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, "<D:propfind xmlns:D=\"DAV:\" xmlns:i=\"");
+ cxBufferPutString(buf, DAV_NS);
+ cxBufferPutString(buf, "\" >\n");
// properties
- s = CX_STR("<D:prop>\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
- s = CX_STR("<D:resourcetype />\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
- s = CX_STR("<i:crypto-key />\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
- s = CX_STR("<i:crypto-name />\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
- s = CX_STR("<i:crypto-hash />\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
- s = CX_STR("</D:prop>\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, "<D:prop>\n");
+ cxBufferPutString(buf, "<D:resourcetype />\n");
+ cxBufferPutString(buf, "<i:crypto-key />\n");
+ cxBufferPutString(buf, "<i:crypto-name />\n");
+ cxBufferPutString(buf, "<i:crypto-hash />\n");
+ cxBufferPutString(buf, "</D:prop>\n");
// end
- s = CX_STR("</D:propfind>\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, "</D:propfind>\n");
return buf;
}
char *crypto_name = NULL; // name set by crypto-name property
char *crypto_key = NULL;
- result->properties = cxLinkedListCreateSimple(CX_STORE_POINTERS); // xmlNode list
+ result->properties = cxLinkedListCreate(NULL, CX_STORE_POINTERS); // xmlNode list
xmlNode *node = parser->current->children;
while(node) {
return -1;
}
status_str = cx_strsubsl(status_str, 9, 3);
- if(!cx_strcmp(status_str, CX_STR("200"))) {
+ if(!cx_strcmp(status_str, cx_str("200"))) {
ok = 1;
}
}
DavResource* parse_propfind_response(DavSession *sn, DavResource *root, CxBuffer *response) {
char *url = NULL;
curl_easy_getinfo(sn->handle, CURLINFO_EFFECTIVE_URL, &url);
- if(!root) {
- printf("methods.c: TODO: remove\n");
- root = dav_resource_new_href(sn, util_url_path(url)); // TODO: remove
- }
//printf("%.*s\n\n", response->size, response->space);
xmlDoc *doc = xmlReadMemory(response->space, response->size, url, NULL, 0);
//DavResource *res = resource;
DavResource *res = NULL;
const char *href = NULL;
- CxList *properties = cxLinkedListCreateSimple(CX_STORE_POINTERS); // xmlNode list
+ CxList *properties = cxLinkedListCreate(NULL, CX_STORE_POINTERS); // xmlNode list
char *crypto_name = NULL; // name set by crypto-name property
char *crypto_key = NULL;
return 1;
}
status_str = cx_strsubsl(status_str, 9, 3);
- if(!cx_strcmp(status_str, CX_STR("200"))) {
+ if(!cx_strcmp(status_str, cx_str("200"))) {
ok = 1;
}
}
}
CxBuffer* create_proppatch_request(DavResourceData *data) {
- CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ CxBuffer *buf = cxBufferCreate(cxDefaultAllocator, NULL, 512, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
cxstring s;
CxMap *namespaces = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 8);
- cxDefineDestructor(namespaces, free);
+ cxSetDestructor(namespaces, free);
{
char prefix[8];
cx_foreach(DavProperty*, p, i) {
if (strcmp(p->ns->name, "DAV:")) {
snprintf(prefix, 8, "x%d", pfxnum++);
- cxMapPut(namespaces, cx_hash_key_str(p->ns->name), strdup(prefix));
+ cxMapPut(namespaces, p->ns->name, strdup(prefix));
}
}
}
cx_foreach(DavProperty*, p, i) {
if (strcmp(p->ns->name, "DAV:")) {
snprintf(prefix, 8, "x%d", pfxnum++);
- cxMapPut(namespaces, cx_hash_key_str(p->ns->name), strdup(prefix));
+ cxMapPut(namespaces, p->ns->name, strdup(prefix));
}
}
}
}
- s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
// write root element and namespaces
- s = CX_STR("<D:propertyupdate xmlns:D=\"DAV:\"");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, "<D:propertyupdate xmlns:D=\"DAV:\"");
CxMapIterator mapi = cxMapIterator(namespaces);
cx_foreach(CxMapEntry*, entry, mapi) {
- s = CX_STR(" xmlns:");
- cxBufferWrite(s.ptr, 1, s.length, buf);
- s = cx_str(entry->value);
- cxBufferWrite(s.ptr, 1, s.length, buf);
- s = CX_STR("=\"");
- cxBufferWrite(s.ptr, 1, s.length, buf);
- s = cx_strn(entry->key->data, entry->key->len);
- cxBufferWrite(s.ptr, 1, s.length, buf);
- s = CX_STR("\"");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, " xmlns:");
+ cxBufferPutString(buf, cx_str(entry->value));
+ cxBufferPutString(buf, "=\"");
+ cxBufferPutString(buf, cx_strn(entry->key->data, entry->key->len));
+ cxBufferPutString(buf, "\"");
}
- s = CX_STR(">\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, ">\n");
if(data->set) {
- s = CX_STR("<D:set>\n<D:prop>\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, "<D:set>\n<D:prop>\n");
CxIterator i = cxListIterator(data->set);
cx_foreach(DavProperty*, property, i) {
- char *prefix = cxMapGet(namespaces, cx_hash_key_str(property->ns->name));
+ char *prefix = cxMapGet(namespaces, property->ns->name);
if(!prefix) {
prefix = "D";
}
// begin tag
- s = CX_STR("<");
- cxBufferWrite(s.ptr, 1, s.length, buf);
- s = cx_str(prefix);
- cxBufferWrite(s.ptr, 1, s.length, buf);
- s = CX_STR(":");
- cxBufferWrite(s.ptr, 1, s.length, buf);
- s = cx_str(property->name);
- cxBufferWrite(s.ptr, 1, s.length, buf);
- s = CX_STR(">");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, "<");
+ cxBufferPutString(buf, prefix);
+ cxBufferPutString(buf, ":");
+ cxBufferPutString(buf, property->name);
+ cxBufferPutString(buf, ">");
// content
DavXmlNode *content = property->value;
}
// end tag
- s = CX_STR("</");
- cxBufferWrite(s.ptr, 1, s.length, buf);
- s = cx_str(prefix);
- cxBufferWrite(s.ptr, 1, s.length, buf);
- s = CX_STR(":");
- cxBufferWrite(s.ptr, 1, s.length, buf);
- s = cx_str(property->name);
- cxBufferWrite(s.ptr, 1, s.length, buf);
- s = CX_STR(">\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, "</");
+ cxBufferPutString(buf, prefix);
+ cxBufferPutString(buf, ":");
+ cxBufferPutString(buf, property->name);
+ cxBufferPutString(buf, ">\n");
}
- s = CX_STR("</D:prop>\n</D:set>\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, "</D:prop>\n</D:set>\n");
}
if(data->remove) {
- s = CX_STR("<D:remove>\n<D:prop>\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, "<D:remove>\n<D:prop>\n");
CxIterator i = cxListIterator(data->remove);
cx_foreach(DavProperty*, property, i) {
- char *prefix = cxMapGet(namespaces, cx_hash_key_str(property->ns->name));
+ char *prefix = cxMapGet(namespaces, property->ns->name);
- s = CX_STR("<");
- cxBufferWrite(s.ptr, 1, s.length, buf);
- s = cx_str(prefix);
- cxBufferWrite(s.ptr, 1, s.length, buf);
- s = CX_STR(":");
- cxBufferWrite(s.ptr, 1, s.length, buf);
- s = cx_str(property->name);
- cxBufferWrite(s.ptr, 1, s.length, buf);
- s = CX_STR(" />\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, "<");
+ cxBufferPutString(buf, prefix);
+ cxBufferPutString(buf, ":");
+ cxBufferPutString(buf, property->name);
+ cxBufferPutString(buf, " />\n");
}
- s = CX_STR("</D:prop>\n</D:remove>\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, "</D:prop>\n</D:remove>\n");
}
- s = CX_STR("</D:propertyupdate>\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, "</D:propertyupdate>\n");
// cleanup namespace map
cxMapFree(namespaces);
}
CxBuffer* create_crypto_proppatch_request(DavSession *sn, DavKey *key, const char *name, const char *hash) {
- CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ CxBuffer *buf = cxBufferCreate(cxDefaultAllocator, NULL, 512, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
cxstring s;
- s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
- s = CX_STR("<D:propertyupdate xmlns:D=\"DAV:\" xmlns:idav=\"" DAV_NS "\">\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, "<D:propertyupdate xmlns:D=\"DAV:\" xmlns:idav=\"" DAV_NS "\">\n");
- s = CX_STR("<D:set>\n<D:prop>\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, "<D:set>\n<D:prop>\n");
if(DAV_ENCRYPT_NAME(sn)) {
- s = CX_STR("<idav:crypto-name>");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, "<idav:crypto-name>");
char *crname = aes_encrypt(name, strlen(name), key);
cxBufferPutString(buf, crname);
free(crname);
- s = CX_STR("</idav:crypto-name>\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, "</idav:crypto-name>\n");
}
- s = CX_STR("<idav:crypto-key>");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, "<idav:crypto-key>");
cxBufferPutString(buf, key->name);
- s = CX_STR("</idav:crypto-key>\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, "</idav:crypto-key>\n");
if(hash) {
- s = CX_STR("<idav:crypto-hash>");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, "<idav:crypto-hash>");
cxBufferPutString(buf, hash);
- s = CX_STR("</idav:crypto-hash>\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, "</idav:crypto-hash>\n");
}
- s = CX_STR("</D:prop>\n</D:set>\n</D:propertyupdate>\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, "</D:prop>\n</D:set>\n</D:propertyupdate>\n");
return buf;
}
CxBuffer *buf = NULL;
if(!read_func) {
- buf = cxBufferCreate(data, length, cxDefaultAllocator, 0);
+ buf = cxBufferCreate(cxDefaultAllocator, data, length, 0);
buf->size = length;
data = buf;
read_func = (dav_read_func)cxBufferRead;
free(ltheader);
}
//cxstring deststr = ucx_sprintf("Destination: %s", dest);
- cxmutstr deststr = cx_strcat(2, CX_STR("Destination: "), cx_str(dest));
+ cxmutstr deststr = cx_strcat(CX_NULLSTR, 2, cx_str("Destination: "), cx_str(dest));
headers = curl_slist_append(headers, deststr.ptr);
if(override) {
headers = curl_slist_append(headers, "Overwrite: T");
CxBuffer* create_lock_request(void) {
- CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ CxBuffer *buf = cxBufferCreate(cxDefaultAllocator, NULL, 512, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
cxstring s;
- s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
- s = CX_STR("<D:lockinfo xmlns:D=\"DAV:\">\n"
+ s = cx_str("<D:lockinfo xmlns:D=\"DAV:\">\n"
"<D:lockscope><D:exclusive/></D:lockscope>\n"
"<D:locktype><D:write/></D:locktype>\n"
"<D:owner><D:href>http://davutils.org/libidav/</D:href></D:owner>\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, s);
- s = CX_STR("</D:lockinfo>\n");
- cxBufferWrite(s.ptr, 1, s.length, buf);
+ cxBufferPutString(buf, "</D:lockinfo>\n");
return buf;
}
#include <stdlib.h>
#include <string.h>
-#include <cx/utils.h>
+#include <cx/streams.h>
+
#include <cx/hash_map.h>
#ifdef _WIN32
return NULL;
}
- CxBuffer *buf = cxBufferCreate(NULL, 2048, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ CxBuffer *buf = cxBufferCreate(cxDefaultAllocator, NULL, 2048, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
cx_stream_copy(in, buf, (cx_read_func)fread, (cx_write_func)cxBufferWrite);
fclose(in);
PwdStore *p = malloc(sizeof(PwdStore));
p->ids = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);
- p->locations = cxLinkedListCreateSimple(CX_STORE_POINTERS);
- p->noloc = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+ p->locations = cxLinkedListCreate(NULL, CX_STORE_POINTERS);
+ p->noloc = cxLinkedListCreate(NULL, CX_STORE_POINTERS);
p->index = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);
p->content = buf;
p->key = NULL;
PwdStore* pwdstore_new(void) {
PwdStore *p = calloc(1, sizeof(PwdStore));
p->ids = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);
- p->locations = cxLinkedListCreateSimple(CX_STORE_POINTERS);
- p->noloc = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+ p->locations = cxLinkedListCreate(NULL, CX_STORE_POINTERS);
+ p->noloc = cxLinkedListCreate(NULL, CX_STORE_POINTERS);
p->index = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);
- p->content = cxBufferCreate(NULL, PWDS_HEADER_SIZE, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ p->content = cxBufferCreate(cxDefaultAllocator, NULL, PWDS_HEADER_SIZE, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
PWDS_MAGIC(p) = PWDS_MAGIC_CHAR;
PWDS_VERSION(p) = 1;
PWDS_ENC(p) = DAV_KEY_AES256;
PwdStore *newp = calloc(1, sizeof(PwdStore));
newp->ids = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);
- newp->locations = cxLinkedListCreateSimple(CX_STORE_POINTERS);
- newp->noloc = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+ newp->locations = cxLinkedListCreate(NULL, CX_STORE_POINTERS);
+ newp->noloc = cxLinkedListCreate(NULL, CX_STORE_POINTERS);
newp->index = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);
newp->content = newbuffer;
newp->key = key;
PwdIndexEntry *entry = e->value;
CxList *locations = NULL;
if(entry->locations) {
- locations = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+ locations = cxLinkedListCreate(NULL, CX_STORE_POINTERS);
CxIterator li = cxListIterator(entry->locations);
cx_foreach(char *, location, li) {
cxListAdd(locations, strdup(location));
}
char *id = NULL;
- CxList *locations = cxLinkedListCreateSimple(CX_STORE_POINTERS);
- cxDefineDestructor(locations, free);
+ CxList *locations = cxLinkedListCreate(NULL, CX_STORE_POINTERS);
+ cxSetDestructor(locations, free);
// get id (required)
int ret = 0;
s->encoffset = PWDS_HEADER_SIZE + indexlen;
// the index starts after the header
- CxBuffer *index = cxBufferCreate(s->content->space+PWDS_HEADER_SIZE, indexlen, cxDefaultAllocator, 0);
+ CxBuffer *index = cxBufferCreate(cxDefaultAllocator, s->content->space+PWDS_HEADER_SIZE, indexlen, 0);
index->size = indexlen;
// read index
// decrypt contet
size_t encsz = p->content->size - p->encoffset;
- CxBuffer *enc = cxBufferCreate(p->content->space + p->encoffset, encsz, cxDefaultAllocator, 0);
+ CxBuffer *enc = cxBufferCreate(cxDefaultAllocator, p->content->space + p->encoffset, encsz, 0);
enc->size = encsz;
enc->size = p->content->size - p->encoffset;
CxBuffer *content = aes_decrypt_buffer(enc, p->key);
}
void pwdstore_free(PwdStore* p) {
- cxDefineDestructor(p->ids, pwdstore_free_entry);
+ cxSetDestructor(p->ids, pwdstore_free_entry);
cxMapFree(p->ids);
cxListFree(p->locations);
}
int pwdstore_has_id(PwdStore *s, const char *id) {
- return cxMapGet(s->index, cx_hash_key_str(id)) ? 1 : 0;
+ return cxMapGet(s->index, id) ? 1 : 0;
}
PwdEntry* pwdstore_get(PwdStore *p, const char *id) {
- PwdEntry *e = cxMapGet(p->ids, cx_hash_key_str(id));
+ PwdEntry *e = cxMapGet(p->ids, id);
if(e && e->user && e->password) {
return e;
} else {
entry->id = strdup(id);
entry->user = strdup(username);
entry->password = strdup(password);
- cxMapPut(p->ids, cx_hash_key_str(id), entry);
+ cxMapPut(p->ids, id, entry);
}
void pwdstore_put_index(PwdStore *p, char *id, CxList *locations) {
- PwdIndexEntry *e = cxMapGet(p->index, cx_hash_key_str(id));
+ PwdIndexEntry *e = cxMapGet(p->index, id);
if(e) {
return;
}
newentry->locations = NULL;
cxListAdd(p->noloc, newentry);
}
- cxMapPut(p->index, cx_hash_key_str(id), newentry);
+ cxMapPut(p->index, id, newentry);
}
void write_index_entry(CxBuffer *out, PwdIndexEntry *e) {
return 1;
}
- CxBuffer *index = cxBufferCreate(NULL, 2048, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
- CxBuffer *content = cxBufferCreate(NULL, 2048, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ CxBuffer *index = cxBufferCreate(cxDefaultAllocator, NULL, 2048, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ CxBuffer *content = cxBufferCreate(cxDefaultAllocator, NULL, 2048, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
// create index
CxIterator i = cxListIterator(p->noloc);
char *ps_password = NULL;
if(secrets->unlock_cmd && strlen(secrets->unlock_cmd) > 0) {
- CxBuffer *cmd_out = cxBufferCreate(NULL, 128, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ CxBuffer *cmd_out = cxBufferCreate(cxDefaultAllocator, NULL, 128, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
if(!util_exec_command(secrets->unlock_cmd, cmd_out)) {
// command successful, get first line from output without newline
// and use that as password for the secretstore
#include "methods.h"
#include "crypto.h"
#include <cx/buffer.h>
-#include <cx/utils.h>
#include <cx/hash_map.h>
#include <cx/printf.h>
#include <cx/mempool.h>
cxstring ns_str = cx_str(ns);
cxstring name_str = cx_str(name);
- return cx_strcat_a(a, 4, ns_str, CX_STR("\0"), name_str, CX_STR("\0"));
+ return cx_strcat_a(a, CX_NULLSTR, 4, ns_str, cx_str("\0"), name_str, cx_str("\0"));
}
static int add2propertylist(const CxAllocator *a, CxList **list, DavProperty *property) {
if(!*list) {
- CxList *newlist = cxLinkedListCreate(a, NULL, CX_STORE_POINTERS);
+ CxList *newlist = cxLinkedListCreate(a, CX_STORE_POINTERS);
if(!newlist) {
return 1;
}
CxMempool *mp = cxMempoolCreateSimple(64);
const CxAllocator *a = mp->allocator;
- CxList *proplist = cxArrayListCreate(a, NULL, sizeof(DavProperty), numprop);
+ CxList *proplist = cxArrayListCreate(a, sizeof(DavProperty), numprop);
for(size_t i=0;i<numprop;i++) {
DavProperty p;
p.name = properties[i].name;
HashStream *s = stream;
if(offset == 0 && whence == SEEK_SET) {
unsigned char buf[DAV_SHA256_DIGEST_LENGTH];
- dav_sha256_final(s->sha, buf);
+ dav_sha256_final_free(s->sha, buf);
s->sha = NULL;
} else {
s->error = 1;
data->read,
data->seek);
} else {
- buf = cxBufferCreate(data->content, data->length, cxDefaultAllocator, 0);
+ buf = cxBufferCreate(cxDefaultAllocator, data->content, data->length, 0);
buf->size = data->length;
enc = aes_encrypter_new(
sn->key,
HashStream hstr;
CxBuffer *iobuf = NULL;
if(!data->read) {
- iobuf = cxBufferCreate(data->content, data->length, cxDefaultAllocator, 0);
+ iobuf = cxBufferCreate(cxDefaultAllocator, data->content, data->length, 0);
iobuf->size = data->length;
init_hash_stream(
&hstr,
data->length);
if(hstr.sha) {
- dav_sha256_final(hstr.sha, (unsigned char*)data->hash);
+ dav_sha256_final_free(hstr.sha, (unsigned char*)data->hash);
char *hash = util_hexstr((unsigned char*)data->hash, 32);
dav_set_string_property_ns(res, DAV_NS, "content-hash", hash);
free(hash);
sn->error = DAV_OK;
if(data->set || data->remove > 0) {
CxBuffer *request = create_proppatch_request(data);
- CxBuffer *response = cxBufferCreate(NULL, 1024, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ CxBuffer *response = cxBufferCreate(cxDefaultAllocator, NULL, 1024, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
//printf("request:\n%.*s\n\n", request->pos, request->space);
CURLcode ret = do_proppatch_request(sn, locktoken, request, response);
DavLock *lock = dav_get_lock(res->session, res->path);
char *locktoken = lock ? lock->token : NULL;
- CxBuffer *response = cxBufferCreate(NULL, 4096, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ CxBuffer *response = cxBufferCreate(cxDefaultAllocator, NULL, 4096, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
CURLcode ret = do_delete_request(res->session, locktoken, response);
long status = 0;
curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status);
util_set_url(sn, dav_resource_get_href(res));
CxBuffer *request = create_lock_request();
- CxBuffer *response = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ CxBuffer *response = cxBufferCreate(cxDefaultAllocator, NULL, 512, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
CURLcode ret = do_lock_request(sn, request, response, timeout);
//printf("\nlock\n");
}
CxBuffer *request = create_crypto_proppatch_request(sn, sn->key, name, hash);
- CxBuffer *response = cxBufferCreate(NULL, 1024, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ CxBuffer *response = cxBufferCreate(cxDefaultAllocator, NULL, 1024, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
util_set_url(sn, href);
// TODO: lock
return NULL;
}
- CxBuffer *content = cxBufferCreate(NULL, 2048, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ CxBuffer *content = cxBufferCreate(cxDefaultAllocator, NULL, 2048, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
// create an xml document containing all properties
CxMap *nsmap = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 8);
- cxDefineDestructor(nsmap, free);
- cxMapPut(nsmap, cx_hash_key_str("DAV:"), strdup("D"));
+ cxSetDestructor(nsmap, free);
+ cxMapPut(nsmap, "DAV:", strdup("D"));
cxBufferPutString(content, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
cxBufferPutString(content, "<D:prop xmlns:D=\"DAV:\">\n");
sn->locks = NULL;
// set proxy
- DavProxy *proxy = cx_strprefix(url, CX_STR("https")) ? context->https_proxy
+ DavProxy *proxy = cx_strprefix(url, cx_str("https")) ? context->https_proxy
: context->http_proxy;
if (proxy->url) {
cxstring p = cx_str(path);
CxBuffer href;
CxBuffer pbuf;
- cxBufferInit(&href, NULL, 256, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
- cxBufferInit(&pbuf, NULL, 256, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ cxBufferInit(&href, cxDefaultAllocator, NULL, 256, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ cxBufferInit(&pbuf, cxDefaultAllocator, NULL, 256, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
int start = 0;
int begin = 0;
char *cp = strdup(path);
//printf("cp: %s\n", cp);
while(strlen(cp) > 1) {
- char *cached = cxMapGet(sn->pathcache, cx_hash_key_str(cp));
+ char *cached = cxMapGet(sn->pathcache, cp);
if(cached) {
start = strlen(cp);
begin = start;
CxBuffer *rqbuf = create_basic_propfind_request();
cxstring remaining = cx_strsubs(p, start);
- CxStrtokCtx elms = cx_strtok(remaining, CX_STR("/"), INT_MAX);
+ CxStrtokCtx elms = cx_strtok(remaining, cx_str("/"), INT_MAX);
DavResource *res = root;
cxBufferPutString(&pbuf, res->path);
// iterate over all remaining path elements
// create lock manager
DavLockManager *locks = cxMalloc(sn->mp->allocator, sizeof(DavLockManager));
locks->resource_locks = cxHashMapCreate(sn->mp->allocator, CX_STORE_POINTERS, 16);
- locks->collection_locks = cxLinkedListCreate(sn->mp->allocator, dav_lock_cmp, CX_STORE_POINTERS);
+ locks->collection_locks = cxLinkedListCreate(sn->mp->allocator, CX_STORE_POINTERS);
+ cxSetCompareFunc(locks->collection_locks, dav_lock_cmp);
sn->locks = locks;
return 0;
}
cxstring p = cx_str(path);
- DavLock *lock = cxMapGet(locks->resource_locks, cx_hash_key(p.ptr, p.length));
+ DavLock *lock = cxMapGet(locks->resource_locks, p);
if(lock) {
return lock;
}
return;
}
- if(!cxMapRemove(locks->resource_locks, cx_hash_key_str(path))) {
+ if(!cxMapRemove(locks->resource_locks, path)) {
return;
}
#include <ctype.h>
#include <cx/string.h>
#include <cx/buffer.h>
-#include <cx/utils.h>
#include <cx/printf.h>
#include <libxml/tree.h>
#include <curl/curl.h>
}
cxmutstr value_cp = cx_strdup(cx_strtrim(value));
- cxMapPut(map, cx_hash_key(key_cp.ptr, key_cp.length), value_cp.ptr);
+ cxMapPut(map, key_cp, value_cp.ptr);
free(key_cp.ptr);
char* util_path_normalize(const char *path) {
size_t len = strlen(path);
CxBuffer buf;
- cxBufferInit(&buf, NULL, len+1, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ cxBufferInit(&buf, cxDefaultAllocator, NULL, len+1, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
if(path[0] == '/') {
cxBufferPut(&buf, '/');
if(seg_len > 0) {
cxstring seg = cx_strn(seg_ptr, seg_len);
- if(!cx_strcmp(seg, CX_STR(".."))) {
+ if(!cx_strcmp(seg, cx_str(".."))) {
for(int j=buf.pos;j>=0;j--) {
char t = j < buf.pos ? buf.space[j] : 0;
if(IS_PATH_SEPARATOR(t) || j == 0) {
break;
}
}
- } else if(!cx_strcmp(seg, CX_STR("."))) {
+ } else if(!cx_strcmp(seg, cx_str("."))) {
// ignore
} else {
if(add_separator) {
}
}
- cxBufferInit(&out, NULL, dircount*3+path_len-last_dir, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ cxBufferInit(&out, cxDefaultAllocator, NULL, dircount*3+path_len-last_dir, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
for(size_t i=0;i<dircount;i++) {
cxBufferPutString(&out, "../");
}
} else {
- cxBufferInit(&out, NULL, path_len - last_dir, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ cxBufferInit(&out, cxDefaultAllocator, NULL, path_len - last_dir, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
}
cxBufferPutString(&out, abspath + last_dir + 1);
if(p) {
path = cx_str((char*)p);
} else {
- path = CX_STR("");
+ path = cx_str("");
}
return util_concat_path_s(base, path).ptr;
cxmutstr util_concat_path_s(cxstring base, cxstring path) {
if(!path.ptr) {
- path = CX_STR("");
+ path = cx_str("");
}
int add_separator = 0;
cxmutstr url;
if(add_separator) {
- url = cx_strcat(3, base, CX_STR("/"), path);
+ url = cx_strcat(CX_NULLSTR, 3, base, cx_str("/"), path);
} else {
- url = cx_strcat(2, base, path);
+ url = cx_strcat(CX_NULLSTR, 2, base, path);
}
return url;
cxmutstr util_concat_path_ext(cxstring base, cxstring path, char separator) {
if(!path.ptr) {
- path = CX_STR("");
+ path = cx_str("");
}
int add_separator = 0;
cxmutstr url;
if(add_separator) {
- url = cx_strcat(3, base, cx_strn(&separator, 1), path);
+ url = cx_strcat(CX_NULLSTR, 3, base, cx_strn(&separator, 1), path);
} else {
- url = cx_strcat(2, base, path);
+ url = cx_strcat(CX_NULLSTR, 2, base, path);
}
return url;
const char *base_path = util_url_path(sn->base_url);
base.length -= strlen(base_path);
- cxmutstr url = cx_strcat(2, base, href_str);
+ cxmutstr url = cx_strcat(CX_NULLSTR, 2, base, href_str);
return url.ptr;
}
}
CxBuffer url;
- cxBufferInit(&url, NULL, 256, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ cxBufferInit(&url, cxDefaultAllocator, NULL, 256, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
// add base url
cxBufferWrite(sn->base_url, 1, strlen(sn->base_url), &url);
cxstring p = cx_strn(path, pathlen);
- CxStrtokCtx tkctx = cx_strtok(p, CX_STR("/"), INT_MAX);
+ CxStrtokCtx tkctx = cx_strtok(p, cx_str("/"), INT_MAX);
cxstring node;
while(cx_strtok_next(&tkctx, &node)) {
if(node.length > 0) {
unsigned char *str = malloc(25);
str[24] = '\0';
- cxstring t = CX_STR(
+ cxstring t = cx_str(
"01234567890"
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ");
cxmutstr util_readline(FILE *stream) {
CxBuffer buf;
- cxBufferInit(&buf, NULL, 128, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ cxBufferInit(&buf, cxDefaultAllocator, NULL, 128, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
int c;
while((c = fgetc(stream)) != EOF) {
// read password input
CxBuffer buf;
- cxBufferInit(&buf, NULL, 128, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ cxBufferInit(&buf, cxDefaultAllocator, NULL, 128, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
int c = 0;
while((c = getpasswordchar()) != EOF) {
if(c == '\n' || c == '\r') {
char* util_hexstr(const unsigned char *data, size_t len) {
size_t buflen = 2*len + 4;
CxBuffer buf;
- cxBufferInit(&buf, NULL, buflen + 1, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ cxBufferInit(&buf, cxDefaultAllocator, NULL, buflen + 1, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
for(int i=0;i<len;i++) {
cx_bprintf(&buf, "%02x", data[i]);
}
// create a version-tree request, which is almost the same as propfind
CxBuffer *rqbuf = create_propfind_request(sn, proplist, "version-tree", 1);
- CxBuffer *rpbuf = cxBufferCreate(NULL, 4096, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ CxBuffer *rpbuf = cxBufferCreate(cxDefaultAllocator, NULL, 4096, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
// do the request
CURLcode ret = do_report_request(sn, rqbuf, rpbuf);
#include "session.h"
#include "methods.h"
#include <cx/buffer.h>
-#include <cx/utils.h>
#include <cx/linked_list.h>
#include <cx/hash_map.h>
#include <cx/compare.h>
if(!context) {
return NULL;
}
- context->sessions = cxLinkedListCreate(cxDefaultAllocator, cx_cmp_ptr, CX_STORE_POINTERS);
- cxDefineDestructor(context->sessions, dav_session_destructor);
+ context->sessions = cxLinkedListCreate(cxDefaultAllocator, CX_STORE_POINTERS);
+ cxSetDestructor(context->sessions, dav_session_destructor);
context->http_proxy = calloc(1, sizeof(DavProxy));
if(!context->http_proxy) {
dav_context_destroy(context);
DavNamespace* dav_get_namespace(DavContext *context, const char *prefix) {
dav_context_lock(context);
- DavNamespace *ns = cxMapGet(context->namespaces, cx_hash_key_str(prefix));
+ DavNamespace *ns = cxMapGet(context->namespaces, prefix);
dav_context_unlock(context);
return ns;
}
DavNamespace* dav_get_namespace_s(DavContext *context, cxstring prefix) {
dav_context_lock(context);
- DavNamespace *ns = cxMapGet(context->namespaces, cx_hash_key(prefix.ptr, prefix.length));
+ DavNamespace *ns = cxMapGet(context->namespaces, prefix);
dav_context_unlock(context);
return ns;
}
proplist = parse_properties_string(sn->context, cx_str(properties));
}
CxBuffer *rqbuf = create_propfind_request(sn, proplist, "propfind", 0);
- CxBuffer *rpbuf = cxBufferCreate(NULL, 4096, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ CxBuffer *rpbuf = cxBufferCreate(cxDefaultAllocator, NULL, 4096, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
//fwrite(rqbuf->space, 1, rqbuf->size, stdout);
//printf("\n");
CURL *handle = sn->handle;
util_set_url(sn, dav_resource_get_href(root));
- CxBuffer *rpbuf = cxBufferCreate(NULL, 4096, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ CxBuffer *rpbuf = cxBufferCreate(cxDefaultAllocator, NULL, 4096, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
DavResource *resource = root;
CURLcode ret = do_propfind_request(sn, rqbuf, rpbuf);
long status = 0;
}
CxList* parse_properties_string(DavContext *context, cxstring str) {
- CxList *proplist = cxLinkedListCreateSimple(sizeof(DavProperty));
+ CxList *proplist = cxLinkedListCreate(NULL, sizeof(DavProperty));
CxStrtokCtx tok = cx_strtok(str, cx_str(","), INT_MAX);
cxstring s;
#include <stdlib.h>
#include <string.h>
-#include <cx/utils.h>
#include <cx/printf.h>
#include "xml.h"
ConvXmlElm ce;
ce.node = node;
ce.parent = NULL;
- CxList *stack = cxLinkedListCreate(cxDefaultAllocator, NULL, sizeof(ConvXmlElm));
+ CxList *stack = cxLinkedListCreate(cxDefaultAllocator, sizeof(ConvXmlElm));
if(!stack) {
return NULL;
}
void* scan_run(void *data) {
CPSettings *settings = data;
- CxList *stack = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+ CxList *stack = cxLinkedListCreate(NULL, CX_STORE_POINTERS);
char *root = strdup("");
int handle_client_msg(CtrlClient *client, cxstring msg) {
printf("msg: %.*s\n", (int)msg.length, msg.ptr);
- if(!cx_strcmp(msg, CX_STR("abort"))) {
+ if(!cx_strcmp(msg, "abort")) {
return -1;
}
static CxList *cfg_bookmarks;
void bookmarks_init(void) {
- cfg_default_dirs = cxArrayListCreateSimple(sizeof(MZBookmark), 8);
- cfg_bookmarks = cxArrayListCreateSimple(sizeof(MZBookmark), 8);
+ cfg_default_dirs = cxArrayListCreate(NULL, sizeof(MZBookmark), 8);
+ cfg_bookmarks = cxArrayListCreate(NULL, sizeof(MZBookmark), 8);
bookmarks_import_xdg_user_dirs(cfg_bookmarks, NULL);
}
cxmutstr bookmark_path;
- if(cx_strprefix(value, CX_STR("$HOME/"))) {
- bookmark_path = cx_strcat(2, cx_str(getenv("HOME")), cx_strsubs(value, 5));
+ if(cx_strprefix(value, "$HOME/")) {
+ bookmark_path = cx_strcat(CX_NULLSTR, 2, cx_str(getenv("HOME")), cx_strsubs(value, 5));
} else {
bookmark_path = cx_strdup(value);
}
const char *icon = "folder-symbolic";
- if(!cx_strcmp(key, CX_STR("XDG_DOWNLOAD_DIR"))) {
+ if(!cx_strcmp(key, "XDG_DOWNLOAD_DIR")) {
icon = "folder-download-symbolic";
- } else if(!cx_strcmp(key, CX_STR("XDG_DOCUMENTS_DIR"))) {
+ } else if(!cx_strcmp(key, "XDG_DOCUMENTS_DIR")) {
icon = "folder-documents-symbolic";
- } else if(!cx_strcmp(key, CX_STR("XDG_MUSIC_DIR"))) {
+ } else if(!cx_strcmp(key, "XDG_MUSIC_DIR")) {
icon = "folder-music-symbolic";
- } else if(!cx_strcmp(key, CX_STR("XDG_PICTURES_DIR"))) {
+ } else if(!cx_strcmp(key, "XDG_PICTURES_DIR")) {
icon = "folder-pictures-symbolic";
- } else if(!cx_strcmp(key, CX_STR("XDG_VIDEOS_DIR"))) {
+ } else if(!cx_strcmp(key, "XDG_VIDEOS_DIR")) {
icon = "folder-videos-symbolic";
- } else if(!cx_strcmp(key, CX_STR("XDG_PUBLICSHARE_DIR"))) {
+ } else if(!cx_strcmp(key, "XDG_PUBLICSHARE_DIR")) {
icon = "folder-publicshare-symbolic";
- } else if(!cx_strcmp(key, CX_STR("XDG_DESKTOP_DIR"))) {
+ } else if(!cx_strcmp(key, "XDG_DESKTOP_DIR")) {
icon = "user-desktop-symbolic";
}
return 0;
}
- op->result = cxArrayListCreateSimple(sizeof(FileInfo), 1024);
+ op->result = cxArrayListCreate(NULL, sizeof(FileInfo), 1024);
op->result->collection.simple_destructor = (cx_destructor_func)cleanup_file_info;
struct dirent *ent;
struct stat s;
closedir(dir);
- op->result->collection.cmpfunc = (cx_compare_func)fileinfo_type_name_cmp;
+ cxSetCompareFunc(op->result, (cx_compare_func)fileinfo_type_name_cmp);
cxListSort(op->result);
return 0;
#include <errno.h>
#include <string.h>
+#ifdef _WIN32
+#include <Windows.h>
+#include <sysinfoapi.h>
+unsigned long cx_system_page_size(void) {
+ static unsigned long ps = 0;
+ if (ps == 0) {
+ SYSTEM_INFO sysinfo;
+ GetSystemInfo(&sysinfo);
+ ps = (unsigned long) sysinfo.dwPageSize;
+ }
+ return ps;
+}
+#else
+#include <unistd.h>
+unsigned long cx_system_page_size(void) {
+ static unsigned long ps = 0;
+ if (ps == 0) {
+ long sc = sysconf(_SC_PAGESIZE);
+ if (sc < 0) {
+ // fallback for systems which do not report a value here
+ ps = 4096; // LCOV_EXCL_LINE
+ } else {
+ ps = (unsigned long) sc;
+ }
+ }
+ return ps;
+}
+#endif
+
static void *cx_malloc_stdlib(
- cx_attr_unused void *d,
+ CX_UNUSED void *d,
size_t n
) {
return malloc(n);
}
static void *cx_realloc_stdlib(
- cx_attr_unused void *d,
+ CX_UNUSED void *d,
void *mem,
size_t n
) {
}
static void *cx_calloc_stdlib(
- cx_attr_unused void *d,
+ CX_UNUSED void *d,
size_t nmemb,
size_t size
) {
}
static void cx_free_stdlib(
- cx_attr_unused void *d,
+ CX_UNUSED void *d,
void *mem
) {
free(mem);
void **mem,
size_t n
) {
+ if (n == 0) {
+ free(*mem);
+ *mem = NULL;
+ return 0;
+ }
void *nmem = realloc(*mem, n);
if (nmem == NULL) {
return 1; // LCOV_EXCL_LINE
size_t nmemb,
size_t size
) {
+ if (nmemb == 0 || size == 0) {
+ free(*mem);
+ *mem = NULL;
+ return 0;
+ }
size_t n;
if (cx_szmul(nmemb, size, &n)) {
errno = EOVERFLOW;
void **mem,
size_t n
) {
+ if (n == 0) {
+ cxFree(allocator, *mem);
+ *mem = NULL;
+ return 0;
+ }
void *nmem = allocator->cl->realloc(allocator->data, *mem, n);
if (nmem == NULL) {
return 1; // LCOV_EXCL_LINE
size_t nmemb,
size_t size
) {
+ if (nmemb == 0 || size == 0) {
+ cxFree(allocator, *mem);
+ *mem = NULL;
+ return 0;
+ }
void *nmem = cxReallocArray(allocator, *mem, nmemb, size);
if (nmem == NULL) {
return 1; // LCOV_EXCL_LINE
) {
allocator->cl->free(allocator->data, mem);
}
+
+void cxFreeDefault(void *mem) {
+ cxDefaultAllocator->cl->free(cxDefaultAllocator->data, mem);
+}
* POSSIBILITY OF SUCH DAMAGE.
*/
+#ifdef WITH_MEMRCHR
+#define _GNU_SOURCE
+#endif
+
#include "cx/array_list.h"
#include "cx/compare.h"
#include <assert.h>
#include <string.h>
#include <errno.h>
-// Default array reallocator
-
-static void *cx_array_default_realloc(
- void *array,
- cx_attr_unused size_t old_capacity,
- size_t new_capacity,
- size_t elem_size,
- cx_attr_unused CxArrayReallocator *alloc
-) {
- size_t n;
- if (cx_szmul(new_capacity, elem_size, &n)) {
- errno = EOVERFLOW;
- return NULL;
- }
- return cxReallocDefault(array, n);
-}
-
-CxArrayReallocator cx_array_default_reallocator_impl = {
- cx_array_default_realloc, NULL, NULL
-};
-
-CxArrayReallocator *cx_array_default_reallocator = &cx_array_default_reallocator_impl;
-
-// Stack-aware array reallocator
+// LOW LEVEL ARRAY LIST FUNCTIONS
-static void *cx_array_advanced_realloc(
- void *array,
- size_t old_capacity,
- size_t new_capacity,
- size_t elem_size,
- cx_attr_unused CxArrayReallocator *alloc
+/**
+ * Intelligently calculates a new capacity, reserving some more
+ * elements than required to prevent too many allocations.
+ *
+ * @param current_capacity the current capacity of the array
+ * @param needed_capacity the required capacity of the array
+ * @return the new capacity
+ */
+static size_t cx_array_grow_capacity(
+ size_t current_capacity,
+ size_t needed_capacity
) {
- // check for overflow
- size_t n;
- if (cx_szmul(new_capacity, elem_size, &n)) {
- errno = EOVERFLOW;
- return NULL;
- }
-
- // retrieve the pointer to the actual allocator
- const CxAllocator *al = alloc->allocator;
-
- // check if the array is still located on the stack
- void *newmem;
- if (array == alloc->stack_ptr) {
- newmem = cxMalloc(al, n);
- if (newmem != NULL && array != NULL) {
- memcpy(newmem, array, old_capacity*elem_size);
- }
- } else {
- newmem = cxRealloc(al, array, n);
+ if (current_capacity >= needed_capacity) {
+ return current_capacity;
}
- return newmem;
+ size_t cap = needed_capacity;
+ size_t alignment;
+ if (cap < 128) alignment = 16;
+ else if (cap < 1024) alignment = 64;
+ else if (cap < 8192) alignment = 512;
+ else alignment = 1024;
+ return cap - (cap % alignment) + alignment;
}
-struct cx_array_reallocator_s cx_array_reallocator(
- const struct cx_allocator_s *allocator,
- const void *stack_ptr
-) {
- if (allocator == NULL) {
- allocator = cxDefaultAllocator;
- }
- return (struct cx_array_reallocator_s) {
- cx_array_advanced_realloc,
- allocator, stack_ptr,
- };
+int cx_array_init_(const CxAllocator *allocator, CxArray *array, size_t elem_size, size_t capacity) {
+ memset(array, 0, sizeof(CxArray));
+ return cx_array_reserve_(allocator, array, elem_size, capacity);
}
-// LOW LEVEL ARRAY LIST FUNCTIONS
-
-static size_t cx_array_align_capacity(
- size_t cap,
- size_t alignment,
- size_t max
-) {
- if (cap > max - alignment) {
- return cap;
- } else {
- return cap - (cap % alignment) + alignment;
- }
+void cx_array_init_fixed_(CxArray *array, const void *data, size_t capacity, size_t size) {
+ array->data = (void*) data;
+ array->capacity = capacity;
+ array->size = size;
}
-int cx_array_reserve(
- void **array,
- void *size,
- void *capacity,
- unsigned width,
- size_t elem_size,
- size_t elem_count,
- CxArrayReallocator *reallocator
-) {
- // assert pointers
- assert(array != NULL);
- assert(size != NULL);
- assert(capacity != NULL);
-
- // default reallocator
- if (reallocator == NULL) {
- reallocator = cx_array_default_reallocator;
+int cx_array_reserve_(const CxAllocator *allocator, CxArray *array, size_t elem_size, size_t capacity) {
+ if (cxReallocateArray(allocator, &array->data, capacity, elem_size)) {
+ return -1; // LCOV_EXCL_LINE
}
-
- // determine size and capacity
- size_t oldcap;
- size_t oldsize;
- size_t max_size;
- if (width == 0 || width == sizeof(size_t)) {
- oldcap = *(size_t*) capacity;
- oldsize = *(size_t*) size;
- max_size = SIZE_MAX;
- } else if (width == sizeof(uint16_t)) {
- oldcap = *(uint16_t*) capacity;
- oldsize = *(uint16_t*) size;
- max_size = UINT16_MAX;
- } else if (width == sizeof(uint8_t)) {
- oldcap = *(uint8_t*) capacity;
- oldsize = *(uint8_t*) size;
- max_size = UINT8_MAX;
- }
-#if CX_WORDSIZE == 64
- else if (width == sizeof(uint32_t)) {
- oldcap = *(uint32_t*) capacity;
- oldsize = *(uint32_t*) size;
- max_size = UINT32_MAX;
- }
-#endif
- else {
- errno = EINVAL;
- return 1;
- }
-
- // assert that the array is allocated when it has capacity
- assert(*array != NULL || oldcap == 0);
-
- // check for overflow
- if (elem_count > max_size - oldsize) {
- errno = EOVERFLOW;
- return 1;
- }
-
- // determine new capacity
- size_t newcap = oldsize + elem_count;
-
- // reallocate if possible
- if (newcap > oldcap) {
- // calculate new capacity (next number divisible by 16)
- newcap = cx_array_align_capacity(newcap, 16, max_size);
-
- // perform reallocation
- void *newmem = reallocator->realloc(
- *array, oldcap, newcap, elem_size, reallocator
- );
- if (newmem == NULL) {
- return 1; // LCOV_EXCL_LINE
- }
-
- // store new pointer
- *array = newmem;
-
- // store new capacity
- if (width == 0 || width == sizeof(size_t)) {
- *(size_t*) capacity = newcap;
- } else if (width == sizeof(uint16_t)) {
- *(uint16_t*) capacity = (uint16_t) newcap;
- } else if (width == sizeof(uint8_t)) {
- *(uint8_t*) capacity = (uint8_t) newcap;
- }
-#if CX_WORDSIZE == 64
- else if (width == sizeof(uint32_t)) {
- *(uint32_t*) capacity = (uint32_t) newcap;
- }
-#endif
+ array->capacity = capacity;
+ if (array->size > capacity) {
+ array->size = capacity;
}
-
return 0;
}
-int cx_array_copy(
- void **target,
- void *size,
- void *capacity,
- unsigned width,
- size_t index,
- const void *src,
- size_t elem_size,
- size_t elem_count,
- CxArrayReallocator *reallocator
-) {
- // assert pointers
- assert(target != NULL);
- assert(size != NULL);
- assert(capacity != NULL);
- assert(src != NULL);
-
- // default reallocator
- if (reallocator == NULL) {
- reallocator = cx_array_default_reallocator;
- }
-
- // determine size and capacity
- size_t oldcap;
- size_t oldsize;
- size_t max_size;
- if (width == 0 || width == sizeof(size_t)) {
- oldcap = *(size_t*) capacity;
- oldsize = *(size_t*) size;
- max_size = SIZE_MAX;
- } else if (width == sizeof(uint16_t)) {
- oldcap = *(uint16_t*) capacity;
- oldsize = *(uint16_t*) size;
- max_size = UINT16_MAX;
- } else if (width == sizeof(uint8_t)) {
- oldcap = *(uint8_t*) capacity;
- oldsize = *(uint8_t*) size;
- max_size = UINT8_MAX;
- }
-#if CX_WORDSIZE == 64
- else if (width == sizeof(uint32_t)) {
- oldcap = *(uint32_t*) capacity;
- oldsize = *(uint32_t*) size;
- max_size = UINT32_MAX;
- }
-#endif
- else {
- errno = EINVAL;
- return 1;
+int cx_array_copy_to_new_(const CxAllocator *allocator, CxArray *array, size_t elem_size, size_t capacity) {
+ CxArray heap_array;
+ if (cx_array_init_(allocator, &heap_array, elem_size, capacity)) {
+ return -1; // LCOV_EXCL_LINE
}
+ heap_array.size = array->size;
+ memcpy(heap_array.data, array->data, elem_size * array->size);
+ *array = heap_array;
+ return 0;
+}
- // assert that the array is allocated when it has capacity
- assert(*target != NULL || oldcap == 0);
+int cx_array_insert_(const CxAllocator *allocator, CxArray *array,
+ size_t elem_size, size_t index, const void *other, size_t n) {
+ // out of bounds and special case check
+ if (index > array->size) return -1;
+ if (n == 0) return 0;
- // check for overflow
- if (index > max_size || elem_count > max_size - index) {
+ // calculate required capacity
+ size_t req_capacity = array->size + n;
+ if (req_capacity <= array->size) {
errno = EOVERFLOW;
- return 1;
+ return -1;
}
- // check if resize is required
- size_t minsize = index + elem_count;
- size_t newsize = oldsize < minsize ? minsize : oldsize;
-
- // reallocate if possible
- size_t newcap = oldcap;
- if (newsize > oldcap) {
- // check, if we need to repair the src pointer
- uintptr_t targetaddr = (uintptr_t) *target;
- uintptr_t srcaddr = (uintptr_t) src;
- bool repairsrc = targetaddr <= srcaddr
- && srcaddr < targetaddr + oldcap * elem_size;
-
- // calculate new capacity (next number divisible by 16)
- newcap = cx_array_align_capacity(newsize, 16, max_size);
- assert(newcap > newsize);
-
- // perform reallocation
- void *newmem = reallocator->realloc(
- *target, oldcap, newcap, elem_size, reallocator
- );
- if (newmem == NULL) {
- return 1; // LCOV_EXCL_LINE
+ // guarantee enough capacity
+ if (array->capacity < req_capacity) {
+ const size_t new_capacity = cx_array_grow_capacity(array->capacity,req_capacity);
+ if (cxReallocateArray(allocator, &array->data, new_capacity, elem_size)) {
+ return -1;
}
+ array->capacity = new_capacity;
+ }
- // repair src pointer, if necessary
- if (repairsrc) {
- src = ((char *) newmem) + (srcaddr - targetaddr);
- }
+ // determine insert position
+ char *dst = array->data;
+ dst += index * elem_size;
- // store new pointer
- *target = newmem;
+ // do we need to move some elements?
+ size_t elems_to_move = array->size - index;
+ if (elems_to_move > 0) {
+ char *target = dst + n * elem_size;
+ memmove(target, dst, elems_to_move * elem_size);
}
- // determine target pointer
- char *start = *target;
- start += index * elem_size;
-
- // copy elements and set new size
- // note: no overflow check here, b/c we cannot get here w/o allocation
- memmove(start, src, elem_count * elem_size);
-
- // if any of size or capacity changed, store them back
- if (newsize != oldsize || newcap != oldcap) {
- if (width == 0 || width == sizeof(size_t)) {
- *(size_t*) capacity = newcap;
- *(size_t*) size = newsize;
- } else if (width == sizeof(uint16_t)) {
- *(uint16_t*) capacity = (uint16_t) newcap;
- *(uint16_t*) size = (uint16_t) newsize;
- } else if (width == sizeof(uint8_t)) {
- *(uint8_t*) capacity = (uint8_t) newcap;
- *(uint8_t*) size = (uint8_t) newsize;
- }
-#if CX_WORDSIZE == 64
- else if (width == sizeof(uint32_t)) {
- *(uint32_t*) capacity = (uint32_t) newcap;
- *(uint32_t*) size = (uint32_t) newsize;
- }
-#endif
+ // place the new elements, if any
+ // otherwise, this function just reserved the memory (a.k.a emplace)
+ if (other != NULL) {
+ memcpy(dst, other, n * elem_size);
}
+ array->size += n;
- // return successfully
return 0;
}
-static int cx_array_insert_sorted_impl(
- void **target,
- size_t *size,
- size_t *capacity,
- cx_compare_func cmp_func,
- const void *sorted_data,
+int cx_array_insert_sorted_c_(
+ const CxAllocator *allocator,
+ CxArray *array,
size_t elem_size,
- size_t elem_count,
- CxArrayReallocator *reallocator,
+ const void *sorted_data,
+ size_t n,
+ cx_compare_func2 cmp_func,
+ void *context,
bool allow_duplicates
) {
// assert pointers
- assert(target != NULL);
- assert(size != NULL);
- assert(capacity != NULL);
+ assert(allocator != NULL);
+ assert(array != NULL);
assert(cmp_func != NULL);
assert(sorted_data != NULL);
- // default reallocator
- if (reallocator == NULL) {
- reallocator = cx_array_default_reallocator;
- }
-
// corner case
- if (elem_count == 0) return 0;
+ if (n == 0) return 0;
// overflow check
- if (elem_count > SIZE_MAX - *size) {
+ // LCOV_EXCL_START
+ if (n > SIZE_MAX - array->size) {
errno = EOVERFLOW;
return 1;
}
+ // LCOV_EXCL_STOP
// store some counts
- size_t old_size = *size;
- size_t old_capacity = *capacity;
+ const size_t old_size = array->size;
+ const size_t old_capacity = array->capacity;
// the necessary capacity is the worst case assumption, including duplicates
- size_t needed_capacity = old_size + elem_count;
+ const size_t needed_capacity = cx_array_grow_capacity(old_capacity, old_size + n);
// if we need more than we have, try a reallocation
if (needed_capacity > old_capacity) {
- size_t new_capacity = cx_array_align_capacity(needed_capacity, 16, SIZE_MAX);
- void *new_mem = reallocator->realloc(
- *target, old_capacity, new_capacity, elem_size, reallocator
- );
- if (new_mem == NULL) {
- // give it up right away, there is no contract
- // that requires us to insert as much as we can
- return 1; // LCOV_EXCL_LINE
+ if (cxReallocateArray(allocator, &array->data, needed_capacity, elem_size)) {
+ return -1; // LCOV_EXCL_LINE
}
- *target = new_mem;
- *capacity = new_capacity;
+ array->capacity = needed_capacity;
}
// now we have guaranteed that we can insert everything
- size_t new_size = old_size + elem_count;
- *size = new_size;
+ size_t new_size = old_size + n;
+ array->size = new_size;
// declare the source and destination indices/pointers
size_t si = 0, di = 0;
const char *src = sorted_data;
- char *dest = *target;
+ char *dest = array->data;
// find the first insertion point
- di = cx_array_binary_search_sup(dest, old_size, elem_size, src, cmp_func);
+ di = cx_array_binary_search_sup_c(dest, old_size, elem_size, src, cmp_func, context);
dest += di * elem_size;
// move the remaining elements in the array completely to the right
// we will call it the "buffer" for parked elements
size_t buf_size = old_size - di;
size_t bi = new_size - buf_size;
- char *bptr = ((char *) *target) + bi * elem_size;
+ char *bptr = ((char *) array->data) + bi * elem_size;
memmove(bptr, dest, buf_size * elem_size);
// while there are both source and buffered elements left,
// copy them interleaving
- while (si < elem_count && bi < new_size) {
- // determine how many source elements can be inserted
+ while (si < n && bi < new_size) {
+ // determine how many source elements can be inserted.
+ // the first element that shall not be inserted is the smallest element
+ // that is strictly larger than the first buffered element
+ // (located at the index of the infimum plus one).
+ // the infimum is guaranteed to exist:
+ // - if all src elements are larger,
+ // there is no buffer, and this loop is skipped
+ // - if any src element is smaller or equal, the infimum exists
+ // - when all src elements that are smaller are copied, the second part
+ // of this loop body will copy the remaining buffer (emptying it)
+ // Therefore, the buffer can never contain an element that is smaller
+ // than any element in the source and the infimum exists.
size_t copy_len, bytes_copied;
- copy_len = cx_array_binary_search_sup(
- src,
- elem_count - si,
- elem_size,
- bptr,
- cmp_func
+ copy_len = cx_array_binary_search_inf_c(
+ src, n - si, elem_size, bptr, cmp_func, context
);
- // binary search gives us the smallest index;
- // we also want to include equal elements here
- while (si + copy_len < elem_count
- && cmp_func(bptr, src+copy_len*elem_size) == 0) {
- copy_len++;
- }
+ copy_len++;
// copy the source elements
if (copy_len > 0) {
// for being a duplicate of the bptr
const char *end_of_src = src + (copy_len - 1) * elem_size;
size_t skip_len = 0;
- while (copy_len > 0 && cmp_func(bptr, end_of_src) == 0) {
+ while (copy_len > 0 && cmp_func(bptr, end_of_src, context) == 0) {
end_of_src -= elem_size;
skip_len++;
copy_len--;
}
- char *last = dest == *target ? NULL : dest - elem_size;
+ char *last = dest == array->data ? NULL : dest - elem_size;
// then iterate through the source chunk
// and skip all duplicates with the last element in the array
size_t more_skipped = 0;
for (unsigned j = 0; j < copy_len; j++) {
- if (last != NULL && cmp_func(last, src) == 0) {
+ if (last != NULL && cmp_func(last, src, context) == 0) {
// duplicate - skip
src += elem_size;
si++;
si += skip_len;
skip_len += more_skipped;
// reduce the actual size by the number of skipped elements
- *size -= skip_len;
+ array->size -= skip_len;
}
}
// when all source elements are in place, we are done
- if (si >= elem_count) break;
+ if (si >= n) break;
// determine how many buffered elements need to be restored
- copy_len = cx_array_binary_search_sup(
+ copy_len = cx_array_binary_search_sup_c(
bptr,
new_size - bi,
elem_size,
src,
- cmp_func
+ cmp_func,
+ context
);
// restore the buffered elements
}
// still source elements left?
- if (si < elem_count) {
+ if (si < n) {
if (allow_duplicates) {
// duplicates allowed or nothing inserted yet: simply copy everything
- memcpy(dest, src, elem_size * (elem_count - si));
+ memcpy(dest, src, elem_size * (n - si));
} else {
- if (dest != *target) {
- // skip all source elements that equal the last element
- char *last = dest - elem_size;
- while (si < elem_count) {
- if (last != NULL && cmp_func(last, src) == 0) {
- src += elem_size;
- si++;
- (*size)--;
- } else {
- break;
- }
- }
- }
- // we must check the elements in the chunk one by one
- while (si < elem_count) {
+ // we must check the remaining source elements one by one
+ // to skip the duplicates.
+ // Note that no source element can equal the last element in the
+ // destination, because that would have created an insertion point
+ // and a buffer, s.t. the above loop already handled the duplicates
+ while (si < n) {
// find a chain of elements that can be copied
size_t copy_len = 1, skip_len = 0;
{
const char *left_src = src;
- while (si + copy_len < elem_count) {
+ while (si + copy_len + skip_len < n) {
const char *right_src = left_src + elem_size;
- int d = cmp_func(left_src, right_src);
+ int d = cmp_func(left_src, right_src, context);
if (d < 0) {
if (skip_len > 0) {
// new larger element found;
left_src += elem_size;
skip_len++;
} else {
- break;
+ // should be unreachable because the requirement is
+ // that the source array is sorted
+ break; // LCOV_EXCL_LINE
}
}
}
src += bytes_copied + skip_len * elem_size;
si += copy_len + skip_len;
di += copy_len;
- *size -= skip_len;
+ array->size -= skip_len;
}
}
}
// buffered elements need to be moved when we skipped duplicates
- size_t total_skipped = new_size - *size;
+ size_t total_skipped = new_size - array->size;
if (bi < new_size && total_skipped > 0) {
// move the remaining buffer to the end of the array
memmove(dest, bptr, elem_size * (new_size - bi));
return 0;
}
-int cx_array_insert_sorted(
- void **target,
- size_t *size,
- size_t *capacity,
- cx_compare_func cmp_func,
- const void *sorted_data,
+int cx_array_insert_sorted_(
+ const CxAllocator *allocator,
+ CxArray *array,
size_t elem_size,
- size_t elem_count,
- CxArrayReallocator *reallocator
+ const void *sorted_data,
+ size_t n,
+ cx_compare_func cmp_func,
+ bool allow_duplicates
) {
- return cx_array_insert_sorted_impl(target, size, capacity,
- cmp_func, sorted_data, elem_size, elem_count, reallocator, true);
+ cx_compare_func_wrapper wrapper = {cmp_func};
+ return cx_array_insert_sorted_c_(allocator, array, elem_size, sorted_data,
+ n, cx_cmp_wrap, &wrapper, allow_duplicates);
}
-int cx_array_insert_unique(
- void **target,
- size_t *size,
- size_t *capacity,
- cx_compare_func cmp_func,
- const void *sorted_data,
- size_t elem_size,
- size_t elem_count,
- CxArrayReallocator *reallocator
-) {
- return cx_array_insert_sorted_impl(target, size, capacity,
- cmp_func, sorted_data, elem_size, elem_count, reallocator, false);
+#ifndef WITH_QSORT_R
+static cx_thread_local cx_compare_func2 cx_array_fn_for_qsort;
+static cx_thread_local void *cx_array_context_for_qsort;
+static int cx_array_qsort_wrapper(const void *l, const void *r) {
+ return cx_array_fn_for_qsort(l, r, cx_array_context_for_qsort);
}
+#endif
-size_t cx_array_binary_search_inf(
+#if defined(WITH_QSORT_R) && defined(__APPLE__)
+// macOS uses a different comparefunc signature for qsort_r
+typedef struct QsortCmpFuncWrapper {
+ cx_compare_func2 fn;
+ void *context;
+} QsortCmpFuncWrapper;
+
+static int sort_comparefunc(void *context, const void *left, const void *right){
+ QsortCmpFuncWrapper *w = context;
+ return w->fn(left, right, w->context);
+}
+#endif
+
+void cx_array_qsort_c(void *array, size_t nmemb, size_t size,
+ cx_compare_func2 fn, void *context) {
+#ifdef WITH_QSORT_R
+#ifndef __APPLE__
+ qsort_r(array, nmemb, size, fn, context);
+#else
+ QsortCmpFuncWrapper wrapper;
+ wrapper.fn = fn;
+ wrapper.context = context;
+ qsort_r(array, nmemb, size, &wrapper, sort_comparefunc);
+#endif
+#else
+ cx_array_fn_for_qsort = fn;
+ cx_array_context_for_qsort = context;
+ qsort(array, nmemb, size, cx_array_qsort_wrapper);
+#endif
+}
+
+void cx_array_sort_(CxArray *array, size_t elem_size,
+ cx_compare_func fn) {
+ qsort(array->data, array->size, elem_size, fn);
+}
+
+void cx_array_sort_c_(CxArray *array, size_t elem_size,
+ cx_compare_func2 fn, void *context) {
+ cx_array_qsort_c(array->data, array->size, elem_size, fn, context);
+}
+
+CxIterator cx_array_iterator_(CxArray *array, size_t elem_size) {
+ return cxIterator(array->data, elem_size, array->size);
+}
+
+CxIterator cx_array_iterator_ptr_(CxArray *array) {
+ return cxIteratorPtr(array->data, array->size);
+}
+
+void cx_array_remove_(CxArray *array, size_t elem_size, size_t index, size_t n, bool fast) {
+ if (n == 0) return;
+ if (index >= array->size) return;
+ if (index + n >= array->size) {
+ // only tail elements are removed
+ array->size = index;
+ return;
+ }
+ array->size -= n;
+ size_t remaining = array->size - index;
+ char *dest = ((char*)array->data) + index * elem_size;
+ if (fast) {
+ char *src = dest + remaining * elem_size;
+ if (n == 1 && elem_size <= CX_WORDSIZE/8) {
+ // try to optimize int-sized values
+ // (from likely to unlikely)
+ if (elem_size == sizeof(int32_t)) {
+ *(int32_t*)dest = *(int32_t*)src;
+ return;
+ }
+#if CX_WORDSIZE == 64
+ if (elem_size == sizeof(int64_t)) {
+ *(int64_t*)dest = *(int64_t*)src;
+ return;
+ }
+#endif
+ if (elem_size == sizeof(int8_t)) {
+ *(int8_t*)dest = *(int8_t*)src;
+ return;
+ }
+ if (elem_size == sizeof(int16_t)) {
+ *(int16_t*)dest = *(int16_t*)src;
+ return;
+ }
+ // note we cannot optimize the last branch, because
+ // the elem_size could be crazily misaligned
+ }
+ memcpy(dest, src, n * elem_size);
+ } else {
+ char *src = dest + n * elem_size;
+ memmove(dest, src, remaining * elem_size);
+ }
+}
+
+void cx_array_free_(const CxAllocator *allocator, CxArray *array) {
+ cxFree(allocator, array->data);
+ array->data = NULL;
+ array->size = array->capacity = 0;
+}
+
+
+// implementation that finds ANY index
+static size_t cx_array_binary_search_inf_impl(
const void *arr,
size_t size,
size_t elem_size,
const void *elem,
- cx_compare_func cmp_func
+ cx_compare_func2 cmp_func,
+ void *context
) {
// special case: empty array
if (size == 0) return 0;
const char *array = arr;
// check the first array element
- result = cmp_func(elem, array);
+ result = cmp_func(elem, array, context);
if (result < 0) {
return size;
} else if (result == 0) {
if (size == 1) return 0;
// check the last array element
- result = cmp_func(elem, array + elem_size * (size - 1));
+ result = cmp_func(elem, array + elem_size * (size - 1), context);
if (result >= 0) {
return size - 1;
}
// so start the binary search
size_t left_index = 1;
size_t right_index = size - 1;
- size_t pivot_index;
+ size_t pivot_index = 0;
while (left_index <= right_index) {
pivot_index = left_index + (right_index - left_index) / 2;
const char *arr_elem = array + pivot_index * elem_size;
- result = cmp_func(elem, arr_elem);
+ result = cmp_func(elem, arr_elem, context);
if (result == 0) {
// found it!
- // check previous elements;
- // when they are equal, report the smallest index
- arr_elem -= elem_size;
- while (pivot_index > 0 && cmp_func(elem, arr_elem) == 0) {
- pivot_index--;
- arr_elem -= elem_size;
- }
return pivot_index;
} else if (result < 0) {
// element is smaller than pivot, continue search left
return result < 0 ? (pivot_index - 1) : pivot_index;
}
-size_t cx_array_binary_search(
+size_t cx_array_binary_search_inf_c(
const void *arr,
size_t size,
size_t elem_size,
const void *elem,
- cx_compare_func cmp_func
+ cx_compare_func2 cmp_func,
+ void *context
+) {
+ size_t index = cx_array_binary_search_inf_impl(
+ arr, size, elem_size, elem, cmp_func, context);
+ // in case of equality, report the largest index
+ const char *e = ((const char *) arr) + (index + 1) * elem_size;
+ while (index + 1 < size && cmp_func(e, elem, context) == 0) {
+ e += elem_size;
+ index++;
+ }
+ return index;
+}
+
+size_t cx_array_binary_search_c(
+ const void *arr,
+ size_t size,
+ size_t elem_size,
+ const void *elem,
+ cx_compare_func2 cmp_func,
+ void *context
) {
- size_t index = cx_array_binary_search_inf(
- arr, size, elem_size, elem, cmp_func
+ size_t index = cx_array_binary_search_inf_c(
+ arr, size, elem_size, elem, cmp_func, context
);
- if (index < size &&
- cmp_func(((const char *) arr) + index * elem_size, elem) == 0) {
+ if (index < size && cmp_func(((const char *) arr) + index * elem_size,
+ elem, context) == 0) {
return index;
} else {
return size;
}
}
-size_t cx_array_binary_search_sup(
+size_t cx_array_binary_search_sup_c(
const void *arr,
size_t size,
size_t elem_size,
const void *elem,
- cx_compare_func cmp_func
+ cx_compare_func2 cmp_func,
+ void *context
) {
- size_t inf = cx_array_binary_search_inf(
- arr, size, elem_size, elem, cmp_func
+ size_t index = cx_array_binary_search_inf_impl(
+ arr, size, elem_size, elem, cmp_func, context
);
- if (inf == size) {
- // no infimum means, first element is supremum
+ const char *e = ((const char *) arr) + index * elem_size;
+ if (index == size) {
+ // no infimum means the first element is supremum
return 0;
- } else if (cmp_func(((const char *) arr) + inf * elem_size, elem) == 0) {
- return inf;
+ } else if (cmp_func(e, elem, context) == 0) {
+ // found an equal element, search the smallest index
+ e -= elem_size; // e now contains the element at index-1
+ while (index > 0 && cmp_func(e, elem, context) == 0) {
+ e -= elem_size;
+ index--;
+ }
+ return index;
} else {
- return inf + 1;
+ // we already have the largest index of the infimum (by design)
+ // the next element is the supremum (or there is no supremum)
+ return index + 1;
}
}
+size_t cx_array_binary_search_inf(
+ const void *arr,
+ size_t size,
+ size_t elem_size,
+ const void *elem,
+ cx_compare_func cmp_func
+) {
+ cx_compare_func_wrapper wrapper = {cmp_func};
+ return cx_array_binary_search_inf_c(arr, size, elem_size, elem, cx_cmp_wrap, &wrapper);
+}
+
+size_t cx_array_binary_search(
+ const void *arr,
+ size_t size,
+ size_t elem_size,
+ const void *elem,
+ cx_compare_func cmp_func
+) {
+ cx_compare_func_wrapper wrapper = {cmp_func};
+ return cx_array_binary_search_c(arr, size, elem_size, elem, cx_cmp_wrap, &wrapper);
+}
+
+size_t cx_array_binary_search_sup(
+ const void *arr,
+ size_t size,
+ size_t elem_size,
+ const void *elem,
+ cx_compare_func cmp_func
+) {
+ cx_compare_func_wrapper wrapper = {cmp_func};
+ return cx_array_binary_search_sup_c(arr, size, elem_size, elem, cx_cmp_wrap, &wrapper);
+}
+
#ifndef CX_ARRAY_SWAP_SBO_SIZE
#define CX_ARRAY_SWAP_SBO_SIZE 128
#endif
struct cx_list_s base;
void *data;
size_t capacity;
- CxArrayReallocator reallocator;
} cx_array_list;
static void cx_arl_destructor(struct cx_list_s *list) {
const void *array,
size_t n
) {
- // out of bounds and special case check
- if (index > list->collection.size || n == 0) return 0;
-
- // get a correctly typed pointer to the list
cx_array_list *arl = (cx_array_list *) list;
-
- // guarantee enough capacity
- if (arl->capacity < list->collection.size + n) {
- size_t new_capacity = list->collection.size + n;
- new_capacity = new_capacity - (new_capacity % 16) + 16;
- if (cxReallocateArray(
- list->collection.allocator,
- &arl->data, new_capacity,
- list->collection.elem_size)
- ) {
- return 0;
- }
- arl->capacity = new_capacity;
- }
-
- // determine insert position
- char *arl_data = arl->data;
- char *insert_pos = arl_data + index * list->collection.elem_size;
-
- // do we need to move some elements?
- if (index < list->collection.size) {
- size_t elems_to_move = list->collection.size - index;
- char *target = insert_pos + n * list->collection.elem_size;
- memmove(target, insert_pos, elems_to_move * list->collection.elem_size);
- }
-
- // place the new elements, if any
- if (array != NULL) {
- memcpy(insert_pos, array, n * list->collection.elem_size);
+ CxArray wrap = {
+ arl->data, list->collection.size, arl->capacity
+ };
+ if (cx_array_insert_(list->collection.allocator, &wrap,
+ list->collection.elem_size, index, array, n)) {
+ return 0;
}
- list->collection.size += n;
-
+ arl->data = wrap.data;
+ arl->capacity = wrap.capacity;
+ list->collection.size = wrap.size;
return n;
}
-static size_t cx_arl_insert_sorted(
+static size_t cx_arl_insert_sorted_impl(
struct cx_list_s *list,
const void *sorted_data,
- size_t n
+ size_t n,
+ bool allow_duplicates
) {
- // get a correctly typed pointer to the list
cx_array_list *arl = (cx_array_list *) list;
+ CxArray wrap = {
+ arl->data, list->collection.size, arl->capacity
+ };
- if (cx_array_insert_sorted(
- &arl->data,
- &list->collection.size,
- &arl->capacity,
- list->collection.cmpfunc,
- sorted_data,
+ if (cx_array_insert_sorted_c_(
+ list->collection.allocator,
+ &wrap,
list->collection.elem_size,
+ sorted_data,
n,
- &arl->reallocator
+ cx_list_compare_wrapper,
+ list,
+ allow_duplicates
)) {
// array list implementation is "all or nothing"
- return 0;
- } else {
- return n;
+ return 0; // LCOV_EXCL_LINE
}
+ arl->data = wrap.data;
+ arl->capacity = wrap.capacity;
+ list->collection.size = wrap.size;
+ return n;
}
-static size_t cx_arl_insert_unique(
+static size_t cx_arl_insert_sorted(
struct cx_list_s *list,
const void *sorted_data,
size_t n
) {
- // get a correctly typed pointer to the list
- cx_array_list *arl = (cx_array_list *) list;
+ return cx_arl_insert_sorted_impl(list, sorted_data, n, true);
+}
- if (cx_array_insert_unique(
- &arl->data,
- &list->collection.size,
- &arl->capacity,
- list->collection.cmpfunc,
- sorted_data,
- list->collection.elem_size,
- n,
- &arl->reallocator
- )) {
- // array list implementation is "all or nothing"
- return 0;
- } else {
- return n;
- }
+static size_t cx_arl_insert_unique(
+ struct cx_list_s *list,
+ const void *sorted_data,
+ size_t n
+) {
+ return cx_arl_insert_sorted_impl(list, sorted_data, n, false);
}
static void *cx_arl_insert_element(
if (iter->index < list->collection.size) {
if (cx_arl_insert_element(list,
iter->index + 1 - prepend, elem) == NULL) {
- return 1;
+ return 1; // LCOV_EXCL_LINE
}
iter->elem_count++;
if (prepend != 0) {
return 0;
} else {
if (cx_arl_insert_element(list, list->collection.size, elem) == NULL) {
- return 1;
+ return 1; // LCOV_EXCL_LINE
}
iter->elem_count++;
iter->index = list->collection.size;
);
}
+ // calculate how many elements would need to be moved
+ size_t remaining = list->collection.size - index - remove;
+
// short-circuit removal of last elements
- if (index + remove == list->collection.size) {
+ if (remaining == 0) {
list->collection.size -= remove;
return remove;
}
// just move the elements to the left
- cx_array_copy(
- &arl->data,
- &list->collection.size,
- &arl->capacity,
- 0,
- index,
- ((char *) arl->data) + (index + remove) * list->collection.elem_size,
- list->collection.elem_size,
- list->collection.size - index - remove,
- &arl->reallocator
- );
+ char *dst_move = arl->data;
+ dst_move += index * list->collection.elem_size;
+ char *first_remaining = dst_move + remove * list->collection.elem_size;
+ memmove(dst_move, first_remaining, remaining * list->collection.elem_size);
// decrease the size
list->collection.size -= remove;
bool remove
) {
assert(list != NULL);
- assert(list->collection.cmpfunc != NULL);
if (list->collection.size == 0) return 0;
char *cur = ((const cx_array_list *) list)->data;
// optimize with binary search, when sorted
if (list->collection.sorted) {
- size_t i = cx_array_binary_search(
+ size_t i = cx_array_binary_search_c(
cur,
list->collection.size,
list->collection.elem_size,
elem,
- list->collection.cmpfunc
+ cx_list_compare_wrapper,
+ list
);
if (remove && i < list->collection.size) {
cx_arl_remove(list, i, 1, NULL);
// fallback: linear search
for (size_t i = 0; i < list->collection.size; i++) {
- if (0 == list->collection.cmpfunc(elem, cur)) {
+ if (0 == cx_list_compare_wrapper(elem, cur, list)) {
if (remove) {
cx_arl_remove(list, i, 1, NULL);
}
}
static void cx_arl_sort(struct cx_list_s *list) {
- assert(list->collection.cmpfunc != NULL);
- qsort(((cx_array_list *) list)->data,
+ cx_array_qsort_c(((cx_array_list *) list)->data,
list->collection.size,
list->collection.elem_size,
- list->collection.cmpfunc
+ cx_list_compare_wrapper,
+ list
);
}
const struct cx_list_s *list,
const struct cx_list_s *other
) {
- assert(list->collection.cmpfunc != NULL);
if (list->collection.size == other->collection.size) {
const char *left = ((const cx_array_list *) list)->data;
const char *right = ((const cx_array_list *) other)->data;
for (size_t i = 0; i < list->collection.size; i++) {
- int d = list->collection.cmpfunc(left, right);
+ int d = cx_list_compare_wrapper(left, right, (void*)list);
if (d != 0) {
return d;
}
}
}
+static int cx_arl_change_capacity(
+ struct cx_list_s *list,
+ size_t new_capacity
+) {
+ cx_array_list *arl = (cx_array_list *)list;
+ return cxReallocateArray(list->collection.allocator,
+ &arl->data, new_capacity, list->collection.elem_size);
+}
static struct cx_iterator_s cx_arl_iterator(
const struct cx_list_s *list,
cx_arl_sort,
cx_arl_compare,
cx_arl_reverse,
+ cx_arl_change_capacity,
cx_arl_iterator,
};
CxList *cxArrayListCreate(
const CxAllocator *allocator,
- cx_compare_func comparator,
size_t elem_size,
size_t initial_capacity
) {
cx_array_list *list = cxCalloc(allocator, 1, sizeof(cx_array_list));
if (list == NULL) return NULL;
- cx_list_init((CxList*)list, &cx_array_list_class,
- allocator, comparator, elem_size);
+ cx_list_init((CxList*)list, &cx_array_list_class, allocator, elem_size);
list->capacity = initial_capacity;
// allocate the array after the real elem_size is known
return NULL;
} // LCOV_EXCL_STOP
- // configure the reallocator
- list->reallocator = cx_array_reallocator(allocator, NULL);
-
return (CxList *) list;
}
#include <string.h>
#include <errno.h>
-#ifdef _WIN32
-#include <Windows.h>
-#include <sysinfoapi.h>
-static unsigned long system_page_size() {
- static unsigned long ps = 0;
- if (ps == 0) {
- SYSTEM_INFO sysinfo;
- GetSystemInfo(&sysinfo);
- ps = sysinfo.dwPageSize;
- }
- return ps;
-}
-#define SYSTEM_PAGE_SIZE system_page_size()
-#else
-#include <unistd.h>
-#define SYSTEM_PAGE_SIZE sysconf(_SC_PAGESIZE)
-#endif
-
static int buffer_copy_on_write(CxBuffer* buffer) {
if (0 == (buffer->flags & CX_BUFFER_COPY_ON_WRITE)) return 0;
void *newspace = cxMalloc(buffer->allocator, buffer->capacity);
- if (NULL == newspace) return -1;
+ if (NULL == newspace) return -1; // LCOV_EXCL_LINE
memcpy(newspace, buffer->space, buffer->size);
buffer->space = newspace;
buffer->flags &= ~CX_BUFFER_COPY_ON_WRITE;
int cxBufferInit(
CxBuffer *buffer,
+ const CxAllocator *allocator,
void *space,
size_t capacity,
- const CxAllocator *allocator,
int flags
-) {
+ ) {
if (allocator == NULL) {
allocator = cxDefaultAllocator;
}
buffer->flags = flags;
if (!space) {
buffer->bytes = cxMalloc(allocator, capacity);
- if (buffer->bytes == NULL) {
- return -1; // LCOV_EXCL_LINE
- }
+ if (buffer->bytes == NULL) return -1; // LCOV_EXCL_LINE
buffer->flags |= CX_BUFFER_FREE_CONTENTS;
} else {
buffer->bytes = space;
}
buffer->capacity = capacity;
+ buffer->max_capacity = SIZE_MAX;
buffer->size = 0;
buffer->pos = 0;
- buffer->flush = NULL;
-
- return 0;
-}
-
-int cxBufferEnableFlushing(
- CxBuffer *buffer,
- CxBufferFlushConfig config
-) {
- buffer->flush = cxMallocDefault(sizeof(CxBufferFlushConfig));
- if (buffer->flush == NULL) return -1; // LCOV_EXCL_LINE
- memcpy(buffer->flush, &config, sizeof(CxBufferFlushConfig));
return 0;
}
void cxBufferDestroy(CxBuffer *buffer) {
- if (buffer->flags & CX_BUFFER_FREE_CONTENTS) {
+ if ((buffer->flags & (CX_BUFFER_FREE_CONTENTS | CX_BUFFER_DO_NOT_FREE))
+ == CX_BUFFER_FREE_CONTENTS) {
cxFree(buffer->allocator, buffer->bytes);
}
- cxFreeDefault(buffer->flush);
memset(buffer, 0, sizeof(CxBuffer));
}
CxBuffer *cxBufferCreate(
+ const CxAllocator *allocator,
void *space,
size_t capacity,
- const CxAllocator *allocator,
int flags
-) {
+ ) {
if (allocator == NULL) {
allocator = cxDefaultAllocator;
}
CxBuffer *buf = cxMalloc(allocator, sizeof(CxBuffer));
- if (buf == NULL) return NULL;
- if (0 == cxBufferInit(buf, space, capacity, allocator, flags)) {
+ if (buf == NULL) return NULL; // LCOV_EXCL_LINE
+ if (0 == cxBufferInit(buf, allocator, space, capacity, flags)) {
return buf;
} else {
// LCOV_EXCL_START
}
+size_t cxBufferPop(CxBuffer *buffer, size_t size, size_t nitems) {
+ size_t len;
+ if (cx_szmul(size, nitems, &len)) {
+ // LCOV_EXCL_START
+ errno = EOVERFLOW;
+ return 0;
+ // LCOV_EXCL_STOP
+ }
+ if (len == 0) return 0;
+ if (len > buffer->size) {
+ if (size == 1) {
+ // simple case: everything can be discarded
+ len = buffer->size;
+ } else {
+ // complicated case: misaligned bytes must stay
+ size_t misalignment = buffer->size % size;
+ len = buffer->size - misalignment;
+ }
+ }
+ buffer->size -= len;
+
+ // adjust position, if required
+ if (buffer->pos > buffer->size) {
+ buffer->pos = buffer->size;
+ }
+
+ return len / size;
+}
+
void cxBufferClear(CxBuffer *buffer) {
if (0 == (buffer->flags & CX_BUFFER_COPY_ON_WRITE)) {
memset(buffer->bytes, 0, buffer->size);
return buffer->pos >= buffer->size;
}
-int cxBufferMinimumCapacity(
- CxBuffer *buffer,
- size_t newcap
-) {
- if (newcap <= buffer->capacity) {
+int cxBufferReserve(CxBuffer *buffer, size_t newcap) {
+ if (newcap == buffer->capacity) {
return 0;
}
-
- unsigned long pagesize = SYSTEM_PAGE_SIZE;
- // if page size is larger than 64 KB - for some reason - truncate to 64 KB
- if (pagesize > 65536) pagesize = 65536;
- if (newcap < pagesize) {
- // when smaller as one page, map to the next power of two
- newcap--;
- newcap |= newcap >> 1;
- newcap |= newcap >> 2;
- newcap |= newcap >> 4;
- // last operation only needed for pages larger 4096 bytes
- // but if/else would be more expensive than just doing this
- newcap |= newcap >> 8;
- newcap++;
- } else {
- // otherwise, map to a multiple of the page size
- newcap -= newcap % pagesize;
- newcap += pagesize;
- // note: if newcap is already page aligned,
- // this gives a full additional page (which is good)
+ if (newcap > buffer->max_capacity) {
+ return -1;
}
-
-
const int force_copy_flags = CX_BUFFER_COPY_ON_WRITE | CX_BUFFER_COPY_ON_EXTEND;
if (buffer->flags & force_copy_flags) {
void *newspace = cxMalloc(buffer->allocator, newcap);
return 0;
} else if (cxReallocate(buffer->allocator,
(void **) &buffer->bytes, newcap) == 0) {
+ buffer->flags |= CX_BUFFER_FREE_CONTENTS;
buffer->capacity = newcap;
+ if (buffer->size > newcap) {
+ buffer->size = newcap;
+ }
return 0;
} else {
return -1; // LCOV_EXCL_LINE
}
}
+int cxBufferMaximumCapacity(CxBuffer *buffer, size_t capacity) {
+ if (capacity < buffer->capacity) {
+ return -1;
+ }
+ buffer->max_capacity = capacity;
+ return 0;
+}
+
+int cxBufferMinimumCapacity(CxBuffer *buffer, size_t newcap) {
+ if (newcap <= buffer->capacity) {
+ return 0;
+ }
+ if (newcap > buffer->max_capacity) {
+ return -1;
+ }
+ if (newcap < buffer->max_capacity) {
+ unsigned long pagesize = cx_system_page_size();
+ // if page size is larger than 64 KB - for some reason - truncate to 64 KB
+ if (pagesize > 65536) pagesize = 65536;
+ if (newcap < pagesize) {
+ // when smaller as one page, map to the next power of two
+ newcap--;
+ newcap |= newcap >> 1;
+ newcap |= newcap >> 2;
+ newcap |= newcap >> 4;
+ // last operation only needed for pages larger 4096 bytes
+ // but if/else would be more expensive than just doing this
+ newcap |= newcap >> 8;
+ newcap++;
+ } else {
+ // otherwise, map to a multiple of the page size
+ newcap -= newcap % pagesize;
+ newcap += pagesize;
+ // note: if newcap is already page aligned,
+ // this gives a full additional page (which is good)
+ }
+ if (newcap > buffer->max_capacity) {
+ newcap = buffer->max_capacity;
+ }
+ }
+ return cxBufferReserve(buffer, newcap);
+}
+
void cxBufferShrink(
CxBuffer *buffer,
size_t reserve
}
}
-static size_t cx_buffer_flush_helper(
- const CxBuffer *buffer,
- const unsigned char *src,
- size_t size,
- size_t nitems
-) {
- // flush data from an arbitrary source
- // does not need to be the buffer's contents
- size_t max_items = buffer->flush->blksize / size;
- size_t fblocks = 0;
- size_t flushed_total = 0;
- while (nitems > 0 && fblocks < buffer->flush->blkmax) {
- fblocks++;
- size_t items = nitems > max_items ? max_items : nitems;
- size_t flushed = buffer->flush->wfunc(
- src, size, items, buffer->flush->target);
- if (flushed > 0) {
- flushed_total += flushed;
- src += flushed * size;
- nitems -= flushed;
- } else {
- // if no bytes can be flushed out anymore, we give up
- break;
- }
- }
- return flushed_total;
-}
-
-static size_t cx_buffer_flush_impl(CxBuffer *buffer, size_t size) {
- // flush the current contents of the buffer
- unsigned char *space = buffer->bytes;
- size_t remaining = buffer->pos / size;
- size_t flushed_total = cx_buffer_flush_helper(
- buffer, space, size, remaining);
-
- // shift the buffer left after flushing
- // IMPORTANT: up to this point, copy on write must have been
- // performed already, because we can't do error handling here
- cxBufferShiftLeft(buffer, flushed_total*size);
-
- return flushed_total;
-}
-
-size_t cxBufferFlush(CxBuffer *buffer) {
- if (buffer_copy_on_write(buffer)) return 0;
- return cx_buffer_flush_impl(buffer, 1);
-}
-
size_t cxBufferWrite(
const void *ptr,
size_t size,
size_t nitems,
CxBuffer *buffer
) {
+ // trivial case
+ if (size == 0 || nitems == 0) return 0;
+
// optimize for easy case
if (size == 1 && (buffer->capacity - buffer->pos) >= nitems) {
if (buffer_copy_on_write(buffer)) return 0;
return nitems;
}
- size_t len, total_flushed = 0;
-cx_buffer_write_retry:
+ size_t len;
if (cx_szmul(size, nitems, &len)) {
errno = EOVERFLOW;
- return total_flushed;
+ return 0;
}
if (buffer->pos > SIZE_MAX - len) {
errno = EOVERFLOW;
- return total_flushed;
+ return 0;
}
+ const size_t required = buffer->pos + len;
- size_t required = buffer->pos + len;
- bool perform_flush = false;
+ // check if we need to auto-extend
if (required > buffer->capacity) {
if (buffer->flags & CX_BUFFER_AUTO_EXTEND) {
- if (buffer->flush != NULL && required > buffer->flush->threshold) {
- perform_flush = true;
- } else {
- if (cxBufferMinimumCapacity(buffer, required)) {
- return total_flushed; // LCOV_EXCL_LINE
- }
- }
- } else {
- if (buffer->flush != NULL) {
- perform_flush = true;
- } else {
- // truncate data, if we can neither extend nor flush
- len = buffer->capacity - buffer->pos;
- if (size > 1) {
- len -= len % size;
- }
- nitems = len / size;
+ size_t newcap = required < buffer->max_capacity
+ ? required : buffer->max_capacity;
+ if (cxBufferMinimumCapacity(buffer, newcap)) {
+ return 0; // LCOV_EXCL_LINE
}
}
}
+ // check again and truncate data if capacity is still not enough
+ if (required > buffer->capacity) {
+ len = buffer->capacity - buffer->pos;
+ if (size > 1) {
+ len -= len % size;
+ }
+ nitems = len / size;
+ }
+
// check here and not above because of possible truncation
if (len == 0) {
- return total_flushed;
+ return 0;
}
// check if we need to copy
if (buffer_copy_on_write(buffer)) return 0;
// perform the operation
- if (perform_flush) {
- size_t items_flushed;
- if (buffer->pos == 0) {
- // if we don't have data in the buffer, but are instructed
- // to flush, it means that we are supposed to relay the data
- items_flushed = cx_buffer_flush_helper(buffer, ptr, size, nitems);
- if (items_flushed == 0) {
- // we needed to relay data, but could not flush anything
- // i.e. we have to give up to avoid endless trying
- return 0;
- }
- nitems -= items_flushed;
- total_flushed += items_flushed;
- if (nitems > 0) {
- ptr = ((unsigned char*)ptr) + items_flushed * size;
- goto cx_buffer_write_retry;
- }
- return total_flushed;
- } else {
- items_flushed = cx_buffer_flush_impl(buffer, size);
- if (items_flushed == 0) {
- // flush target is full, let's try to truncate
- size_t remaining_space;
- if (buffer->flags & CX_BUFFER_AUTO_EXTEND) {
- remaining_space = buffer->flush->threshold > buffer->pos
- ? buffer->flush->threshold - buffer->pos
- : 0;
- } else {
- remaining_space = buffer->capacity > buffer->pos
- ? buffer->capacity - buffer->pos
- : 0;
- }
- nitems = remaining_space / size;
- if (nitems == 0) {
- return total_flushed;
- }
- }
- goto cx_buffer_write_retry;
- }
- } else {
- memcpy(buffer->bytes + buffer->pos, ptr, len);
- buffer->pos += len;
- if (buffer->pos > buffer->size) {
- buffer->size = buffer->pos;
- }
- return total_flushed + nitems;
+ memcpy(buffer->bytes + buffer->pos, ptr, len);
+ buffer->pos += len;
+ if (buffer->pos > buffer->size) {
+ buffer->size = buffer->pos;
}
+ return nitems;
}
size_t cxBufferAppend(
size_t nitems,
CxBuffer *buffer
) {
- size_t pos = buffer->pos;
- size_t append_pos = buffer->size;
- buffer->pos = append_pos;
- size_t written = cxBufferWrite(ptr, size, nitems, buffer);
- // the buffer might have been flushed
- // we must compute a possible delta for the position
- // expected: pos = append_pos + written
- // -> if this is not the case, there is a delta
- size_t delta = append_pos + written*size - buffer->pos;
- if (delta > pos) {
- buffer->pos = 0;
- } else {
- buffer->pos = pos - delta;
- }
+ // trivial case
+ if (size == 0 || nitems == 0) return 0;
+
+ const size_t pos = buffer->pos;
+ buffer->pos = buffer->size;
+ const size_t written = cxBufferWrite(ptr, size, nitems, buffer);
+ buffer->pos = pos;
return written;
}
}
int cxBufferTerminate(CxBuffer *buffer) {
- if (0 == cxBufferPut(buffer, 0)) {
- buffer->size = buffer->pos - 1;
- return 0;
+ // try to extend / shrink the buffer
+ if (buffer->pos >= buffer->capacity) {
+ if ((buffer->flags & CX_BUFFER_AUTO_EXTEND) == 0) {
+ return -1;
+ }
+ if (cxBufferReserve(buffer, buffer->pos + 1)) {
+ return -1; // LCOV_EXCL_LINE
+ }
} else {
- return -1;
+ buffer->size = buffer->pos;
+ cxBufferShrink(buffer, 1);
+ // set the capacity explicitly, in case shrink was skipped due to CoW
+ buffer->capacity = buffer->size + 1;
}
+
+ // check if we are still on read-only memory
+ if (buffer_copy_on_write(buffer)) return -1;
+
+ // write the terminator and exit
+ buffer->space[buffer->pos] = '\0';
+ return 0;
}
-size_t cxBufferPutString(
- CxBuffer *buffer,
- const char *str
-) {
- return cxBufferWrite(str, 1, strlen(str), buffer);
+size_t cx_buffer_put_string(CxBuffer *buffer, cxstring str) {
+ return cxBufferWrite(str.ptr, 1, str.length, buffer);
+}
+
+size_t cx_buffer_append_string(CxBuffer *buffer, cxstring str) {
+ return cxBufferAppend(str.ptr, 1, str.length, buffer);
}
size_t cxBufferRead(
#include "cx/compare.h"
#include <math.h>
+#include <string.h>
int cx_vcmp_int(int a, int b) {
if (a == b) {
return p1 < p2 ? -1 : 1;
}
}
+
+int cx_cmp_wrap(
+ const void *ptr1,
+ const void *ptr2,
+ void *w
+) {
+ cx_compare_func_wrapper *wrapper = w;
+ return wrapper->cmp(ptr1, ptr2);
+}
#include "common.h"
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/**
* The class definition for an allocator.
*/
* @return either the specified @p target, a pointer to the allocated memory,
* or @c NULL, if any error occurred
*/
-typedef void*(cx_clone_func)(void *target, const void *source,
+typedef void*(*cx_clone_func)(void *target, const void *source,
const CxAllocator *allocator, void *data);
/**
- * Reallocate a previously allocated block and changes the pointer in-place,
- * if necessary.
+ * Returns the system's memory page size.
*
- * @note This will use stdlib reallocate and @em not the cxDefaultAllocator.
+ * If the page size cannot be retrieved from the system,
+ * a default of 4096 bytes is assumed.
*
- * @par Error handling
- * @c errno will be set by realloc() on failure.
+ * @return the system's memory page size in bytes
+ */
+CX_EXTERN CX_NODISCARD
+unsigned long cx_system_page_size(void);
+
+/**
+ * Reallocate a previously allocated block.
+ *
+ * Internal function - do not use.
*
* @param mem pointer to the pointer to allocated block
* @param n the new size in bytes
* @retval non-zero failure
* @see cx_reallocatearray()
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_reallocate_(void **mem, size_t n);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_reallocate_(void **mem, size_t n);
/**
- * Reallocate a previously allocated block and changes the pointer in-place,
- * if necessary.
+ * Reallocate a previously allocated block.
*
- * The size is calculated by multiplying @p nemb and @p size.
- *
- * @note This will use stdlib reallocate and @em not the cxDefaultAllocator.
- *
- * @par Error handling
- * @c errno will be set by realloc() on failure or when the multiplication of
- * @p nmemb and @p size overflows.
+ * Internal function - do not use.
*
* @param mem pointer to the pointer to allocated block
* @param nmemb the number of elements
* @retval non-zero failure
* @see cx_reallocate()
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_reallocatearray_(void **mem, size_t nmemb, size_t size);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_reallocatearray_(void **mem, size_t nmemb, size_t size);
/**
* Reallocate a previously allocated block and changes the pointer in-place,
* @param allocator the allocator
* @param mem a pointer to the block to free
*/
-cx_attr_nonnull_arg(1)
-CX_EXPORT void cxFree(const CxAllocator *allocator, void *mem);
+CX_EXTERN CX_NONNULL_ARG(1)
+void cxFree(const CxAllocator *allocator, void *mem);
/**
* Allocate @p n bytes of memory.
* @param n the number of bytes
* @return a pointer to the allocated memory
*/
-cx_attr_nodiscard cx_attr_nonnull
-cx_attr_malloc cx_attr_dealloc_ucx cx_attr_allocsize(2)
-CX_EXPORT void *cxMalloc(const CxAllocator *allocator, size_t n);
+CX_EXTERN CX_NODISCARD CX_NONNULL
+CX_MALLOC CX_DEALLOC_UCX CX_ALLOCSIZE(2)
+void *cxMalloc(const CxAllocator *allocator, size_t n);
/**
* Reallocate the previously allocated block in @p mem, making the new block
*
* @note Re-allocating a block allocated by a different allocator is undefined.
*
+ * @attention This function is bug-prone. Consider using cxReallocate().
+ *
* @param allocator the allocator
* @param mem pointer to the previously allocated block
* @param n the new size in bytes
* @return a pointer to the reallocated memory
*/
-cx_attr_nodiscard cx_attr_nonnull_arg(1)
-cx_attr_dealloc_ucx cx_attr_allocsize(3)
-CX_EXPORT void *cxRealloc(const CxAllocator *allocator, void *mem, size_t n);
+CX_EXTERN CX_NODISCARD CX_NONNULL_ARG(1)
+CX_DEALLOC_UCX CX_ALLOCSIZE(3)
+void *cxRealloc(const CxAllocator *allocator, void *mem, size_t n);
/**
- * Reallocate the previously allocated block in @p mem, making the new block
- * @p n bytes long.
+ * Reallocate the previously allocated block in @p mem.
+ *
* This function may return the same pointer passed to it if moving
* the memory was not necessary.
*
*
* @note Re-allocating a block allocated by a different allocator is undefined.
*
+ * @attention This function is bug-prone. Consider using cxReallocateArray().
+ *
* @param allocator the allocator
* @param mem pointer to the previously allocated block
* @param nmemb the number of elements
* @param size the size of each element
* @return a pointer to the reallocated memory
*/
-cx_attr_nodiscard cx_attr_nonnull_arg(1)
-cx_attr_dealloc_ucx cx_attr_allocsize(3, 4)
-CX_EXPORT void *cxReallocArray(const CxAllocator *allocator,
+CX_EXTERN CX_NODISCARD CX_NONNULL_ARG(1)
+CX_DEALLOC_UCX CX_ALLOCSIZE(3, 4)
+void *cxReallocArray(const CxAllocator *allocator,
void *mem, size_t nmemb, size_t size);
/**
- * Reallocate a previously allocated block and changes the pointer in-place,
- * if necessary.
- * This function acts like cxRealloc() using the pointer pointed to by @p mem.
- *
- * @note Re-allocating a block allocated by a different allocator is undefined.
+ * Reallocate a previously allocated block.
*
- * @par Error handling
- * @c errno will be set if the underlying realloc function does so.
+ * Internal function - do not use.
*
* @param allocator the allocator
* @param mem pointer to the pointer to allocated block
* @retval zero success
* @retval non-zero failure
*/
-cx_attr_nodiscard cx_attr_nonnull
-CX_EXPORT int cxReallocate_(const CxAllocator *allocator, void **mem, size_t n);
+CX_EXTERN CX_NODISCARD CX_NONNULL
+int cxReallocate_(const CxAllocator *allocator, void **mem, size_t n);
/**
* Reallocate a previously allocated block and changes the pointer in-place,
cxReallocate_(allocator, (void**)(mem), n)
/**
- * Reallocate a previously allocated block and changes the pointer in-place,
- * if necessary.
- * This function acts like cxReallocArray() using the pointer pointed to
- * by @p mem.
+ * Reallocate a previously allocated block.
*
- * @note Re-allocating a block allocated by a different allocator is undefined.
- *
- * @par Error handling
- * @c errno will be set, if the underlying realloc function does so or the
- * multiplication of @p nmemb and @p size overflows.
+ * Internal function - do not use.
*
* @param allocator the allocator
* @param mem pointer to the pointer to allocated block
* @retval zero success
* @retval non-zero on failure
*/
-cx_attr_nodiscard cx_attr_nonnull
-CX_EXPORT int cxReallocateArray_(const CxAllocator *allocator,
+CX_EXTERN CX_NODISCARD CX_NONNULL
+int cxReallocateArray_(const CxAllocator *allocator,
void **mem, size_t nmemb, size_t size);
/**
cxReallocateArray_(allocator, (void**) (mem), nmemb, size)
/**
- * Allocate @p nmemb elements of @p n bytes each, all initialized to zero.
+ * Allocate @p nmemb elements of @p size bytes each, all initialized to zero.
*
* @param allocator the allocator
* @param nmemb the number of elements
* @param size the size of each element in bytes
* @return a pointer to the allocated memory
*/
-cx_attr_nonnull_arg(1) cx_attr_nodiscard
-cx_attr_malloc cx_attr_dealloc_ucx cx_attr_allocsize(2, 3)
-CX_EXPORT void *cxCalloc(const CxAllocator *allocator, size_t nmemb, size_t size);
+CX_EXTERN CX_NONNULL_ARG(1) CX_NODISCARD
+CX_MALLOC CX_DEALLOC_UCX CX_ALLOCSIZE(2, 3)
+void *cxCalloc(const CxAllocator *allocator, size_t nmemb, size_t size);
/**
* Allocate @p n bytes of memory and sets every byte to zero.
* @param n the number of bytes
* @return a pointer to the allocated memory
*/
-cx_attr_nodiscard cx_attr_nonnull
-cx_attr_malloc cx_attr_dealloc_ucx cx_attr_allocsize(2)
-CX_EXPORT void *cxZalloc(const CxAllocator *allocator, size_t n);
+CX_EXTERN CX_NODISCARD CX_NONNULL
+CX_MALLOC CX_DEALLOC_UCX CX_ALLOCSIZE(2)
+void *cxZalloc(const CxAllocator *allocator, size_t n);
/**
+ * Allocate @p n bytes of memory.
+ *
* Convenience macro that invokes cxMalloc() with the cxDefaultAllocator.
+ *
+ * @param n (@c size_t) the number of bytes
+ * @return (@c void*) a pointer to the allocated memory
*/
-#define cxMallocDefault(...) cxMalloc(cxDefaultAllocator, __VA_ARGS__)
+#define cxMallocDefault(n) cxMalloc(cxDefaultAllocator, n)
+
/**
+ * Allocate @p n bytes of memory and sets every byte to zero.
+ *
* Convenience macro that invokes cxZalloc() with the cxDefaultAllocator.
+ *
+ * @param n (@c size_t) the number of bytes
+ * @return (@c void*) a pointer to the allocated memory
*/
-#define cxZallocDefault(...) cxZalloc(cxDefaultAllocator, __VA_ARGS__)
+#define cxZallocDefault(n) cxZalloc(cxDefaultAllocator, n)
+
/**
+ * Allocate @p nmemb elements of @p size bytes each, all initialized to zero.
+ *
* Convenience macro that invokes cxCalloc() with the cxDefaultAllocator.
+ *
+ * @param nmemb (@c size_t) the number of elements
+ * @param size (@c size_t) the size of each element in bytes
+ * @return (@c void*) a pointer to the allocated memory
*/
-#define cxCallocDefault(...) cxCalloc(cxDefaultAllocator, __VA_ARGS__)
+#define cxCallocDefault(nmemb, size) cxCalloc(cxDefaultAllocator, nmemb, size)
+
/**
+ * Reallocate the previously allocated block in @p mem.
+ *
+ * This function may return the same pointer passed to it if moving
+ * the memory was not necessary.
+ *
* Convenience macro that invokes cxRealloc() with the cxDefaultAllocator.
+ *
+ * @attention This function is bug-prone. Consider using cxReallocateDefault().
+ *
+ * @param mem (@c void*) pointer to the previously allocated block
+ * @param n (@c size_t) the new size in bytes
+ * @return (@c void*) a pointer to the reallocated memory
*/
-#define cxReallocDefault(...) cxRealloc(cxDefaultAllocator, __VA_ARGS__)
+#define cxReallocDefault(mem, n) cxRealloc(cxDefaultAllocator, mem, n)
+
/**
+ * Reallocate a previously allocated block and changes the pointer in-place,
+ * if necessary.
+ * This function acts like cxRealloc() using the pointer pointed to by @p mem.
+ *
* Convenience macro that invokes cxReallocate() with the cxDefaultAllocator.
+ *
+ * @note Re-allocating a block allocated by a different allocator is undefined.
+ *
+ * @par Error handling
+ * @c errno will be set if the underlying realloc function does so.
+ *
+ * @param mem (@c void**) pointer to the pointer to allocated block
+ * @param n (@c size_t) the new size in bytes
+ * @retval zero success
+ * @retval non-zero failure
*/
-#define cxReallocateDefault(...) cxReallocate(cxDefaultAllocator, __VA_ARGS__)
+#define cxReallocateDefault(mem, n) cxReallocate(cxDefaultAllocator, mem, n)
+
/**
+ * Reallocate a previously allocated block and changes the pointer in-place,
+ * if necessary.
+ * This function acts like cxReallocArray() using the pointer pointed to
+ * by @p mem.
+ *
* Convenience macro that invokes cxReallocateArray() with the cxDefaultAllocator.
+ *
+ * @note Re-allocating a block allocated by a different allocator is undefined.
+ *
+ * @par Error handling
+ * @c errno will be set, if the underlying realloc function does so or the
+ * multiplication of @p nmemb and @p size overflows.
+ *
+ * @param mem (@c void**) pointer to the pointer to allocated block
+ * @param nmemb (@c size_t) the number of elements
+ * @param size (@c size_t) the size of each element
+ * @retval zero success
+ * @retval non-zero failure
*/
-#define cxReallocateArrayDefault(...) cxReallocateArray(cxDefaultAllocator, __VA_ARGS__)
+#define cxReallocateArrayDefault(mem, nmemb, size) \
+ cxReallocateArray(cxDefaultAllocator, mem, nmemb, size)
+
/**
+ * Reallocate the previously allocated block in @p mem.
+ *
* Convenience macro that invokes cxReallocArray() with the cxDefaultAllocator.
+ *
+ * This function may return the same pointer passed to it if moving
+ * the memory was not necessary.
+ *
+ * The size is calculated by multiplying @p nemb and @p size.
+ * If that multiplication overflows, this function returns @c NULL, and @c errno
+ * will be set.
+ *
+ * @note Re-allocating a block allocated by a different allocator is undefined.
+ *
+ * @attention This function is bug-prone. Consider using cxReallocateArrayDefault().
+ *
+ * @param mem (@c void*) pointer to the previously allocated block
+ * @param nmemb (@c size_t) the number of elements
+ * @param size (@c size_t) the size of each element
+ * @return (@c void*) a pointer to the reallocated memory
*/
-#define cxReallocArrayDefault(...) cxReallocArray(cxDefaultAllocator, __VA_ARGS__)
+#define cxReallocArrayDefault(mem, nmemb, size) cxReallocArray(cxDefaultAllocator, mem, nmemb, size)
+
/**
- * Convenience macro that invokes cxFree() with the cxDefaultAllocator.
+ * Free a block of memory.
+ *
+ * Convenience function that invokes cxFree() with the cxDefaultAllocator.
+ *
+ * @param mem the memory to deallocate
*/
-#define cxFreeDefault(...) cxFree(cxDefaultAllocator, __VA_ARGS__)
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
+CX_EXTERN
+void cxFreeDefault(void *mem);
#endif // UCX_ALLOCATOR_H
#include "list.h"
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/**
* The maximum item size in an array list that fits into
* a stack buffer when swapped.
CX_EXPORT extern const unsigned cx_array_swap_sbo_size;
/**
- * Declares variables for an array that can be used with the convenience macros.
+ * Declares a typed array with size and capacity.
*
- * @par Examples
- * @code
- * // integer array with at most 255 elements
- * CX_ARRAY_DECLARE_SIZED(int, myarray, uint8_t)
+ * @param type the type of the elements
+ * @param name the name of the array
+ */
+#define CX_ARRAY(type, name) \
+ struct { \
+ type *data; \
+ size_t size; \
+ size_t capacity; \
+ } name
+
+/**
+ * Internal structure for arrays.
*
- * // array of MyObject* pointers where size and capacity are stored as unsigned int
- * CX_ARRAY_DECLARE_SIZED(MyObject*, objects, unsigned int)
+ * A generalization of array structures declared with CX_ARRAY().
+ */
+typedef struct cx_array_s {
+ /** The array data. */
+ void *data;
+ /** The number of elements. */
+ size_t size;
+ /** The maximum number of elements. */
+ size_t capacity;
+} CxArray;
+
+/**
+ * Initializes an array by allocating memory.
*
- * // initializing code
- * cx_array_initialize(myarray, 16); // reserve space for 16
- * cx_array_initialize(objects, 100); // reserve space for 100
- * @endcode
+ * Internal function - do not use manually.
*
- * @param type the type of the data
- * @param name the name of the array
- * @param size_type the type of the size (should be uint8_t, uint16_t, uint32_t, or size_t)
+ * @param allocator the allocator for the array
+ * @param array a pointer to the array structure
+ * @param elem_size size of one element
+ * @param capacity the initial maximum number of elements
+ * @retval zero allocation was successful
+ * @retval non-zero allocation failed
+ */
+CX_EXTERN CX_NONNULL
+int cx_array_init_(const CxAllocator *allocator, CxArray *array, size_t elem_size, size_t capacity);
+
+/**
+ * Initializes an array by allocating memory.
+ *
+ * The size is set to zero.
*
- * @see cx_array_initialize()
- * @see cx_array_simple_add()
- * @see cx_array_simple_copy()
- * @see cx_array_simple_add_sorted()
- * @see cx_array_simple_insert_sorted()
+ * @attention If the array was already initialized, this will leak memory.
+ * Use cx_array_reserve() to change the capacity of an initialized array.
+ *
+ * @param allocator (@c CxAllocator*) the allocator for the array
+ * @param array the name of the array
+ * @param capacity (@c size_t) the initial maximum number of elements
+ * @retval zero allocation was successful
+ * @retval non-zero allocation failed
*/
-#define CX_ARRAY_DECLARE_SIZED(type, name, size_type) \
- type * name; \
- /** Array size. */ size_type name##_size; \
- /** Array capacity. */ size_type name##_capacity
+#define cx_array_init_a(allocator, array, capacity) cx_array_init_(allocator, (CxArray*)&(array), sizeof((array).data[0]), capacity)
/**
- * Declares variables for an array that can be used with the convenience macros.
+ * Initializes an array by allocating memory.
*
- * The size and capacity variables will have type @c size_t.
- * Use #CX_ARRAY_DECLARE_SIZED() to specify a different type.
+ * The size is set to zero.
*
- * @par Examples
- * @code
- * // int array
- * CX_ARRAY_DECLARE(int, myarray)
+ * @attention If the array was already initialized, this will leak memory.
*
- * // initializing code
- * cx_array_initialize(myarray, 32); // reserve space for 32
- * @endcode
+ * @param array the name of the array
+ * @param capacity (@c size_t) the initial maximum number of elements
+ * @retval zero allocation was successful
+ * @retval non-zero allocation failed
+ */
+#define cx_array_init(array, capacity) cx_array_init_a(cxDefaultAllocator, array, capacity)
+
+/**
+ * Initializes an array with fixed size memory.
*
- * @param type the type of the data
- * @param name the name of the array
+ * Internal function - do not use manually.
*
- * @see cx_array_initialize()
- * @see cx_array_simple_add()
- * @see cx_array_simple_copy()
- * @see cx_array_simple_add_sorted()
- * @see cx_array_simple_insert_sorted()
+ * @param array a pointer to the array structure
+ * @param data the fixed size array
+ * @param capacity the capacity of the fixed size array
+ * @param size the number of initialized elements in the fixed size array
*/
-#define CX_ARRAY_DECLARE(type, name) CX_ARRAY_DECLARE_SIZED(type, name, size_t)
+CX_EXTERN CX_NONNULL
+void cx_array_init_fixed_(CxArray *array, const void *data, size_t capacity, size_t size);
/**
- * Initializes an array with the given capacity.
+ * Initializes an array with fixed size memory.
*
- * The type of the capacity depends on the type used during declaration.
+ * This is useful, for example, when you want to work with memory on the stack
+ * and only want to move to the heap when the stack memory is not enough.
*
- * @par Examples
- * @code
- * CX_ARRAY_DECLARE_SIZED(int, arr1, uint8_t)
- * CX_ARRAY_DECLARE(int, arr2) // size and capacity are implicitly size_t
+ * With the @p num_initialized argument you can specify how many elements in the
+ * fixed size array are already correctly initialized, which determines the
+ * initial size of the array.
*
- * // initializing code
- * cx_array_initialize(arr1, 500); // error: maximum for uint8_t is 255
- * cx_array_initialize(arr2, 500); // OK
- * @endcode
+ * The capacity is determined automatically by the compiler.
*
+ * @attention When you add elements to an array that was initialized with fixed
+ * size memory, you MUST check the capacity before adding the element and invoke
+ * cx_array_copy_to_new() when you intend to exceed the capacity.
*
- * The memory for the array is allocated with the cxDefaultAllocator.
+ * @attention When you pass a pointer to an array that does not have a fixed
+ * size, the behavior is unspecified.
*
- * @param array the name of the array
- * @param capacity the initial capacity
- * @see cx_array_initialize_a()
- * @see CX_ARRAY_DECLARE_SIZED()
- * @see CX_ARRAY_DECLARE()
+ * @param array the name of the array to initialize
+ * @param fixed_size_array (@c void*) the fixed size array
+ * @param num_initialized (@c size_t) the number of already initialized elements in the fixed size array
+ * @see cx_array_copy_to_new()
*/
-#define cx_array_initialize(array, capacity) \
- array##_capacity = capacity; \
- array##_size = 0; \
- array = cxMallocDefault(sizeof(array[0]) * capacity)
+#define cx_array_init_fixed(array, fixed_size_array, num_initialized) \
+ cx_array_init_fixed_((CxArray*)&(array), fixed_size_array, cx_nmemb(fixed_size_array), num_initialized)
/**
- * Initializes an array with the given capacity using the specified allocator.
+ * Changes the capacity of an array.
*
- * @par Example
- * @code
- * CX_ARRAY_DECLARE(int, myarray)
+ * Internal function - do not use.
*
+ * @param allocator the allocator
+ * @param array a pointer to the array structure
+ * @param elem_size the size of one element
+ * @param capacity the new capacity
+ * @retval zero allocation was successful
+ * @retval non-zero allocation failed
+ */
+CX_EXTERN CX_NONNULL
+int cx_array_reserve_(const CxAllocator *allocator, CxArray *array, size_t elem_size, size_t capacity);
+
+/**
+ * Changes the capacity of an array.
*
- * const CxAllocator *al = // ...
- * cx_array_initialize_a(al, myarray, 128);
- * // ...
- * cxFree(al, myarray); // remember to free with the same allocator
- * @endcode
+ * If required, the size is reduced to fit into the new capacity.
*
- * @param allocator (@c CxAllocator*) the allocator
+ * @param allocator (@c CxAllocator*) the allocator for the array
* @param array the name of the array
- * @param capacity the initial capacity
- * @see cx_array_initialize()
- * @see CX_ARRAY_DECLARE_SIZED()
- * @see CX_ARRAY_DECLARE()
+ * @param capacity (@c size_t) the new maximum number of elements
+ * @retval zero allocation was successful
+ * @retval non-zero allocation failed
*/
-#define cx_array_initialize_a(allocator, array, capacity) \
- array##_capacity = capacity; \
- array##_size = 0; \
- array = cxMalloc(allocator, sizeof(array[0]) * capacity)
+#define cx_array_reserve_a(allocator, array, capacity) \
+ cx_array_reserve_(allocator, (CxArray*)&(array), sizeof((array).data[0]), capacity)
/**
- * Defines a reallocation mechanism for arrays.
- * You can create your own, use cx_array_reallocator(), or
- * use the #cx_array_default_reallocator.
+ * Changes the capacity of an array.
+ *
+ * If required, the size is reduced to fit into the new capacity.
+ *
+ * @param array the name of the array
+ * @param capacity (@c size_t) the new maximum number of elements
+ * @retval zero allocation was successful
+ * @retval non-zero allocation failed
*/
-struct cx_array_reallocator_s {
- /**
- * Reallocates space for the given array.
- *
- * Implementations are not required to free the original array.
- * This allows reallocation of static or stack memory by allocating heap memory
- * and copying the array contents; namely when @c stack_ptr in this struct
- * is not @c NULL and @p array equals @c stack_ptr.
- *
- * @param array the array to reallocate
- * @param old_capacity the old number of elements
- * @param new_capacity the new number of elements
- * @param elem_size the size of each element
- * @param alloc a reference to this allocator
- * @return a pointer to the reallocated memory or @c NULL on failure
- */
- void *(*realloc)( void *array, size_t old_capacity, size_t new_capacity,
- size_t elem_size, struct cx_array_reallocator_s *alloc);
-
- /**
- * The allocator that shall be used for the reallocations.
- */
- const CxAllocator *allocator;
- /**
- * Optional pointer to stack memory
- * if the array is originally located on the stack.
- */
- const void *stack_ptr;
-};
+#define cx_array_reserve(array, capacity) \
+ cx_array_reserve_a(cxDefaultAllocator, array, capacity)
/**
- * Typedef for the array reallocator struct.
+ * Copies the array to a new memory region.
+ *
+ * Internal function - do not use.
+ *
+ * @param allocator the allocator for new new memory
+ * @param array a pointer to the array structure
+ * @param elem_size the size of one element
+ * @param capacity the new capacity
+ * @retval zero allocation was successful
+ * @retval non-zero allocation failed
*/
-typedef struct cx_array_reallocator_s CxArrayReallocator;
+CX_EXTERN CX_NONNULL
+int cx_array_copy_to_new_(const CxAllocator *allocator, CxArray *array, size_t elem_size, size_t capacity);
/**
- * A default array reallocator that is based on the cxDefaultAllocator.
+ * Copies the array to a new memory region.
+ *
+ * This is useful when you have initialized the array with a fixed size memory
+ * using cx_array_init_fixed(), and now you want to increase the capacity.
+ *
+ * @attention When the original memory does not belong to stack memory, and
+ * you do not have another reference to this memory, it will leak.
+ *
+ * @param allocator (@c CxAllocator*) the allocator for the new memory
+ * @param array the name of the array
+ * @param capacity (@c size_t) the new maximum number of elements
+ * @retval zero allocation was successful
+ * @retval non-zero allocation failed
+ * @see cx_array_init_fixed()
*/
-CX_EXPORT extern CxArrayReallocator *cx_array_default_reallocator;
+#define cx_array_copy_to_new_a(allocator, array, capacity) \
+ cx_array_copy_to_new_(allocator, (CxArray*)&(array), sizeof((array).data[0]), capacity)
/**
- * Creates a new array reallocator.
+ * Copies the array to a new memory region.
*
- * When @p allocator is @c NULL, the cxDefaultAllocator will be used.
+ * This is useful when you have initialized the array with a fixed size memory
+ * using cx_array_init_fixed(), and now you want to increase the capacity.
*
- * When @p stack_ptr is not @c NULL, the reallocator is supposed to be used
- * @em only for the specific array initially located at @p stack_ptr.
- * When reallocation is needed, the reallocator checks if the array is
- * still located at @p stack_ptr and copies the contents to the heap.
+ * @attention When the original memory does not belong to stack memory, and
+ * you do not have another reference to this memory, it will leak.
*
- * @note Invoking this function with both arguments being @c NULL will return a
- * reallocator that behaves like #cx_array_default_reallocator.
+ * @param array the name of the array
+ * @param capacity (@c size_t) the new maximum number of elements
+ * @retval zero allocation was successful
+ * @retval non-zero allocation failed
+ * @see cx_array_init_fixed()
+ */
+#define cx_array_copy_to_new(array, capacity) \
+ cx_array_copy_to_new_a(cxDefaultAllocator, array, capacity)
+
+/**
+ * Inserts data into an array.
+ *
+ * Internal function - do not use.
*
- * @param allocator the allocator this reallocator shall be based on
- * @param stack_ptr the address of the array when the array is initially located
- * on the stack or shall not reallocate in place
- * @return an array reallocator
+ * @param allocator the allocator to use for a possible reallocation
+ * @param array a pointer to the array structure
+ * @param elem_size the size of one element
+ * @param index the index where to insert the @p other data
+ * @param other a pointer to an array of data that shall be inserted
+ * @param n the number of elements that shall be inserted
+ * @retval zero success
+ * @retval non-zero a re-allocation was necessary but failed
*/
-CX_EXPORT CxArrayReallocator cx_array_reallocator(
- const struct cx_allocator_s *allocator, const void *stack_ptr);
+CX_EXTERN CX_NONNULL_ARG(1, 2)
+int cx_array_insert_(const CxAllocator *allocator, CxArray *array,
+ size_t elem_size, size_t index, const void *other, size_t n);
/**
- * Reserves memory for additional elements.
+ * Appends an element to an array.
*
- * This function checks if the @p capacity of the array is sufficient to hold
- * at least @p size plus @p elem_count elements. If not, a reallocation is
- * performed with the specified @p reallocator.
- * You can create your own reallocator by hand, use #cx_array_default_reallocator,
- * or use the convenience function cx_array_reallocator() to create a custom reallocator.
+ * When the capacity is not enough to hold the new element, a re-allocation is attempted.
*
- * This function can be useful to replace subsequent calls to cx_array_copy()
- * with one single cx_array_reserve() and then - after guaranteeing a
- * sufficient capacity - use simple memmove() or memcpy().
+ * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation
+ * @param array the name of the array where the element shall be added
+ * @param element the element that shall be added
+ * @retval zero success
+ * @retval non-zero a re-allocation was necessary but failed
+ */
+#define cx_array_add_a(allocator, array, element) \
+ cx_array_insert_(allocator, (CxArray*)&(array), sizeof((array).data[0]), (array).size, (void*)&(element), 1)
+
+/**
+ * Appends an element to an array.
*
- * The @p width in bytes refers to the size and capacity.
- * Both must have the same width.
- * Supported are 0, 1, 2, and 4, as well as 8 if running on a 64-bit
- * architecture. If set to zero, the native word width is used.
+ * When the capacity is not enough to hold the new element, a re-allocation is attempted.
*
- * @param array a pointer to the target array
- * @param size a pointer to the size of the array
- * @param capacity a pointer to the capacity of the array
- * @param width the width in bytes for the @p size and @p capacity or zero for default
- * @param elem_size the size of one element
- * @param elem_count the number of expected additional elements
- * @param reallocator the array reallocator to use
- * (@c NULL defaults to #cx_array_default_reallocator)
+ * @param array the name of the array where the element shall be added
+ * @param element (@c void*) a pointer to the element that shall be added
* @retval zero success
- * @retval non-zero failure
- * @see cx_array_reallocator()
- */
-cx_attr_nonnull_arg(1, 2, 3)
-CX_EXPORT int cx_array_reserve(void **array, void *size, void *capacity,
- unsigned width, size_t elem_size, size_t elem_count,
- CxArrayReallocator *reallocator);
-
-/**
- * Copies elements from one array to another.
- *
- * The elements are copied to the @p target array at the specified @p index,
- * overwriting possible elements. The @p index does not need to be in range of
- * the current array @p size. If the new index plus the number of elements added
- * extends the array's size, the remaining @p capacity is used.
- *
- * If the @p capacity is also insufficient to hold the new data, a reallocation
- * attempt is made with the specified @p reallocator.
- * You can create your own reallocator by hand, use #cx_array_default_reallocator,
- * or use the convenience function cx_array_reallocator() to create a custom reallocator.
- *
- * The @p width in bytes refers to the size and capacity.
- * Both must have the same width.
- * Supported are 0, 1, 2, and 4, as well as 8 if running on a 64-bit
- * architecture. If set to zero, the native word width is used.
- *
- * @param target a pointer to the target array
- * @param size a pointer to the size of the target array
- * @param capacity a pointer to the capacity of the target array
- * @param width the width in bytes for the @p size and @p capacity or zero for default
- * @param index the index where the copied elements shall be placed
- * @param src the source array
- * @param elem_size the size of one element
- * @param elem_count the number of elements to copy
- * @param reallocator the array reallocator to use
- * (@c NULL defaults to #cx_array_default_reallocator)
+ * @retval non-zero a re-allocation was necessary but failed
+ */
+#define cx_array_add(array, element) \
+ cx_array_add_a(cxDefaultAllocator, array, element)
+
+/**
+ * Inserts an element into an array.
+ *
+ * When the capacity is not enough to hold the new element, a re-allocation is attempted.
+ *
+ * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation
+ * @param array the name of the array where the element shall be inserted
+ * @param index (@c size_t) the index where to insert the @p element
+ * @param element the element that shall be inserted
* @retval zero success
- * @retval non-zero failure
- * @see cx_array_reallocator()
+ * @retval non-zero a re-allocation was necessary but failed
*/
-cx_attr_nonnull_arg(1, 2, 3, 6)
-CX_EXPORT int cx_array_copy(void **target, void *size, void *capacity, unsigned width,
- size_t index, const void *src, size_t elem_size, size_t elem_count,
- CxArrayReallocator *reallocator);
+#define cx_array_insert_a(allocator, array, index, element) \
+ cx_array_insert_(allocator, (CxArray*)&(array), sizeof((array).data[0]), index, (void*)&(element), 1)
/**
- * Convenience macro that uses cx_array_copy() with a default layout and
- * the specified reallocator.
+ * Inserts an element into an array.
+ *
+ * When the capacity is not enough to hold the new element, a re-allocation is attempted.
*
- * @param reallocator (@c CxArrayReallocator*) the array reallocator to use
- * @param array the name of the array (NOT a pointer or alias to the array)
- * @param index (@c size_t) the index where the copied elements shall be placed
- * @param src (@c void*) the source array
- * @param count (@c size_t) the number of elements to copy
+ * @param array the name of the array where the element shall be inserted
+ * @param index (@c size_t) the index where to insert the @p element
+ * @param element (@c void*) a pointer to the element that shall be inserted
* @retval zero success
- * @retval non-zero failure
- * @see CX_ARRAY_DECLARE()
- * @see cx_array_simple_copy()
+ * @retval non-zero a re-allocation was necessary but failed
*/
-#define cx_array_simple_copy_a(reallocator, array, index, src, count) \
- cx_array_copy((void**)&(array), &(array##_size), &(array##_capacity), \
- sizeof(array##_size), index, src, sizeof((array)[0]), count, \
- reallocator)
+#define cx_array_insert(array, index, element) \
+ cx_array_insert_a(cxDefaultAllocator, array, index, element)
/**
- * Convenience macro that uses cx_array_copy() with a default layout and
- * the default reallocator.
+ * Inserts data into an array.
*
- * @param array the name of the array (NOT a pointer or alias to the array)
- * @param index (@c size_t) the index where the copied elements shall be placed
- * @param src (@c void*) the source array
- * @param count (@c size_t) the number of elements to copy
+ * When the capacity is not enough to hold the new elements, a re-allocation is attempted.
+ *
+ * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation
+ * @param array the name of the array where the elements shall be inserted
+ * @param index (@c size_t) the index where to insert the @p other data
+ * @param other (@c void*) a pointer to an array of data that shall be inserted
+ * @param n (@c size_t) the number of elements that shall be inserted
* @retval zero success
- * @retval non-zero failure
- * @see CX_ARRAY_DECLARE()
- * @see cx_array_simple_copy_a()
+ * @retval non-zero a re-allocation was necessary but failed
*/
-#define cx_array_simple_copy(array, index, src, count) \
- cx_array_simple_copy_a(NULL, array, index, src, count)
+#define cx_array_insert_array_a(allocator, array, index, other, n) \
+ cx_array_insert_(allocator, (CxArray*)&(array), sizeof((array).data[0]), index, other, n)
/**
- * Convenience macro that uses cx_array_reserve() with a default layout and
- * the specified reallocator.
+ * Inserts data into an array.
+ *
+ * When the capacity is not enough to hold the new elements, a re-allocation is attempted.
*
- * @param reallocator (@c CxArrayReallocator*) the array reallocator to use
- * @param array the name of the array (NOT a pointer or alias to the array)
- * @param count (@c size_t) the number of expected @em additional elements
+ * @param array the name of the array where the elements shall be inserted
+ * @param index (@c size_t) the index where to insert the @p other data
+ * @param other (@c void*) a pointer to an array of data that shall be inserted
+ * @param n (@c size_t) the number of elements that shall be inserted
* @retval zero success
- * @retval non-zero failure
- * @see CX_ARRAY_DECLARE()
- * @see cx_array_simple_reserve()
+ * @retval non-zero a re-allocation was necessary but failed
*/
-#define cx_array_simple_reserve_a(reallocator, array, count) \
- cx_array_reserve((void**)&(array), &(array##_size), &(array##_capacity), \
- sizeof(array##_size), sizeof((array)[0]), count, \
- reallocator)
+#define cx_array_insert_array(array, index, other, n) \
+ cx_array_insert_array_a(cxDefaultAllocator, array, index, other, n)
/**
- * Convenience macro that uses cx_array_reserve() with a default layout and
- * the default reallocator.
+ * Appends data to an array.
*
- * @param array the name of the array (NOT a pointer or alias to the array)
- * @param count (@c size_t) the number of expected additional elements
+ * When the capacity is not enough to hold the new elements, a re-allocation is attempted.
+ *
+ * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation
+ * @param array the name of the array where the elements shall be added
+ * @param other (@c void*) a pointer to an array of data that shall be added
+ * @param n (@c size_t) the number of elements that shall be added
* @retval zero success
- * @retval non-zero failure
- * @see CX_ARRAY_DECLARE()
- * @see cx_array_simple_reserve_a()
+ * @retval non-zero a re-allocation was necessary but failed
*/
-#define cx_array_simple_reserve(array, count) \
- cx_array_simple_reserve_a(NULL, array, count)
+#define cx_array_add_array_a(allocator, array, other, n) \
+ cx_array_insert_(allocator, (CxArray*)&(array), sizeof((array).data[0]), (array).size, other, n)
/**
- * Adds an element to an array with the possibility of allocating more space.
+ * Appends data to an array.
*
- * The element @p elem is added to the end of the @p target array which contains
- * @p size elements, already. The @p capacity must point to a variable denoting
- * the current maximum number of elements the array can hold.
+ * When the capacity is not enough to hold the new elements, a re-allocation is attempted.
*
- * If the capacity is insufficient to hold the new element, an attempt to
- * increase the @p capacity is made and the new capacity is written back.
+ * @param array the name of the array where the elements shall be added
+ * @param other (@c void*) a pointer to an array of data that shall be added
+ * @param n (@c size_t) the number of elements that shall be added
+ * @retval zero success
+ * @retval non-zero a re-allocation was necessary but failed
+ */
+#define cx_array_add_array(array, other, n) \
+ cx_array_add_array_a(cxDefaultAllocator, array, other, n)
+
+/**
+ * Inserts sorted data into a sorted array.
*
- * The \@ SIZE_TYPE is flexible and can be any unsigned integer type.
- * It is important, however, that @p size and @p capacity are pointers to
- * variables of the same type.
+ * Internal function - do not use.
*
- * @param target (@c void**) a pointer to the target array
- * @param size (@c SIZE_TYPE*) a pointer to the size of the target array
- * @param capacity (@c SIZE_TYPE*) a pointer to the capacity of the target array
- * @param elem_size (@c size_t) the size of one element
- * @param elem (@c void*) a pointer to the element to add
- * @param reallocator (@c CxArrayReallocator*) the array reallocator to use
+ * @param allocator the allocator to use for a possible reallocation
+ * @param array a pointer to the array structure
+ * @param elem_size the size of one element
+ * @param sorted_data a pointer to an array of data that shall be inserted
+ * @param n the number of elements that shall be inserted
+ * @param cmp_func the compare function
+ * @param allow_duplicates @c false if duplicates shall be skipped during insertion
* @retval zero success
- * @retval non-zero failure
+ * @retval non-zero a re-allocation was necessary but failed
*/
-#define cx_array_add(target, size, capacity, elem_size, elem, reallocator) \
- cx_array_copy((void**)(target), size, capacity, sizeof(*(size)), \
- *(size), elem, elem_size, 1, reallocator)
+CX_EXTERN CX_NONNULL
+int cx_array_insert_sorted_(const CxAllocator *allocator, CxArray *array,
+ size_t elem_size, const void *sorted_data, size_t n,
+ cx_compare_func cmp_func, bool allow_duplicates);
/**
- * Convenience macro that uses cx_array_add() with a default layout and
- * the specified reallocator.
+ * Inserts an element into a sorted array.
+ *
+ * When the capacity is not enough to hold the new element, a re-allocation is attempted.
*
- * @param reallocator (@c CxArrayReallocator*) the array reallocator to use
- * @param array the name of the array (NOT a pointer or alias to the array)
- * @param elem the element to add (NOT a pointer, address is automatically taken)
+ * @attention if the array is not sorted according to the specified @p cmp_func, the behavior is undefined.
+ *
+ * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation
+ * @param array the name of the array where the elements shall be inserted
+ * @param element the element that shall be inserted
+ * @param cmp_func (@c cx_compare_func) the compare function that establishes the order
* @retval zero success
- * @retval non-zero failure
- * @see CX_ARRAY_DECLARE()
- * @see cx_array_simple_add()
+ * @retval non-zero a re-allocation was necessary but failed
*/
-#define cx_array_simple_add_a(reallocator, array, elem) \
- cx_array_simple_copy_a(reallocator, array, array##_size, &(elem), 1)
+#define cx_array_insert_sorted_a(allocator, array, element, cmp_func) \
+ cx_array_insert_sorted_(allocator, (CxArray*)&(array), sizeof((array).data[0]), (void*)&(element), 1, cmp_func, true)
/**
- * Convenience macro that uses cx_array_add() with a default layout and
- * the default reallocator.
+ * Inserts an element into a sorted array.
+ *
+ * When the capacity is not enough to hold the new element, a re-allocation is attempted.
*
- * @param array the name of the array (NOT a pointer or alias to the array)
- * @param elem the element to add (NOT a pointer, address is automatically taken)
+ * @attention if the array is not sorted according to the specified @p cmp_func, the behavior is undefined.
+ *
+ * @param array the name of the array where the elements shall be inserted
+ * @param element the element that shall be inserted
+ * @param cmp_func (@c cx_compare_func) the compare function that establishes the order
* @retval zero success
- * @retval non-zero failure
- * @see CX_ARRAY_DECLARE()
- * @see cx_array_simple_add_a()
+ * @retval non-zero a re-allocation was necessary but failed
*/
-#define cx_array_simple_add(array, elem) \
- cx_array_simple_add_a(cx_array_default_reallocator, array, elem)
+#define cx_array_insert_sorted(array, element, cmp_func) \
+ cx_array_insert_sorted_a(cxDefaultAllocator, array, element, cmp_func)
/**
- * Inserts a sorted array into another sorted array.
+ * Inserts sorted data into a sorted array.
*
- * If either the target or the source array is not already sorted with respect
- * to the specified @p cmp_func, the behavior is undefined.
+ * When the capacity is not enough to hold the new elements, a re-allocation is attempted.
*
- * If the capacity is insufficient to hold the new data, a reallocation
- * attempt is made.
- * You can create your own reallocator by hand, use #cx_array_default_reallocator,
- * or use the convenience function cx_array_reallocator() to create a custom reallocator.
+ * @attention if either array is not sorted according to the specified @p cmp_func, the behavior is undefined.
*
- * @param target a pointer to the target array
- * @param size a pointer to the size of the target array
- * @param capacity a pointer to the capacity of the target array
- * @param cmp_func the compare function for the elements
- * @param src the source array
- * @param elem_size the size of one element
- * @param elem_count the number of elements to insert
- * @param reallocator the array reallocator to use
- * (@c NULL defaults to #cx_array_default_reallocator)
+ * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation
+ * @param array the name of the array where the elements shall be inserted
+ * @param sorted_data (@c void*) a pointer to an array of sorted data that shall be inserted
+ * @param n (@c size_t) the number of elements that shall be inserted
+ * @param cmp_func (@c cx_compare_func) the compare function that establishes the order
* @retval zero success
- * @retval non-zero failure
+ * @retval non-zero a re-allocation was necessary but failed
*/
-cx_attr_nonnull_arg(1, 2, 3, 5)
-CX_EXPORT int cx_array_insert_sorted(void **target, size_t *size, size_t *capacity,
- cx_compare_func cmp_func, const void *src, size_t elem_size, size_t elem_count,
- CxArrayReallocator *reallocator);
+#define cx_array_insert_sorted_array_a(allocator, array, sorted_data, n, cmp_func) \
+ cx_array_insert_sorted_(allocator, (CxArray*)&(array), sizeof((array).data[0]), sorted_data, n, cmp_func, true)
/**
- * Inserts an element into a sorted array.
+ * Inserts sorted data into a sorted array.
+ *
+ * When the capacity is not enough to hold the new elements, a re-allocation is attempted.
*
- * If the target array is not already sorted with respect
- * to the specified @p cmp_func, the behavior is undefined.
+ * @attention if either array is not sorted according to the specified @p cmp_func, the behavior is undefined.
+ *
+ * @param array the name of the array where the elements shall be inserted
+ * @param sorted_data (@c void*) a pointer to an array of sorted data that shall be inserted
+ * @param n (@c size_t) the number of elements that shall be inserted
+ * @param cmp_func (@c cx_compare_func) the compare function that establishes the order
+ * @retval zero success
+ * @retval non-zero a re-allocation was necessary but failed
+ */
+#define cx_array_insert_sorted_array(array, sorted_data, n, cmp_func) \
+ cx_array_insert_sorted_array_a(cxDefaultAllocator, array, sorted_data, n, cmp_func)
+
+/**
+ * Inserts an element into a sorted array if it is not already contained.
*
- * If the capacity is not enough to hold the new data, a reallocation
- * attempt is made.
+ * When the capacity is not enough to hold the new element, a re-allocation is attempted.
*
- * The \@ SIZE_TYPE is flexible and can be any unsigned integer type.
- * It is important, however, that @p size and @p capacity are pointers to
- * variables of the same type.
+ * @attention if the array is not sorted according to the specified @p cmp_func, the behavior is undefined.
*
- * @param target (@c void**) a pointer to the target array
- * @param size (@c SIZE_TYPE*) a pointer to the size of the target array
- * @param capacity (@c SIZE_TYPE*) a pointer to the capacity of the target array
- * @param elem_size (@c size_t) the size of one element
- * @param elem (@c void*) a pointer to the element to add
- * @param cmp_func (@c cx_cmp_func) the compare function for the elements
- * @param reallocator (@c CxArrayReallocator*) the array reallocator to use
+ * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation
+ * @param array the name of the array where the elements shall be inserted
+ * @param element the element that shall be inserted
+ * @param cmp_func (@c cx_compare_func) the compare function that establishes the order
* @retval zero success
- * @retval non-zero failure
+ * @retval non-zero a re-allocation was necessary but failed
*/
-#define cx_array_add_sorted(target, size, capacity, elem_size, elem, cmp_func, reallocator) \
- cx_array_insert_sorted((void**)(target), size, capacity, cmp_func, elem, elem_size, 1, reallocator)
+#define cx_array_insert_unique_a(allocator, array, element, cmp_func) \
+ cx_array_insert_sorted_(allocator, (CxArray*)&(array), sizeof((array).data[0]), (void*)&(element), 1, cmp_func, false)
/**
- * Convenience macro for cx_array_add_sorted() with a default
- * layout and the specified reallocator.
+ * Inserts an element into a sorted array if it is not already contained.
+ *
+ * When the capacity is not enough to hold the new element, a re-allocation is attempted.
+ *
+ * @attention if the array is not sorted according to the specified @p cmp_func, the behavior is undefined.
*
- * @param reallocator (@c CxArrayReallocator*) the array reallocator to use
- * @param array the name of the array (NOT a pointer or alias to the array)
- * @param elem the element to add (NOT a pointer, address is automatically taken)
- * @param cmp_func (@c cx_cmp_func) the compare function for the elements
+ * @param array the name of the array where the elements shall be inserted
+ * @param element the element that shall be inserted
+ * @param cmp_func (@c cx_compare_func) the compare function that establishes the order
* @retval zero success
- * @retval non-zero failure
- * @see CX_ARRAY_DECLARE()
- * @see cx_array_simple_add_sorted()
+ * @retval non-zero a re-allocation was necessary but failed
*/
-#define cx_array_simple_add_sorted_a(reallocator, array, elem, cmp_func) \
- cx_array_add_sorted(&array, &(array##_size), &(array##_capacity), \
- sizeof((array)[0]), &(elem), cmp_func, reallocator)
+#define cx_array_insert_unique(array, element, cmp_func) \
+ cx_array_insert_unique_a(cxDefaultAllocator, array, element, cmp_func)
/**
- * Convenience macro for cx_array_add_sorted() with a default
- * layout and the default reallocator.
+ * Inserts sorted data into a sorted array, skipping duplicates.
*
- * @param array the name of the array (NOT a pointer or alias to the array)
- * @param elem the element to add (NOT a pointer, address is automatically taken)
- * @param cmp_func (@c cx_cmp_func) the compare function for the elements
+ * When the capacity is not enough to hold the new elements, a re-allocation is attempted.
+ *
+ * @attention if either array is not sorted according to the specified @p cmp_func, the behavior is undefined.
+ *
+ * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation
+ * @param array the name of the array where the elements shall be inserted
+ * @param sorted_data (@c void*) a pointer to an array of sorted data that shall be inserted
+ * @param n (@c size_t) the number of elements that shall be inserted
+ * @param cmp_func (@c cx_compare_func) the compare function that establishes the order
* @retval zero success
- * @retval non-zero failure
- * @see CX_ARRAY_DECLARE()
- * @see cx_array_simple_add_sorted_a()
+ * @retval non-zero a re-allocation was necessary but failed
*/
-#define cx_array_simple_add_sorted(array, elem, cmp_func) \
- cx_array_simple_add_sorted_a(NULL, array, elem, cmp_func)
+#define cx_array_insert_unique_array_a(allocator, array, sorted_data, n, cmp_func) \
+ cx_array_insert_sorted_(allocator, (CxArray*)&(array), sizeof((array).data[0]), sorted_data, n, cmp_func, false)
/**
- * Convenience macro for cx_array_insert_sorted() with a default
- * layout and the specified reallocator.
+ * Inserts sorted data into a sorted array, skipping duplicates.
+ *
+ * When the capacity is not enough to hold the new elements, a re-allocation is attempted.
*
- * @param reallocator (@c CxArrayReallocator*) the array reallocator to use
- * @param array the name of the array (NOT a pointer or alias to the array)
- * @param src (@c void*) pointer to the source array
- * @param n (@c size_t) number of elements in the source array
- * @param cmp_func (@c cx_cmp_func) the compare function for the elements
+ * @attention if either array is not sorted according to the specified @p cmp_func, the behavior is undefined.
+ *
+ * @param array the name of the array where the elements shall be inserted
+ * @param sorted_data (@c void*) a pointer to an array of sorted data that shall be inserted
+ * @param n (@c size_t) the number of elements that shall be inserted
+ * @param cmp_func (@c cx_compare_func) the compare function that establishes the order
* @retval zero success
- * @retval non-zero failure
- * @see CX_ARRAY_DECLARE()
- * @see cx_array_simple_insert_sorted()
+ * @retval non-zero a re-allocation was necessary but failed
*/
-#define cx_array_simple_insert_sorted_a(reallocator, array, src, n, cmp_func) \
- cx_array_insert_sorted((void**)(&array), &(array##_size), &(array##_capacity), \
- cmp_func, src, sizeof((array)[0]), n, reallocator)
+#define cx_array_insert_unique_array(array, sorted_data, n, cmp_func) \
+ cx_array_insert_unique_array_a(cxDefaultAllocator, array, sorted_data, n, cmp_func)
/**
- * Convenience macro for cx_array_insert_sorted() with a default
- * layout and the default reallocator.
+ * Inserts sorted data into a sorted array.
+ *
+ * Internal function - do not use.
*
- * @param array the name of the array (NOT a pointer or alias to the array)
- * @param src (@c void*) pointer to the source array
- * @param n (@c size_t) number of elements in the source array
- * @param cmp_func (@c cx_cmp_func) the compare function for the elements
+ * @param allocator the allocator to use for a possible reallocation
+ * @param array a pointer to the array structure
+ * @param elem_size the size of one element
+ * @param sorted_data a pointer to an array of data that shall be inserted
+ * @param n the number of elements that shall be inserted
+ * @param cmp_func the compare function
+ * @param context additional context for the compare function
+ * @param allow_duplicates @c false if duplicates shall be skipped during insertion
* @retval zero success
- * @retval non-zero failure
- * @see CX_ARRAY_DECLARE()
- * @see cx_array_simple_insert_sorted_a()
+ * @retval non-zero a re-allocation was necessary but failed
*/
-#define cx_array_simple_insert_sorted(array, src, n, cmp_func) \
- cx_array_simple_insert_sorted_a(NULL, array, src, n, cmp_func)
+CX_EXTERN CX_NONNULL_ARG(1, 2, 4, 6)
+int cx_array_insert_sorted_c_(const CxAllocator *allocator, CxArray *array,
+ size_t elem_size, const void *sorted_data, size_t n,
+ cx_compare_func2 cmp_func, void *context, bool allow_duplicates);
+/**
+ * Inserts an element into a sorted array.
+ *
+ * When the capacity is not enough to hold the new element, a re-allocation is attempted.
+ *
+ * @attention if the array is not sorted according to the specified @p cmp_func, the behavior is undefined.
+ *
+ * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation
+ * @param array the name of the array where the elements shall be inserted
+ * @param element the element that shall be inserted
+ * @param cmp_func (@c cx_compare_func2) the compare function that establishes the order
+ * @param context (@c void*) additional context for the compare function
+ * @retval zero success
+ * @retval non-zero a re-allocation was necessary but failed
+ */
+#define cx_array_insert_sorted_ca(allocator, array, element, cmp_func, context) \
+ cx_array_insert_sorted_c_(allocator, (CxArray*)&(array), sizeof((array).data[0]), (void*)&(element), 1, cmp_func, context, true)
/**
- * Inserts a sorted array into another sorted array, avoiding duplicates.
+ * Inserts an element into a sorted array.
*
- * If either the target or the source array is not already sorted with respect
- * to the specified @p cmp_func, the behavior is undefined.
+ * When the capacity is not enough to hold the new element, a re-allocation is attempted.
*
- * If the capacity is insufficient to hold the new data, a reallocation
- * attempt is made.
- * You can create your own reallocator by hand, use #cx_array_default_reallocator,
- * or use the convenience function cx_array_reallocator() to create a custom reallocator.
+ * @attention if the array is not sorted according to the specified @p cmp_func, the behavior is undefined.
*
- * @param target a pointer to the target array
- * @param size a pointer to the size of the target array
- * @param capacity a pointer to the capacity of the target array
- * @param cmp_func the compare function for the elements
- * @param src the source array
- * @param elem_size the size of one element
- * @param elem_count the number of elements to insert
- * @param reallocator the array reallocator to use
- * (@c NULL defaults to #cx_array_default_reallocator)
+ * @param array the name of the array where the elements shall be inserted
+ * @param element the element that shall be inserted
+ * @param cmp_func (@c cx_compare_func2) the compare function that establishes the order
+ * @param context (@c void*) additional context for the compare function
* @retval zero success
- * @retval non-zero failure
+ * @retval non-zero a re-allocation was necessary but failed
*/
-cx_attr_nonnull_arg(1, 2, 3, 5)
-CX_EXPORT int cx_array_insert_unique(void **target, size_t *size, size_t *capacity,
- cx_compare_func cmp_func, const void *src, size_t elem_size, size_t elem_count,
- CxArrayReallocator *reallocator);
+#define cx_array_insert_sorted_c(array, element, cmp_func, context) \
+ cx_array_insert_sorted_ca(cxDefaultAllocator, array, element, cmp_func, context)
/**
- * Inserts an element into a sorted array if it does not exist.
+ * Inserts sorted data into a sorted array.
+ *
+ * When the capacity is not enough to hold the new elements, a re-allocation is attempted.
*
- * If the target array is not already sorted with respect
- * to the specified @p cmp_func, the behavior is undefined.
+ * @attention if either array is not sorted according to the specified @p cmp_func, the behavior is undefined.
+ *
+ * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation
+ * @param array the name of the array where the elements shall be inserted
+ * @param sorted_data (@c void*) a pointer to an array of sorted data that shall be inserted
+ * @param n (@c size_t) the number of elements that shall be inserted
+ * @param cmp_func (@c cx_compare_func2) the compare function that establishes the order
+ * @param context (@c void*) additional context for the compare function
+ * @retval zero success
+ * @retval non-zero a re-allocation was necessary but failed
+ */
+#define cx_array_insert_sorted_array_ca(allocator, array, sorted_data, n, cmp_func, context) \
+ cx_array_insert_sorted_c_(allocator, (CxArray*)&(array), sizeof((array).data[0]), sorted_data, n, cmp_func, context, true)
+
+/**
+ * Inserts sorted data into a sorted array.
*
- * If the capacity is insufficient to hold the new data, a reallocation
- * attempt is made.
+ * When the capacity is not enough to hold the new elements, a re-allocation is attempted.
*
- * The \@ SIZE_TYPE is flexible and can be any unsigned integer type.
- * It is important, however, that @p size and @p capacity are pointers to
- * variables of the same type.
+ * @attention if either array is not sorted according to the specified @p cmp_func, the behavior is undefined.
*
- * @param target (@c void**) a pointer to the target array
- * @param size (@c SIZE_TYPE*) a pointer to the size of the target array
- * @param capacity (@c SIZE_TYPE*) a pointer to the capacity of the target array
- * @param elem_size (@c size_t) the size of one element
- * @param elem (@c void*) a pointer to the element to add
- * @param cmp_func (@c cx_cmp_func) the compare function for the elements
- * @param reallocator (@c CxArrayReallocator*) the array reallocator to use
- * @retval zero success (also when the element was already present)
- * @retval non-zero failure
+ * @param array the name of the array where the elements shall be inserted
+ * @param sorted_data (@c void*) a pointer to an array of sorted data that shall be inserted
+ * @param n (@c size_t) the number of elements that shall be inserted
+ * @param cmp_func (@c cx_compare_func2) the compare function that establishes the order
+ * @param context (@c void*) additional context for the compare function
+ * @retval zero success
+ * @retval non-zero a re-allocation was necessary but failed
*/
-#define cx_array_add_unique(target, size, capacity, elem_size, elem, cmp_func, reallocator) \
- cx_array_insert_unique((void**)(target), size, capacity, cmp_func, elem, elem_size, 1, reallocator)
+#define cx_array_insert_sorted_array_c(array, sorted_data, n, cmp_func, context) \
+ cx_array_insert_sorted_array_ca(cxDefaultAllocator, array, sorted_data, n, cmp_func, context)
/**
- * Convenience macro for cx_array_add_unique() with a default
- * layout and the specified reallocator.
+ * Inserts an element into a sorted array if it is not already contained.
+ *
+ * When the capacity is not enough to hold the new element, a re-allocation is attempted.
*
- * @param reallocator (@c CxArrayReallocator*) the array reallocator to use
- * @param array the name of the array (NOT a pointer or alias to the array)
- * @param elem the element to add (NOT a pointer, address is automatically taken)
- * @param cmp_func (@c cx_cmp_func) the compare function for the elements
+ * @attention if the array is not sorted according to the specified @p cmp_func, the behavior is undefined.
+ *
+ * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation
+ * @param array the name of the array where the elements shall be inserted
+ * @param element the element that shall be inserted
+ * @param cmp_func (@c cx_compare_func2) the compare function that establishes the order
+ * @param context (@c void*) additional context for the compare function
* @retval zero success
- * @retval non-zero failure
- * @see CX_ARRAY_DECLARE()
- * @see cx_array_simple_add_unique()
+ * @retval non-zero a re-allocation was necessary but failed
*/
-#define cx_array_simple_add_unique_a(reallocator, array, elem, cmp_func) \
- cx_array_add_unique(&array, &(array##_size), &(array##_capacity), \
- sizeof((array)[0]), &(elem), cmp_func, reallocator)
+#define cx_array_insert_unique_ca(allocator, array, element, cmp_func, context) \
+ cx_array_insert_sorted_c_(allocator, (CxArray*)&(array), sizeof((array).data[0]), (void*)&(element), 1, cmp_func, context, false)
/**
- * Convenience macro for cx_array_add_unique() with a default
- * layout and the default reallocator.
+ * Inserts an element into a sorted array if it is not already contained.
+ *
+ * When the capacity is not enough to hold the new element, a re-allocation is attempted.
+ *
+ * @attention if the array is not sorted according to the specified @p cmp_func, the behavior is undefined.
*
- * @param array the name of the array (NOT a pointer or alias to the array)
- * @param elem the element to add (NOT a pointer, address is automatically taken)
- * @param cmp_func (@c cx_cmp_func) the compare function for the elements
+ * @param array the name of the array where the elements shall be inserted
+ * @param element the element that shall be inserted
+ * @param cmp_func (@c cx_compare_func2) the compare function that establishes the order
+ * @param context (@c void*) additional context for the compare function
* @retval zero success
- * @retval non-zero failure
- * @see CX_ARRAY_DECLARE()
- * @see cx_array_simple_add_unique_a()
+ * @retval non-zero a re-allocation was necessary but failed
*/
-#define cx_array_simple_add_unique(array, elem, cmp_func) \
- cx_array_simple_add_unique_a(NULL, array, elem, cmp_func)
+#define cx_array_insert_unique_c(array, element, cmp_func, context) \
+ cx_array_insert_unique_ca(cxDefaultAllocator, array, element, cmp_func, context)
/**
- * Convenience macro for cx_array_insert_unique() with a default
- * layout and the specified reallocator.
+ * Inserts sorted data into a sorted array, skipping duplicates.
+ *
+ * When the capacity is not enough to hold the new elements, a re-allocation is attempted.
+ *
+ * @attention if either array is not sorted according to the specified @p cmp_func, the behavior is undefined.
*
- * @param reallocator (@c CxArrayReallocator*) the array reallocator to use
- * @param array the name of the array (NOT a pointer or alias to the array)
- * @param src (@c void*) pointer to the source array
- * @param n (@c size_t) number of elements in the source array
- * @param cmp_func (@c cx_cmp_func) the compare function for the elements
+ * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation
+ * @param array the name of the array where the elements shall be inserted
+ * @param sorted_data (@c void*) a pointer to an array of sorted data that shall be inserted
+ * @param n (@c size_t) the number of elements that shall be inserted
+ * @param cmp_func (@c cx_compare_func2) the compare function that establishes the order
+ * @param context (@c void*) additional context for the compare function
* @retval zero success
- * @retval non-zero failure
- * @see CX_ARRAY_DECLARE()
- * @see cx_array_simple_insert_unique()
+ * @retval non-zero a re-allocation was necessary but failed
*/
-#define cx_array_simple_insert_unique_a(reallocator, array, src, n, cmp_func) \
- cx_array_insert_unique((void**)(&array), &(array##_size), &(array##_capacity), \
- cmp_func, src, sizeof((array)[0]), n, reallocator)
+#define cx_array_insert_unique_array_ca(allocator, array, sorted_data, n, cmp_func, context) \
+ cx_array_insert_sorted_c_(allocator, (CxArray*)&(array), sizeof((array).data[0]), sorted_data, n, cmp_func, context, false)
/**
- * Convenience macro for cx_array_insert_unique() with a default
- * layout and the default reallocator.
+ * Inserts sorted data into a sorted array, skipping duplicates.
*
- * @param array the name of the array (NOT a pointer or alias to the array)
- * @param src (@c void*) pointer to the source array
- * @param n (@c size_t) number of elements in the source array
- * @param cmp_func (@c cx_cmp_func) the compare function for the elements
+ * When the capacity is not enough to hold the new elements, a re-allocation is attempted.
+ *
+ * @attention if either array is not sorted according to the specified @p cmp_func, the behavior is undefined.
+ *
+ * @param array the name of the array where the elements shall be inserted
+ * @param sorted_data (@c void*) a pointer to an array of sorted data that shall be inserted
+ * @param n (@c size_t) the number of elements that shall be inserted
+ * @param cmp_func (@c cx_compare_func2) the compare function that establishes the order
+ * @param context (@c void*) additional context for the compare function
* @retval zero success
- * @retval non-zero failure
- * @see CX_ARRAY_DECLARE()
- * @see cx_array_simple_insert_unique_a()
+ * @retval non-zero a re-allocation was necessary but failed
*/
-#define cx_array_simple_insert_unique(array, src, n, cmp_func) \
- cx_array_simple_insert_unique_a(NULL, array, src, n, cmp_func)
+#define cx_array_insert_unique_array_c(array, sorted_data, n, cmp_func, context) \
+ cx_array_insert_unique_array_ca(cxDefaultAllocator, array, sorted_data, n, cmp_func, context)
+
+/**
+ * An alternative to qsort_r() when that is not available on your platform.
+ *
+ * If it is available, qsort_r() is used directly.
+ *
+ * @param array the array that shall be sorted
+ * @param nmemb the number of elements in the array
+ * @param size the size of one element
+ * @param fn the compare function
+ * @param context the context for the compare function
+ */
+CX_EXTERN CX_NONNULL
+void cx_array_qsort_c(void *array, size_t nmemb, size_t size,
+ cx_compare_func2 fn, void *context);
+
+/**
+ * Sorts an array.
+ *
+ * Internal function - do not use.
+ *
+ * @param array a pointer to the array structure
+ * @param elem_size the size of one element
+ * @param fn the compare function
+ */
+CX_EXTERN CX_NONNULL
+void cx_array_sort_(CxArray *array, size_t elem_size,
+ cx_compare_func fn);
+
+/**
+ * Sorts an array.
+ *
+ * Internal function - do not use.
+ *
+ * @param array a pointer to the array structure
+ * @param elem_size the size of one element
+ * @param fn the compare function
+ * @param context the context for the compare function
+ */
+CX_EXTERN CX_NONNULL
+void cx_array_sort_c_(CxArray *array, size_t elem_size,
+ cx_compare_func2 fn, void *context);
+
+/**
+ * Sorts an array.
+ *
+ * @param array the name of the array
+ * @param fn (@c cx_compare_func) the compare function
+ */
+#define cx_array_sort(array, fn) \
+ cx_array_sort_((CxArray*)&(array), sizeof((array).data[0]), fn)
+
+/**
+ * Sorts an array.
+ *
+ * @param array the name of the array
+ * @param fn (@c cx_compare_func2) the compare function
+ * @param context (@c void*) the context for the compare function
+ */
+#define cx_array_sort_c(array, fn, context) \
+ cx_array_sort_c_((CxArray*)&(array), sizeof((array).data[0]), fn, context)
+
+/**
+ * Creates an iterator over the elements of an array.
+ *
+ * Internal function - do not use.
+ *
+ * @param array a pointer to the array structure
+ * @param elem_size the size of one element
+ * @return an iterator over the elements
+ */
+CX_EXTERN CX_NODISCARD CX_NONNULL
+CxIterator cx_array_iterator_(CxArray *array, size_t elem_size);
+
+/**
+ * Creates an iterator over the elements of an array.
+ *
+ * The iterator will yield pointers to the elements.
+ *
+ * This iterator cannot be used to remove elements
+ * because it does not get a modifiable reference to the array's size.
+ *
+ * @param array the name of the array
+ * @return an iterator over the elements
+ * @see cx_array_iterator_ptr()
+ */
+#define cx_array_iterator(array) \
+ cx_array_iterator_((CxArray*)&(array), sizeof((array).data[0]))
+
+/**
+ * Creates an iterator over the elements of an array containing pointers.
+ *
+ * Internal function - do not use.
+ *
+ * @param array the name of the array
+ * @return an iterator over the elements
+ */
+CX_EXTERN CX_NODISCARD CX_NONNULL
+CxIterator cx_array_iterator_ptr_(CxArray *array);
+
+/**
+ * Creates an iterator over the elements of an array containing pointers.
+ *
+ * The iterator will yield the elements themselves, which are supposed to
+ * be pointers.
+ *
+ * This iterator cannot be used to remove elements
+ * because it does not get a modifiable reference to the array's size.
+ *
+ * @param array the name of the array
+ * @return an iterator over the elements
+ * @see cx_array_iterator()
+ */
+#define cx_array_iterator_ptr(array) \
+ cx_array_iterator_ptr_((CxArray*)&(array))
+
+
+/**
+ * Removes elements from the array.
+ *
+ * Internal function - do not use.
+ *
+ * @param array a pointer to the array structure
+ * @param elem_size the size of one element
+ * @param index the index of the first element to remove
+ * @param n the number of elements to remove
+ * @param fast indicates whether tail elements should be copied into the gap
+ */
+CX_EXTERN CX_NONNULL
+void cx_array_remove_(CxArray *array, size_t elem_size, size_t index, size_t n, bool fast);
+
+/**
+ * Removes one element from the array.
+ *
+ * Tail elements are all moved by one. If you don't need a stable order
+ * in the array, consider using cx_array_remove_fast().
+ *
+ * If the index is out of bounds, this function does nothing.
+ *
+ * @param array the name of the array
+ * @param index (@c size_t) the index of the element to remove
+ * @see cx_array_remove_fast()
+ */
+#define cx_array_remove(array, index) \
+ cx_array_remove_((CxArray*)&(array), sizeof((array).data[0]), index, 1, false)
+
+/**
+ * Removes one element from the array.
+ *
+ * The gap will be filled with a copy of the last element in the array.
+ * This changes the order of elements. If you want a stable order,
+ * use cx_array_remove() instead.
+ *
+ * If the index is out of bounds, this function does nothing.
+ *
+ * @param array the name of the array
+ * @param index (@c size_t) the index of the element to remove
+ * @see cx_array_remove()
+ */
+#define cx_array_remove_fast(array, index) \
+ cx_array_remove_((CxArray*)&(array), sizeof((array).data[0]), index, 1, true)
+
+/**
+ * Removes multiple elements from the array.
+ *
+ * Tail elements are all moved to close the gap. If you don't need a stable
+ * order in the array, consider using cx_array_remove_array_fast().
+ *
+ * If the index is out of bounds, this function does nothing.
+ * If @n overflows the array, this function removes as many elements as it can.
+ *
+ * @param array the name of the array
+ * @param index (@c size_t) the index of the first element to remove
+ * @param n (@c size_t) the number of elements to remove
+ * @see cx_array_remove_array_fast()
+ */
+#define cx_array_remove_array(array, index, n) \
+ cx_array_remove_((CxArray*)&(array), sizeof((array).data[0]), index, n, false)
+
+/**
+ * Removes multiple elements from the array.
+ *
+ * Tail elements are copied into the gap. If you have more tail elements
+ * than the number of elements that are removed, this will change the order
+ * of elements. If you want a stable order, use cx_array_remove_array() instead.
+ *
+ * If the index is out of bounds, this function does nothing.
+ * If @n overflows the array, this function removes as many elements as it can.
+ *
+ * @param array the name of the array
+ * @param index (@c size_t) the index of the first element to remove
+ * @param n (@c size_t) the number of elements to remove
+ * @see cx_array_remove_array()
+ */
+#define cx_array_remove_array_fast(array, index, n) \
+ cx_array_remove_((CxArray*)&(array), sizeof((array).data[0]), index, n, true)
+
+/**
+ * Deallocates an array.
+ *
+ * Internal function - do not use.
+ *
+ * @param allocator (@c CxAllocator*) the allocator which was used to allocate the array
+ * @param array a pointer to the array structure
+ */
+CX_EXTERN CX_NONNULL
+void cx_array_free_(const CxAllocator *allocator, CxArray *array);
+
+/**
+ * Deallocates an array.
+ *
+ * The structure is reset to zero and can be re-initialized with
+ * cx_array_inita().
+ *
+ * @param array the name of the array
+ */
+#define cx_array_free(array) cx_array_free_(cxDefaultAllocator, (CxArray*)&(array))
+
+/**
+ * Deallocates an array.
+ *
+ * The structure is reset to zero and can be re-initialized with
+ * cx_array_init_a().
+ *
+ * @param allocator (@c CxAllocator*) the allocator which was used to allocate the array
+ * @param array the name of the array
+ */
+#define cx_array_free_a(allocator, array) cx_array_free_(allocator, (CxArray*)&(array))
+
/**
* Searches the largest lower bound in a sorted array.
* in @p arr that is less or equal to @p elem with respect to @p cmp_func.
* When no such element exists, @p size is returned.
*
+ * When such an element exists more than once, the largest index of all those
+ * elements is returned.
+ *
* If @p elem is contained in the array, this is identical to
* #cx_array_binary_search().
*
* @see cx_array_binary_search_sup()
* @see cx_array_binary_search()
*/
-cx_attr_nonnull
-CX_EXPORT size_t cx_array_binary_search_inf(const void *arr, size_t size,
+CX_EXTERN CX_NONNULL
+size_t cx_array_binary_search_inf(const void *arr, size_t size,
size_t elem_size, const void *elem, cx_compare_func cmp_func);
/**
* Searches an item in a sorted array.
*
+ * When such an element exists more than once, the largest index of all those
+ * elements is returned.
+ *
* If the array is not sorted with respect to the @p cmp_func, the behavior
* is undefined.
*
* @see cx_array_binary_search_inf()
* @see cx_array_binary_search_sup()
*/
-cx_attr_nonnull
-CX_EXPORT size_t cx_array_binary_search(const void *arr, size_t size,
+CX_EXTERN CX_NONNULL
+size_t cx_array_binary_search(const void *arr, size_t size,
size_t elem_size, const void *elem, cx_compare_func cmp_func);
/**
* in @p arr that is greater or equal to @p elem with respect to @p cmp_func.
* When no such element exists, @p size is returned.
*
+ * When such an element exists more than once, the smallest index of all those
+ * elements is returned.
+ *
* If @p elem is contained in the array, this is identical to
* #cx_array_binary_search().
*
* @see cx_array_binary_search_inf()
* @see cx_array_binary_search()
*/
-cx_attr_nonnull
-CX_EXPORT size_t cx_array_binary_search_sup(const void *arr, size_t size,
+CX_EXTERN CX_NONNULL
+size_t cx_array_binary_search_sup(const void *arr, size_t size,
size_t elem_size, const void *elem, cx_compare_func cmp_func);
+
+/**
+ * Searches the largest lower bound in a sorted array.
+ *
+ * In other words, this function returns the index of the largest element
+ * in @p arr that is less or equal to @p elem with respect to @p cmp_func.
+ * When no such element exists, @p size is returned.
+ *
+ * When such an element exists more than once, the largest index of all those
+ * elements is returned.
+ *
+ * If @p elem is contained in the array, this is identical to
+ * #cx_array_binary_search().
+ *
+ * If the array is not sorted with respect to the @p cmp_func, the behavior
+ * is undefined.
+ *
+ * @param arr the array to search
+ * @param size the size of the array
+ * @param elem_size the size of one element
+ * @param elem the element to find
+ * @param cmp_func the compare function
+ * @param context the context for the compare function
+ * @return the index of the largest lower bound, or @p size
+ * @see cx_array_binary_search_sup()
+ * @see cx_array_binary_search()
+ */
+CX_EXTERN CX_NONNULL
+size_t cx_array_binary_search_inf_c(const void *arr, size_t size,
+ size_t elem_size, const void *elem, cx_compare_func2 cmp_func, void *context);
+
+/**
+ * Searches an item in a sorted array.
+ *
+ * When such an element exists more than once, the largest index of all those
+ * elements is returned.
+ *
+ * If the array is not sorted with respect to the @p cmp_func, the behavior
+ * is undefined.
+ *
+ * @param arr the array to search
+ * @param size the size of the array
+ * @param elem_size the size of one element
+ * @param elem the element to find
+ * @param cmp_func the compare function
+ * @param context the context for the compare function
+ * @return the index of the element in the array, or @p size if the element
+ * cannot be found
+ * @see cx_array_binary_search_inf()
+ * @see cx_array_binary_search_sup()
+ */
+CX_EXTERN CX_NONNULL
+size_t cx_array_binary_search_c(const void *arr, size_t size,
+ size_t elem_size, const void *elem, cx_compare_func2 cmp_func, void *context);
+
+/**
+ * Searches the smallest upper bound in a sorted array.
+ *
+ * In other words, this function returns the index of the smallest element
+ * in @p arr that is greater or equal to @p elem with respect to @p cmp_func.
+ * When no such element exists, @p size is returned.
+ *
+ * When such an element exists more than once, the smallest index of all those
+ * elements is returned.
+ *
+ * If @p elem is contained in the array, this is identical to
+ * #cx_array_binary_search().
+ *
+ * If the array is not sorted with respect to the @p cmp_func, the behavior
+ * is undefined.
+ *
+ * @param arr the array to search
+ * @param size the size of the array
+ * @param elem_size the size of one element
+ * @param elem the element to find
+ * @param cmp_func the compare function
+ * @param context the context for the compare function
+ * @return the index of the smallest upper bound, or @p size
+ * @see cx_array_binary_search_inf()
+ * @see cx_array_binary_search()
+ */
+CX_EXTERN CX_NONNULL
+size_t cx_array_binary_search_sup_c(const void *arr, size_t size,
+ size_t elem_size, const void *elem, cx_compare_func2 cmp_func, void *context);
+
/**
* Swaps two array elements.
*
* @param idx1 index of the first element
* @param idx2 index of the second element
*/
-cx_attr_nonnull
-CX_EXPORT void cx_array_swap(void *arr, size_t elem_size, size_t idx1, size_t idx2);
+CX_EXTERN CX_NONNULL
+void cx_array_swap(void *arr, size_t elem_size, size_t idx1, size_t idx2);
/**
* Allocates an array list for storing elements with @p elem_size bytes each.
*
* If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of
* copies of the added elements, and the compare function will be automatically set
- * to cx_cmp_ptr(), if none is given.
+ * to cx_cmp_ptr().
*
* @param allocator the allocator for allocating the list memory
* (if @c NULL, the cxDefaultAllocator will be used)
- * @param comparator the comparator for the elements
- * (if @c NULL, and the list is not storing pointers, sort and find
- * functions will not work)
* @param elem_size the size of each element in bytes
* @param initial_capacity the initial number of elements the array can store
* @return the created list
*/
-cx_attr_nodiscard
-cx_attr_malloc
-cx_attr_dealloc(cxListFree, 1)
-CX_EXPORT CxList *cxArrayListCreate(const CxAllocator *allocator,
- cx_compare_func comparator, size_t elem_size, size_t initial_capacity);
-
-/**
- * Allocates an array list for storing elements with @p elem_size bytes each.
- *
- * The list will use the cxDefaultAllocator and @em NO compare function.
- * If you want to call functions that need a compare function, you have to
- * set it immediately after creation or use cxArrayListCreate().
- *
- * If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of
- * copies of the added elements and the compare function will be automatically set
- * to cx_cmp_ptr().
- *
- * @param elem_size (@c size_t) the size of each element in bytes
- * @param initial_capacity (@c size_t) the initial number of elements the array can store
- * @return the created list
- */
-#define cxArrayListCreateSimple(elem_size, initial_capacity) \
- cxArrayListCreate(NULL, NULL, elem_size, initial_capacity)
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
+CX_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxListFree, 1)
+CxList *cxArrayListCreate(const CxAllocator *allocator,
+ size_t elem_size, size_t initial_capacity);
#endif // UCX_ARRAY_LIST_H
#include "common.h"
#include "allocator.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
+#include "string.h"
/**
* No buffer features enabled (all flags cleared).
*/
#define CX_BUFFER_COPY_ON_EXTEND 0x08
+/**
+ * If this flag is enabled, the buffer will never free its contents regardless of #CX_BUFFER_FREE_CONTENTS.
+ *
+ * This is useful, for example, when you want to keep a pointer to the data after destroying the buffer.
+ */
+#define CX_BUFFER_DO_NOT_FREE 0x10
+
/**
* Function pointer for cxBufferWrite that is compatible with cx_write_func.
* @see cx_write_func
*/
#define cxBufferReadFunc ((cx_read_func) cxBufferRead)
-/**
- * Configuration for automatic flushing.
- */
-struct cx_buffer_flush_config_s {
- /**
- * The buffer may not extend beyond this threshold before starting to flush.
- *
- * Only used when the buffer uses #CX_BUFFER_AUTO_EXTEND.
- * The threshold will be the maximum capacity the buffer is extended to
- * before flushing.
- */
- size_t threshold;
- /**
- * The block size for the elements to flush.
- */
- size_t blksize;
- /**
- * The maximum number of blocks to flush in one cycle.
- *
- * @attention while it is guaranteed that cxBufferFlush() will not flush
- * more blocks, this is not necessarily the case for cxBufferWrite().
- * After performing a flush cycle, cxBufferWrite() will retry the write
- * operation and potentially trigger another flush cycle, until the
- * flush target accepts no more data.
- */
- size_t blkmax;
-
- /**
- * The target for the write function.
- */
- void *target;
-
- /**
- * The write-function used for flushing.
- * If NULL, the flushed content gets discarded.
- */
- cx_write_func wfunc;
-};
-
-/**
- * Type alias for the flush configuration struct.
- *
- * @code
- * struct cx_buffer_flush_config_s {
- * size_t threshold;
- * size_t blksize;
- * size_t blkmax;
- * void *target;
- * cx_write_func wfunc;
- * };
- * @endcode
- */
-typedef struct cx_buffer_flush_config_s CxBufferFlushConfig;
/** Structure for the UCX buffer data. */
struct cx_buffer_s {
};
/** The allocator to use for automatic memory management. */
const CxAllocator *allocator;
- /**
- * Optional flush configuration
- *
- * @see cxBufferEnableFlushing()
- */
- CxBufferFlushConfig *flush;
/** Current position of the buffer. */
size_t pos;
/** Current capacity (i.e. maximum size) of the buffer. */
size_t capacity;
+ /** Maximum capacity that this buffer may grow to. */
+ size_t max_capacity;
/** Current size of the buffer content. */
size_t size;
/**
* space will be leaking after the copy-on-write operation.
*
* @param buffer the buffer to initialize
- * @param space pointer to the memory area, or @c NULL to allocate
- * new memory
- * @param capacity the capacity of the buffer
* @param allocator the allocator this buffer shall use for automatic
* memory management
* (if @c NULL, the cxDefaultAllocator will be used)
+ * @param space pointer to the memory area, or @c NULL to allocate
+ * new memory
+ * @param capacity the capacity of the buffer
* @param flags buffer features (see cx_buffer_s.flags)
* @return zero on success, non-zero if a required allocation failed
*/
-cx_attr_nonnull_arg(1)
-CX_EXPORT int cxBufferInit(CxBuffer *buffer, void *space, size_t capacity,
- const CxAllocator *allocator, int flags);
-
-/**
- * Configures the buffer for flushing.
- *
- * Flushing can happen automatically when data is written
- * to the buffer (see cxBufferWrite()) or manually when
- * cxBufferFlush() is called.
- *
- * @param buffer the buffer
- * @param config the flush configuration
- * @retval zero success
- * @retval non-zero failure
- * @see cxBufferFlush()
- * @see cxBufferWrite()
- */
-cx_attr_nonnull
-CX_EXPORT int cxBufferEnableFlushing(CxBuffer *buffer, CxBufferFlushConfig config);
+CX_EXTERN CX_NONNULL_ARG(1)
+int cxBufferInit(CxBuffer *buffer, const CxAllocator *allocator,
+ void *space, size_t capacity, int flags);
/**
* Destroys the buffer contents.
* @param buffer the buffer which contents shall be destroyed
* @see cxBufferInit()
*/
-cx_attr_nonnull
-CX_EXPORT void cxBufferDestroy(CxBuffer *buffer);
+CX_EXTERN CX_NONNULL
+void cxBufferDestroy(CxBuffer *buffer);
/**
* Deallocates the buffer.
* @param buffer the buffer to deallocate
* @see cxBufferCreate()
*/
-CX_EXPORT void cxBufferFree(CxBuffer *buffer);
+CX_EXTERN
+void cxBufferFree(CxBuffer *buffer);
/**
* Allocates and initializes a fresh buffer.
* Then this function will allocate the space and enforce
* the #CX_BUFFER_FREE_CONTENTS flag.
*
- * @param space pointer to the memory area, or @c NULL to allocate
- * new memory
- * @param capacity the capacity of the buffer
* @param allocator the allocator to use for allocating the structure and the automatic
* memory management within the buffer
* (if @c NULL, the cxDefaultAllocator will be used)
+ * @param space pointer to the memory area, or @c NULL to allocate
+ * new memory
+ * @param capacity the capacity of the buffer
* @param flags buffer features (see cx_buffer_s.flags)
* @return a pointer to the buffer on success, @c NULL if a required allocation failed
*/
-cx_attr_malloc cx_attr_dealloc(cxBufferFree, 1) cx_attr_nodiscard
-CX_EXPORT CxBuffer *cxBufferCreate(void *space, size_t capacity,
- const CxAllocator *allocator, int flags);
+CX_EXTERN CX_MALLOC CX_DEALLOC(cxBufferFree, 1) CX_NODISCARD
+CxBuffer *cxBufferCreate(const CxAllocator *allocator, void *space,
+ size_t capacity, int flags);
/**
* Shifts the contents of the buffer by the given offset.
* @see cxBufferShiftLeft()
* @see cxBufferShiftRight()
*/
-cx_attr_nonnull
-CX_EXPORT int cxBufferShift(CxBuffer *buffer, off_t shift);
+CX_EXTERN CX_NONNULL
+int cxBufferShift(CxBuffer *buffer, off_t shift);
/**
* Shifts the buffer to the right.
* @retval non-zero if a required auto-extension or copy-on-write fails
* @see cxBufferShift()
*/
-cx_attr_nonnull
-CX_EXPORT int cxBufferShiftRight(CxBuffer *buffer, size_t shift);
+CX_EXTERN CX_NONNULL
+int cxBufferShiftRight(CxBuffer *buffer, size_t shift);
/**
* Shifts the buffer to the left.
* @retval non-zero if the buffer uses copy-on-write and the allocation fails
* @see cxBufferShift()
*/
-cx_attr_nonnull
-CX_EXPORT int cxBufferShiftLeft(CxBuffer *buffer, size_t shift);
+CX_EXTERN CX_NONNULL
+int cxBufferShiftLeft(CxBuffer *buffer, size_t shift);
/**
* @retval non-zero if the position is invalid
*
*/
-cx_attr_nonnull
-CX_EXPORT int cxBufferSeek(CxBuffer *buffer, off_t offset, int whence);
+CX_EXTERN CX_NONNULL
+int cxBufferSeek(CxBuffer *buffer, off_t offset, int whence);
+
+/**
+ * Discards items from the end of the buffer.
+ *
+ * When the current position points to a byte that gets discarded,
+ * the position is set to the buffer size.
+ *
+ * @param buffer the buffer
+ * @param size the size of one item
+ * @param nitems the number of items to discard
+ * @return the actual number of discarded items
+ */
+CX_EXTERN CX_NONNULL
+size_t cxBufferPop(CxBuffer *buffer, size_t size, size_t nitems);
/**
* Clears the buffer by resetting the position and deleting the data.
* @param buffer the buffer to be cleared
* @see cxBufferReset()
*/
-cx_attr_nonnull
-CX_EXPORT void cxBufferClear(CxBuffer *buffer);
+CX_EXTERN CX_NONNULL
+void cxBufferClear(CxBuffer *buffer);
/**
* Resets the buffer by resetting the position and size to zero.
* @param buffer the buffer to be cleared
* @see cxBufferClear()
*/
-cx_attr_nonnull
-CX_EXPORT void cxBufferReset(CxBuffer *buffer);
+CX_EXTERN CX_NONNULL
+void cxBufferReset(CxBuffer *buffer);
/**
* Tests, if the buffer position has exceeded the buffer size.
* byte of the buffer's contents
* @retval false otherwise
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT bool cxBufferEof(const CxBuffer *buffer);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+bool cxBufferEof(const CxBuffer *buffer);
+/**
+ * Ensures that the buffer has the required capacity.
+ *
+ * If the current capacity is not sufficient, the buffer will be extended.
+ * If the current capacity is larger, the buffer is shrunk and superfluous
+ * content is discarded.
+ *
+ * This function will reserve no more bytes than requested, in contrast to
+ * cxBufferMinimumCapacity(), which may reserve more bytes to improve the
+ * number of future necessary reallocations.
+ *
+ * @param buffer the buffer
+ * @param capacity the required capacity for this buffer
+ * @retval zero on success
+ * @retval non-zero on allocation failure
+ * @see cxBufferShrink()
+ * @see cxBufferMinimumCapacity()
+ */
+CX_EXTERN CX_NONNULL
+int cxBufferReserve(CxBuffer *buffer, size_t capacity);
+
+/**
+ * Limits the buffer's capacity.
+ *
+ * If the current capacity is already larger, this function fails and returns
+ * non-zero.
+ *
+ * The capacity limit will affect auto-extension features, as well as future
+ * calls to cxBufferMinimumCapacity() and cxBufferReserve().
+ *
+ * @param buffer the buffer
+ * @param capacity the maximum allowed capacity for this buffer
+ * @retval zero the limit is applied
+ * @retval non-zero the new limit is smaller than the current capacity
+ * @see cxBufferReserve()
+ * @see cxBufferMinimumCapacity()
+ */
+CX_EXTERN CX_NONNULL
+int cxBufferMaximumCapacity(CxBuffer *buffer, size_t capacity);
/**
* Ensures that the buffer has a minimum capacity.
*
- * If the current capacity is not sufficient, the buffer will be extended.
+ * If the current capacity is not sufficient, the buffer will be generously
+ * extended.
*
* The new capacity will be a power of two until the system's page size is reached.
* Then, the new capacity will be a multiple of the page size.
* @param capacity the minimum required capacity for this buffer
* @retval zero the capacity was already sufficient or successfully increased
* @retval non-zero on allocation failure
+ * @see cxBufferMaximumCapacity()
+ * @see cxBufferReserve()
* @see cxBufferShrink()
*/
-cx_attr_nonnull
-CX_EXPORT int cxBufferMinimumCapacity(CxBuffer *buffer, size_t capacity);
+CX_EXTERN CX_NONNULL
+int cxBufferMinimumCapacity(CxBuffer *buffer, size_t capacity);
/**
* Shrinks the capacity of the buffer to fit its current size.
*
* @param buffer the buffer
* @param reserve the number of bytes that shall remain reserved
+ * @see cxBufferReserve()
* @see cxBufferMinimumCapacity()
*/
-cx_attr_nonnull
-CX_EXPORT void cxBufferShrink(CxBuffer *buffer, size_t reserve);
+CX_EXTERN CX_NONNULL
+void cxBufferShrink(CxBuffer *buffer, size_t reserve);
/**
* Writes data to a CxBuffer.
*
- * If automatic flushing is not enabled, the data is simply written into the
- * buffer at the current position, and the position of the buffer is increased
- * by the number of bytes written.
- *
- * 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.
+ * If auto-extension is enabled, the buffer's capacity is automatically
+ * increased when it is not large enough to hold all data.
+ * By default, the capacity grows indefinitely, unless limited with
+ * cxBufferMaximumCapacity().
+ * When auto-extension fails, this function writes no data and returns zero.
*
- * This function only stops writing more elements when the flush target and this
- * buffer are both incapable of taking more data or all data has been written.
- *
- * If, after flushing, the number of items that shall be written still exceeds
- * the capacity or flush threshold, this function tries to write all items directly
- * to the flush target, if possible.
- *
- * The number returned by this function is the number of elements from
- * @c ptr that could be written to either the flush target or the buffer.
- * That means it does @em not include the number of items that were already in
- * the buffer and were also flushed during the process.
- *
- * @attention
- * When @p size is larger than one and the contents of the buffer are not aligned
- * with @p size, flushing stops after all complete items have been flushed, leaving
- * the misaligned part in the buffer.
- * Afterward, this function only writes as many items as possible to the buffer.
+ * The position of the buffer is moved alongside the written data.
*
* @note The signature is compatible with the fwrite() family of functions.
*
* @see cxBufferAppend()
* @see cxBufferRead()
*/
-cx_attr_nonnull
-CX_EXPORT size_t cxBufferWrite(const void *ptr, size_t size,
+CX_EXTERN CX_NONNULL
+size_t cxBufferWrite(const void *ptr, size_t size,
size_t nitems, CxBuffer *buffer);
/**
* @see cxBufferWrite()
* @see cxBufferRead()
*/
-cx_attr_nonnull
-CX_EXPORT size_t cxBufferAppend(const void *ptr, size_t size,
+CX_EXTERN CX_NONNULL
+size_t cxBufferAppend(const void *ptr, size_t size,
size_t nitems, CxBuffer *buffer);
-/**
- * Performs a single flush-run on the specified buffer.
- *
- * Does nothing when the position in the buffer is zero.
- * Otherwise, the data until the current position minus
- * one is considered for flushing.
- * Note carefully that flushing will never exceed the
- * current @em position, even when the size of the
- * buffer is larger than the current position.
- *
- * One flush run will try to flush @c blkmax many
- * blocks of size @c blksize until either the @p buffer
- * has no more data to flush or the write function
- * used for flushing returns zero.
- *
- * The buffer is shifted left for that many bytes
- * the flush operation has successfully flushed.
- *
- * @par Example 1
- * Assume you have a buffer with size 340 and you are
- * at position 200. The flush configuration is
- * @c blkmax=4 and @c blksize=64 .
- * Assume that the entire flush operation is successful.
- * All 200 bytes on the left-hand-side from the current
- * position are written.
- * That means the size of the buffer is now 140 and the
- * position is zero.
- *
- * @par Example 2
- * Same as Example 1, but now the @c blkmax is 1.
- * The size of the buffer is now 276, and the position is 136.
- *
- * @par Example 3
- * Same as Example 1, but now assume the flush target
- * only accepts 100 bytes before returning zero.
- * That means the flush operation manages to flush
- * one complete block and one partial block, ending
- * up with a buffer with size 240 and position 100.
- *
- * @remark Just returns zero when flushing was not enabled with
- * cxBufferEnableFlushing().
- *
- * @remark When the buffer uses copy-on-write, the memory
- * is copied first, before attempting any flush.
- * This is, however, considered an erroneous use of the
- * buffer because it makes little sense to put
- * readonly data into an UCX buffer for flushing instead
- * of writing it directly to the target.
- *
- * @param buffer the buffer
- * @return the number of successfully flushed bytes
- * @see cxBufferEnableFlushing()
- */
-cx_attr_nonnull
-CX_EXPORT size_t cxBufferFlush(CxBuffer *buffer);
-
/**
* Reads data from a CxBuffer.
*
* @see cxBufferWrite()
* @see cxBufferAppend()
*/
-cx_attr_nonnull
-CX_EXPORT size_t cxBufferRead(void *ptr, size_t size,
+CX_EXTERN CX_NONNULL
+size_t cxBufferRead(void *ptr, size_t size,
size_t nitems, CxBuffer *buffer);
/**
*
* 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 the buffer extension fails, @c EOF is returned.
+ * the buffer capacity is extended, unless a limit set by
+ * cxBufferMaximumCapacity() is reached.
+ * If the feature is disabled or the buffer extension fails, @c EOF is returned.
*
* On successful writing, 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 been written or @c EOF when the end of the stream is
- * reached, and automatic extension is not enabled or not possible
+ * @return the byte that has been written or @c EOF when the end of the
+ * stream is reached, and automatic extension is not enabled or not possible
* @see cxBufferTerminate()
*/
-cx_attr_nonnull
-CX_EXPORT int cxBufferPut(CxBuffer *buffer, int c);
+CX_EXTERN CX_NONNULL
+int cxBufferPut(CxBuffer *buffer, int c);
/**
* Writes a terminating zero to a buffer at the current position.
*
- * If successful, sets the size to the current position and advances the position by one.
+ * If successful, also sets the size to the current position and shrinks the buffer.
*
* The purpose of this function is to have the written data ready to be used as
* a C string with the buffer's size being the length of that string.
*
* @param buffer the buffer to write to
* @return zero, if the terminator could be written, non-zero otherwise
+ * @see cxBufferShrink()
*/
-cx_attr_nonnull
-CX_EXPORT int cxBufferTerminate(CxBuffer *buffer);
+CX_EXTERN CX_NONNULL
+int cxBufferTerminate(CxBuffer *buffer);
/**
- * Writes a string to a buffer.
+ * Internal function - do not use.
*
- * This is a convenience function for <code>cxBufferWrite(str, 1, strlen(str), buffer)</code>.
+ * @param buffer the buffer
+ * @param str the string
+ * @return the number of bytes written
+ * @see cxBufferPutString()
+ */
+CX_EXTERN CX_NONNULL
+size_t cx_buffer_put_string(CxBuffer *buffer, cxstring str);
+
+/**
+ * Writes a string to a buffer with cxBufferWrite().
+ *
+ * @param buffer (@c CxBuffer*) the buffer
+ * @param str (any string) the zero-terminated string
+ * @return (@c size_t) the number of bytes written
+ * @see cxBufferWrite()
+ * @see cx_strcast()
+ */
+#define cxBufferPutString(buffer, str) cx_buffer_put_string(buffer, cx_strcast(str))
+
+/**
+ * Internal function - do not use.
*
* @param buffer the buffer
- * @param str the zero-terminated string
+ * @param str the string
* @return the number of bytes written
+ * @see cxBufferPutString()
+ */
+CX_EXTERN CX_NONNULL
+size_t cx_buffer_append_string(CxBuffer *buffer, cxstring str);
+
+/**
+ * Appends a string to a buffer with cxBufferAppend().
+ *
+ * @param buffer (@c CxBuffer*) the buffer
+ * @param str (any string) the zero-terminated string
+ * @return (@c size_t) the number of bytes written
+ * @see cxBufferAppend()
+ * @see cx_strcast()
*/
-cx_attr_nonnull cx_attr_cstr_arg(2)
-CX_EXPORT size_t cxBufferPutString(CxBuffer *buffer, const char *str);
+#define cxBufferAppendString(buffer, str) cx_buffer_append_string(buffer, cx_strcast(str))
/**
* Gets a character from a buffer.
* @param buffer the buffer to read from
* @return the character or @c EOF, if the end of the buffer is reached
*/
-cx_attr_nonnull
-CX_EXPORT int cxBufferGet(CxBuffer *buffer);
+CX_EXTERN CX_NONNULL
+int cxBufferGet(CxBuffer *buffer);
-#ifdef __cplusplus
+/**
+ * Gets the data in a buffer as a @c cxstring.
+ *
+ * @param buffer the buffer
+ * @return the data in the buffer interpreted as a @c cxstring
+ */
+CX_NONNULL CX_INLINE
+cxstring cx_bstr(CxBuffer *buffer) {
+ return cx_strn(buffer->space, buffer->size);
+}
+
+/**
+ * Gets the data in a buffer as a @c cxmutstr.
+ *
+ * @param buffer the buffer
+ * @return the data in the buffer interpreted as a @c cxmutstr
+ */
+CX_NONNULL CX_INLINE
+cxmutstr cx_bstr_m(CxBuffer *buffer) {
+ return cx_mutstrn(buffer->space, buffer->size);
}
-#endif
#endif // UCX_BUFFER_H
#include "iterator.h"
#include "compare.h"
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/**
* Special constant used for creating collections that are storing pointers.
*/
* The allocator to use.
*/
const CxAllocator *allocator;
- /**
- * The comparator function for the elements.
- */
- cx_compare_func cmpfunc;
/**
* The size of each element.
*/
* The number of currently stored elements.
*/
size_t size;
+ /**
+ * A two-argument comparator function for the elements.
+ */
+ cx_compare_func simple_cmp;
+ /**
+ * A three-argument comparator function for the elements.
+ * If specified, this function has precedence over the @c simple_cmp function.
+ */
+ cx_compare_func2 advanced_cmp;
+ /**
+ * A pointer to custom data for the @c advanced_cmp function
+ */
+ void *cmp_data;
/**
* An optional simple destructor for the collection's elements.
*
*/
#define cxCollectionStoresPointers(c) ((c)->collection.store_pointer)
+
+/**
+ * Convenience macro for adding indirection to an element if the collection is storing pointers.
+ *
+ * @param c a pointer to a struct that contains #CX_COLLECTION_BASE
+ * @param elem the pointer that shall be taken the address from, if the collection is storing pointers
+ * @return if the collection is storing pointers, takes the address of @p elem, otherwise returns @p elem
+ */
+#define cx_ref(c, elem) (cxCollectionStoresPointers(c) ? ((void*)&(elem)) : (elem))
+
+/**
+ * Convenience macro for dereferencing an element if the collection is storing pointers.
+ *
+ * @param c a pointer to a struct that contains #CX_COLLECTION_BASE
+ * @param elem a pointer to the collection element
+ * @return if the collection is storing pointers, dereferences @p elem, otherwise returns @p elem
+ */
+#define cx_deref(c, elem) (cxCollectionStoresPointers(c) ? *((void**)(elem)) : (elem))
+
/**
* Indicates whether the collection can guarantee that the stored elements are currently sorted.
*
*/
#define cxCollectionSorted(c) ((c)->collection.sorted || (c)->collection.size == 0)
+/**
+ * Sets a simple compare function for a collection.
+ *
+ * Erases a possible advanced compare function.
+ * If you want to set both, because you want to access the simple function
+ * in your advanced function, you must set the simple function first.
+ *
+ * @param c a pointer to a struct that contains #CX_COLLECTION_BASE
+ * @param func (@c cx_compare_func) the compare function
+ */
+#define cxSetCompareFunc(c, func) \
+ (c)->collection.simple_cmp = (cx_compare_func)(func); \
+ (c)->collection.advanced_cmp = NULL
+
+/**
+ * Sets an advanced compare function that supports custom data for a collection.
+ *
+ * @param c a pointer to a struct that contains #CX_COLLECTION_BASE
+ * @param func (@c cx_compare_func2) the compare function
+ * @param data (@c void*) the pointer to custom data that is passed to the compare function
+ */
+#define cxSetAdvancedCompareFunc(c, func, data) \
+ (c)->collection.advanced_cmp = (cx_compare_func2) func; \
+ (c)->collection.cmp_data = data
+
+/**
+ * Invokes the simple comparator function for two elements.
+ *
+ * Usually only used by collection implementations. There should be no need
+ * to invoke this macro manually.
+ *
+ * @param c a pointer to a struct that contains #CX_COLLECTION_BASE
+ * @param left (@c void*) pointer to data
+ * @param right (@c void*) pointer to data
+ */
+#define cx_invoke_simple_compare_func(c, left, right) \
+ (c)->collection.simple_cmp(left, right)
+
+/**
+ * Invokes the advanced comparator function for two elements.
+ *
+ * Usually only used by collection implementations. There should be no need
+ * to invoke this macro manually.
+ *
+ * @param c a pointer to a struct that contains #CX_COLLECTION_BASE
+ * @param left (@c void*) pointer to data
+ * @param right (@c void*) pointer to data
+ */
+#define cx_invoke_advanced_compare_func(c, left, right) \
+ (c)->collection.advanced_cmp(left, right, (c)->collection.cmp_data)
+
+
+/**
+ * Invokes the configured comparator function for two elements.
+ *
+ * Usually only used by collection implementations. There should be no need
+ * to invoke this macro manually.
+ *
+ * @param c a pointer to a struct that contains #CX_COLLECTION_BASE
+ * @param left (@c void*) pointer to data
+ * @param right (@c void*) pointer to data
+ */
+#define cx_invoke_compare_func(c, left, right) \
+ (((c)->collection.advanced_cmp) ? \
+ cx_invoke_advanced_compare_func(c,left,right) : \
+ cx_invoke_simple_compare_func(c,left,right))
+
/**
* Sets a simple destructor function for this collection.
*
* @param c a pointer to a struct that contains #CX_COLLECTION_BASE
- * @param destr the destructor function
+ * @param destr (@c cx_destructor_func) the destructor function
*/
-#define cxDefineDestructor(c, destr) \
+#define cxSetDestructor(c, destr) \
(c)->collection.simple_destructor = (cx_destructor_func) destr
/**
- * Sets a simple destructor function for this collection.
+ * Sets an advanced destructor function for this collection.
*
* @param c a pointer to a struct that contains #CX_COLLECTION_BASE
- * @param destr the destructor function
+ * @param destr (@c cx_destructor_func2) the destructor function
+ * @param data (@c void*) the additional data the advanced destructor is invoked with
*/
-#define cxDefineAdvancedDestructor(c, destr, data) \
+#define cxSetAdvancedDestructor(c, destr, data) \
(c)->collection.advanced_destructor = (cx_destructor_func2) destr; \
(c)->collection.destructor_data = data
if ((c)->collection.simple_destructor) cx_invoke_simple_destructor(c,e); \
if ((c)->collection.advanced_destructor) cx_invoke_advanced_destructor(c,e)
-#ifdef __cplusplus
-} // extern "C"
-#endif
+/**
+ * Invokes all available destructor functions for a specific element.
+ *
+ * Usually only used by collection implementations. There should be no need
+ * to invoke this macro manually.
+ *
+ * In contrast to cx_invoke_destructor(), this macro does not automatically
+ * dereference pointers to the elements when cxCollectionStoresPointers()
+ * returns true.
+ *
+ * @param c a pointer to a struct that contains #CX_COLLECTION_BASE
+ * @param e pointer to the element
+ */
+#define cx_invoke_destructor_raw(c, e) \
+ if ((c)->collection.simple_destructor) (c)->collection.simple_destructor(e); \
+ if ((c)->collection.advanced_destructor) (c)->collection.advanced_destructor((c)->collection.destructor_data, e)
+
#endif // UCX_COLLECTION_H
#define UCX_COMMON_H
/** Major UCX version as integer constant. */
-#define UCX_VERSION_MAJOR 3
+#define UCX_VERSION_MAJOR 4
/** Minor UCX version as integer constant. */
-#define UCX_VERSION_MINOR 1
+#define UCX_VERSION_MINOR 0
/** Version constant which ensures to increase monotonically. */
#define UCX_VERSION (((UCX_VERSION_MAJOR)<<16)|UCX_VERSION_MINOR)
/**
* Inform the compiler that falling through a switch case is intentional.
*/
-#define cx_attr_fallthrough __attribute__((__fallthrough__))
+#define CX_FALLTHROUGH __attribute__((__fallthrough__))
/**
* All pointer arguments must be non-NULL.
*/
-#define cx_attr_nonnull __attribute__((__nonnull__))
+#define CX_NONNULL __attribute__((__nonnull__))
/**
* The specified pointer arguments must be non-NULL.
*/
-#define cx_attr_nonnull_arg(...) __attribute__((__nonnull__(__VA_ARGS__)))
+#define CX_NONNULL_ARG(...) __attribute__((__nonnull__(__VA_ARGS__)))
/**
* The returned value is guaranteed to be non-NULL.
*/
-#define cx_attr_returns_nonnull __attribute__((__returns_nonnull__))
+#define CX_RETURNS_NONNULL __attribute__((__returns_nonnull__))
/**
* The attributed function always returns freshly allocated memory.
*/
-#define cx_attr_malloc __attribute__((__malloc__))
+#define CX_MALLOC __attribute__((__malloc__))
#if !defined(__clang__) && __GNUC__ >= 11
/**
* @param freefunc the function that shall be used to free the memory
* @param freefunc_arg the index of the pointer argument in @p freefunc
*/
-#define cx_attr_dealloc(freefunc, freefunc_arg) \
+#define CX_DEALLOC(freefunc, freefunc_arg) \
__attribute__((__malloc__(freefunc, freefunc_arg)))
#else
/**
* Not supported in clang.
*/
-#define cx_attr_dealloc(...)
+#define CX_DEALLOC(...)
#endif // __clang__
/**
* Shortcut to specify #cxFree() as deallocator.
*/
-#define cx_attr_dealloc_ucx cx_attr_dealloc(cxFree, 2)
+#define CX_DEALLOC_UCX CX_DEALLOC(cxFree, 2)
/**
* Specifies the parameters from which the allocation size is calculated.
*/
-#define cx_attr_allocsize(...) __attribute__((__alloc_size__(__VA_ARGS__)))
+#define CX_ALLOCSIZE(...) __attribute__((__alloc_size__(__VA_ARGS__)))
#ifdef __clang__
/**
* No support for @c null_terminated_string_arg in clang or GCC below 14.
*/
-#define cx_attr_cstr_arg(idx)
+#define CX_CSTR_ARG(idx)
/**
* No support for the access attribute in clang.
*/
-#define cx_attr_access(mode, ...)
+#define CX_ACCESS(mode, ...)
#else
#if __GNUC__ < 10
/**
* No support for access attribute in GCC < 10.
*/
-#define cx_attr_access(mode, ...)
+#define CX_ACCESS(mode, ...)
#else
/**
* Helper macro to define access macros.
*/
-#define cx_attr_access(mode, ...) __attribute__((__access__(mode, __VA_ARGS__)))
+#define CX_ACCESS(mode, ...) __attribute__((__access__(mode, __VA_ARGS__)))
#endif // __GNUC__ < 10
#if __GNUC__ < 14
/**
* No support for @c null_terminated_string_arg in clang or GCC below 14.
*/
-#define cx_attr_cstr_arg(idx)
+#define CX_CSTR_ARG(idx)
#else
/**
* The specified argument is expected to be a zero-terminated string.
*
* @param idx the index of the argument
*/
-#define cx_attr_cstr_arg(idx) \
+#define CX_CSTR_ARG(idx) \
__attribute__((__null_terminated_string_arg__(idx)))
#endif // __GNUC__ < 14
#endif // __clang__
* Takes one or two arguments: the index of the pointer and (optionally) the
* index of another argument specifying the maximum number of accessed bytes.
*/
-#define cx_attr_access_r(...) cx_attr_access(__read_only__, __VA_ARGS__)
+#define CX_ACCESS_R(...) CX_ACCESS(__read_only__, __VA_ARGS__)
/**
* Specifies that the function will read and write through the given pointer.
* Takes one or two arguments: the index of the pointer and (optionally) the
* index of another argument specifying the maximum number of accessed bytes.
*/
-#define cx_attr_access_rw(...) cx_attr_access(__read_write__, __VA_ARGS__)
+#define CX_ACCESS_RW(...) CX_ACCESS(__read_write__, __VA_ARGS__)
/**
* Specifies that the function will only write through the given pointer.
* Takes one or two arguments: the index of the pointer and (optionally) the
* index of another argument specifying the maximum number of accessed bytes.
*/
-#define cx_attr_access_w(...) cx_attr_access(__write_only__, __VA_ARGS__)
+#define CX_ACCESS_W(...) CX_ACCESS(__write_only__, __VA_ARGS__)
/**
* Do not warn about unused variable.
*/
-#define cx_attr_unused __attribute__((__unused__))
+#define CX_UNUSED __attribute__((__unused__))
/**
* Warn about discarded return value.
*/
-#define cx_attr_nodiscard __attribute__((__warn_unused_result__))
+#define CX_NODISCARD __attribute__((__warn_unused_result__))
// ---------------------------------------------------------------------------
-// MSVC specifics
+// Support for thread_local
// ---------------------------------------------------------------------------
+#ifdef __cplusplus
+#define cx_thread_local thread_local
+#else // ! __cplusplus
#ifdef _MSC_VER
-// fix missing _Thread_local support
-#define _Thread_local __declspec(thread)
+#define cx_thread_local __declspec(thread)
+#else // ! _MSC_VER
+#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 202300L
+/** Platform independent thread-local macro. */
+#define cx_thread_local _Thread_local
+#else // C23 or newer
+/** Platform independent thread-local macro. */
+#define cx_thread_local thread_local
+#endif // C23
#endif // _MSC_VER
+#endif // __cplusplus
// ---------------------------------------------------------------------------
// Exported and inlined functions
#define CX_EXPORT
#endif // CX_WINDLL / CX_WINDLL_EXPORT
+#ifdef __cplusplus
+#define CX_EXTERN extern "C" CX_EXPORT
+#define CX_FPTR extern "C" typedef
+#else
+/** Declares a function with external linkage. */
+#define CX_EXTERN CX_EXPORT
+/** Defines a function pointer. */
+#define CX_FPTR typedef
+#endif
+
#ifdef __GNUC__
/**
* Declares a function to be inlined.
*/
#define CX_INLINE __attribute__((always_inline)) static inline
#else
+/**
+ * Declares a function to be inlined.
+ */
#define CX_INLINE static inline
#endif
/**
* @retval zero success
* @retval non-zero the multiplication would overflow
*/
-#if __cplusplus
-extern "C"
-#endif
-CX_EXPORT int cx_szmul_impl(size_t a, size_t b, size_t *result);
+CX_EXTERN
+int cx_szmul_impl(size_t a, size_t b, size_t *result);
#endif // cx_szmul
#endif // UCX_COMMON_H
#include "common.h"
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/**
* A comparator function comparing two arbitrary values.
*
* can be used, but they are NOT compatible with this function
* pointer.
*/
-typedef int (*cx_compare_func)(const void *left, const void *right);
+CX_FPTR int (*cx_compare_func)(const void *left, const void *right);
+
+/**
+ * A comparator function comparing two arbitrary values.
+ *
+ * Functions with this signature allow specifying a pointer to custom data.
+ */
+CX_FPTR int (*cx_compare_func2)(const void *left, const void *right, void *data);
/**
* Compares two integers of type int.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_int(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_int(const void *i1, const void *i2);
/**
* Compares two integers of type int.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_int(int i1, int i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_int(int i1, int i2);
/**
* Compares two integers of type long int.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_longint(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_longint(const void *i1, const void *i2);
/**
* Compares two integers of type long int.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_longint(long int i1, long int i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_longint(long int i1, long int i2);
/**
* Compares two integers of type long long.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_longlong(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_longlong(const void *i1, const void *i2);
/**
* Compares two integers of type long long.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_longlong(long long int i1, long long int i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_longlong(long long int i1, long long int i2);
/**
* Compares two integers of type int16_t.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_int16(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_int16(const void *i1, const void *i2);
/**
* Compares two integers of type int16_t.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_int16(int16_t i1, int16_t i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_int16(int16_t i1, int16_t i2);
/**
* Compares two integers of type int32_t.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_int32(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_int32(const void *i1, const void *i2);
/**
* Compares two integers of type int32_t.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_int32(int32_t i1, int32_t i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_int32(int32_t i1, int32_t i2);
/**
* Compares two integers of type int64_t.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_int64(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_int64(const void *i1, const void *i2);
/**
* Compares two integers of type int64_t.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_int64(int64_t i1, int64_t i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_int64(int64_t i1, int64_t i2);
/**
* Compares two integers of type unsigned int.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_uint(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_uint(const void *i1, const void *i2);
/**
* Compares two integers of type unsigned int.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_uint(unsigned int i1, unsigned int i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_uint(unsigned int i1, unsigned int i2);
/**
* Compares two integers of type unsigned long int.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_ulongint(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_ulongint(const void *i1, const void *i2);
/**
* Compares two integers of type unsigned long int.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_ulongint(unsigned long int i1, unsigned long int i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_ulongint(unsigned long int i1, unsigned long int i2);
/**
* Compares two integers of type unsigned long long.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_ulonglong(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_ulonglong(const void *i1, const void *i2);
/**
* Compares two integers of type unsigned long long.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_ulonglong(unsigned long long int i1, unsigned long long int i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_ulonglong(unsigned long long int i1, unsigned long long int i2);
/**
* Compares two integers of type uint16_t.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_uint16(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_uint16(const void *i1, const void *i2);
/**
* Compares two integers of type uint16_t.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_uint16(uint16_t i1, uint16_t i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_uint16(uint16_t i1, uint16_t i2);
/**
* Compares two integers of type uint32_t.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_uint32(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_uint32(const void *i1, const void *i2);
/**
* Compares two integers of type uint32_t.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_uint32(uint32_t i1, uint32_t i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_uint32(uint32_t i1, uint32_t i2);
/**
* Compares two integers of type uint64_t.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_uint64(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_uint64(const void *i1, const void *i2);
/**
* Compares two integers of type uint64_t.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_uint64(uint64_t i1, uint64_t i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_uint64(uint64_t i1, uint64_t i2);
/**
* Compares two integers of type size_t.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_size(const void *i1, const void *i2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_size(const void *i1, const void *i2);
/**
* Compares two integers of type size_t.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_size(size_t i1, size_t i2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_size(size_t i1, size_t i2);
/**
* Compares two real numbers of type float with precision 1e-6f.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_float(const void *f1, const void *f2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_float(const void *f1, const void *f2);
/**
* Compares two real numbers of type float with precision 1e-6f.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_float(float f1, float f2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_float(float f1, float f2);
/**
* Compares two real numbers of type double with precision 1e-14.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_double(const void *d1, const void *d2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_double(const void *d1, const void *d2);
/**
* Compares two real numbers of type double with precision 1e-14.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_double(double d1, double d2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_double(double d1, double d2);
/**
* Compares the integer representation of two pointers.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_intptr(const void *ptr1, const void *ptr2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_intptr(const void *ptr1, const void *ptr2);
/**
* Compares the integer representation of two pointers.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_intptr(intptr_t ptr1, intptr_t ptr2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_intptr(intptr_t ptr1, intptr_t ptr2);
/**
* Compares the unsigned integer representation of two pointers.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_uintptr(const void *ptr1, const void *ptr2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_uintptr(const void *ptr1, const void *ptr2);
/**
* Compares the unsigned integer representation of two pointers.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nodiscard
-CX_EXPORT int cx_vcmp_uintptr(uintptr_t ptr1, uintptr_t ptr2);
+CX_EXTERN CX_NODISCARD
+int cx_vcmp_uintptr(uintptr_t ptr1, uintptr_t ptr2);
/**
* Compares the pointers specified in the arguments without dereferencing.
* @retval 0 if both arguments are equal
* @retval 1 if the left argument is greater than the right argument
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cx_cmp_ptr(const void *ptr1, const void *ptr2);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_ptr(const void *ptr1, const void *ptr2);
-#ifdef __cplusplus
-} // extern "C"
-#endif
+/** Wraps a compare function for cx_cmp_wrap. */
+typedef struct {
+ /** The wrapped compare function */
+ cx_compare_func cmp;
+} cx_compare_func_wrapper;
+
+/**
+ * A @c cx_compare_func2 wrapper for a @c cx_compare_func().
+ *
+ * @param ptr1 pointer one
+ * @param ptr2 pointer two
+ * @param cmp_wrapper a pointer to a @c cx_compare_func_wrapper
+ * @return the result of the invoked compare function
+ * @see cx_compare_func_wrapper
+ */
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cx_cmp_wrap(const void *ptr1, const void *ptr2, void* cmp_wrapper);
#endif //UCX_COMPARE_H
#include "common.h"
#include "string.h"
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/** Internal structure for a key within a hash map. */
struct cx_hash_key_s {
/**
* @param key the key, the hash shall be computed for
* @see cx_hash_key()
*/
-cx_attr_nonnull
-CX_EXPORT void cx_hash_murmur(CxHashKey *key);
+CX_EXTERN CX_NONNULL
+void cx_hash_murmur(CxHashKey *key);
/**
* Mixes up a 32-bit integer to be used as a hash.
* @param x the integer
* @return the hash
*/
-CX_EXPORT uint32_t cx_hash_u32(uint32_t x);
+CX_INLINE
+uint32_t cx_hash_u32(uint32_t x) {
+ x = ((x >> 16) ^ x) * 0x45d9f3bu;
+ x = ((x >> 16) ^ x) * 0x45d9f3bu;
+ x = (x >> 16) ^ x;
+ return x;
+}
/**
* Mixes up a 64-bit integer to be used as a hash.
* @param x the integer
* @return the hash
*/
-CX_EXPORT uint64_t cx_hash_u64(uint64_t x);
+CX_INLINE
+uint64_t cx_hash_u64(uint64_t x){
+ x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
+ x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb);
+ x = x ^ (x >> 31);
+ return x;
+}
+
+/**
+ * Computes a hash key for an arbitrary object.
+ *
+ * The computation uses the in-memory representation that might not be
+ * the same on different platforms. Therefore, this hash should not be
+ * used for data exchange with different machines.
+ *
+ * @param obj a pointer to an arbitrary object
+ * @param len the length of the object in memory
+ * @return the hash key
+ */
+CX_EXTERN CX_NODISCARD CX_ACCESS_R(1, 2)
+CxHashKey cx_hash_key(const void *obj, size_t len);
/**
* Computes a hash key from a 32-bit integer.
* @param x the integer
* @return the hash key
*/
-cx_attr_nodiscard
-CX_EXPORT CxHashKey cx_hash_key_u32(uint32_t x);
+CX_NODISCARD CX_INLINE
+CxHashKey cx_hash_key_u32(uint32_t x) {
+ CxHashKey key;
+ key.data = NULL;
+ key.len = 0;
+ key.hash = cx_hash_u32(x);
+ return key;
+}
/**
* Computes a hash key from a 64-bit integer.
* @param x the integer
* @return the hash key
*/
-cx_attr_nodiscard
-CX_EXPORT CxHashKey cx_hash_key_u64(uint64_t x);
+CX_NODISCARD CX_INLINE
+CxHashKey cx_hash_key_u64(uint64_t x) {
+ CxHashKey key;
+ key.data = NULL;
+ key.len = 0;
+ key.hash = cx_hash_u64(x);
+ return key;
+}
/**
* Computes a hash key from a string.
* @param str the string
* @return the hash key
*/
-cx_attr_nodiscard cx_attr_cstr_arg(1)
-CX_EXPORT CxHashKey cx_hash_key_str(const char *str);
+CX_NODISCARD CX_CSTR_ARG(1) CX_INLINE
+CxHashKey cx_hash_key_str(const char *str) {
+ return cx_hash_key((const void*)str, str == NULL ? 0 : strlen(str));
+}
/**
* Computes a hash key from a string.
* @param str the string
* @return the hash key
*/
-cx_attr_nodiscard cx_attr_cstr_arg(1)
-CX_EXPORT CxHashKey cx_hash_key_ustr(const unsigned char *str);
+CX_NODISCARD CX_CSTR_ARG(1) CX_INLINE
+CxHashKey cx_hash_key_ustr(const unsigned char *str) {
+ return cx_hash_key((const void*)str, str == NULL ? 0 : strlen((const char*)str));
+}
/**
* Computes a hash key from a byte array.
* @param len the length
* @return the hash key
*/
-cx_attr_nodiscard cx_attr_access_r(1, 2)
-CX_EXPORT CxHashKey cx_hash_key_bytes(const unsigned char *bytes, size_t len);
-
-/**
- * Computes a hash key for an arbitrary object.
- *
- * The computation uses the in-memory representation that might not be
- * the same on different platforms. Therefore, this hash should not be
- * used for data exchange with different machines.
- *
- * @param obj a pointer to an arbitrary object
- * @param len the length of the object in memory
- * @return the hash key
- */
-cx_attr_nodiscard
-cx_attr_access_r(1, 2)
-CX_EXPORT CxHashKey cx_hash_key(const void *obj, size_t len);
+CX_NODISCARD CX_ACCESS_R(1, 2) CX_INLINE
+CxHashKey cx_hash_key_bytes(const unsigned char *bytes, size_t len) {
+ return cx_hash_key((const void*)bytes, len);
+}
/**
* Computes a hash key from a UCX string.
* @param str the string
* @return the hash key
*/
-cx_attr_nodiscard
-CX_EXPORT CxHashKey cx_hash_key_cxstr(cxstring str);
+CX_NODISCARD CX_INLINE
+CxHashKey cx_hash_key_cxstr(cxstring str) {
+ return cx_hash_key((void*)str.ptr, str.length);
+}
/**
* Computes a hash key from a UCX string.
* @param str the string
* @return the hash key
*/
-cx_attr_nodiscard
-CX_EXPORT CxHashKey cx_hash_key_mutstr(cxmutstr str);
+CX_NODISCARD CX_INLINE
+CxHashKey cx_hash_key_mutstr(cxmutstr str) {
+ return cx_hash_key((void*)str.ptr, str.length);
+}
/**
* The identity function for the CX_HASH_KEY() macro.
* You should never need to use this manually.
*
* @param key the key
- * @return a copy of the key
+ * @return a copy of the key (not the data)
*/
-cx_attr_nodiscard
-CX_INLINE CxHashKey cx_hash_key_identity(CxHashKey key) {
+CX_NODISCARD CX_INLINE
+CxHashKey cx_hash_key_identity(CxHashKey key) {
return key;
}
+/**
+ * The dereference function for the CX_HASH_KEY() macro.
+ * You should never need to use this manually.
+ *
+ * @param key a pointer to a key
+ * @return a copy of the key (not the data)
+ */
+CX_NODISCARD CX_INLINE
+CxHashKey cx_hash_key_deref(const CxHashKey *key) {
+ return *key;
+}
+
#ifndef __cplusplus
/**
* Creates a hash key from any of the supported types with implicit length.
*
- * Does nothing when passing a CxHashkey.
+ * Does nothing when passing a CxHashKey and dereferences CxHashKey pointers.
*
* Supported types are UCX strings, zero-terminated C strings,
* and 32-bit or 64-bit unsigned integers.
* @returns the @c CxHashKey
*/
#define CX_HASH_KEY(key) _Generic((key), \
+ CxHashKey*: cx_hash_key_deref, \
+ const CxHashKey*: cx_hash_key_deref, \
CxHashKey: cx_hash_key_identity, \
cxstring: cx_hash_key_cxstr, \
cxmutstr: cx_hash_key_mutstr, \
/**
* Compare function for hash keys.
*
- * @param left the first key
- * @param right the second key
+ * The pointers are untyped to be compatible with the cx_compare_func signature.
+ *
+ * @param left (@c CxHashKey*) the first key
+ * @param right (@c CxHashKey*) the second key
* @return zero when the keys equal, non-zero when they differ
*/
-cx_attr_nodiscard cx_attr_nonnull
-CX_EXPORT int cx_hash_key_cmp(const CxHashKey *left, const CxHashKey *right);
+CX_EXTERN CX_NODISCARD CX_NONNULL
+int cx_hash_key_cmp(const void *left, const void *right);
-#ifdef __cplusplus
-} // extern "C"
+/**
+ * Interprets the key data as a string and returns it.
+ *
+ * @param key the key
+ * @return the key data as a string
+ */
+CX_EXTERN
+cxstring cx_hash_key_as_string(const CxHashKey *key);
+#ifdef __cplusplus
// ----------------------------------------------------------
// Overloads of CX_HASH_KEY (the C++ version of a _Generic)
// ----------------------------------------------------------
return key;
}
+CX_CPPDECL CxHashKey CX_HASH_KEY(const CxHashKey *key) {
+ return *key;
+}
+
CX_CPPDECL CxHashKey CX_HASH_KEY(cxstring str) {
return cx_hash_key_cxstr(str);
}
#include "map.h"
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/** Internal structure for an element of a hash map. */
struct cx_hash_map_element_s;
* @param buckets the initial number of buckets in this hash map
* @return a pointer to the new hash map
*/
-cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxMapFree, 1)
-CX_EXPORT CxMap *cxHashMapCreate(const CxAllocator *allocator,
+CX_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxMapFree, 1)
+CxMap *cxHashMapCreate(const CxAllocator *allocator,
size_t itemsize, size_t buckets);
-/**
- * Creates a new hash map with a default number of buckets.
- *
- * If @p elem_size is #CX_STORE_POINTERS, the created map stores pointers instead of
- * copies of the added elements.
- *
- * @note Iterators provided by this hash map implementation provide the remove operation.
- * The index value of an iterator is incremented when the iterator advanced without
- * removing an entry.
- * In other words, when the iterator is finished, @c index==size .
- *
- * @param itemsize (@c size_t) the size of one element
- * @return (@c CxMap*) a pointer to the new hash map
- */
-#define cxHashMapCreateSimple(itemsize) cxHashMapCreate(NULL, itemsize, 0)
-
/**
* Increases the number of buckets, if necessary.
*
* @retval zero success
* @retval non-zero if a memory allocation error occurred
*/
-cx_attr_nonnull
-CX_EXPORT int cxMapRehash(CxMap *map);
-
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
+CX_EXTERN CX_NONNULL
+int cxMapRehash(CxMap *map);
#endif // UCX_HASH_MAP_H
#include "common.h"
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/**
* Common data for all iterators.
*/
*/
#define cxIteratorFlagRemoval(iter) ((iter).base.remove = (iter).base.allow_remove)
-/**
- * Obtains a reference to an arbitrary iterator.
- *
- * This is useful for APIs that expect some iterator as an argument.
- *
- * @param iter the iterator
- * @return (@c struct @c cx_iterator_base_s*) a pointer to the iterator
- */
-#define cxIteratorRef(iter) &((iter).base)
-
/**
* Loops over an iterator.
*
* use cxIteratorPtr() to create an iterator which directly
* yields the stored pointers.
*
- * While the iterator is in use, the array may only be altered by removing
- * elements through #cxIteratorFlagRemoval(). Every other change to the array
- * will bring this iterator to an undefined state.
- *
- * When @p remove_keeps_order is set to @c false, removing an element will only
- * move the last element to the position of the removed element, instead of
- * moving all subsequent elements by one. Usually, when the order of elements is
- * not important, this parameter should be set to @c false.
- *
* @param array a pointer to the array (can be @c NULL)
* @param elem_size the size of one array element
* @param elem_count the number of elements in the array
- * @param remove_keeps_order @c true if the order of elements must be preserved
- * when removing an element
* @return an iterator for the specified array
* @see cxIteratorPtr()
*/
-cx_attr_nodiscard
-CX_EXPORT CxIterator cxIterator(const void *array,
- size_t elem_size, size_t elem_count, bool remove_keeps_order);
+CX_EXTERN CX_NODISCARD
+CxIterator cxIterator(const void *array,
+ size_t elem_size, size_t elem_count);
/**
* Creates an iterator for the specified plain pointer array.
* hand, an iterator created with cxIterator() would return the
* addresses of those pointers within the array).
*
- * While the iterator is in use, the array may only be altered by removing
- * elements through #cxIteratorFlagRemoval(). Every other change to the array
- * will bring this iterator to an undefined state.
- *
- * When @p remove_keeps_order is set to @c false, removing an element will only
- * move the last element to the position of the removed element, instead of
- * moving all subsequent elements by one. Usually, when the order of elements is
- * not important, this parameter should be set to @c false.
- *
* @param array a pointer to the array (can be @c NULL)
* @param elem_count the number of elements in the array
- * @param remove_keeps_order @c true if the order of elements must be preserved
- * when removing an element
* @return an iterator for the specified array
* @see cxIterator()
*/
-cx_attr_nodiscard
-CX_EXPORT CxIterator cxIteratorPtr(const void *array, size_t elem_count,
- bool remove_keeps_order);
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
+CX_EXTERN CX_NODISCARD
+CxIterator cxIteratorPtr(const void *array, size_t elem_count);
#endif // UCX_ITERATOR_H
#include "string.h"
#include "buffer.h"
#include "array_list.h"
-
-#include <string.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
+#include "map.h"
/**
* The type of the parsed token.
* Reserved.
*/
CX_JSON_NOTHING, // this allows us to always return non-NULL values
+ /**
+ * No meaningful data.
+ */
+ CX_JSON_UNINITIALIZED,
/**
* A JSON object.
*/
typedef struct cx_json_value_s CxJsonValue;
/**
- * Type alias for the JSON array struct.
- */
-typedef struct cx_json_array_s CxJsonArray;
-/**
- * Type alias for the JSON object struct.
+ * Type alias for the map representing a JSON object.
+ * The map contains pointers of type @c CxJsonValue.
*/
-typedef struct cx_json_object_s CxJsonObject;
+typedef CxMap* CxJsonObject;
/**
* Type alias for a JSON string.
*/
*/
typedef enum cx_json_literal CxJsonLiteral;
-/**
- * Type alias for a key/value pair in a JSON object.
- */
-typedef struct cx_json_obj_value_s CxJsonObjValue;
-
-/**
- * JSON array structure.
- */
-struct cx_json_array_s {
- /**
- * The array data.
- */
- CX_ARRAY_DECLARE(CxJsonValue*, array);
-};
-
-/**
- * JSON object structure.
- */
-struct cx_json_object_s {
- /**
- * The key/value entries.
- */
- CX_ARRAY_DECLARE(CxJsonObjValue, values);
- /**
- * The original indices to reconstruct the order in which the members were added.
- */
- size_t *indices;
-};
-
-/**
- * Structure for a key/value entry in a JSON object.
- */
-struct cx_json_obj_value_s {
- /**
- * The key (or name in JSON terminology) of the value.
- */
- cxmutstr name;
- /**
- * The value.
- */
- CxJsonValue *value;
-};
-
/**
* Structure for a JSON value.
*/
/**
* The array data if the type is #CX_JSON_ARRAY.
*/
- CxJsonArray array;
+ CX_ARRAY(CxJsonValue*, array);
/**
* The object data if the type is #CX_JSON_OBJECT.
*/
* The literal type if the type is #CX_JSON_LITERAL.
*/
CxJsonLiteral literal;
- } value;
+ };
};
/**
* The allocator used for produced JSON values.
*/
const CxAllocator *allocator;
+
/**
* The input buffer.
*/
CxBuffer buffer;
- /**
- * Used internally.
- *
- * Remembers the prefix of the last uncompleted token.
- */
- CxJsonToken uncompleted;
-
/**
* A pointer to an intermediate state of the currently parsed value.
*
CxJsonValue *parsed;
/**
- * A pointer to an intermediate state of a currently parsed object member.
+ * The name of a not yet completely parsed object member.
*
* Never access this value manually.
*/
- CxJsonObjValue uncompleted_member;
+ cxmutstr uncompleted_member_name;
+
+ /**
+ * Internal buffer for uncompleted tokens.
+ */
+ cxmutstr uncompleted_content;
+
+ /**
+ * The expected type of the currently parsed, uncompleted token.
+ */
+ CxJsonTokenType uncompleted_tokentype;
/**
* State stack.
*/
- CX_ARRAY_DECLARE_SIZED(int, states, unsigned);
+ CX_ARRAY(int, states);
/**
* Value buffer stack.
*/
- CX_ARRAY_DECLARE_SIZED(CxJsonValue*, vbuf, unsigned);
+ CX_ARRAY(CxJsonValue*, vbuf);
/**
* Internally reserved memory for the state stack.
* Set true to enable pretty output.
*/
bool pretty;
- /**
- * Set false to output the members in the order in which they were added.
- */
- bool sort_members;
/**
* The maximum number of fractional digits in a number value.
* The default value is 6 and values larger than 15 are reduced to 15.
*
* @return new JSON writer settings
*/
-cx_attr_nodiscard
-CX_EXPORT CxJsonWriter cxJsonWriterCompact(void);
+CX_EXTERN CX_NODISCARD
+CxJsonWriter cxJsonWriterCompact(void);
/**
* Creates a default writer configuration for pretty output.
* @param use_spaces false if you want tabs, true if you want four spaces instead
* @return new JSON writer settings
*/
-cx_attr_nodiscard
-CX_EXPORT CxJsonWriter cxJsonWriterPretty(bool use_spaces);
+CX_EXTERN CX_NODISCARD
+CxJsonWriter cxJsonWriterPretty(bool use_spaces);
/**
* Writes a JSON value to a buffer or stream.
* @retval zero success
* @retval non-zero when no or not all data could be written
*/
-cx_attr_nonnull_arg(1, 2, 3)
-CX_EXPORT int cxJsonWrite(void* target, const CxJsonValue* value,
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3)
+int cxJsonWrite(void* target, const CxJsonValue* value,
cx_write_func wfunc, const CxJsonWriter* settings);
+
+/**
+ * Produces a compact string representation of the specified JSON value.
+ *
+ * @param allocator the allocator for the string
+ * @param value the JSON value
+ * @return the produced string
+ * @see cxJsonWrite()
+ * @see cxJsonWriterCompact()
+ * @see cxJsonToPrettyString()
+ */
+CX_EXTERN CX_NONNULL_ARG(2) CX_NODISCARD
+cxmutstr cxJsonToString(const CxAllocator *allocator, CxJsonValue *value);
+
+/**
+ * Produces a pretty string representation of the specified JSON value.
+ *
+ * @param allocator the allocator for the string
+ * @param value the JSON value
+ * @return the produced string
+ * @see cxJsonWrite()
+ * @see cxJsonWriterPretty()
+ * @see cxJsonToString()
+ */
+CX_EXTERN CX_NONNULL_ARG(2) CX_NODISCARD
+cxmutstr cxJsonToPrettyString(const CxAllocator *allocator, CxJsonValue *value);
+
/**
* Initializes the JSON interface.
*
* @param allocator the allocator that shall be used for the produced values
* @see cxJsonDestroy()
*/
-cx_attr_nonnull_arg(1)
-CX_EXPORT void cxJsonInit(CxJson *json, const CxAllocator *allocator);
+CX_EXTERN CX_NONNULL_ARG(1)
+void cxJsonInit(CxJson *json, const CxAllocator *allocator);
/**
* Destroys the JSON interface.
* @param json the JSON interface
* @see cxJsonInit()
*/
-cx_attr_nonnull
-CX_EXPORT void cxJsonDestroy(CxJson *json);
+CX_EXTERN CX_NONNULL
+void cxJsonDestroy(CxJson *json);
/**
* Destroys and re-initializes the JSON interface.
*
- * You might want to use this to reset the parser after
- * encountering a syntax error.
+ * You must use this to reset the parser after encountering a syntax error
+ * if you want to continue using it.
*
* @param json the JSON interface
*/
-cx_attr_nonnull
-CX_EXPORT void cxJsonReset(CxJson *json);
+CX_EXTERN CX_NONNULL
+void cxJsonReset(CxJson *json);
/**
* Fills the input buffer.
* @retval non-zero internal allocation error
* @see cxJsonFill()
*/
-cx_attr_nonnull cx_attr_access_r(2, 3)
-CX_EXPORT int cxJsonFilln(CxJson *json, const char *buf, size_t len);
+CX_EXTERN CX_NONNULL_ARG(1) CX_ACCESS_R(2, 3)
+int cxJsonFilln(CxJson *json, const char *buf, size_t len);
/**
* @retval zero success
* @retval non-zero internal allocation error
*/
-cx_attr_nonnull
-CX_INLINE int cx_json_fill(CxJson *json, cxstring str) {
+CX_NONNULL CX_INLINE
+int cx_json_fill(CxJson *json, cxstring str) {
return cxJsonFilln(json, str.ptr, str.length);
}
*/
#define cxJsonFill(json, str) cx_json_fill(json, cx_strcast(str))
+
+/**
+ * Internal function - use cxJsonFromString() instead.
+ *
+ * @param allocator the allocator for the JSON value
+ * @param str the string to parse
+ * @param value a pointer where the JSON value shall be stored to
+ * @return status code
+ */
+CX_EXTERN CX_NONNULL_ARG(3) CX_ACCESS_W(3)
+CxJsonStatus cx_json_from_string(const CxAllocator *allocator,
+ cxstring str, CxJsonValue **value);
+
+/**
+ * Parses a string into a JSON value.
+ *
+ * @param allocator (@c CxAllocator*) the allocator for the JSON value
+ * @param str (any string) the string to parse
+ * @param value (@c CxJsonValue**) a pointer where the JSON value shall be stored to
+ * @retval CX_JSON_NO_ERROR success
+ * @retval CX_JSON_NO_DATA the string was empty or blank
+ * @retval CX_JSON_INCOMPLETE_DATA the string unexpectedly ended
+ * @retval CX_JSON_BUFFER_ALLOC_FAILED allocating internal buffer space failed
+ * @retval CX_JSON_VALUE_ALLOC_FAILED allocating memory for the CxJsonValue failed
+ * @retval CX_JSON_FORMAT_ERROR_NUMBER the JSON text contains an illegally formatted number
+ * @retval CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN JSON syntax error
+ */
+#define cxJsonFromString(allocator, str, value) \
+ cx_json_from_string(allocator, cx_strcast(str), value)
+
+/**
+ * Recursively deallocates the memory of a JSON value.
+ *
+ * @remark The type of each deallocated value will be changed
+ * to #CX_JSON_NOTHING, and values of such a type will be skipped
+ * by the deallocation. That means this function protects
+ * you from double-frees when you are accidentally freeing
+ * a nested value and then the parent value (or vice versa).
+ *
+ * @param value the value
+ */
+CX_EXTERN
+void cxJsonValueFree(CxJsonValue *value);
+
/**
* Creates a new (empty) JSON object.
*
* @see cxJsonObjPutObj()
* @see cxJsonArrAddValues()
*/
-cx_attr_nodiscard
-CX_EXPORT CxJsonValue* cxJsonCreateObj(const CxAllocator* allocator);
+CX_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxJsonValueFree, 1)
+CxJsonValue* cxJsonCreateObj(const CxAllocator* allocator);
/**
* Creates a new (empty) JSON array.
*
+ * Optionally, this function already allocates memory with the given capacity.
+ *
* @param allocator the allocator to use
+ * @param capacity optional capacity or zero if it's unknown how many elements the array will have
* @return the new JSON array or @c NULL if allocation fails
* @see cxJsonObjPutArr()
* @see cxJsonArrAddValues()
*/
-cx_attr_nodiscard
-CX_EXPORT CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator);
+CX_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxJsonValueFree, 1)
+CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator, size_t capacity);
/**
* Creates a new JSON number value.
* @see cxJsonObjPutNumber()
* @see cxJsonArrAddNumbers()
*/
-cx_attr_nodiscard
-CX_EXPORT CxJsonValue* cxJsonCreateNumber(const CxAllocator* allocator, double num);
+CX_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxJsonValueFree, 1)
+CxJsonValue* cxJsonCreateNumber(const CxAllocator* allocator, double num);
/**
* Creates a new JSON number value based on an integer.
* @see cxJsonObjPutInteger()
* @see cxJsonArrAddIntegers()
*/
-cx_attr_nodiscard
-CX_EXPORT CxJsonValue* cxJsonCreateInteger(const CxAllocator* allocator, int64_t num);
+CX_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxJsonValueFree, 1)
+CxJsonValue* cxJsonCreateInteger(const CxAllocator* allocator, int64_t num);
/**
* Creates a new JSON string.
*
+ * Internal function - use cxJsonCreateString() instead.
+ *
* @param allocator the allocator to use
* @param str the string data
* @return the new JSON value or @c NULL if allocation fails
- * @see cxJsonCreateString()
* @see cxJsonObjPutString()
- * @see cxJsonArrAddStrings()
+ * @see cxJsonArrAddCxStrings()
*/
-cx_attr_nodiscard cx_attr_nonnull_arg(2) cx_attr_cstr_arg(2)
-CX_EXPORT CxJsonValue* cxJsonCreateString(const CxAllocator* allocator, const char *str);
+CX_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxJsonValueFree, 1)
+CxJsonValue* cx_json_create_string(const CxAllocator* allocator, cxstring str);
/**
* Creates a new JSON string.
*
- * @param allocator the allocator to use
- * @param str the string data
- * @return the new JSON value or @c NULL if allocation fails
- * @see cxJsonCreateCxString()
- * @see cxJsonObjPutCxString()
+ * @param allocator (@c CxAllocator*) the allocator to use
+ * @param str the string
+ * @return (@c CxJsonValue*) the new JSON value or @c NULL if allocation fails
+ * @see cxJsonObjPutString()
* @see cxJsonArrAddCxStrings()
*/
-cx_attr_nodiscard
-CX_EXPORT CxJsonValue* cxJsonCreateCxString(const CxAllocator* allocator, cxstring str);
+#define cxJsonCreateString(allocator, str) cx_json_create_string(allocator, cx_strcast(str))
/**
* Creates a new JSON literal.
* @see cxJsonObjPutLiteral()
* @see cxJsonArrAddLiterals()
*/
-cx_attr_nodiscard
-CX_EXPORT CxJsonValue* cxJsonCreateLiteral(const CxAllocator* allocator, CxJsonLiteral lit);
+CX_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxJsonValueFree, 1)
+CxJsonValue* cxJsonCreateLiteral(const CxAllocator* allocator, CxJsonLiteral lit);
/**
* Adds number values to a JSON array.
* @retval zero success
* @retval non-zero allocation failure
*/
-cx_attr_nonnull cx_attr_access_r(2, 3)
-CX_EXPORT int cxJsonArrAddNumbers(CxJsonValue* arr, const double* num, size_t count);
+CX_EXTERN CX_NONNULL CX_ACCESS_R(2, 3)
+int cxJsonArrAddNumbers(CxJsonValue* arr, const double* num, size_t count);
/**
* Adds number values, of which all are integers, to a JSON array.
* @retval zero success
* @retval non-zero allocation failure
*/
-cx_attr_nonnull cx_attr_access_r(2, 3)
-CX_EXPORT int cxJsonArrAddIntegers(CxJsonValue* arr, const int64_t* num, size_t count);
+CX_EXTERN CX_NONNULL CX_ACCESS_R(2, 3)
+int cxJsonArrAddIntegers(CxJsonValue* arr, const int64_t* num, size_t count);
/**
* Adds strings to a JSON array.
* @retval non-zero allocation failure
* @see cxJsonArrAddCxStrings()
*/
-cx_attr_nonnull cx_attr_access_r(2, 3)
-CX_EXPORT int cxJsonArrAddStrings(CxJsonValue* arr, const char* const* str, size_t count);
+CX_EXTERN CX_NONNULL CX_ACCESS_R(2, 3)
+int cxJsonArrAddStrings(CxJsonValue* arr, const char* const* str, size_t count);
/**
* Adds strings to a JSON array.
* @retval non-zero allocation failure
* @see cxJsonArrAddStrings()
*/
-cx_attr_nonnull cx_attr_access_r(2, 3)
-CX_EXPORT int cxJsonArrAddCxStrings(CxJsonValue* arr, const cxstring* str, size_t count);
+CX_EXTERN CX_NONNULL CX_ACCESS_R(2, 3)
+int cxJsonArrAddCxStrings(CxJsonValue* arr, const cxstring* str, size_t count);
/**
* Adds literals to a JSON array.
* @retval zero success
* @retval non-zero allocation failure
*/
-cx_attr_nonnull cx_attr_access_r(2, 3)
-CX_EXPORT int cxJsonArrAddLiterals(CxJsonValue* arr, const CxJsonLiteral* lit, size_t count);
+CX_EXTERN CX_NONNULL CX_ACCESS_R(2, 3)
+int cxJsonArrAddLiterals(CxJsonValue* arr, const CxJsonLiteral* lit, size_t count);
/**
* Add arbitrary values to a JSON array.
* @retval zero success
* @retval non-zero allocation failure
*/
-cx_attr_nonnull cx_attr_access_r(2, 3)
-CX_EXPORT int cxJsonArrAddValues(CxJsonValue* arr, CxJsonValue* const* val, size_t count);
+CX_EXTERN CX_NONNULL CX_ACCESS_R(2, 3)
+int cxJsonArrAddValues(CxJsonValue* arr, CxJsonValue* const* val, size_t count);
+
+/**
+ * Adds or replaces a value within a JSON object.
+ *
+ * Internal function - use cxJsonObjPut().
+ *
+ * @param obj the JSON object
+ * @param name the name of the value
+ * @param child the value
+ * @retval zero success
+ * @retval non-zero allocation failure
+ */
+CX_EXTERN CX_NONNULL
+int cx_json_obj_put(CxJsonValue* obj, cxstring name, CxJsonValue* child);
/**
* Adds or replaces a value within a JSON object.
* @note If a value with the specified @p name already exists,
* it will be (recursively) freed with its own allocator.
*
- * @param obj the JSON object
- * @param name the name of the value
- * @param child the value
+ * @param obj (@c CxJsonValue*) the JSON object
+ * @param name (any string) the name of the value
+ * @param child (@c CxJsonValue*) the value
* @retval zero success
* @retval non-zero allocation failure
*/
-cx_attr_nonnull
-CX_EXPORT int cxJsonObjPut(CxJsonValue* obj, cxstring name, CxJsonValue* child);
+#define cxJsonObjPut(obj, name, child) cx_json_obj_put(obj, cx_strcast(name), child)
/**
* Creates a new JSON object and adds it to an existing object.
*
+ * Internal function - use cxJsonObjPutObj().
+ *
* @param obj the target JSON object
* @param name the name of the new value
* @return the new value or @c NULL if allocation fails
* @see cxJsonObjPut()
* @see cxJsonCreateObj()
*/
-cx_attr_nonnull
-CX_EXPORT CxJsonValue* cxJsonObjPutObj(CxJsonValue* obj, cxstring name);
+CX_EXTERN CX_NONNULL CX_MALLOC CX_DEALLOC(cxJsonValueFree, 1)
+CxJsonValue* cx_json_obj_put_obj(CxJsonValue* obj, cxstring name);
+
+/**
+ * Creates a new JSON object and adds it to an existing object.
+ *
+ * @param obj (@c CxJsonValue*) the target JSON object
+ * @param name (any string) the name of the new value
+ * @return (@c CxJsonValue*) the new value or @c NULL if allocation fails
+ * @see cxJsonObjPut()
+ * @see cxJsonCreateObj()
+ */
+#define cxJsonObjPutObj(obj, name) cx_json_obj_put_obj(obj, cx_strcast(name))
/**
* Creates a new JSON array and adds it to an object.
*
+ * Internal function - use cxJsonObjPutArr().
+ *
* @param obj the target JSON object
* @param name the name of the new value
+ * @param capacity optional initial capacity
* @return the new value or @c NULL if allocation fails
* @see cxJsonObjPut()
* @see cxJsonCreateArr()
*/
-cx_attr_nonnull
-CX_EXPORT CxJsonValue* cxJsonObjPutArr(CxJsonValue* obj, cxstring name);
+CX_EXTERN CX_NONNULL CX_MALLOC CX_DEALLOC(cxJsonValueFree, 1)
+CxJsonValue* cx_json_obj_put_arr(CxJsonValue* obj, cxstring name, size_t capacity);
+
+/**
+ * Creates a new JSON array and adds it to an object.
+ *
+ * @param obj (@c CxJsonValue*) the target JSON object
+ * @param name (any string) the name of the new value
+ * @param capacity (@c size_t) optional initial capacity
+ * @return (@c CxJsonValue*) the new value or @c NULL if allocation fails
+ * @see cxJsonObjPut()
+ * @see cxJsonCreateArr()
+ */
+#define cxJsonObjPutArr(obj, name, capacity) cx_json_obj_put_arr(obj, cx_strcast(name), capacity)
/**
* Creates a new JSON number and adds it to an object.
*
+ * Internal function - use cxJsonObjPutNumber().
+ *
* @param obj the target JSON object
* @param name the name of the new value
* @param num the numeric value
* @see cxJsonObjPut()
* @see cxJsonCreateNumber()
*/
-cx_attr_nonnull
-CX_EXPORT CxJsonValue* cxJsonObjPutNumber(CxJsonValue* obj, cxstring name, double num);
+CX_EXTERN CX_NONNULL CX_MALLOC CX_DEALLOC(cxJsonValueFree, 1)
+CxJsonValue* cx_json_obj_put_number(CxJsonValue* obj, cxstring name, double num);
+
+/**
+ * Creates a new JSON number and adds it to an object.
+ *
+ * @param obj (@c CxJsonValue*) the target JSON object
+ * @param name (any string) the name of the new value
+ * @param num (@c double) the numeric value
+ * @return (@c CxJsonValue*) the new value or @c NULL if allocation fails
+ * @see cxJsonObjPut()
+ * @see cxJsonCreateNumber()
+ */
+#define cxJsonObjPutNumber(obj, name, num) cx_json_obj_put_number(obj, cx_strcast(name), num)
/**
* Creates a new JSON number, based on an integer, and adds it to an object.
*
+ * Internal function - use cxJsonObjPutInteger().
+ *
* @param obj the target JSON object
* @param name the name of the new value
* @param num the numeric value
* @see cxJsonObjPut()
* @see cxJsonCreateInteger()
*/
-cx_attr_nonnull
-CX_EXPORT CxJsonValue* cxJsonObjPutInteger(CxJsonValue* obj, cxstring name, int64_t num);
+CX_EXTERN CX_NONNULL CX_MALLOC CX_DEALLOC(cxJsonValueFree, 1)
+CxJsonValue* cx_json_obj_put_integer(CxJsonValue* obj, cxstring name, int64_t num);
+
+/**
+ * Creates a new JSON number, based on an integer, and adds it to an object.
+ *
+ * @param obj (@c CxJsonValue*) the target JSON object
+ * @param name (any string) the name of the new value
+ * @param num (@c int64_t) the numeric value
+ * @return (@c CxJsonValue*) the new value or @c NULL if allocation fails
+ * @see cxJsonObjPut()
+ * @see cxJsonCreateInteger()
+ */
+#define cxJsonObjPutInteger(obj, name, num) cx_json_obj_put_integer(obj, cx_strcast(name), num)
/**
* Creates a new JSON string and adds it to an object.
*
- * The string data is copied.
+ * Internal function - use cxJsonObjPutString()
*
* @param obj the target JSON object
* @param name the name of the new value
* @see cxJsonObjPut()
* @see cxJsonCreateString()
*/
-cx_attr_nonnull cx_attr_cstr_arg(3)
-CX_EXPORT CxJsonValue* cxJsonObjPutString(CxJsonValue* obj, cxstring name, const char* str);
+CX_EXTERN CX_NONNULL CX_MALLOC CX_DEALLOC(cxJsonValueFree, 1)
+CxJsonValue* cx_json_obj_put_string(CxJsonValue* obj, cxstring name, cxstring str);
/**
* Creates a new JSON string and adds it to an object.
*
* The string data is copied.
*
- * @param obj the target JSON object
- * @param name the name of the new value
- * @param str the string data
- * @return the new value or @c NULL if allocation fails
+ * @param obj (@c CxJsonValue*) the target JSON object
+ * @param name (any string) the name of the new value
+ * @param str (any string) the string data
+ * @return (@c CxJsonValue*) the new value or @c NULL if allocation fails
* @see cxJsonObjPut()
- * @see cxJsonCreateCxString()
+ * @see cxJsonCreateString()
*/
-cx_attr_nonnull
-CX_EXPORT CxJsonValue* cxJsonObjPutCxString(CxJsonValue* obj, cxstring name, cxstring str);
+#define cxJsonObjPutString(obj, name, str) cx_json_obj_put_string(obj, cx_strcast(name), cx_strcast(str))
/**
* Creates a new JSON literal and adds it to an object.
*
+ * Internal function - use cxJsonObjPutLiteral().
+ *
* @param obj the target JSON object
* @param name the name of the new value
* @param lit the type of literal
* @see cxJsonObjPut()
* @see cxJsonCreateLiteral()
*/
-cx_attr_nonnull
-CX_EXPORT CxJsonValue* cxJsonObjPutLiteral(CxJsonValue* obj, cxstring name, CxJsonLiteral lit);
+CX_EXTERN CX_NONNULL CX_MALLOC CX_DEALLOC(cxJsonValueFree, 1)
+CxJsonValue* cx_json_obj_put_literal(CxJsonValue* obj, cxstring name, CxJsonLiteral lit);
/**
- * Recursively deallocates the memory of a JSON value.
- *
- * @remark The type of each deallocated value will be changed
- * to #CX_JSON_NOTHING, and values of such a type will be skipped
- * by the deallocation. That means this function protects
- * you from double-frees when you are accidentally freeing
- * a nested value and then the parent value (or vice versa).
+ * Creates a new JSON literal and adds it to an object.
*
- * @param value the value
+ * @param obj (@c CxJsonValue*) the target JSON object
+ * @param name (any string) the name of the new value
+ * @param lit (@c CxJsonLiteral) the type of literal
+ * @return (@c CxJsonValue*) the new value or @c NULL if allocation fails
+ * @see cxJsonObjPut()
+ * @see cxJsonCreateLiteral()
*/
-CX_EXPORT void cxJsonValueFree(CxJsonValue *value);
+#define cxJsonObjPutLiteral(obj, name, lit) cx_json_obj_put_literal(obj, cx_strcast(name), lit)
/**
* Tries to obtain the next JSON value.
* @retval CX_JSON_FORMAT_ERROR_NUMBER the JSON text contains an illegally formatted number
* @retval CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN JSON syntax error
*/
-cx_attr_nonnull cx_attr_access_w(2)
-CX_EXPORT CxJsonStatus cxJsonNext(CxJson *json, CxJsonValue **value);
+CX_EXTERN CX_NONNULL CX_ACCESS_W(2)
+CxJsonStatus cxJsonNext(CxJson *json, CxJsonValue **value);
/**
* Checks if the specified value is a JSON object.
* @retval true the value is a JSON object
* @retval false otherwise
*/
-cx_attr_nonnull
-CX_INLINE bool cxJsonIsObject(const CxJsonValue *value) {
+CX_NONNULL CX_NODISCARD CX_INLINE
+bool cxJsonIsObject(const CxJsonValue *value) {
return value->type == CX_JSON_OBJECT;
}
* @retval true the value is a JSON array
* @retval false otherwise
*/
-cx_attr_nonnull
-CX_INLINE bool cxJsonIsArray(const CxJsonValue *value) {
+CX_NONNULL CX_NODISCARD CX_INLINE
+bool cxJsonIsArray(const CxJsonValue *value) {
return value->type == CX_JSON_ARRAY;
}
* @retval true the value is a string
* @retval false otherwise
*/
-cx_attr_nonnull
-CX_INLINE bool cxJsonIsString(const CxJsonValue *value) {
+CX_NONNULL CX_NODISCARD CX_INLINE
+bool cxJsonIsString(const CxJsonValue *value) {
return value->type == CX_JSON_STRING;
}
* @retval false otherwise
* @see cxJsonIsInteger()
*/
-cx_attr_nonnull
-CX_INLINE bool cxJsonIsNumber(const CxJsonValue *value) {
+CX_NONNULL CX_NODISCARD CX_INLINE
+bool cxJsonIsNumber(const CxJsonValue *value) {
return value->type == CX_JSON_NUMBER || value->type == CX_JSON_INTEGER;
}
* @retval false otherwise
* @see cxJsonIsNumber()
*/
-cx_attr_nonnull
-CX_INLINE bool cxJsonIsInteger(const CxJsonValue *value) {
+CX_NONNULL CX_NODISCARD CX_INLINE
+bool cxJsonIsInteger(const CxJsonValue *value) {
return value->type == CX_JSON_INTEGER;
}
* @see cxJsonIsFalse()
* @see cxJsonIsNull()
*/
-cx_attr_nonnull
-CX_INLINE bool cxJsonIsLiteral(const CxJsonValue *value) {
+CX_NONNULL CX_NODISCARD CX_INLINE
+bool cxJsonIsLiteral(const CxJsonValue *value) {
return value->type == CX_JSON_LITERAL;
}
* @see cxJsonIsTrue()
* @see cxJsonIsFalse()
*/
-cx_attr_nonnull
-CX_INLINE bool cxJsonIsBool(const CxJsonValue *value) {
- return cxJsonIsLiteral(value) && value->value.literal != CX_JSON_NULL;
+CX_NONNULL CX_NODISCARD CX_INLINE
+bool cxJsonIsBool(const CxJsonValue *value) {
+ return cxJsonIsLiteral(value) && value->literal != CX_JSON_NULL;
}
/**
* @see cxJsonIsBool()
* @see cxJsonIsFalse()
*/
-cx_attr_nonnull
-CX_INLINE bool cxJsonIsTrue(const CxJsonValue *value) {
- return cxJsonIsLiteral(value) && value->value.literal == CX_JSON_TRUE;
+CX_NONNULL CX_NODISCARD CX_INLINE
+bool cxJsonIsTrue(const CxJsonValue *value) {
+ return cxJsonIsLiteral(value) && value->literal == CX_JSON_TRUE;
}
/**
* @see cxJsonIsBool()
* @see cxJsonIsTrue()
*/
-cx_attr_nonnull
-CX_INLINE bool cxJsonIsFalse(const CxJsonValue *value) {
- return cxJsonIsLiteral(value) && value->value.literal == CX_JSON_FALSE;
+CX_NONNULL CX_NODISCARD CX_INLINE
+bool cxJsonIsFalse(const CxJsonValue *value) {
+ return cxJsonIsLiteral(value) && value->literal == CX_JSON_FALSE;
}
/**
* @retval false otherwise
* @see cxJsonIsLiteral()
*/
-cx_attr_nonnull
-CX_INLINE bool cxJsonIsNull(const CxJsonValue *value) {
- return cxJsonIsLiteral(value) && value->value.literal == CX_JSON_NULL;
+CX_NONNULL CX_NODISCARD CX_INLINE
+bool cxJsonIsNull(const CxJsonValue *value) {
+ return cxJsonIsLiteral(value) && value->literal == CX_JSON_NULL;
}
/**
* @return the value represented as C string
* @see cxJsonIsString()
*/
-cx_attr_nonnull cx_attr_returns_nonnull
-CX_EXPORT char *cxJsonAsString(const CxJsonValue *value);
+CX_EXTERN CX_NONNULL CX_RETURNS_NONNULL CX_NODISCARD
+char *cxJsonAsString(const CxJsonValue *value);
/**
* Obtains a UCX string from the given JSON value.
* @return the value represented as UCX string
* @see cxJsonIsString()
*/
-cx_attr_nonnull
-CX_EXPORT cxstring cxJsonAsCxString(const CxJsonValue *value);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+cxstring cxJsonAsCxString(const CxJsonValue *value);
/**
* Obtains a mutable UCX string from the given JSON value.
* @return the value represented as mutable UCX string
* @see cxJsonIsString()
*/
-cx_attr_nonnull
-CX_EXPORT cxmutstr cxJsonAsCxMutStr(const CxJsonValue *value);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+cxmutstr cxJsonAsCxMutStr(const CxJsonValue *value);
/**
* Obtains a double-precision floating-point value from the given JSON value.
* @return the value represented as double
* @see cxJsonIsNumber()
*/
-cx_attr_nonnull
-CX_EXPORT double cxJsonAsDouble(const CxJsonValue *value);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+double cxJsonAsDouble(const CxJsonValue *value);
/**
* Obtains a 64-bit signed integer from the given JSON value.
* @see cxJsonIsNumber()
* @see cxJsonIsInteger()
*/
-cx_attr_nonnull
-CX_EXPORT int64_t cxJsonAsInteger(const CxJsonValue *value);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int64_t cxJsonAsInteger(const CxJsonValue *value);
/**
* Obtains a Boolean value from the given JSON value.
* @return the value represented as double
* @see cxJsonIsLiteral()
*/
-cx_attr_nonnull
-CX_INLINE bool cxJsonAsBool(const CxJsonValue *value) {
- return value->value.literal == CX_JSON_TRUE;
+CX_NONNULL CX_NODISCARD CX_INLINE
+bool cxJsonAsBool(const CxJsonValue *value) {
+ return value->literal == CX_JSON_TRUE;
}
/**
* @return the size of the array
* @see cxJsonIsArray()
*/
-cx_attr_nonnull
-CX_INLINE size_t cxJsonArrSize(const CxJsonValue *value) {
- return value->value.array.array_size;
+CX_NONNULL CX_NODISCARD CX_INLINE
+size_t cxJsonArrSize(const CxJsonValue *value) {
+ return value->array.size;
}
/**
* @return the value at the specified index
* @see cxJsonIsArray()
*/
-cx_attr_nonnull cx_attr_returns_nonnull
-CX_EXPORT CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index);
+CX_EXTERN CX_NONNULL CX_RETURNS_NONNULL CX_NODISCARD
+CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index);
/**
* Removes an element from a JSON array.
* @return the removed value from the specified index or @c NULL when the index was out of bounds
* @see cxJsonIsArray()
*/
-cx_attr_nonnull
-CX_EXPORT CxJsonValue *cxJsonArrRemove(CxJsonValue *value, size_t index);
+CX_EXTERN CX_NONNULL
+CxJsonValue *cxJsonArrRemove(CxJsonValue *value, size_t index);
/**
* Returns an iterator over the JSON array elements.
* @return an iterator over the array elements
* @see cxJsonIsArray()
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT CxIterator cxJsonArrIter(const CxJsonValue *value);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+CxIterator cxJsonArrIter(const CxJsonValue *value);
+
+/**
+ * Returns the size of a JSON object.
+ *
+ * If the @p value is not a JSON object, the behavior is undefined.
+ *
+ * @param value the JSON value
+ * @return the size of the object, i.e., the number of key/value pairs
+ * @see cxJsonIsObject()
+ */
+CX_NONNULL CX_INLINE
+size_t cxJsonObjSize(const CxJsonValue *value) {
+ return cxCollectionSize(value->object);
+}
/**
- * Returns an iterator over the JSON object members.
+ * Returns a map iterator over the JSON object members.
*
- * The iterator yields values of type @c CxJsonObjValue* which
- * contain the name and value of the member.
+ * The iterator yields values of type @c CxMapEntry* which
+ * contain the name and the @c CxJsonObjValue* of the member.
*
* If the @p value is not a JSON object, the behavior is undefined.
*
* @return an iterator over the object members
* @see cxJsonIsObject()
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT CxIterator cxJsonObjIter(const CxJsonValue *value);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+CxMapIterator cxJsonObjIter(const CxJsonValue *value);
/**
* Internal function, do not use.
* @param name the key to look up
* @return the value corresponding to the key
*/
-cx_attr_nonnull cx_attr_returns_nonnull
-CX_EXPORT CxJsonValue *cx_json_obj_get(const CxJsonValue *value, cxstring name);
+CX_EXTERN CX_NONNULL CX_RETURNS_NONNULL CX_NODISCARD
+CxJsonValue *cx_json_obj_get(const CxJsonValue *value, cxstring name);
/**
* Returns a value corresponding to a key in a JSON object.
* @param name the key to look up
* @return the value corresponding to the key or @c NULL when the key is not part of the object
*/
-cx_attr_nonnull
-CX_EXPORT CxJsonValue *cx_json_obj_remove(CxJsonValue *value, cxstring name);
+CX_EXTERN CX_NONNULL
+CxJsonValue *cx_json_obj_remove(CxJsonValue *value, cxstring name);
/**
* Removes and returns a value corresponding to a key in a JSON object.
*/
#define cxJsonObjRemove(value, name) cx_json_obj_remove(value, cx_strcast(name))
-#ifdef __cplusplus
-}
-#endif
+/**
+ * Performs a deep comparison of two JSON values.
+ *
+ * The order of object members is ignored during comparison.
+ *
+ * @param json the JSON value
+ * @param other the other JSON value that the JSON value is compared to
+ * @retval zero the values are equal (except for ordering of object members)
+ * @retval non-zero the values differ
+ */
+CX_EXTERN CX_NODISCARD
+int cxJsonCompare(const CxJsonValue *json, const CxJsonValue *other);
+
+
+/**
+ * Creates a deep copy of the specified JSON value.
+ *
+ * If you need a @c cx_clone_func compatible version, see cxJsonCloneFunc().
+ *
+ * @note when you are cloning @c NULL, you will get a pointer to a statically
+ * allocated value which represents nothing.
+ *
+ * @param value the value to be cloned
+ * @param allocator the allocator for the new value
+ * @return the new value or @c NULL if any allocation was unsuccessful
+ * @see cxJsonCloneFunc()
+ */
+CX_EXTERN CX_NODISCARD
+CxJsonValue* cxJsonClone(const CxJsonValue* value,
+ const CxAllocator* allocator);
+
+/**
+ * A @c cx_clone_func compatible version of cxJsonClone().
+ *
+ * Internal function - use cxJsonCloneFunc() to get a properly casted function pointer.
+ *
+ * @param target the target memory or @c NULL
+ * @param source the value to be cloned
+ * @param allocator the allocator for the new value
+ * @param data unused
+ * @return the new value or @c NULL if any allocation was unsuccessful
+ * @see cxJsonClone()
+ */
+CX_EXTERN CX_NODISCARD
+CxJsonValue* cx_json_clone_func(
+ CxJsonValue* target, const CxJsonValue* source,
+ const CxAllocator* allocator, void *data);
+
+/**
+ * A @c cx_clone_func compatible version of cxJsonClone().
+ *
+ * @param target (@c CxJsonValue*) the target memory or @c NULL
+ * @param source (@c CxJsonValue*) the value to be cloned
+ * @param allocator (@c CxAllocator*) the allocator for the new value
+ * @param data unused
+ * @return the new value or @c NULL if any allocation was unsuccessful
+ * @see cxJsonClone()
+ */
+#define cxJsonCloneFunc ((cx_clone_func) cx_json_clone_func)
#endif /* UCX_JSON_H */
#include "list.h"
#include "map.h"
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/**
* Allocates a linked list with a lookup-map for storing elements with @p elem_size bytes each.
*
* If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of
* copies of the added elements, and the compare function will be automatically set
- * to cx_cmp_ptr() if none is given.
+ * to cx_cmp_ptr().
*
* After creating the list, it can also be used as a map after converting the pointer
* to a CxMap pointer with cxKvListAsMap().
*
* @param allocator the allocator for allocating the list nodes
* (if @c NULL, the cxDefaultAllocator will be used)
- * @param comparator the comparator for the elements
- * (if @c NULL, and the list is not storing pointers, sort and find
- * functions will not work)
* @param elem_size the size of each element in bytes
* @return the created list
* @see cxKvListAsMap()
* @see cxKvListAsList()
*/
-cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxListFree, 1)
-CX_EXPORT CxList *cxKvListCreate(const CxAllocator *allocator,
- cx_compare_func comparator, size_t elem_size);
+CX_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxListFree, 1)
+CxList *cxKvListCreate(const CxAllocator *allocator,
+ size_t elem_size);
/**
* Allocates a linked list with a lookup-map for storing elements with @p elem_size bytes each.
*
* If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of
* copies of the added elements, and the compare function will be automatically set
- * to cx_cmp_ptr() if none is given.
+ * to cx_cmp_ptr().
*
* This function creates the list with cxKvListCreate() and immediately applies
* cxKvListAsMap(). If you want to use the returned object as a list, you can call
*
* @param allocator the allocator for allocating the list nodes
* (if @c NULL, the cxDefaultAllocator will be used)
- * @param comparator the comparator for the elements
- * (if @c NULL, and the list is not storing pointers, sort and find
- * functions will not work)
* @param elem_size the size of each element in bytes
* @return the created list wrapped into the CxMap interface
* @see cxKvListAsMap()
* @see cxKvListAsList()
*/
-cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxMapFree, 1)
-CX_EXPORT CxMap *cxKvListCreateAsMap(const CxAllocator *allocator,
- cx_compare_func comparator, size_t elem_size);
-
-/**
- * Allocates a linked list with a lookup-map for storing elements with @p elem_size bytes each.
- *
- * The list will use cxDefaultAllocator and no comparator function. If you want
- * to call functions that need a comparator, you must either set one immediately
- * after list creation or use cxKvListCreate().
- *
- * If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of
- * copies of the added elements, and the compare function will be automatically set
- * to cx_cmp_ptr().
- *
- * After creating the list, it can also be used as a map after converting the pointer
- * to a CxMap pointer with cxKvListAsMap().
- * When you want to use the list interface again, you can also convert the map pointer back
- * with cxKvListAsList().
- *
- * @param elem_size (@c size_t) the size of each element in bytes
- * @return (@c CxList*) the created list
- * @see cxKvListAsMap()
- * @see cxKvListAsList()
- */
-#define cxKvListCreateSimple(elem_size) cxKvListCreate(NULL, NULL, elem_size)
-
-/**
- * Allocates a linked list with a lookup-map for storing elements with @p elem_size bytes each.
- *
- * The list will use cxDefaultAllocator and no comparator function. If you want
- * to call functions that need a comparator, you must either set one immediately
- * after list creation or use cxKvListCreate().
- *
- * If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of
- * copies of the added elements, and the compare function will be automatically set
- * to cx_cmp_ptr().
- *
- * This macro behaves as if the list was created with cxKvListCreateSimple() and
- * immediately followed up by cxKvListAsMap().
- * If you want to use the returned object as a list, you can call cxKvListAsList() later.
- *
- * @param elem_size (@c size_t) the size of each element in bytes
- * @return (@c CxMap*) the created list wrapped into the CxMap interface
- * @see cxKvListAsMap()
- * @see cxKvListAsList()
- */
-#define cxKvListCreateAsMapSimple(elem_size) cxKvListCreateAsMap(NULL, NULL, elem_size)
+CX_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxMapFree, 1)
+CxMap *cxKvListCreateAsMap(const CxAllocator *allocator,
+ size_t elem_size);
/**
* Converts a map pointer belonging to a key-value-List back to the original list pointer.
* @param map a map pointer that was returned by a call to cxKvListAsMap()
* @return the original list pointer
*/
-cx_attr_nodiscard cx_attr_nonnull cx_attr_returns_nonnull
-CX_EXPORT CxList *cxKvListAsList(CxMap *map);
+CX_EXTERN CX_NODISCARD CX_NONNULL CX_RETURNS_NONNULL
+CxList *cxKvListAsList(CxMap *map);
/**
* Converts a map pointer belonging to a key-value-List back to the original list pointer.
*
- * @param list a list created by cxKvListCreate() or cxKvListCreateSimple()
+ * @param list a list created by cxKvListCreate()
* @return a map pointer that lets you use the list as if it was a map
*/
-cx_attr_nodiscard cx_attr_nonnull cx_attr_returns_nonnull
-CX_EXPORT CxMap *cxKvListAsMap(CxList *list);
+CX_EXTERN CX_NODISCARD CX_NONNULL CX_RETURNS_NONNULL
+CxMap *cxKvListAsMap(CxList *list);
/**
* Sets or updates the key of a list item.
* @retval non-zero memory allocation failure or the index is out of bounds
* @see cxKvListSetKey()
*/
-cx_attr_nonnull
-CX_EXPORT int cx_kv_list_set_key(CxList *list, size_t index, CxHashKey key);
+CX_EXTERN CX_NONNULL
+int cx_kv_list_set_key(CxList *list, size_t index, CxHashKey key);
/**
* Inserts an item into the list at the specified index and associates it with the specified key.
* @retval non-zero memory allocation failure or the index is out of bounds
* @see cxKvListInsert()
*/
-cx_attr_nonnull
-CX_EXPORT int cx_kv_list_insert(CxList *list, size_t index, CxHashKey key, void *value);
+CX_EXTERN CX_NONNULL
+int cx_kv_list_insert(CxList *list, size_t index, CxHashKey key, void *value);
/**
* Sets or updates the key of a list item.
*/
#define cxKvListInsert(list, index, key, value) cx_kv_list_insert(list, index, CX_HASH_KEY(key), value)
-
/**
* Removes the key of a list item.
*
* @retval zero success
* @retval non-zero the index is out of bounds
*/
-cx_attr_nonnull
-CX_EXPORT int cxKvListRemoveKey(CxList *list, size_t index);
+CX_EXTERN CX_NONNULL
+int cxKvListRemoveKey(CxList *list, size_t index);
/**
* Returns the key of a list item.
* @param index the index of the element in the list
* @return a pointer to the key or @c NULL when the index is out of bounds or the item does not have a key
*/
-cx_attr_nonnull
-CX_EXPORT const CxHashKey *cxKvListGetKey(CxList *list, size_t index);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+const CxHashKey *cxKvListGetKey(CxList *list, size_t index);
/**
* Adds an item into the list and associates it with the specified key.
*/
#define cxKvListAdd(list, key, value) cxKvListInsert(list, (list)->collection.size, key, value)
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
#endif // UCX_KV_LIST_H
#include "common.h"
#include "list.h"
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/**
* Metadata for a linked list.
*/
* Location of the payload (mandatory).
*/
off_t loc_data;
+ /**
+ * Location of extra data (optional).
+ * Negative when no extra data is requested.
+ * @see cx_linked_list_extra_data()
+ */
+ off_t loc_extra;
/**
* Additional bytes to allocate @em behind the payload (e.g. for metadata).
+ * @see cx_linked_list_extra_data()
*/
size_t extra_data_len;
/**
*
* If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of
* copies of the added elements, and the compare function will be automatically set
- * to cx_cmp_ptr() if none is given.
+ * to cx_cmp_ptr().
*
* @param allocator the allocator for allocating the list nodes
* (if @c NULL, the cxDefaultAllocator will be used)
- * @param comparator the comparator for the elements
- * (if @c NULL, and the list is not storing pointers, sort and find
- * functions will not work)
* @param elem_size the size of each element in bytes
* @return the created list
*/
-cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxListFree, 1)
-CX_EXPORT CxList *cxLinkedListCreate(const CxAllocator *allocator,
- cx_compare_func comparator, size_t elem_size);
+CX_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxListFree, 1)
+CxList *cxLinkedListCreate(const CxAllocator *allocator,
+ size_t elem_size);
/**
- * Allocates a linked list for storing elements with @p elem_size bytes each.
+ * Instructs the linked list to reserve extra data in each node.
*
- * The list will use cxDefaultAllocator and no comparator function. If you want
- * to call functions that need a comparator, you must either set one immediately
- * after list creation or use cxLinkedListCreate().
+ * The extra data will be aligned and placed behind the element data.
+ * The exact location in the node is stored in the @c loc_extra field
+ * of the linked list.
*
- * If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of
- * copies of the added elements, and the compare function will be automatically set
- * to cx_cmp_ptr().
+ * You should usually not use this function except when you are creating an
+ * own linked-list implementation that is based on the UCX linked list and
+ * needs to store extra data in each node.
*
- * @param elem_size (@c size_t) the size of each element in bytes
- * @return (@c CxList*) the created list
+ * @param list the list (must be a linked list)
+ * @param len the length of the extra data
*/
-#define cxLinkedListCreateSimple(elem_size) \
- cxLinkedListCreate(NULL, NULL, elem_size)
+CX_EXTERN CX_NONNULL
+void cx_linked_list_extra_data(cx_linked_list *list, size_t len);
/**
* Finds the node at a certain index.
* @param index the search index
* @return the node found at the specified index
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT void *cx_linked_list_at(const void *start,size_t start_index,
+CX_EXTERN CX_NONNULL CX_NODISCARD
+void *cx_linked_list_at(const void *start,size_t start_index,
ptrdiff_t loc_advance, size_t index);
/**
* @param start a pointer to the start node
* @param loc_advance the location of the pointer to advance
* @param loc_data the location of the @c data pointer within your node struct
+ * @param elem a pointer to the element to find
+ * @param found_index an optional pointer where the index of the found node
+ * (given that @p start has index 0) is stored
* @param cmp_func a compare function to compare @p elem against the node data
+ * @return a pointer to the found node or @c NULL if no matching node was found
+ */
+CX_EXTERN CX_NONNULL_ARG(1, 4, 6)
+void *cx_linked_list_find(const void *start, ptrdiff_t loc_advance,
+ ptrdiff_t loc_data, const void *elem, size_t *found_index,
+ cx_compare_func cmp_func);
+
+/**
+ * Finds the node containing an element within a linked list.
+ *
+ * @param start a pointer to the start node
+ * @param loc_advance the location of the pointer to advance
+ * @param loc_data the location of the @c data pointer within your node struct
* @param elem a pointer to the element to find
* @param found_index an optional pointer where the index of the found node
* (given that @p start has index 0) is stored
+ * @param cmp_func a compare function to compare @p elem against the node data
+ * @param context additional context for the compare function
* @return a pointer to the found node or @c NULL if no matching node was found
*/
-cx_attr_nonnull_arg(1, 4, 5)
-CX_EXPORT void *cx_linked_list_find(const void *start, ptrdiff_t loc_advance,
- ptrdiff_t loc_data, cx_compare_func cmp_func, const void *elem,
- size_t *found_index);
+CX_EXTERN CX_NONNULL_ARG(1, 4, 6)
+void *cx_linked_list_find_c(const void *start, ptrdiff_t loc_advance,
+ ptrdiff_t loc_data, const void *elem, size_t *found_index,
+ cx_compare_func2 cmp_func, void *context);
/**
* Finds the first node in a linked list.
* @param loc_prev the location of the @c prev pointer
* @return a pointer to the first node
*/
-cx_attr_nonnull cx_attr_returns_nonnull
-CX_EXPORT void *cx_linked_list_first(const void *node, ptrdiff_t loc_prev);
+CX_EXTERN CX_NONNULL CX_RETURNS_NONNULL
+void *cx_linked_list_first(const void *node, ptrdiff_t loc_prev);
/**
* Finds the last node in a linked list.
* @param loc_next the location of the @c next pointer
* @return a pointer to the last node
*/
-cx_attr_nonnull cx_attr_returns_nonnull
-CX_EXPORT void *cx_linked_list_last(const void *node, ptrdiff_t loc_next);
+CX_EXTERN CX_NONNULL CX_RETURNS_NONNULL
+void *cx_linked_list_last(const void *node, ptrdiff_t loc_next);
/**
* Finds the predecessor of a node in case it is not linked.
* @param node the successor of the node to find
* @return the node or @c NULL if @p node has no predecessor
*/
-cx_attr_nonnull
-CX_EXPORT void *cx_linked_list_prev(const void *begin, ptrdiff_t loc_next, const void *node);
+CX_EXTERN CX_NONNULL
+void *cx_linked_list_prev(const void *begin, ptrdiff_t loc_next, const void *node);
/**
* Adds a new node to a linked list.
* @param loc_next the location of a @c next pointer within your node struct (required)
* @param new_node a pointer to the node that shall be appended
*/
-cx_attr_nonnull_arg(5)
-CX_EXPORT void cx_linked_list_add(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node);
+CX_EXTERN CX_NONNULL_ARG(5)
+void cx_linked_list_add(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node);
/**
* Prepends a new node to a linked list.
* @param loc_next the location of a @c next pointer within your node struct (required)
* @param new_node a pointer to the node that shall be prepended
*/
-cx_attr_nonnull_arg(5)
-CX_EXPORT void cx_linked_list_prepend(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node);
+CX_EXTERN CX_NONNULL_ARG(5)
+void cx_linked_list_prepend(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node);
/**
* Links two nodes.
* @param loc_prev the location of a @c prev pointer within your node struct (negative if your struct does not have one)
* @param loc_next the location of a @c next pointer within your node struct (required)
*/
-cx_attr_nonnull
-CX_EXPORT void cx_linked_list_link(void *left, void *right, ptrdiff_t loc_prev, ptrdiff_t loc_next);
+CX_EXTERN CX_NONNULL
+void cx_linked_list_link(void *left, void *right, ptrdiff_t loc_prev, ptrdiff_t loc_next);
/**
* Unlinks two nodes.
* @param loc_prev the location of a @c prev pointer within your node struct (negative if your struct does not have one)
* @param loc_next the location of a @c next pointer within your node struct (required)
*/
-cx_attr_nonnull
-CX_EXPORT void cx_linked_list_unlink(void *left, void *right, ptrdiff_t loc_prev, ptrdiff_t loc_next);
+CX_EXTERN CX_NONNULL
+void cx_linked_list_unlink(void *left, void *right, ptrdiff_t loc_prev, ptrdiff_t loc_next);
/**
* Inserts a new node after a given node of a linked list.
* @param node the node after which to insert (@c NULL if you want to prepend the node to the list)
* @param new_node a pointer to the node that shall be inserted
*/
-cx_attr_nonnull_arg(6)
-CX_EXPORT void cx_linked_list_insert(void **begin, void **end,
+CX_EXTERN CX_NONNULL_ARG(6)
+void cx_linked_list_insert(void **begin, void **end,
ptrdiff_t loc_prev, ptrdiff_t loc_next, void *node, void *new_node);
/**
* @param insert_begin a pointer to the first node of the chain that shall be inserted
* @param insert_end a pointer to the last node of the chain (or NULL if the last node shall be determined)
*/
-cx_attr_nonnull_arg(6)
-CX_EXPORT void cx_linked_list_insert_chain(void **begin, void **end,
+CX_EXTERN CX_NONNULL_ARG(6)
+void cx_linked_list_insert_chain(void **begin, void **end,
ptrdiff_t loc_prev, ptrdiff_t loc_next, void *node, void *insert_begin, void *insert_end);
/**
* @param new_node a pointer to the node that shall be inserted
* @param cmp_func a compare function that will receive the node pointers
*/
-cx_attr_nonnull_arg(1, 5, 6)
-CX_EXPORT void cx_linked_list_insert_sorted(void **begin, void **end,
+CX_EXTERN CX_NONNULL_ARG(1, 5, 6)
+void cx_linked_list_insert_sorted(void **begin, void **end,
ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node, cx_compare_func cmp_func);
/**
* @param insert_begin a pointer to the first node of the chain that shall be inserted
* @param cmp_func a compare function that will receive the node pointers
*/
-cx_attr_nonnull_arg(1, 5, 6)
-CX_EXPORT void cx_linked_list_insert_sorted_chain(void **begin, void **end,
+CX_EXTERN CX_NONNULL_ARG(1, 5, 6)
+void cx_linked_list_insert_sorted_chain(void **begin, void **end,
ptrdiff_t loc_prev, ptrdiff_t loc_next, void *insert_begin, cx_compare_func cmp_func);
/**
* @retval zero when the node was inserted
* @retval non-zero when a node with the same value already exists
*/
-cx_attr_nonnull_arg(1, 5, 6)
-CX_EXPORT int cx_linked_list_insert_unique(void **begin, void **end,
+CX_EXTERN CX_NONNULL_ARG(1, 5, 6)
+int cx_linked_list_insert_unique(void **begin, void **end,
ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node, cx_compare_func cmp_func);
/**
* @param cmp_func a compare function that will receive the node pointers
* @return a pointer to a new chain with all duplicates that were not inserted (or @c NULL when there were no duplicates)
*/
-cx_attr_nonnull_arg(1, 5, 6) cx_attr_nodiscard
-CX_EXPORT void *cx_linked_list_insert_unique_chain(void **begin, void **end,
+CX_EXTERN CX_NONNULL_ARG(1, 5, 6) CX_NODISCARD
+void *cx_linked_list_insert_unique_chain(void **begin, void **end,
ptrdiff_t loc_prev, ptrdiff_t loc_next, void *insert_begin, cx_compare_func cmp_func);
+/**
+ * Inserts a node into a sorted linked list.
+ * The new node must not be part of any list yet.
+ *
+ * If the list starting with the node pointed to by @p begin is not sorted
+ * already, the behavior is undefined.
+ *
+ * @param begin a pointer to the beginning node pointer (required)
+ * @param end a pointer to the end node pointer (if your list has one)
+ * @param loc_prev the location of a @c prev pointer within your node struct (negative if your struct does not have one)
+ * @param loc_next the location of a @c next pointer within your node struct (required)
+ * @param new_node a pointer to the node that shall be inserted
+ * @param cmp_func a compare function that will receive the node pointers
+ * @param context context for the compare function
+ */
+CX_EXTERN CX_NONNULL_ARG(1, 5, 6)
+void cx_linked_list_insert_sorted_c(void **begin, void **end,
+ ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node, cx_compare_func2 cmp_func, void *context);
+
+/**
+ * Inserts a chain of nodes into a sorted linked list.
+ * The chain must not be part of any list yet.
+ *
+ * If either the list starting with the node pointed to by @p begin or the list
+ * starting with @p insert_begin is not sorted, the behavior is undefined.
+ *
+ * @attention In contrast to cx_linked_list_insert_chain(), the source chain
+ * will be broken and inserted into the target list so that the resulting list
+ * will be sorted according to @p cmp_func. That means each node in the source
+ * chain may be re-linked with nodes from the target list.
+ *
+ * @param begin a pointer to the beginning node pointer (required)
+ * @param end a pointer to the end node pointer (if your list has one)
+ * @param loc_prev the location of a @c prev pointer within your node struct (negative if your struct does not have one)
+ * @param loc_next the location of a @c next pointer within your node struct (required)
+ * @param insert_begin a pointer to the first node of the chain that shall be inserted
+ * @param cmp_func a compare function that will receive the node pointers
+ * @param context context for the compare function
+ */
+CX_EXTERN CX_NONNULL_ARG(1, 5, 6)
+void cx_linked_list_insert_sorted_chain_c(void **begin, void **end,
+ ptrdiff_t loc_prev, ptrdiff_t loc_next, void *insert_begin, cx_compare_func2 cmp_func, void *context);
+
+/**
+ * Inserts a node into a sorted linked list if no other node with the same value already exists.
+ * The new node must not be part of any list yet.
+ *
+ * If the list starting with the node pointed to by @p begin is not sorted
+ * already, the behavior is undefined.
+ *
+ * @param begin a pointer to the beginning node pointer (required)
+ * @param end a pointer to the end node pointer (if your list has one)
+ * @param loc_prev the location of a @c prev pointer within your node struct (negative if your struct does not have one)
+ * @param loc_next the location of a @c next pointer within your node struct (required)
+ * @param new_node a pointer to the node that shall be inserted
+ * @param cmp_func a compare function that will receive the node pointers
+ * @param context the context for the compare function
+ * @retval zero when the node was inserted
+ * @retval non-zero when a node with the same value already exists
+ */
+CX_EXTERN CX_NONNULL_ARG(1, 5, 6)
+int cx_linked_list_insert_unique_c(void **begin, void **end,
+ ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node, cx_compare_func2 cmp_func, void *context);
+
+/**
+ * Inserts a chain of nodes into a sorted linked list, avoiding duplicates.
+ * The chain must not be part of any list yet.
+ *
+ * If either the list starting with the node pointed to by @p begin or the list
+ * starting with @p insert_begin is not sorted, the behavior is undefined.
+ *
+ * @attention In contrast to cx_linked_list_insert_sorted(), not all nodes of the
+ * chain might be added. This function returns a new chain consisting of all the duplicates.
+ *
+ * @param begin a pointer to the beginning node pointer (required)
+ * @param end a pointer to the end node pointer (if your list has one)
+ * @param loc_prev the location of a @c prev pointer within your node struct (negative if your struct does not have one)
+ * @param loc_next the location of a @c next pointer within your node struct (required)
+ * @param insert_begin a pointer to the first node of the chain that shall be inserted
+ * @param cmp_func a compare function that will receive the node pointers
+ * @param context context for the compare function
+ * @return a pointer to a new chain with all duplicates that were not inserted (or @c NULL when there were no duplicates)
+ */
+CX_EXTERN CX_NONNULL_ARG(1, 5, 6) CX_NODISCARD
+void *cx_linked_list_insert_unique_chain_c(void **begin, void **end,
+ ptrdiff_t loc_prev, ptrdiff_t loc_next, void *insert_begin, cx_compare_func2 cmp_func, void *context);
+
/**
* Removes a chain of nodes from the linked list.
*
* @param num the number of nodes to remove
* @return the actual number of nodes that were removed (can be less when the list did not have enough nodes)
*/
-cx_attr_nonnull_arg(5)
-CX_EXPORT size_t cx_linked_list_remove_chain(void **begin, void **end,
+CX_EXTERN CX_NONNULL_ARG(5)
+size_t cx_linked_list_remove_chain(void **begin, void **end,
ptrdiff_t loc_prev, ptrdiff_t loc_next, void *node, size_t num);
/**
* @param loc_next the location of a @c next pointer within your node struct (required)
* @param node the node to remove
*/
-cx_attr_nonnull_arg(5)
-CX_EXPORT void cx_linked_list_remove(void **begin, void **end,
+CX_EXTERN CX_NONNULL_ARG(5)
+void cx_linked_list_remove(void **begin, void **end,
ptrdiff_t loc_prev, ptrdiff_t loc_next, void *node);
/**
* @param loc_next the location of the @c next pointer within the node struct
* @return the size of the list or zero if @p node is @c NULL
*/
-cx_attr_nodiscard
-CX_EXPORT size_t cx_linked_list_size(const void *node, ptrdiff_t loc_next);
+CX_EXTERN CX_NODISCARD
+size_t cx_linked_list_size(const void *node, ptrdiff_t loc_next);
/**
* Sorts a linked list based on a comparison function.
*
- * This function can work with linked lists of the following structure:
- * @code
- * typedef struct node node;
- * struct node {
- * node* prev;
- * node* next;
- * my_payload data;
- * }
- * @endcode
- *
* @note This is a recursive function with at most logarithmic recursion depth.
*
* @param begin a pointer to the beginning node pointer (required)
* @param loc_data the location of the @c data pointer within your node struct
* @param cmp_func the compare function defining the sort order
*/
-cx_attr_nonnull_arg(1, 6)
-CX_EXPORT void cx_linked_list_sort(void **begin, void **end,
+CX_EXTERN CX_NONNULL_ARG(1, 6)
+void cx_linked_list_sort(void **begin, void **end,
ptrdiff_t loc_prev, ptrdiff_t loc_next, ptrdiff_t loc_data, cx_compare_func cmp_func);
+/**
+ * Sorts a linked list based on a comparison function.
+ *
+ * @note This is a recursive function with at most logarithmic recursion depth.
+ *
+ * @param begin a pointer to the beginning node pointer (required)
+ * @param end a pointer to the end node pointer (optional)
+ * @param loc_prev the location of a @c prev pointer within your node struct (negative if not present)
+ * @param loc_next the location of a @c next pointer within your node struct (required)
+ * @param loc_data the location of the @c data pointer within your node struct
+ * @param cmp_func the compare function defining the sort order
+ * @param context additional context for the compare function
+ */
+CX_EXTERN CX_NONNULL_ARG(1, 6)
+void cx_linked_list_sort_c(void **begin, void **end,
+ ptrdiff_t loc_prev, ptrdiff_t loc_next, ptrdiff_t loc_data, cx_compare_func2 cmp_func, void *context);
/**
* Compares two lists element wise.
* @return the first non-zero result of invoking @p cmp_func or: negative if the left list is smaller than the
* right list, positive if the left list is larger than the right list, zero if both lists are equal.
*/
-cx_attr_nonnull_arg(5)
-CX_EXPORT int cx_linked_list_compare(const void *begin_left, const void *begin_right,
+CX_EXTERN CX_NONNULL_ARG(5)
+int cx_linked_list_compare(const void *begin_left, const void *begin_right,
ptrdiff_t loc_advance, ptrdiff_t loc_data, cx_compare_func cmp_func);
+/**
+ * Compares two lists element wise.
+ *
+ * @attention Both lists must have the same structure.
+ *
+ * @param begin_left the beginning of the left list (@c NULL denotes an empty list)
+ * @param begin_right the beginning of the right list (@c NULL denotes an empty list)
+ * @param loc_advance the location of the pointer to advance
+ * @param loc_data the location of the @c data pointer within your node struct
+ * @param cmp_func the function to compare the elements
+ * @param context the context for the compare function
+ * @return the first non-zero result of invoking @p cmp_func or: negative if the left list is smaller than the
+ * right list, positive if the left list is larger than the right list, zero if both lists are equal.
+ */
+CX_EXTERN CX_NONNULL_ARG(5)
+int cx_linked_list_compare_c(const void *begin_left, const void *begin_right,
+ ptrdiff_t loc_advance, ptrdiff_t loc_data, cx_compare_func2 cmp_func, void *context);
+
/**
* Reverses the order of the nodes in a linked list.
*
* @param loc_prev the location of a @c prev pointer within your node struct (negative if your struct does not have one)
* @param loc_next the location of a @c next pointer within your node struct (required)
*/
-cx_attr_nonnull_arg(1)
-CX_EXPORT void cx_linked_list_reverse(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next);
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
+CX_EXTERN CX_NONNULL_ARG(1)
+void cx_linked_list_reverse(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next);
#endif // UCX_LINKED_LIST_H
#include "common.h"
#include "collection.h"
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/**
* List class type.
*/
* The list class definition.
*/
const cx_list_class *cl;
- /**
- * The actual implementation in case the list class is delegating.
- */
- const cx_list_class *climpl;
};
/**
*/
void (*reverse)(struct cx_list_s *list);
+ /**
+ * Optional member function for changing the capacity.
+ * If the list does not support overallocation, this can be set to @c NULL.
+ */
+ int (*change_capacity)(struct cx_list_s *list, size_t new_capacity);
+
/**
* Member function for returning an iterator pointing to the specified index.
*/
* @param n the number of elements to insert
* @return the number of elements actually inserted
*/
-cx_attr_nonnull
-CX_EXPORT size_t cx_list_default_insert_array(struct cx_list_s *list,
+CX_EXTERN CX_NONNULL_ARG(1)
+size_t cx_list_default_insert_array(struct cx_list_s *list,
size_t index, const void *data, size_t n);
/**
* @param n the number of elements to insert
* @return the number of elements actually inserted
*/
-cx_attr_nonnull
-CX_EXPORT size_t cx_list_default_insert_sorted(struct cx_list_s *list,
+CX_EXTERN CX_NONNULL
+size_t cx_list_default_insert_sorted(struct cx_list_s *list,
const void *sorted_data, size_t n);
/**
* @param n the number of elements to insert
* @return the number of elements from the @p sorted_data that are definitely present in the list after this call
*/
-cx_attr_nonnull
-CX_EXPORT size_t cx_list_default_insert_unique(struct cx_list_s *list,
+CX_EXTERN CX_NONNULL
+size_t cx_list_default_insert_unique(struct cx_list_s *list,
const void *sorted_data, size_t n);
/**
*
* @param list the list that shall be sorted
*/
-cx_attr_nonnull
-CX_EXPORT void cx_list_default_sort(struct cx_list_s *list);
+CX_EXTERN CX_NONNULL
+void cx_list_default_sort(struct cx_list_s *list);
/**
* Default unoptimized swap implementation.
* @retval non-zero when indices are out of bounds or memory
* allocation for the temporary buffer fails
*/
-cx_attr_nonnull
-CX_EXPORT int cx_list_default_swap(struct cx_list_s *list, size_t i, size_t j);
+CX_EXTERN CX_NONNULL
+int cx_list_default_swap(struct cx_list_s *list, size_t i, size_t j);
/**
* Initializes a list struct.
* The purpose of this function is to be called in the initialization code
* of your list to set certain members correctly.
*
- * This is particularly important when you want your list to support
- * #CX_STORE_POINTERS as @p elem_size. This function will wrap the list
- * class accordingly and make sure that you can implement your list as if
- * it was only storing objects, and the wrapper will automatically enable
- * the feature of storing pointers.
+ * This is particularly useful when you want your list to support
+ * #CX_STORE_POINTERS as @p elem_size.
*
* @par Example
*
* @code
* CxList *myCustomListCreate(
* const CxAllocator *allocator,
- * cx_compare_func comparator,
* size_t elem_size
* ) {
* if (allocator == NULL) {
* allocator = cxDefaultAllocator;
* }
*
- * MyCustomList *list = cxCalloc(allocator, 1, sizeof(MyCustomList));
+ * MyCustomList *list = cxZalloc(allocator, sizeof(MyCustomList));
* if (list == NULL) return NULL;
*
* // initialize
* cx_list_init((CxList*)list, &my_custom_list_class,
- * allocator, comparator, elem_size);
+ * allocator, elem_size);
*
* // ... some more custom stuff ...
*
* @param list the list to initialize
* @param cl the list class
* @param allocator the allocator for the elements
- * @param comparator a compare function for the elements
* @param elem_size the size of one element
*/
-cx_attr_nonnull_arg(1, 2, 3)
-CX_EXPORT void cx_list_init(struct cx_list_s *list,
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3)
+void cx_list_init(struct cx_list_s *list,
struct cx_list_class_s *cl, const struct cx_allocator_s *allocator,
- cx_compare_func comparator, size_t elem_size);
+ size_t elem_size);
+
+/**
+ * A @c cx_compare_func2 compatible wrapper for the compare functions of a list.
+ *
+ * @param left first element
+ * @param right second element
+ * @param list the list which is comparing the elements
+ * @return the comparison result
+ */
+CX_EXTERN CX_NONNULL
+int cx_list_compare_wrapper(
+ const void *left, const void *right, void *list);
/**
* Returns the number of elements currently stored in the list.
* @param list the list
* @return the number of currently stored elements
*/
-cx_attr_nonnull
-CX_EXPORT size_t cxListSize(const CxList *list);
+CX_EXTERN CX_NONNULL
+size_t cxListSize(const CxList *list);
/**
* Adds an item to the end of the list.
* @see cxListAddArray()
* @see cxListEmplace()
*/
-cx_attr_nonnull
-CX_EXPORT int cxListAdd(CxList *list, const void *elem);
+CX_EXTERN CX_NONNULL
+int cxListAdd(CxList *list, const void *elem);
/**
* Adds multiple items to the end of the list.
* @return the number of added elements
* @see cxListEmplaceArray()
*/
-cx_attr_nonnull
-CX_EXPORT size_t cxListAddArray(CxList *list, const void *array, size_t n);
+CX_EXTERN CX_NONNULL
+size_t cxListAddArray(CxList *list, const void *array, size_t n);
/**
* Inserts an item at the specified index.
* @see cxListInsertBefore()
* @see cxListEmplaceAt()
*/
-cx_attr_nonnull
-CX_EXPORT int cxListInsert(CxList *list, size_t index, const void *elem);
+CX_EXTERN CX_NONNULL
+int cxListInsert(CxList *list, size_t index, const void *elem);
/**
* Allocates memory for an element at the specified index and returns a pointer to that memory.
* @see cxListEmplaceArrayAt()
* @see cxListInsert()
*/
-cx_attr_nonnull
-CX_EXPORT void *cxListEmplaceAt(CxList *list, size_t index);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+void *cxListEmplaceAt(CxList *list, size_t index);
/**
* Allocates memory for an element at the end of the list and returns a pointer to that memory.
* @see cxListEmplaceAt()
* @see cxListAdd()
*/
-cx_attr_nonnull
-CX_EXPORT void *cxListEmplace(CxList *list);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+void *cxListEmplace(CxList *list);
/**
* Allocates memory for multiple elements and returns an iterator.
* @see cxListEmplaceAt()
* @see cxListInsertArray()
*/
-cx_attr_nonnull
-CX_EXPORT CxIterator cxListEmplaceArrayAt(CxList *list, size_t index, size_t n);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+CxIterator cxListEmplaceArrayAt(CxList *list, size_t index, size_t n);
/**
* Allocates memory for multiple elements and returns an iterator.
* @see cxListEmplace()
* @see cxListAddArray()
*/
-cx_attr_nonnull
-CX_EXPORT CxIterator cxListEmplaceArray(CxList *list, size_t n);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+CxIterator cxListEmplaceArray(CxList *list, size_t n);
/**
* Inserts an item into a sorted list.
* @retval zero success
* @retval non-zero memory allocation failure
*/
-cx_attr_nonnull
-CX_EXPORT int cxListInsertSorted(CxList *list, const void *elem);
+CX_EXTERN CX_NONNULL
+int cxListInsertSorted(CxList *list, const void *elem);
/**
* Inserts an item into a list if it does not exist.
* @retval zero success (also when the element was already in the list)
* @retval non-zero memory allocation failure
*/
-cx_attr_nonnull
-CX_EXPORT int cxListInsertUnique(CxList *list, const void *elem);
+CX_EXTERN CX_NONNULL
+int cxListInsertUnique(CxList *list, const void *elem);
/**
* Inserts multiple items to the list at the specified index.
* @return the number of added elements
* @see cxListEmplaceArrayAt()
*/
-cx_attr_nonnull
-CX_EXPORT size_t cxListInsertArray(CxList *list, size_t index, const void *array, size_t n);
+CX_EXTERN CX_NONNULL
+size_t cxListInsertArray(CxList *list, size_t index, const void *array, size_t n);
/**
* Inserts a sorted array into a sorted list.
* @param n the number of elements to add
* @return the number of added elements
*/
-cx_attr_nonnull
-CX_EXPORT size_t cxListInsertSortedArray(CxList *list, const void *array, size_t n);
+CX_EXTERN CX_NONNULL
+size_t cxListInsertSortedArray(CxList *list, const void *array, size_t n);
/**
* Inserts an array into a list, skipping duplicates.
*
* @return the number of elements from the @p sorted_data that are definitely present in the list after this call
*/
-cx_attr_nonnull
-CX_EXPORT size_t cxListInsertUniqueArray(CxList *list, const void *array, size_t n);
+CX_EXTERN CX_NONNULL
+size_t cxListInsertUniqueArray(CxList *list, const void *array, size_t n);
/**
* Inserts an element after the current location of the specified iterator.
* @see cxListInsert()
* @see cxListInsertBefore()
*/
-cx_attr_nonnull
-CX_EXPORT int cxListInsertAfter(CxIterator *iter, const void *elem);
+CX_EXTERN CX_NONNULL
+int cxListInsertAfter(CxIterator *iter, const void *elem);
/**
* Inserts an element before the current location of the specified iterator.
* @see cxListInsert()
* @see cxListInsertAfter()
*/
-cx_attr_nonnull
-CX_EXPORT int cxListInsertBefore(CxIterator *iter, const void *elem);
+CX_EXTERN CX_NONNULL
+int cxListInsertBefore(CxIterator *iter, const void *elem);
/**
* Removes the element at the specified index.
* @retval zero success
* @retval non-zero index out of bounds
*/
-cx_attr_nonnull
-CX_EXPORT int cxListRemove(CxList *list, size_t index);
+CX_EXTERN CX_NONNULL
+int cxListRemove(CxList *list, size_t index);
/**
* Removes and returns the element at the specified index.
* @retval zero success
* @retval non-zero index out of bounds
*/
-cx_attr_nonnull cx_attr_access_w(3)
-CX_EXPORT int cxListRemoveAndGet(CxList *list, size_t index, void *targetbuf);
+CX_EXTERN CX_NONNULL CX_ACCESS_W(3)
+int cxListRemoveAndGet(CxList *list, size_t index, void *targetbuf);
/**
* Removes and returns the first element of the list.
* @see cxListPopFront()
* @see cxListRemoveAndGetLast()
*/
-cx_attr_nonnull cx_attr_access_w(2)
-CX_EXPORT int cxListRemoveAndGetFirst(CxList *list, void *targetbuf);
+CX_EXTERN CX_NONNULL CX_ACCESS_W(2)
+int cxListRemoveAndGetFirst(CxList *list, void *targetbuf);
/**
* Removes and returns the first element of the list.
* @retval zero success
* @retval non-zero the list is empty
*/
-cx_attr_nonnull cx_attr_access_w(2)
-CX_EXPORT int cxListRemoveAndGetLast(CxList *list, void *targetbuf);
+CX_EXTERN CX_NONNULL CX_ACCESS_W(2)
+int cxListRemoveAndGetLast(CxList *list, void *targetbuf);
/**
* Removes and returns the last element of the list.
* @param num the number of elements to remove
* @return the actual number of removed elements
*/
-cx_attr_nonnull
-CX_EXPORT size_t cxListRemoveArray(CxList *list, size_t index, size_t num);
+CX_EXTERN CX_NONNULL
+size_t cxListRemoveArray(CxList *list, size_t index, size_t num);
/**
* Removes and returns multiple elements starting at the specified index.
* @param targetbuf a buffer where to copy the elements
* @return the actual number of removed elements
*/
-cx_attr_nonnull cx_attr_access_w(4)
-CX_EXPORT size_t cxListRemoveArrayAndGet(CxList *list, size_t index, size_t num, void *targetbuf);
+CX_EXTERN CX_NONNULL CX_ACCESS_W(4)
+size_t cxListRemoveArrayAndGet(CxList *list, size_t index, size_t num, void *targetbuf);
/**
* Removes all elements from this list.
*
* @param list the list
*/
-cx_attr_nonnull
-CX_EXPORT void cxListClear(CxList *list);
+CX_EXTERN CX_NONNULL
+void cxListClear(CxList *list);
/**
* Swaps two items in the list.
* @retval non-zero one of the indices is out of bounds,
* or the swap needed extra memory, but allocation failed
*/
-cx_attr_nonnull
-CX_EXPORT int cxListSwap(CxList *list, size_t i, size_t j);
+CX_EXTERN CX_NONNULL
+int cxListSwap(CxList *list, size_t i, size_t j);
/**
* Returns a pointer to the element at the specified index.
* @param index the index of the element
* @return a pointer to the element or @c NULL if the index is out of bounds
*/
-cx_attr_nonnull
-CX_EXPORT void *cxListAt(const CxList *list, size_t index);
+CX_EXTERN CX_NONNULL
+void *cxListAt(const CxList *list, size_t index);
/**
* Returns a pointer to the first element.
* @param list the list
* @return a pointer to the first element or @c NULL if the list is empty
*/
-cx_attr_nonnull
-CX_EXPORT void *cxListFirst(const CxList *list);
+CX_EXTERN CX_NONNULL
+void *cxListFirst(const CxList *list);
/**
* Returns a pointer to the last element.
* @param list the list
* @return a pointer to the last element or @c NULL if the list is empty
*/
-cx_attr_nonnull
-CX_EXPORT void *cxListLast(const CxList *list);
+CX_EXTERN CX_NONNULL
+void *cxListLast(const CxList *list);
/**
* Sets the element at the specified index in the list.
* @retval zero on success
* @retval non-zero when index is out of bounds
*/
-cx_attr_nonnull
-CX_EXPORT int cxListSet(CxList *list, size_t index, const void *elem);
+CX_EXTERN CX_NONNULL
+int cxListSet(CxList *list, size_t index, const void *elem);
/**
* Returns an iterator pointing to the item at the specified index.
* @param index the index where the iterator shall point at
* @return a new iterator
*/
-cx_attr_nodiscard
-CX_EXPORT CxIterator cxListIteratorAt(const CxList *list, size_t index);
+CX_EXTERN CX_NODISCARD
+CxIterator cxListIteratorAt(const CxList *list, size_t index);
/**
* Returns a backwards iterator pointing to the item at the specified index.
* @param index the index where the iterator shall point at
* @return a new iterator
*/
-cx_attr_nodiscard
-CX_EXPORT CxIterator cxListBackwardsIteratorAt(const CxList *list, size_t index);
+CX_EXTERN CX_NODISCARD
+CxIterator cxListBackwardsIteratorAt(const CxList *list, size_t index);
/**
* Returns an iterator pointing to the first item of the list.
* @param list the list
* @return a new iterator
*/
-cx_attr_nodiscard
-CX_EXPORT CxIterator cxListIterator(const CxList *list);
+CX_EXTERN CX_NODISCARD
+CxIterator cxListIterator(const CxList *list);
/**
* Returns a backwards iterator pointing to the last item of the list.
* @param list the list
* @return a new iterator
*/
-cx_attr_nodiscard
-CX_EXPORT CxIterator cxListBackwardsIterator(const CxList *list);
+CX_EXTERN CX_NODISCARD
+CxIterator cxListBackwardsIterator(const CxList *list);
/**
* Returns the index of the first element that equals @p elem.
* @see cxListIndexValid()
* @see cxListContains()
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT size_t cxListFind(const CxList *list, const void *elem);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+size_t cxListFind(const CxList *list, const void *elem);
/**
* Checks if the list contains the specified element.
* @retval false if the element is not contained
* @see cxListFind()
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT bool cxListContains(const CxList* list, const void* elem);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+bool cxListContains(const CxList* list, const void* elem);
/**
* Checks if the specified index is within bounds.
* @retval true if the index is within bounds
* @retval false if the index is out of bounds
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT bool cxListIndexValid(const CxList *list, size_t index);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+bool cxListIndexValid(const CxList *list, size_t index);
/**
* Removes and returns the index of the first element that equals @p elem.
* when the element is not found or could not be removed
* @see cxListIndexValid()
*/
-cx_attr_nonnull
-CX_EXPORT size_t cxListFindRemove(CxList *list, const void *elem);
+CX_EXTERN CX_NONNULL
+size_t cxListFindRemove(CxList *list, const void *elem);
/**
* Sorts the list.
*
* @param list the list
*/
-cx_attr_nonnull
-CX_EXPORT void cxListSort(CxList *list);
+CX_EXTERN CX_NONNULL
+void cxListSort(CxList *list);
/**
* Reverses the order of the items.
*
* @param list the list
*/
-cx_attr_nonnull
-CX_EXPORT void cxListReverse(CxList *list);
+CX_EXTERN CX_NONNULL
+void cxListReverse(CxList *list);
/**
* Compares a list to another list of the same type.
* @retval positive the first list is larger
* or the first non-equal element in the first list is larger
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT int cxListCompare(const CxList *list, const CxList *other);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+int cxListCompare(const CxList *list, const CxList *other);
/**
* Deallocates the memory of the specified list structure.
*
* @param list the list that shall be freed
*/
-CX_EXPORT void cxListFree(CxList *list);
+CX_EXTERN
+void cxListFree(CxList *list);
+
+
+/**
+ * Performs a deep clone of one list into another.
+ *
+ * If the destination list already contains elements, the cloned elements
+ * are appended to that list.
+ *
+ * @attention If the cloned elements need to be destroyed by a destructor
+ * function, you must make sure that the destination list also uses this
+ * destructor function.
+ *
+ * @param dst the destination list
+ * @param src the source list
+ * @param clone_func the clone function for the elements
+ * @param clone_allocator the allocator that is passed to the clone function
+ * @param data optional additional data that is passed to the clone function
+ * @retval zero when all elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ * @see cxListCloneShallow()
+ */
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3)
+int cxListClone(CxList *dst, const CxList *src,
+ cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
+
+/**
+ * Clones elements from a list only if they are not present in another list.
+ *
+ * If the @p minuend does not contain duplicates, this is equivalent to adding
+ * the set difference to the destination list.
+ *
+ * This function is optimized for the case when both the @p minuend and the
+ * @p subtrahend are sorted.
+ *
+ * @param dst the destination list
+ * @param minuend the list to subtract elements from
+ * @param subtrahend the elements that shall be subtracted
+ * @param clone_func the clone function for the elements
+ * @param clone_allocator the allocator that is passed to the clone function
+ * @param data optional additional data that is passed to the clone function
+ * @retval zero when the elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ * @see cxListDifferenceShallow()
+ */
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3, 4)
+int cxListDifference(CxList *dst,
+ const CxList *minuend, const CxList *subtrahend,
+ cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
+
+/**
+ * Clones elements from a list only if they are also present in another list.
+ *
+ * This function is optimized for the case when both the @p src and the
+ * @p other list are sorted.
+ *
+ * If the destination list already contains elements, the intersection is appended
+ * to that list.
+ *
+ * @param dst the destination list
+ * @param src the list to clone the elements from
+ * @param other the list to check the elements for existence
+ * @param clone_func the clone function for the elements
+ * @param clone_allocator the allocator that is passed to the clone function
+ * @param data optional additional data that is passed to the clone function
+ * @retval zero when the elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ * @see cxListIntersectionShallow()
+ */
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3, 4)
+int cxListIntersection(CxList *dst, const CxList *src, const CxList *other,
+ cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
+/**
+ * Performs a deep clone of one list into another, skipping duplicates.
+ *
+ * This function is optimized for the case when both the @p src and the
+ * @p other list are sorted.
+ * In that case, the union will also be sorted.
+ *
+ * If the destination list already contains elements, the union is appended
+ * to that list. In that case the destination is not necessarily sorted.
+ *
+ * @param dst the destination list
+ * @param src the primary source list
+ * @param other the other list, where elements are only cloned from
+ * when they are not in @p src
+ * @param clone_func the clone function for the elements
+ * @param clone_allocator the allocator that is passed to the clone function
+ * @param data optional additional data that is passed to the clone function
+ * @retval zero when the elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ * @see cxListUnionShallow()
+ */
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3, 4)
+int cxListUnion(CxList *dst, const CxList *src, const CxList *other,
+ cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
+
+/**
+ * Performs a shallow clone of one list into another.
+ *
+ * This function uses the default allocator, if needed, and performs
+ * shallow clones with @c memcpy().
+ *
+ * If the destination list already contains elements, the cloned elements
+ * are appended to that list.
+ *
+ * @attention If the cloned elements need to be destroyed by a destructor
+ * function, you must make sure that the destination list also uses this
+ * destructor function.
+ *
+ * @param dst the destination list
+ * @param src the source list
+ * @retval zero when all elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ * @see cxListClone()
+ */
+CX_EXTERN CX_NONNULL
+int cxListCloneShallow(CxList *dst, const CxList *src);
-#ifdef __cplusplus
-} // extern "C"
-#endif
+/**
+ * Clones elements from a list only if they are not present in another list.
+ *
+ * This function uses the default allocator, if needed, and performs
+ * shallow clones with @c memcpy().
+ *
+ * If the @p minuend does not contain duplicates, this is equivalent to adding
+ * the set difference to the destination list.
+ *
+ * This function is optimized for the case when both the @p minuend and the
+ * @p subtrahend are sorted.
+ *
+ * @param dst the destination list
+ * @param minuend the list to subtract elements from
+ * @param subtrahend the elements that shall be subtracted
+ * @retval zero when the elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ * @see cxListDifference()
+ */
+CX_EXTERN CX_NONNULL
+int cxListDifferenceShallow(CxList *dst,
+ const CxList *minuend, const CxList *subtrahend);
+
+/**
+ * Clones elements from a list only if they are also present in another list.
+ *
+ * This function uses the default allocator, if needed, and performs
+ * shallow clones with @c memcpy().
+ *
+ * This function is optimized for the case when both the @p src and the
+ * @p other list are sorted.
+ *
+ * If the destination list already contains elements, the intersection is appended
+ * to that list.
+ *
+ * @param dst the destination list
+ * @param src the list to clone the elements from
+ * @param other the list to check the elements for existence
+ * @retval zero when the elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ * @see cxListIntersection()
+ */
+CX_EXTERN CX_NONNULL
+int cxListIntersectionShallow(CxList *dst, const CxList *src, const CxList *other);
+
+/**
+ * Performs a deep clone of one list into another, skipping duplicates.
+ *
+ * This function uses the default allocator, if needed, and performs
+ * shallow clones with @c memcpy().
+ *
+ * This function is optimized for the case when both the @p src and the
+ * @p other list are sorted.
+ * In that case, the union will also be sorted.
+ *
+ * If the destination list already contains elements, the union is appended
+ * to that list. In that case the destination is not necessarily sorted.
+ *
+ * @param dst the destination list
+ * @param src the primary source list
+ * @param other the other list, where elements are only cloned from
+ * when they are not in @p src
+ * @retval zero when the elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ * @see cxListUnion()
+ */
+CX_EXTERN CX_NONNULL
+int cxListUnionShallow(CxList *dst, const CxList *src, const CxList *other);
+
+/**
+ * Asks the list to reserve enough memory for a given total number of elements.
+ *
+ * List implementations are free to choose if reserving memory upfront makes
+ * sense.
+ * For example, array-based implementations usually do support reserving memory
+ * for additional elements while linked lists usually don't.
+ *
+ * @note When the requested capacity is smaller than the current size,
+ * this function returns zero without performing any action.
+ *
+ * @param list the list
+ * @param capacity the expected total number of elements
+ * @retval zero on success or when overallocation is not supported
+ * @retval non-zero when an allocation error occurred
+ * @see cxListShrink()
+ */
+CX_EXTERN CX_NONNULL
+int cxListReserve(CxList *list, size_t capacity);
+
+/**
+ * Advises the list to free any overallocated memory.
+ *
+ * Lists that do not support overallocation simply return zero.
+ *
+ * This function usually returns zero, except for very special and custom
+ * list and/or allocator implementations where freeing memory can fail.
+ *
+ * @param list the list
+ * @return usually zero
+ */
+CX_EXTERN CX_NONNULL
+int cxListShrink(CxList *list);
#endif // UCX_LIST_H
#include "string.h"
#include "hash_key.h"
-#ifdef __cplusplus
-extern "C" {
+#ifndef UCX_LIST_H
+// forward-declare CxList
+typedef struct cx_list_s CxList;
#endif
/** Type for the UCX map. */
* Add or overwrite an element.
* If the @p value is @c NULL, the implementation
* shall only allocate memory instead of adding an existing value to the map.
- * Returns a pointer to the allocated memory or @c NULL if allocation fails.
+ * Returns a map entry where the pointer to the key is @c NULL if allocation fails.
*/
- void *(*put)(CxMap *map, CxHashKey key, void *value);
+ CxMapEntry (*put)(CxMap *map, CxHashKey key, void *value);
/**
* Returns an element.
*
* @param map the map to be freed
*/
-CX_EXPORT void cxMapFree(CxMap *map);
-
+CX_EXTERN
+void cxMapFree(CxMap *map);
/**
* Clears a map by removing all elements.
*
* @param map the map to be cleared
*/
-cx_attr_nonnull
-CX_EXPORT void cxMapClear(CxMap *map);
+CX_EXTERN CX_NONNULL
+void cxMapClear(CxMap *map);
/**
* Returns the number of elements in this map.
* @param map the map
* @return the number of stored elements
*/
-cx_attr_nonnull
-CX_EXPORT size_t cxMapSize(const CxMap *map);
+CX_EXTERN CX_NONNULL
+size_t cxMapSize(const CxMap *map);
/**
* Creates a value iterator for a map.
* @param map the map to create the iterator for (can be @c NULL)
* @return an iterator for the currently stored values
*/
-cx_attr_nodiscard
-CX_EXPORT CxMapIterator cxMapIteratorValues(const CxMap *map);
+CX_EXTERN CX_NODISCARD
+CxMapIterator cxMapIteratorValues(const CxMap *map);
/**
* Creates a key iterator for a map.
* @param map the map to create the iterator for (can be @c NULL)
* @return an iterator for the currently stored keys
*/
-cx_attr_nodiscard
-CX_EXPORT CxMapIterator cxMapIteratorKeys(const CxMap *map);
+CX_EXTERN CX_NODISCARD
+CxMapIterator cxMapIteratorKeys(const CxMap *map);
/**
* Creates an iterator for a map.
* @see cxMapIteratorKeys()
* @see cxMapIteratorValues()
*/
-cx_attr_nodiscard
-CX_EXPORT CxMapIterator cxMapIterator(const CxMap *map);
+CX_EXTERN CX_NODISCARD
+CxMapIterator cxMapIterator(const CxMap *map);
/**
* Puts a key/value-pair into the map.
* @retval non-zero value on memory allocation failure
* @see cxMapPut()
*/
-cx_attr_nonnull
-CX_EXPORT int cx_map_put(CxMap *map, CxHashKey key, void *value);
+CX_EXTERN CX_NONNULL
+int cx_map_put(CxMap *map, CxHashKey key, void *value);
/**
* Puts a key/value-pair into the map.
* @param map the map
* @param key the key
* @return the pointer to the allocated memory or @c NULL if allocation fails
- * @retval zero success
- * @retval non-zero value on memory allocation failure
* @see cxMapEmplace()
*/
-cx_attr_nonnull
-CX_EXPORT void *cx_map_emplace(CxMap *map, CxHashKey key);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+void *cx_map_emplace(CxMap *map, CxHashKey key);
/**
* Allocates memory for a value in the map associated with the specified key.
* @param map (@c CxMap*) the map
* @param key (any supported key type) the key
* @return the pointer to the allocated memory or @c NULL if allocation fails
- * @retval zero success
- * @retval non-zero value on memory allocation failure
* @see CX_HASH_KEY()
*/
#define cxMapEmplace(map, key) cx_map_emplace(map, CX_HASH_KEY(key))
* @return the value
* @see cxMapGet()
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT void *cx_map_get(const CxMap *map, CxHashKey key);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+void *cx_map_get(const CxMap *map, CxHashKey key);
/**
* Retrieves a value by using a key.
*
* @param map (@c CxMap*) the map
* @param key (any supported key type) the key
- * @return (@c void*) the value
+ * @return (@c void*) the value or @c NULL when no value with that @p key exists
* @see CX_HASH_KEY()
*/
#define cxMapGet(map, key) cx_map_get(map, CX_HASH_KEY(key))
+/**
+ * Checks if a map contains a specific key.
+ *
+ * @param map (@c CxMap*) the map
+ * @param key (any supported key type) the key
+ * @retval true if the key exists in the map
+ * @retval false if the key does not exist in the map
+ * @see CX_HASH_KEY()
+ */
+#define cxMapContains(map, key) (cxMapGet(map, key) != NULL)
+
/**
* Removes a key/value-pair from the map by using the key.
*
* @see cxMapRemove()
* @see cxMapRemoveAndGet()
*/
-cx_attr_nonnull_arg(1)
-CX_EXPORT int cx_map_remove(CxMap *map, CxHashKey key, void *targetbuf);
+CX_EXTERN CX_NONNULL_ARG(1)
+int cx_map_remove(CxMap *map, CxHashKey key, void *targetbuf);
/**
* Removes a key/value-pair from the map by using the key.
*/
#define cxMapRemoveAndGet(map, key, targetbuf) cx_map_remove(map, CX_HASH_KEY(key), targetbuf)
-#ifdef __cplusplus
-} // extern "C"
-#endif
+/**
+ * Performs a deep clone of one map into another.
+ *
+ * If the destination map already contains entries, the cloned entries
+ * are added to that map, possibly overwriting existing elements when
+ * the keys already exist.
+ *
+ * When elements in the destination map need to be replaced, any destructor
+ * function is called on the replaced elements before replacing them.
+ *
+ * @attention If the cloned elements need to be destroyed by a destructor
+ * function, you must make sure that the destination map also uses this
+ * destructor function.
+ *
+ * @param dst the destination map
+ * @param src the source map
+ * @param clone_func the clone function for the values
+ * @param clone_allocator the allocator that is passed to the clone function
+ * @param data optional additional data that is passed to the clone function
+ * @retval zero when all elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ */
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3)
+int cxMapClone(CxMap *dst, const CxMap *src,
+ cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
+
+/**
+ * Clones entries of a map if their key is not present in another map.
+ *
+ * @param dst the destination map
+ * @param minuend the map to subtract the entries from
+ * @param subtrahend the map containing the elements to be subtracted
+ * @param clone_func the clone function for the values
+ * @param clone_allocator the allocator that is passed to the clone function
+ * @param data optional additional data that is passed to the clone function
+ * @retval zero when the elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ */
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3, 4)
+int cxMapDifference(CxMap *dst, const CxMap *minuend, const CxMap *subtrahend,
+ cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
+
+/**
+ * Clones entries of a map if their key is not present in a list.
+ *
+ * Note that the list must contain keys of type @c CxKey
+ * (or pointers to such keys) and must use @c cx_hash_key_cmp
+ * as the compare function.
+ * Generic key types cannot be processed in this case.
+ *
+ * @param dst the destination map
+ * @param src the source map
+ * @param keys the list of @c CxKey items
+ * @param clone_func the clone function for the values
+ * @param clone_allocator the allocator that is passed to the clone function
+ * @param data optional additional data that is passed to the clone function
+ * @retval zero when the elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ */
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3, 4)
+int cxMapListDifference(CxMap *dst, const CxMap *src, const CxList *keys,
+ cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
+
+
+/**
+ * Clones entries of a map only if their key is present in another map.
+ *
+ * @param dst the destination map
+ * @param src the map to clone the entries from
+ * @param other the map to check for existence of the keys
+ * @param clone_func the clone function for the values
+ * @param clone_allocator the allocator that is passed to the clone function
+ * @param data optional additional data that is passed to the clone function
+ * @retval zero when the elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ */
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3, 4)
+int cxMapIntersection(CxMap *dst, const CxMap *src, const CxMap *other,
+ cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
+
+/**
+ * Clones entries of a map only if their key is present in a list.
+ *
+ * Note that the list must contain keys of type @c CxKey
+ * (or pointers to such keys) and must use @c cx_hash_key_cmp
+ * as the compare function.
+ * Generic key types cannot be processed in this case.
+ *
+ * @param dst the destination map
+ * @param src the source map
+ * @param keys the list of @c CxKey items
+ * @param clone_func the clone function for the values
+ * @param clone_allocator the allocator that is passed to the clone function
+ * @param data optional additional data that is passed to the clone function
+ * @retval zero when the elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ */
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3, 4)
+int cxMapListIntersection(CxMap *dst, const CxMap *src, const CxList *keys,
+ cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
+
+/**
+ * Clones entries into a map if their key does not exist yet.
+ *
+ * If you want to calculate the union of two maps into a fresh new map,
+ * you can proceed as follows:
+ * 1. Clone the first map into a fresh, empty map.
+ * 2. Use this function to clone the second map into the result from step 1.
+ *
+ * @param dst the destination map
+ * @param src the map to clone the entries from
+ * @param clone_func the clone function for the values
+ * @param clone_allocator the allocator that is passed to the clone function
+ * @param data optional additional data that is passed to the clone function
+ * @retval zero when the elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ */
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3)
+int cxMapUnion(CxMap *dst, const CxMap *src,
+ cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data);
+
+/**
+ * Performs a shallow clone of one map into another.
+ *
+ * This function uses the default allocator, if needed, and performs
+ * shallow clones with @c memcpy().
+ *
+ * If the destination map already contains entries, the cloned entries
+ * are added to that map, possibly overwriting existing elements when
+ * the keys already exist.
+ *
+ * When elements in the destination map need to be replaced, any destructor
+ * function is called on the replaced elements before replacing them.
+ *
+ * @attention If the cloned elements need to be destroyed by a destructor
+ * function, you must make sure that the destination map also uses this
+ * destructor function.
+ *
+ * @param dst the destination map
+ * @param src the source map
+ * @retval zero when all elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ * @see cxMapClone()
+ */
+CX_EXTERN CX_NONNULL
+int cxMapCloneShallow(CxMap *dst, const CxMap *src);
+
+/**
+ * Clones entries of a map if their key is not present in another map.
+ *
+ * This function uses the default allocator, if needed, and performs
+ * shallow clones with @c memcpy().
+ *
+ * @param dst the destination map
+ * @param minuend the map to subtract the entries from
+ * @param subtrahend the map containing the elements to be subtracted
+ * @retval zero when the elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ */
+CX_EXTERN CX_NONNULL
+int cxMapDifferenceShallow(CxMap *dst, const CxMap *minuend, const CxMap *subtrahend);
+
+/**
+ * Clones entries of a map if their key is not present in a list.
+ *
+ * This function uses the default allocator, if needed, and performs
+ * shallow clones with @c memcpy().
+ *
+ * Note that the list must contain keys of type @c CxKey
+ * (or pointers to such keys) and must use @c cx_hash_key_cmp
+ * as the compare function.
+ * Generic key types cannot be processed in this case.
+ *
+ * @param dst the destination map
+ * @param src the source map
+ * @param keys the list of @c CxKey items
+ * @retval zero when the elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ * @see cxMapListDifference()
+ */
+CX_EXTERN CX_NONNULL
+int cxMapListDifferenceShallow(CxMap *dst, const CxMap *src, const CxList *keys);
+
+/**
+ * Clones entries of a map only if their key is present in another map.
+ *
+ * This function uses the default allocator, if needed, and performs
+ * shallow clones with @c memcpy().
+ *
+ * @param dst the destination map
+ * @param src the map to clone the entries from
+ * @param other the map to check for existence of the keys
+ * @retval zero when the elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ */
+CX_EXTERN CX_NONNULL
+int cxMapIntersectionShallow(CxMap *dst, const CxMap *src, const CxMap *other);
+
+/**
+ * Clones entries of a map only if their key is present in a list.
+ *
+ * This function uses the default allocator, if needed, and performs
+ * shallow clones with @c memcpy().
+ *
+ * Note that the list must contain keys of type @c CxKey
+ * (or pointers to such keys) and must use @c cx_hash_key_cmp
+ * as the compare function.
+ * Generic key types cannot be processed in this case.
+ *
+ * @param dst the destination map
+ * @param src the source map
+ * @param keys the list of @c CxKey items
+ * @retval zero when the elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ */
+CX_EXTERN CX_NONNULL
+int cxMapListIntersectionShallow(CxMap *dst, const CxMap *src, const CxList *keys);
+
+/**
+ * Clones entries into a map if their key does not exist yet.
+ *
+ * This function uses the default allocator, if needed, and performs
+ * shallow clones with @c memcpy().
+ *
+ * If you want to calculate the union of two maps into a fresh new map,
+ * you can proceed as follows:
+ * 1. Clone the first map into a fresh, empty map.
+ * 2. Use this function to clone the second map into the result from step 1.
+ *
+ * @param dst the destination map
+ * @param src the map to clone the entries from
+ * @retval zero when the elements were successfully cloned
+ * @retval non-zero when an allocation error occurred
+ */
+CX_EXTERN CX_NONNULL
+int cxMapUnionShallow(CxMap *dst, const CxMap *src);
+
+/**
+ * Compares the entries of two maps.
+ *
+ * @param map the map
+ * @param other the other map that the first map is compared to
+ * @retval zero when both maps have the same key sets
+ * and the values are pairwise equivalent
+ * @retval negative when the first @p map has fewer keys than the @p other map
+ * @retval positive when the first @p map has more keys than the @p other map
+ * @retval non-zero (unspecified whether positive or negative) when the size
+ * of both maps is equal but a key or a value is different
+ */
+CX_EXTERN CX_NONNULL
+int cxMapCompare(const CxMap *map, const CxMap *other);
#endif // UCX_MAP_H
#include "common.h"
#include "allocator.h"
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/** A memory block in a simple memory pool. */
struct cx_mempool_memory_s {
/** The destructor. */
*
* @param pool the memory pool to free
*/
-CX_EXPORT void cxMempoolFree(CxMempool *pool);
+CX_EXTERN
+void cxMempoolFree(CxMempool *pool);
/**
* Creates an array-based memory pool.
* @param type the type of memory pool
* @return the created memory pool or @c NULL if allocation failed
*/
-cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxMempoolFree, 1)
-CX_EXPORT CxMempool *cxMempoolCreate(size_t capacity, enum cx_mempool_type type);
+CX_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxMempoolFree, 1)
+CxMempool *cxMempoolCreate(size_t capacity, enum cx_mempool_type type);
/**
* Creates a basic array-based memory pool.
* @param pool the memory pool
* @param fnc the destructor that shall be applied to all memory blocks
*/
-cx_attr_nonnull_arg(1)
-CX_EXPORT void cxMempoolGlobalDestructor(CxMempool *pool, cx_destructor_func fnc);
+CX_EXTERN CX_NONNULL_ARG(1)
+void cxMempoolGlobalDestructor(CxMempool *pool, cx_destructor_func fnc);
/**
* Sets the global destructor for all memory blocks within the specified pool.
* @param fnc the destructor that shall be applied to all memory blocks
* @param data additional data for the destructor function
*/
-cx_attr_nonnull_arg(1)
-CX_EXPORT void cxMempoolGlobalDestructor2(CxMempool *pool, cx_destructor_func2 fnc, void *data);
+CX_EXTERN CX_NONNULL_ARG(1)
+void cxMempoolGlobalDestructor2(CxMempool *pool, cx_destructor_func2 fnc, void *data);
/**
* Sets the destructor function for a specific allocated memory object.
* @param memory the object allocated in the pool
* @param fnc the destructor function
*/
-cx_attr_nonnull
-CX_EXPORT void cxMempoolSetDestructor(void *memory, cx_destructor_func fnc);
+CX_EXTERN CX_NONNULL
+void cxMempoolSetDestructor(void *memory, cx_destructor_func fnc);
/**
* Sets the destructor function for a specific allocated memory object.
* @param fnc the destructor function
* @param data additional data for the destructor function
*/
-cx_attr_nonnull
-CX_EXPORT void cxMempoolSetDestructor2(void *memory, cx_destructor_func2 fnc, void *data);
+CX_EXTERN CX_NONNULL
+void cxMempoolSetDestructor2(void *memory, cx_destructor_func2 fnc, void *data);
/**
* Removes the destructor function for a specific allocated memory object.
*
* @param memory the object allocated in the pool
*/
-cx_attr_nonnull
-CX_EXPORT void cxMempoolRemoveDestructor(void *memory);
+CX_EXTERN CX_NONNULL
+void cxMempoolRemoveDestructor(void *memory);
/**
* Removes the destructor function for a specific allocated memory object.
*
* @param memory the object allocated in the pool
*/
-cx_attr_nonnull
-CX_EXPORT void cxMempoolRemoveDestructor2(void *memory);
+CX_EXTERN CX_NONNULL
+void cxMempoolRemoveDestructor2(void *memory);
/**
* Registers foreign memory with this pool.
* @retval zero success
* @retval non-zero failure
*/
-cx_attr_nonnull
-CX_EXPORT int cxMempoolRegister(CxMempool *pool, void *memory, cx_destructor_func destr);
-
+CX_EXTERN CX_NONNULL
+int cxMempoolRegister(CxMempool *pool, void *memory, cx_destructor_func destr);
/**
* Registers foreign memory with this pool.
* @retval zero success
* @retval non-zero failure
*/
-cx_attr_nonnull
-CX_EXPORT int cxMempoolRegister2(CxMempool *pool, void *memory, cx_destructor_func2 destr, void *data);
+CX_EXTERN CX_NONNULL
+int cxMempoolRegister2(CxMempool *pool, void *memory, cx_destructor_func2 destr, void *data);
/**
* Transfers all the memory managed by one pool to another.
* @retval zero success
* @retval non-zero allocation failure or incompatible pools
*/
-cx_attr_nonnull
-CX_EXPORT int cxMempoolTransfer(CxMempool *source, CxMempool *dest);
+CX_EXTERN CX_NONNULL
+int cxMempoolTransfer(CxMempool *source, CxMempool *dest);
/**
* Transfers an object from one pool to another.
* @retval zero success
* @retval non-zero failure, or the object was not found in the source pool, or the pools are incompatible
*/
-cx_attr_nonnull
-CX_EXPORT int cxMempoolTransferObject(CxMempool *source, CxMempool *dest, const void *obj);
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
+CX_EXTERN CX_NONNULL
+int cxMempoolTransferObject(CxMempool *source, CxMempool *dest, const void *obj);
#endif // UCX_MEMPOOL_H
* @param fmt_idx index of the format string parameter
* @param arg_idx index of the first formatting argument
*/
-#define cx_attr_printf(fmt_idx, arg_idx) \
+#define CX_PRINTF_ARGS(fmt_idx, arg_idx) \
__attribute__((__format__(printf, fmt_idx, arg_idx)))
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
/**
* The maximum string length that fits into stack memory.
*/
* @param ... additional arguments
* @return the total number of bytes written or an error code from stdlib printf implementation
*/
-cx_attr_nonnull_arg(1, 2, 3) cx_attr_printf(3, 4) cx_attr_cstr_arg(3)
-CX_EXPORT int cx_fprintf(void *stream, cx_write_func wfc, const char *fmt, ...);
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3) CX_PRINTF_ARGS(3, 4) CX_CSTR_ARG(3)
+int cx_fprintf(void *stream, cx_write_func wfc, const char *fmt, ...);
/**
* A @c vfprintf like function which writes the output to a stream by
* @return the total number of bytes written or an error code from stdlib printf implementation
* @see cx_fprintf()
*/
-cx_attr_nonnull cx_attr_cstr_arg(3)
-CX_EXPORT int cx_vfprintf(void *stream, cx_write_func wfc, const char *fmt, va_list ap);
+CX_EXTERN CX_NONNULL CX_CSTR_ARG(3)
+int cx_vfprintf(void *stream, cx_write_func wfc, const char *fmt, va_list ap);
/**
* An @c asprintf like function which allocates space for a string
* @return the formatted string
* @see cx_strfree_a()
*/
-cx_attr_nonnull_arg(1, 2) cx_attr_printf(2, 3) cx_attr_cstr_arg(2)
-CX_EXPORT cxmutstr cx_asprintf_a(const CxAllocator *allocator, const char *fmt, ...);
+CX_EXTERN CX_NONNULL_ARG(1, 2) CX_PRINTF_ARGS(2, 3) CX_CSTR_ARG(2)
+cxmutstr cx_asprintf_a(const CxAllocator *allocator, const char *fmt, ...);
/**
* An @c asprintf like function which allocates space for a string
* @return the formatted string
* @see cx_asprintf_a()
*/
-cx_attr_nonnull cx_attr_cstr_arg(2)
-CX_EXPORT cxmutstr cx_vasprintf_a(const CxAllocator *allocator, const char *fmt, va_list ap);
+CX_EXTERN CX_NONNULL CX_CSTR_ARG(2)
+cxmutstr cx_vasprintf_a(const CxAllocator *allocator, const char *fmt, va_list ap);
/**
* A @c vasprintf like function which allocates space for a string
* @param ... additional arguments
* @return the length of the produced string or an error code from stdlib printf implementation
*/
-cx_attr_nonnull_arg(1, 2, 3, 4) cx_attr_printf(4, 5) cx_attr_cstr_arg(4)
-CX_EXPORT int cx_sprintf_a(const CxAllocator *alloc, char **str, size_t *len, const char *fmt, ...);
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3, 4) CX_PRINTF_ARGS(4, 5) CX_CSTR_ARG(4)
+int cx_sprintf_a(const CxAllocator *alloc, char **str, size_t *len, const char *fmt, ...);
/**
* @param ap argument list
* @return the length of the produced string or an error code from stdlib printf implementation
*/
-cx_attr_nonnull cx_attr_cstr_arg(4) cx_attr_access_rw(2) cx_attr_access_rw(3)
-CX_EXPORT int cx_vsprintf_a(const CxAllocator *alloc, char **str, size_t *len, const char *fmt, va_list ap);
+CX_EXTERN CX_NONNULL CX_CSTR_ARG(4) CX_ACCESS_RW(2) CX_ACCESS_RW(3)
+int cx_vsprintf_a(const CxAllocator *alloc, char **str, size_t *len, const char *fmt, va_list ap);
/**
* @param ... additional arguments
* @return the length of the produced string or an error code from stdlib printf implementation
*/
-cx_attr_nonnull_arg(1, 2, 4, 5) cx_attr_printf(5, 6) cx_attr_cstr_arg(5)
-cx_attr_access_rw(2) cx_attr_access_rw(3) cx_attr_access_rw(4)
-CX_EXPORT int cx_sprintf_sa(const CxAllocator *alloc, char *buf, size_t *len, char **str, const char *fmt, ...);
+CX_EXTERN CX_NONNULL_ARG(1, 2, 4, 5) CX_PRINTF_ARGS(5, 6) CX_CSTR_ARG(5)
+CX_ACCESS_RW(2) CX_ACCESS_RW(3) CX_ACCESS_RW(4)
+int cx_sprintf_sa(const CxAllocator *alloc, char *buf, size_t *len, char **str, const char *fmt, ...);
/**
* An @c sprintf like function which allocates a new string when the buffer is not large enough.
* @param ap argument list
* @return the length of the produced string or an error code from stdlib printf implementation
*/
-cx_attr_nonnull cx_attr_cstr_arg(5)
-CX_EXPORT int cx_vsprintf_sa(const CxAllocator *alloc, char *buf, size_t *len, char **str, const char *fmt, va_list ap);
-
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
+CX_EXTERN CX_NONNULL CX_CSTR_ARG(5)
+int cx_vsprintf_sa(const CxAllocator *alloc, char *buf, size_t *len, char **str, const char *fmt, va_list ap);
#endif //UCX_PRINTF_H
#include "map.h"
#include "buffer.h"
-#include <stdio.h>
-#include <string.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/**
* Configures the expected characters for the properties parser.
*/
*/
char comment3;
- /*
+ /**
* The character, when appearing at the end of a line, continues that line.
* This is '\' by default.
*/
- /**
- * Reserved for future use.
- */
char continuation;
};
*/
CX_PROPERTIES_BUFFER_ALLOC_FAILED,
/**
- * Initializing the properties source failed.
- *
- * @see cx_properties_read_init_func
+ * A file operation failed.
+ * Only for cxPropertiesLoad().
+ * It is system-specific if errno is set.
*/
- CX_PROPERTIES_READ_INIT_FAILED,
+ CX_PROPERTIES_FILE_ERROR,
/**
- * Reading from a properties source failed.
- *
- * @see cx_properties_read_func
+ * A map operation failed.
+ * Only for cxPropertiesLoad().
*/
- CX_PROPERTIES_READ_FAILED,
- /**
- * Sinking a k/v-pair failed.
- *
- * @see cx_properties_sink_func
- */
- CX_PROPERTIES_SINK_FAILED,
+ CX_PROPERTIES_MAP_ERROR,
};
/**
*/
typedef struct cx_properties_s CxProperties;
-
-/**
- * Typedef for a properties sink.
- */
-typedef struct cx_properties_sink_s CxPropertiesSink;
-
-/**
- * A function that consumes a k/v-pair in a sink.
- *
- * The sink could be a map, and the sink function would be calling
- * a map function to store the k/v-pair.
- *
- * @param prop the properties interface that wants to sink a k/v-pair
- * @param sink the sink
- * @param key the key
- * @param value the value
- * @retval zero success
- * @retval non-zero sinking the k/v-pair failed
- */
-typedef int(*cx_properties_sink_func)(
- CxProperties *prop,
- CxPropertiesSink *sink,
- cxstring key,
- cxstring value
-);
-
-/**
- * Defines a sink for k/v-pairs.
- */
-struct cx_properties_sink_s {
- /**
- * The sink object.
- */
- void *sink;
- /**
- * Optional custom data.
- */
- void *data;
- /**
- * A function for consuming k/v-pairs into the sink.
- */
- cx_properties_sink_func sink_func;
-};
-
-
-/**
- * Typedef for a properties source.
- */
-typedef struct cx_properties_source_s CxPropertiesSource;
-
-/**
- * A function that reads data from a source.
- *
- * When the source is depleted, implementations SHALL provide an empty
- * string in the @p target and return zero.
- * A non-zero return value is only permitted in case of an error.
- *
- * The meaning of the optional parameters is implementation-dependent.
- *
- * @param prop the properties interface that wants to read from the source
- * @param src the source
- * @param target a string buffer where the read data shall be stored
- * @retval zero success
- * @retval non-zero reading the data failed
- */
-typedef int(*cx_properties_read_func)(
- CxProperties *prop,
- CxPropertiesSource *src,
- cxstring *target
-);
-
-/**
- * A function that may initialize additional memory for the source.
- *
- * @param prop the properties interface that wants to read from the source
- * @param src the source
- * @retval zero initialization was successful
- * @retval non-zero otherwise
- */
-typedef int(*cx_properties_read_init_func)(
- CxProperties *prop,
- CxPropertiesSource *src
-);
-
-/**
- * A function that cleans memory initialized by the read_init_func.
- *
- * @param prop the properties interface that wants to read from the source
- * @param src the source
- */
-typedef void(*cx_properties_read_clean_func)(
- CxProperties *prop,
- CxPropertiesSource *src
-);
-
-/**
- * Defines a properties source.
- */
-struct cx_properties_source_s {
- /**
- * The source object.
- *
- * For example, a file stream or a string.
- */
- void *src;
- /**
- * Optional additional data pointer.
- */
- void *data_ptr;
- /**
- * Optional size information.
- */
- size_t data_size;
- /**
- * A function that reads data from the source.
- */
- cx_properties_read_func read_func;
- /**
- * Optional function that may prepare the source for reading data.
- */
- cx_properties_read_init_func read_init_func;
- /**
- * Optional function that cleans additional memory allocated by the
- * read_init_func.
- */
- cx_properties_read_clean_func read_clean_func;
-};
-
/**
* Initialize a properties interface.
*
* @param config the properties configuration
* @see cxPropertiesInitDefault()
*/
-cx_attr_nonnull
-CX_EXPORT void cxPropertiesInit(CxProperties *prop, CxPropertiesConfig config);
+CX_EXTERN CX_NONNULL
+void cxPropertiesInit(CxProperties *prop, CxPropertiesConfig config);
/**
* Destroys the properties interface.
*
* @param prop the properties interface
*/
-cx_attr_nonnull
-CX_EXPORT void cxPropertiesDestroy(CxProperties *prop);
+CX_EXTERN CX_NONNULL
+void cxPropertiesDestroy(CxProperties *prop);
/**
* Destroys and re-initializes the properties interface.
*
* @param prop the properties interface
*/
-cx_attr_nonnull
-CX_EXPORT void cxPropertiesReset(CxProperties *prop);
+CX_EXTERN CX_NONNULL
+void cxPropertiesReset(CxProperties *prop);
/**
* Initialize a properties parser with the default configuration.
* @retval non-zero a memory allocation was necessary but failed
* @see cxPropertiesFill()
*/
-cx_attr_nonnull cx_attr_access_r(2, 3)
-CX_EXPORT int cxPropertiesFilln(CxProperties *prop, const char *buf, size_t len);
+CX_EXTERN CX_NONNULL CX_ACCESS_R(2, 3)
+int cxPropertiesFilln(CxProperties *prop, const char *buf, size_t len);
/**
* Internal function, do not use.
* @retval zero success
* @retval non-zero a memory allocation was necessary but failed
*/
-cx_attr_nonnull
-CX_INLINE int cx_properties_fill(CxProperties *prop, cxstring str) {
+CX_NONNULL CX_INLINE
+int cx_properties_fill(CxProperties *prop, cxstring str) {
return cxPropertiesFilln(prop, str.ptr, str.length);
}
* @param buf a pointer to stack memory
* @param capacity the capacity of the stack memory
*/
-cx_attr_nonnull
-CX_EXPORT void cxPropertiesUseStack(CxProperties *prop, char *buf, size_t capacity);
+CX_EXTERN CX_NONNULL
+void cxPropertiesUseStack(CxProperties *prop, char *buf, size_t capacity);
/**
* Retrieves the next key/value-pair.
* @retval CX_PROPERTIES_INVALID_MISSING_DELIMITER the properties data contains a line without delimiter
* @retval CX_PROPERTIES_BUFFER_ALLOC_FAILED an internal allocation was necessary but failed
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT CxPropertiesStatus cxPropertiesNext(CxProperties *prop, cxstring *key, cxstring *value);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+CxPropertiesStatus cxPropertiesNext(CxProperties *prop, cxstring *key, cxstring *value);
/**
- * Creates a properties sink for an UCX map.
- *
- * The values stored in the map will be pointers to freshly allocated,
- * zero-terminated C strings (@c char*), which means the @p map should have been
- * created with #CX_STORE_POINTERS.
- *
- * The cxDefaultAllocator will be used unless you specify a custom
- * allocator in the optional @c data field of the returned sink.
- *
- * @param map the map that shall consume the k/v-pairs.
- * @return the sink
- * @see cxPropertiesLoad()
+ * The size of the stack memory that `cxPropertiesLoad()` will reserve with `cxPropertiesUseStack()`.
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT CxPropertiesSink cxPropertiesMapSink(CxMap *map);
+CX_EXPORT extern const unsigned cx_properties_load_buf_size;
/**
- * Creates a properties source based on an UCX string.
- *
- * @param str the string
- * @return the properties source
- * @see cxPropertiesLoad()
+ * The size of the stack memory that `cxPropertiesLoad()` will use to read contents from the file.
*/
-cx_attr_nodiscard
-CX_EXPORT CxPropertiesSource cxPropertiesStringSource(cxstring str);
+CX_EXPORT extern const unsigned cx_properties_load_fill_size;
/**
- * Creates a properties source based on C string with the specified length.
+ * Internal function - use cxPropertiesLoad() instead.
*
- * @param str the string
- * @param len the length
- * @return the properties source
- * @see cxPropertiesLoad()
+ * @param allocator the allocator for the values
+ * @param filename the file name
+ * @param target the target map
+ * @param config the parser config
+ * @return status code
*/
-cx_attr_nonnull cx_attr_nodiscard cx_attr_access_r(1, 2)
-CX_EXPORT CxPropertiesSource cxPropertiesCstrnSource(const char *str, size_t len);
+CX_EXTERN CX_NONNULL_ARG(3)
+CxPropertiesStatus cx_properties_load(const CxAllocator *allocator,
+ cxstring filename, CxMap *target, CxPropertiesConfig config);
/**
- * Creates a properties source based on a C string.
+ * Loads properties from a file and inserts them into a map.
*
- * The length will be determined with strlen(), so the string MUST be
- * zero-terminated.
+ * Entries are added to the map, possibly overwriting existing entries.
*
- * @param str the string
- * @return the properties source
- * @see cxPropertiesLoad()
- */
-cx_attr_nonnull cx_attr_nodiscard cx_attr_cstr_arg(1)
-CX_EXPORT CxPropertiesSource cxPropertiesCstrSource(const char *str);
-
-/**
- * Creates a properties source based on an FILE.
+ * The map must either store pointers of type @c char*, or elements of type cxmutstr.
+ * Any other configuration is not supported.
*
- * @param file the file
- * @param chunk_size how many bytes may be read in one operation
+ * @note When the parser finds an error, all successfully parsed keys before the error
+ * are added to the map nonetheless.
*
- * @return the properties source
- * @see cxPropertiesLoad()
+ * @param allocator the allocator for the values that will be stored in the map
+ * @param filename (any string) the absolute or relative path to the file
+ * @param target (@c CxMap*) the map where the properties shall be added
+ * @param config the parser config
+ * @retval CX_PROPERTIES_NO_ERROR (zero) at least one key/value pair was found
+ * @retval CX_PROPERTIES_NO_DATA the file is syntactically OK, but does not contain properties
+ * @retval CX_PROPERTIES_INCOMPLETE_DATA unexpected end of file
+ * @retval CX_PROPERTIES_INVALID_EMPTY_KEY the properties data contains an illegal empty key
+ * @retval CX_PROPERTIES_INVALID_MISSING_DELIMITER the properties data contains a line without delimiter
+ * @retval CX_PROPERTIES_BUFFER_ALLOC_FAILED an internal allocation was necessary but failed
+ * @retval CX_PROPERTIES_FILE_ERROR a file operation failed; depending on the system @c errno might be set
+ * @retval CX_PROPERTIES_MAP_ERROR storing a key/value pair in the map failed
+ * @see cxPropertiesLoadDefault()
*/
-cx_attr_nonnull cx_attr_nodiscard cx_attr_access_r(1)
-CX_EXPORT CxPropertiesSource cxPropertiesFileSource(FILE *file, size_t chunk_size);
-
+#define cxPropertiesLoad(allocator, filename, target, config) \
+ cx_properties_load(allocator, cx_strcast(filename), target, config)
/**
- * Loads properties data from a source and transfers it to a sink.
+ * Loads properties from a file and inserts them into a map with a default config.
*
- * This function tries to read as much data from the source as possible.
- * When the source was completely consumed and at least on k/v-pair was found,
- * the return value will be #CX_PROPERTIES_NO_ERROR.
- * When the source was consumed but no k/v-pairs were found, the return value
- * will be #CX_PROPERTIES_NO_DATA.
- * In case the source data ends unexpectedly, the #CX_PROPERTIES_INCOMPLETE_DATA
- * is returned. In that case you should call this function again with the same
- * sink and either an updated source or the same source if the source is able to
- * yield the missing data.
+ * Entries are added to the map, possibly overwriting existing entries.
*
- * The other result codes apply, according to their description.
+ * The map must either store pointers of type @c char*, or elements of type cxmutstr.
+ * Any other configuration is not supported.
*
- * @param prop the properties interface
- * @param sink the sink
- * @param source the source
- * @retval CX_PROPERTIES_NO_ERROR (zero) a key/value pair was found
- * @retval CX_PROPERTIES_READ_INIT_FAILED initializing the source failed
- * @retval CX_PROPERTIES_READ_FAILED reading from the source failed
- * @retval CX_PROPERTIES_SINK_FAILED sinking the properties into the sink failed
- * @retval CX_PROPERTIES_NO_DATA the source did not provide any key/value pairs
- * @retval CX_PROPERTIES_INCOMPLETE_DATA the source did not provide enough data
+ * @note When the parser finds an error, all successfully parsed keys before the error
+ * are added to the map nonetheless.
+ *
+ * @param allocator the allocator for the values that will be stored in the map
+ * @param filename (any string) the absolute or relative path to the file
+ * @param target (@c CxMap*) the map where the properties shall be added
+ * @retval CX_PROPERTIES_NO_ERROR (zero) at least one key/value pair was found
+ * @retval CX_PROPERTIES_NO_DATA the file is syntactically OK, but does not contain properties
+ * @retval CX_PROPERTIES_INCOMPLETE_DATA unexpected end of file
* @retval CX_PROPERTIES_INVALID_EMPTY_KEY the properties data contains an illegal empty key
* @retval CX_PROPERTIES_INVALID_MISSING_DELIMITER the properties data contains a line without delimiter
* @retval CX_PROPERTIES_BUFFER_ALLOC_FAILED an internal allocation was necessary but failed
+ * @retval CX_PROPERTIES_FILE_ERROR a file operation failed; depending on the system @c errno might be set
+ * @retval CX_PROPERTIES_MAP_ERROR storing a key/value pair in the map failed
+ * @see cxPropertiesLoad()
*/
-cx_attr_nonnull
-CX_EXPORT CxPropertiesStatus cxPropertiesLoad(CxProperties *prop,
- CxPropertiesSink sink, CxPropertiesSource source);
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
+#define cxPropertiesLoadDefault(allocator, filename, target) \
+ cx_properties_load(allocator, cx_strcast(filename), target, cx_properties_config_default)
#endif // UCX_PROPERTIES_H
#include "common.h"
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/**
* Reads data from a stream and writes it to another stream.
*
* iterations.
* @return the total number of bytes copied
*/
-cx_attr_nonnull_arg(1, 2, 3, 4)
-cx_attr_access_r(1) cx_attr_access_w(2) cx_attr_access_w(5)
-CX_EXPORT size_t cx_stream_bncopy(void *src, void *dest,
+CX_EXTERN CX_NONNULL_ARG(1, 2, 3, 4)
+CX_ACCESS_R(1) CX_ACCESS_W(2) CX_ACCESS_W(5)
+size_t cx_stream_bncopy(void *src, void *dest,
cx_read_func rfnc, cx_write_func wfnc,
char *buf, size_t bufsize, size_t n);
* @param n the maximum number of bytes that shall be copied.
* @return total number of bytes copied
*/
-cx_attr_nonnull cx_attr_access_r(1) cx_attr_access_w(2)
-CX_EXPORT size_t cx_stream_ncopy(void *src, void *dest,
+CX_EXTERN CX_NONNULL CX_ACCESS_R(1) CX_ACCESS_W(2)
+size_t cx_stream_ncopy(void *src, void *dest,
cx_read_func rfnc, cx_write_func wfnc, size_t n);
/**
#define cx_stream_copy(src, dest, rfnc, wfnc) \
cx_stream_ncopy(src, dest, rfnc, wfnc, SIZE_MAX)
-#ifdef __cplusplus
-}
-#endif
-
#endif // UCX_STREAMS_H
#include "common.h"
#include "allocator.h"
+#include <string.h>
+
+/** Convenience macro for creating a null string */
+#define CX_NULLSTR cx_mutstr(NULL)
+
/** Expands a UCX string as printf arguments. */
#define CX_SFMT(s) (int) (s).length, (s).ptr
*/
typedef struct cx_strtok_ctx_s CxStrtokCtx;
-#ifdef __cplusplus
-extern "C" {
-
-/**
- * A literal initializer for an UCX string structure.
- *
- * @param literal the string literal
- */
-#define CX_STR(literal) cxstring{literal, sizeof(literal) - 1}
-
-#else // __cplusplus
-
-/**
- * A literal initializer for an UCX string structure.
- *
- * The argument MUST be a string (const char*) @em literal.
- *
- * @param literal the string literal
- */
-#define CX_STR(literal) ((cxstring){literal, sizeof(literal) - 1})
-
-#endif
-
-
/**
* Wraps a mutable string that must be zero-terminated.
*
*
* @see cx_mutstrn()
*/
-cx_attr_nodiscard cx_attr_cstr_arg(1)
-CX_EXPORT cxmutstr cx_mutstr(char *cstring);
+CX_NODISCARD CX_CSTR_ARG(1)
+CX_INLINE cxmutstr cx_mutstr(char *cstring) {
+ cxmutstr str;
+ str.ptr = cstring;
+ str.length = cstring == NULL ? 0 : strlen(cstring);
+ return str;
+}
/**
* Wraps a string that does not need to be zero-terminated.
*
* @see cx_mutstr()
*/
-cx_attr_nodiscard cx_attr_access_rw(1, 2)
-CX_EXPORT cxmutstr cx_mutstrn(char *cstring, size_t length);
+CX_NODISCARD CX_ACCESS_RW(1, 2)
+CX_INLINE cxmutstr cx_mutstrn(char *cstring, size_t length) {
+ cxmutstr str;
+ str.ptr = cstring;
+ str.length = length;
+ return str;
+}
/**
* Wraps a string that must be zero-terminated.
*
* @see cx_strn()
*/
-cx_attr_nodiscard cx_attr_cstr_arg(1)
-CX_EXPORT cxstring cx_str(const char *cstring);
+CX_NODISCARD CX_CSTR_ARG(1)
+CX_INLINE cxstring cx_str(const char *cstring) {
+ cxstring str;
+ str.ptr = cstring;
+ str.length = cstring == NULL ? 0 : strlen(cstring);
+ return str;
+}
/**
*
* @see cx_str()
*/
-cx_attr_nodiscard cx_attr_access_r(1, 2)
-CX_EXPORT cxstring cx_strn(const char *cstring, size_t length);
+CX_NODISCARD CX_ACCESS_R(1, 2)
+CX_INLINE cxstring cx_strn(const char *cstring, size_t length) {
+ cxstring str;
+ str.ptr = cstring;
+ str.length = length;
+ return str;
+}
#ifdef __cplusplus
-} // extern "C"
-cx_attr_nodiscard
-CX_CPPDECL cxstring cx_strcast(cxmutstr str) {
- return cx_strn(str.ptr, str.length);
+CX_NODISCARD
+CX_CPPDECL cxmutstr cx_strcast_m(cxmutstr str) {
+ return str;
}
-cx_attr_nodiscard
-CX_CPPDECL cxstring cx_strcast(cxstring str) {
+CX_NODISCARD
+CX_CPPDECL cxstring cx_strcast_m(cxstring str) {
return str;
}
-cx_attr_nodiscard
-CX_CPPDECL cxstring cx_strcast(const char *str) {
+CX_NODISCARD
+CX_CPPDECL cxmutstr cx_strcast_m(char *str) {
+ return cx_mutstr(str);
+}
+CX_NODISCARD
+CX_CPPDECL cxmutstr cx_strcast_m(unsigned char *str) {
+ return cx_mutstr(reinterpret_cast<char*>(str));
+}
+CX_NODISCARD
+CX_CPPDECL cxstring cx_strcast_m(const char *str) {
return cx_str(str);
}
-cx_attr_nodiscard
-CX_CPPDECL cxstring cx_strcast(const unsigned char *str) {
- return cx_str(static_cast<const char*>(str));
+CX_NODISCARD
+CX_CPPDECL cxstring cx_strcast_m(const unsigned char *str) {
+ return cx_str(reinterpret_cast<const char*>(str));
}
-extern "C" {
+CX_NODISCARD
+CX_CPPDECL cxstring cx_strcast_(cxmutstr str) {
+ return cx_strn(str.ptr, str.length);
+}
+CX_NODISCARD
+CX_CPPDECL cxstring cx_strcast_(cxstring str) {
+ return str;
+}
+#define cx_strcast(s) cx_strcast_(cx_strcast_m(s))
#else
/**
* Internal function, do not use.
* @param str
* @return
+ * @see cx_strcast_m()
* @see cx_strcast()
*/
-cx_attr_nodiscard
-CX_INLINE cxstring cx_strcast_m(cxmutstr str) {
- return (cxstring) {str.ptr, str.length};
+CX_NODISCARD
+CX_INLINE cxmutstr cx_strcast_cxms(cxmutstr str) {
+ return str;
}
/**
* Internal function, do not use.
* @param str
* @return
+ * @see cx_strcast_m()
* @see cx_strcast()
*/
-cx_attr_nodiscard
-CX_INLINE cxstring cx_strcast_c(cxstring str) {
+CX_NODISCARD
+CX_INLINE cxstring cx_strcast_cxs(cxstring str) {
return str;
}
* Internal function, do not use.
* @param str
* @return
+ * @see cx_strcast_m()
+ * @see cx_strcast()
+ */
+CX_NODISCARD
+CX_INLINE cxmutstr cx_strcast_uc(unsigned char *str) {
+ return cx_mutstr((char*)str);
+}
+
+/**
+ * Internal function, do not use.
+ * @param str
+ * @return
+ * @see cx_strcast_m()
+ * @see cx_strcast()
+ */
+CX_NODISCARD
+CX_INLINE cxmutstr cx_strcast_c(char *str) {
+ return cx_mutstr(str);
+}
+
+/**
+ * Internal function, do not use.
+ * @param str
+ * @return
+ * @see cx_strcast_m()
* @see cx_strcast()
*/
-cx_attr_nodiscard
-CX_INLINE cxstring cx_strcast_u(const unsigned char *str) {
+CX_NODISCARD
+CX_INLINE cxstring cx_strcast_ucc(const unsigned char *str) {
return cx_str((const char*)str);
}
* Internal function, do not use.
* @param str
* @return
+ * @see cx_strcast_m()
* @see cx_strcast()
*/
-cx_attr_nodiscard
-CX_INLINE cxstring cx_strcast_z(const char *str) {
+CX_NODISCARD
+CX_INLINE cxstring cx_strcast_cc(const char *str) {
return cx_str(str);
}
* Wraps any string into an UCX string.
*
* @param str (any supported string type) the string to cast
- * @return (@c cxstring) the string wrapped as UCX string
- */
-#define cx_strcast(str) _Generic((str), \
- cxmutstr: cx_strcast_m, \
- cxstring: cx_strcast_c, \
- const unsigned char*: cx_strcast_u, \
- unsigned char *: cx_strcast_u, \
- const char*: cx_strcast_z, \
- char *: cx_strcast_z) (str)
+ * @return (@c cxstring) or (@c cxmutstr) the string wrapped as UCX string
+ */
+#define cx_strcast_m(str) _Generic((str), \
+ cxstring: cx_strcast_cxs, \
+ cxmutstr: cx_strcast_cxms, \
+ const unsigned char*: cx_strcast_ucc, \
+ unsigned char *: cx_strcast_uc, \
+ const char*: cx_strcast_cc, \
+ char *: cx_strcast_c) (str)
+
+/**
+ * Internal function, do not use.
+ * @param str
+ * @return
+ */
+CX_INLINE cxstring cx_strcast_1(cxmutstr str) {
+ return (cxstring){str.ptr, str.length};
+}
+
+/**
+ * Internal function, do not use.
+ * @param str
+ * @return
+ */
+CX_INLINE cxstring cx_strcast_2(cxstring str) {
+ return str;
+}
+
+/** internal conversion macro */
+#define cx_strcast_(str) _Generic((str), \
+ cxmutstr: cx_strcast_1, \
+ cxstring: cx_strcast_2)(str)
+
+/**
+ * Converts any string to a cxstring.
+ *
+ * @param str (any supported string type) the string to cast
+ * @return he string converted to a (@c cxstring)
+ */
+#define cx_strcast(str) cx_strcast_(cx_strcast_m(str))
#endif
+/**
+ * Casts away constness and converts a cxstring to a cxmutstr.
+ * For internal use only!
+ * @param str
+ * @return
+ */
+CX_INLINE cxmutstr cx_mutstrcast(cxstring str) {
+ cxmutstr s;
+ s.ptr = (char*)str.ptr;
+ s.length = str.length;
+ return s;
+}
+
/**
* Passes the pointer in this string to the cxDefaultAllocator's @c free() function.
*
*
* @param str the string to free
*/
-CX_EXPORT void cx_strfree(cxmutstr *str);
+CX_EXTERN
+void cx_strfree(cxmutstr *str);
/**
* Passes the pointer in this string to the allocator's free function.
* @param alloc the allocator
* @param str the string to free
*/
-cx_attr_nonnull_arg(1)
-CX_EXPORT void cx_strfree_a(const CxAllocator *alloc, cxmutstr *str);
+CX_EXTERN CX_NONNULL_ARG(1)
+void cx_strfree_a(const CxAllocator *alloc, cxmutstr *str);
/**
* Copies a string.
*
- * The memory in the @p dest structure is either allocated or re-allocated to fit the entire
- * source string, including a zero-terminator.
- *
- * The string in @p dest is guaranteed to be zero-terminated, regardless of whether @p src is.
+ * Internal function - do not use.
*
* @param alloc the allocator
* @param dest a pointer to the structure where to copy the contents to
*
* @retval zero success
* @retval non-zero if re-allocation failed
+ * @see cx_strcpy_a()
*/
-cx_attr_nonnull_arg(1)
-CX_EXPORT int cx_strcpy_a(const CxAllocator *alloc, cxmutstr *dest, cxstring src);
-
+CX_EXTERN CX_NONNULL_ARG(1)
+int cx_strcpy_a_(const CxAllocator *alloc, cxmutstr *dest, cxstring src);
/**
* Copies a string.
*
* The string in @p dest is guaranteed to be zero-terminated, regardless of whether @p src is.
*
+ * @param alloc (@c CxAllocator*) the allocator
* @param dest (@c cxmutstr*) a pointer to the structure where to copy the contents to
- * @param src (@c cxstring) the source string
+ * @param src the source string
+ * @retval zero success
+ * @retval non-zero if re-allocation failed
+ */
+#define cx_strcpy_a(alloc, dest, src) cx_strcpy_a_(alloc, dest, cx_strcast(src))
+
+/**
+ * Copies a string.
*
+ * The memory in the @p dest structure is either allocated or re-allocated to fit the entire
+ * source string, including a zero-terminator.
+ *
+ * The string in @p dest is guaranteed to be zero-terminated, regardless of whether @p src is.
+ *
+ * @param dest (@c cxmutstr*) a pointer to the structure where to copy the contents to
+ * @param src the source string
* @retval zero success
* @retval non-zero if re-allocation failed
*/
* @param ... all strings
* @return the accumulated length of all strings
*/
-cx_attr_nodiscard
-CX_EXPORT size_t cx_strlen(size_t count, ...);
+CX_EXTERN CX_NODISCARD
+size_t cx_strlen(size_t count, ...);
/**
* Concatenates strings.
* If @p str already contains a string, the memory will be reallocated and
* the other strings are appended. Otherwise, new memory is allocated.
*
- * If memory allocation fails, the pointer in the returned string will
- * be @c NULL. Depending on the allocator, @c errno might be set.
- *
* @note It is guaranteed that there is only one allocation for the
* resulting string.
* It is also guaranteed that the returned string is zero-terminated.
+ * If allocation fails, the @c ptr in the returned string will be @c NULL.
*
* @param alloc the allocator to use
* @param str the string the other strings shall be concatenated to
* @param ... all other UCX strings
* @return the concatenated string
*/
-cx_attr_nodiscard cx_attr_nonnull
-CX_EXPORT cxmutstr cx_strcat_ma(const CxAllocator *alloc,
+CX_EXTERN CX_NONNULL
+cxmutstr cx_strcat_a(const CxAllocator *alloc,
cxmutstr str, size_t count, ...);
-/**
- * Concatenates strings and returns a new string.
- *
- * The resulting string will be allocated by the specified allocator.
- * So developers @em must pass the return value to cx_strfree_a() eventually.
- *
-* If memory allocation fails, the pointer in the returned string will
- * be @c NULL. Depending on the allocator, @c errno might be set.
- *
- * @note It is guaranteed that there is only one allocation for the
- * resulting string.
- * It is also guaranteed that the returned string is zero-terminated.
- *
- * @param alloc (@c CxAllocator*) the allocator to use
- * @param count (@c size_t) the number of the other following strings to concatenate
- * @param ... all other UCX strings
- * @return (@c cxmutstr) the concatenated string
- */
-#define cx_strcat_a(alloc, count, ...) \
- cx_strcat_ma(alloc, cx_mutstrn(NULL, 0), count, __VA_ARGS__)
-
/**
* Concatenates strings and returns a new string.
*
* The resulting string will be allocated by the cxDefaultAllocator.
* So developers @em must pass the return value to cx_strfree() eventually.
*
-* If memory allocation fails, the pointer in the returned string will
- * be @c NULL and @c errno might be set.
- *
* @note It is guaranteed that there is only one allocation for the
* resulting string.
* It is also guaranteed that the returned string is zero-terminated.
+ * If allocation fails, the @c ptr in the returned string will be @c NULL.
*
+ * @param str (@c cxmutstr*) the string the other strings shall be concatenated to
* @param count (@c size_t) the number of the other following strings to concatenate
* @param ... all other UCX strings
- * @return (@c cxmutstr) the concatenated string
- */
-#define cx_strcat(count, ...) \
- cx_strcat_ma(cxDefaultAllocator, cx_mutstrn(NULL, 0), count, __VA_ARGS__)
-
-/**
- * Concatenates strings.
- *
- * The resulting string will be allocated by the cxDefaultAllocator.
- * So developers @em must pass the return value to cx_strfree() eventually.
- *
- * If @p str already contains a string, the memory will be reallocated and
- * the other strings are appended. Otherwise, new memory is allocated.
- *
-* If memory allocation fails, the pointer in the returned string will
- * be @c NULL and @c errno might be set.
- *
- * @note It is guaranteed that there is only one allocation for the
- * resulting string.
- * It is also guaranteed that the returned string is zero-terminated.
- *
- * @param str (@c cxmutstr) the string the other strings shall be concatenated to
- * @param count (@c size_t) the number of the other following strings to concatenate
- * @param ... all other strings
- * @return (@c cxmutstr) the concatenated string
+ * @return the concatenated string
*/
-#define cx_strcat_m(str, count, ...) \
- cx_strcat_ma(cxDefaultAllocator, str, count, __VA_ARGS__)
+#define cx_strcat(str, count, ...) \
+ cx_strcat_a(cxDefaultAllocator, str, count, __VA_ARGS__)
/**
- * Returns a substring starting at the specified location.
+ * Returns a substring.
*
- * @attention the new string references the same memory area as the
- * input string and is usually @em not zero-terminated.
- * Use cx_strdup() to get a copy.
+ * Internal function - do not use.
*
* @param string input string
* @param start start location of the substring
+ * @param length the maximum length of the returned string
* @return a substring of @p string starting at @p start
- *
* @see cx_strsubsl()
- * @see cx_strsubs_m()
- * @see cx_strsubsl_m()
*/
-cx_attr_nodiscard
-CX_EXPORT cxstring cx_strsubs(cxstring string, size_t start);
+CX_EXTERN CX_NODISCARD
+cxstring cx_strsubsl_(cxstring string, size_t start, size_t length);
/**
- * Returns a substring starting at the specified location.
- *
- * The returned string will be limited to @p length bytes or the number
- * of bytes available in @p string, whichever is smaller.
+ * Returns a substring.
*
- * @attention the new string references the same memory area as the
- * input string and is usually @em not zero-terminated.
- * Use cx_strdup() to get a copy.
+ * Internal function - do not use.
*
* @param string input string
* @param start start location of the substring
- * @param length the maximum length of the returned string
* @return a substring of @p string starting at @p start
- *
* @see cx_strsubs()
- * @see cx_strsubs_m()
- * @see cx_strsubsl_m()
*/
-cx_attr_nodiscard
-CX_EXPORT cxstring cx_strsubsl(cxstring string, size_t start, size_t length);
+CX_EXTERN CX_NODISCARD
+cxstring cx_strsubs_(cxstring string, size_t start);
+
+/**
+ * Internal conversion function - do not use.
+ * @param string
+ * @param start
+ * @return
+ */
+CX_INLINE
+cxmutstr cx_strsubs_m_(cxmutstr string, size_t start) {
+ return cx_mutstrcast(cx_strsubs_(cx_strcast(string), start));
+}
+
+/**
+ * Internal conversion function - do not use.
+ * @param string
+ * @param start
+ * @param length
+ * @return
+ */
+CX_INLINE
+cxmutstr cx_strsubsl_m_(cxmutstr string, size_t start, size_t length) {
+ return cx_mutstrcast(cx_strsubsl_(cx_strcast(string), start, length));
+}
+#ifdef __cplusplus
+CX_CPPDECL cxstring cx_strsubs_cpp_(cxstring string, size_t start) {
+ return cx_strsubs_(string, start);
+}
+CX_CPPDECL cxstring cx_strsubsl_cpp_(cxstring string, size_t start, size_t length) {
+ return cx_strsubsl_(string, start, length);
+}
+CX_CPPDECL cxmutstr cx_strsubs_cpp_(cxmutstr string, size_t start) {
+ return cx_strsubs_m_(string, start);
+}
+CX_CPPDECL cxmutstr cx_strsubsl_cpp_(cxmutstr string, size_t start, size_t length) {
+ return cx_strsubsl_m_(string, start, length);
+}
+#define cx_strsubs(string, start) cx_strsubs_cpp_(cx_strcast_m(string), start)
+#define cx_strsubsl(string, start, length) cx_strsubsl_cpp_(cx_strcast_m(string), start, length)
+#else
/**
* Returns a substring starting at the specified location.
*
* @attention the new string references the same memory area as the
- * input string and is usually @em not zero-terminated.
+ * input string and is @em not zero-terminated.
* Use cx_strdup() to get a copy.
*
* @param string input string
- * @param start start location of the substring
- * @return a substring of @p string starting at @p start
+ * @param start (@c size_t) start location of the substring
+ * @return (@c cxstring or @c cxmutstr) a substring of @p string starting at @p start
*
- * @see cx_strsubsl_m()
- * @see cx_strsubs()
* @see cx_strsubsl()
*/
-cx_attr_nodiscard
-CX_EXPORT cxmutstr cx_strsubs_m(cxmutstr string, size_t start);
+#define cx_strsubs(string, start) _Generic(cx_strcast_m(string), \
+ cxstring: cx_strsubs_, \
+ cxmutstr: cx_strsubs_m_)(cx_strcast_m(string), start)
/**
* Returns a substring starting at the specified location.
* @param length the maximum length of the returned string
* @return a substring of @p string starting at @p start
*
- * @see cx_strsubs_m()
* @see cx_strsubs()
- * @see cx_strsubsl()
*/
-cx_attr_nodiscard
-CX_EXPORT cxmutstr cx_strsubsl_m(cxmutstr string, size_t start, size_t length);
+#define cx_strsubsl(string, start, length) _Generic(cx_strcast_m(string), \
+ cxstring: cx_strsubsl_, \
+ cxmutstr: cx_strsubsl_m_)(cx_strcast_m(string), start, length)
+#endif
/**
- * Returns a substring starting at the location of the first occurrence of the
- * specified character.
+ * Returns the character at the specified index offset.
*
- * If the string does not contain the character, an empty string is returned.
+ * Internal function - do not use.
*
- * @param string the string where to locate the character
- * @param chr the character to locate
- * @return a substring starting at the first location of @p chr
- *
- * @see cx_strchr_m()
+ * @param str the string
+ * @param index the index offset
+ * @return the character at the index
+ * @see cx_strat()
*/
-cx_attr_nodiscard
-CX_EXPORT cxstring cx_strchr(cxstring string, int chr);
+CX_INLINE
+char cx_strat_(cxstring str, off_t index) {
+ size_t i;
+ if (index >= 0) {
+ i = index;
+ } else {
+ i = (size_t) (str.length + index);
+ }
+ if (i >= str.length) {
+ return '\0';
+ }
+ return str.ptr[i];
+}
/**
- * Returns a substring starting at the location of the first occurrence of the
- * specified character.
+ * Returns the character at the specified index offset.
*
- * If the string does not contain the character, an empty string is returned.
+ * When the @p index is negative, the character is counted from the end of the
+ * string where -1 denotes the last character in the string.
*
- * @param string the string where to locate the character
- * @param chr the character to locate
- * @return a substring starting at the first location of @p chr
+ * When the @p index is out of bounds, the function returns zero.
*
+ * @param str the string
+ * @param index the index offset
+ * @return the character at the index
+ * @see cx_strat()
+ */
+#define cx_strat(str, index) cx_strat_(cx_strcast(str), index)
+
+/**
+ * Searches for a character in a string.
+ * Internal function - do not use.
+ * @param string
+ * @param chr
+ * @return
* @see cx_strchr()
*/
-cx_attr_nodiscard
-CX_EXPORT cxmutstr cx_strchr_m(cxmutstr string, int chr);
+CX_EXTERN CX_NODISCARD
+cxstring cx_strchr_(cxstring string, int chr);
/**
- * Returns a substring starting at the location of the last occurrence of the
+ * Searches for a character in a string.
+ * Internal function - do not use.
+ * @param string
+ * @param chr
+ * @return
+ * @see cx_strrchr()
+ */
+CX_EXTERN CX_NODISCARD
+cxstring cx_strrchr_(cxstring string, int chr);
+
+#ifdef __cplusplus
+CX_CPPDECL cxstring cx_strchr_cpp_(cxstring string, int chr) {
+ return cx_strchr_(string, chr);
+}
+CX_CPPDECL cxmutstr cx_strchr_cpp_(cxmutstr string, int chr) {
+ return cx_mutstrcast(cx_strchr_(cx_strcast(string), chr));
+}
+#define cx_strchr(s, chr) cx_strchr_cpp_(cx_strcast_m(s), chr)
+CX_CPPDECL cxstring cx_strrchr_cpp_(cxstring string, int chr) {
+ return cx_strrchr_(string, chr);
+}
+CX_CPPDECL cxmutstr cx_strrchr_cpp_(cxmutstr string, int chr) {
+ return cx_mutstrcast(cx_strrchr_(cx_strcast(string), chr));
+}
+#define cx_strrchr(s, chr) cx_strrchr_cpp_(cx_strcast_m(s), chr)
+#else
+/**
+ * Internal conversion function - do not use.
+ * @param string
+ * @param chr
+ * @return
+ */
+CX_INLINE cxmutstr cx_strchr_m_(cxmutstr string, int chr) {
+ return cx_mutstrcast(cx_strchr_(cx_strcast(string), chr));
+}
+/**
+ * Internal conversion function - do not use.
+ * @param string
+ * @param chr
+ * @return
+ */
+CX_INLINE cxmutstr cx_strrchr_m_(cxmutstr string, int chr) {
+ return cx_mutstrcast(cx_strrchr_(cx_strcast(string), chr));
+}
+
+/**
+ * Returns a substring starting at the location of the first occurrence of the
* specified character.
*
* If the string does not contain the character, an empty string is returned.
*
* @param string the string where to locate the character
- * @param chr the character to locate
- * @return a substring starting at the last location of @p chr
- *
- * @see cx_strrchr_m()
+ * @param chr (@c int) the character to locate
+ * @return (@c cxstring or @c cxmutstr) a substring starting at the first
+ * location of @p chr
*/
-cx_attr_nodiscard
-CX_EXPORT cxstring cx_strrchr(cxstring string, int chr);
+#define cx_strchr(string, chr) _Generic(cx_strcast_m(string), \
+ cxstring: cx_strchr_, \
+ cxmutstr: cx_strchr_m_)(cx_strcast_m(string), chr)
/**
* Returns a substring starting at the location of the last occurrence of the
* If the string does not contain the character, an empty string is returned.
*
* @param string the string where to locate the character
- * @param chr the character to locate
- * @return a substring starting at the last location of @p chr
- *
- * @see cx_strrchr()
+ * @param chr (@c int) the character to locate
+ * @return (@c cxstring or @c cxmutstr) a substring starting at the last
+ * location of @p chr
*/
-cx_attr_nodiscard
-CX_EXPORT cxmutstr cx_strrchr_m(cxmutstr string, int chr);
+#define cx_strrchr(string, chr) _Generic(cx_strcast_m(string), \
+ cxstring: cx_strrchr_, \
+ cxmutstr: cx_strrchr_m_)(cx_strcast_m(string), chr)
+#endif
/**
- * Returns a substring starting at the location of the first occurrence of the
- * specified string.
+ * Searches for a specific substring.
*
- * If @p haystack does not contain @p needle, an empty string is returned.
- *
- * If @p needle is an empty string, the complete @p haystack is
- * returned.
+ * Internal function - do not use.
*
* @param haystack the string to be scanned
- * @param needle string containing the sequence of characters to match
- * @return a substring starting at the first occurrence of
- * @p needle, or an empty string, if the sequence is not
- * contained
- * @see cx_strstr_m()
+ * @param needle string containing the sequence of characters to match
+ * @return a substring starting at the first occurrence of @p needle,
+ * or an empty string, if the sequence is not contained
+ * @see cx_strstr()
*/
-cx_attr_nodiscard
-CX_EXPORT cxstring cx_strstr(cxstring haystack, cxstring needle);
+CX_EXTERN CX_NODISCARD
+cxstring cx_strstr_(cxstring haystack, cxstring needle);
+
+#ifdef __cplusplus
+CX_CPPDECL cxstring cx_strstr_cpp_(cxstring haystack, cxstring needle) {
+ return cx_strstr_(haystack, needle);
+}
+CX_CPPDECL cxmutstr cx_strstr_cpp_(cxmutstr haystack, cxstring needle) {
+ return cx_mutstrcast(cx_strstr_(cx_strcast(haystack), needle));
+}
+#define cx_strstr(h,n) cx_strstr_cpp_(cx_strcast_m(h), cx_strcast(n))
+#else
+/**
+ * Internal conversion - do not use.
+ * @param haystack
+ * @param needle
+ * @return
+ */
+CX_INLINE cxmutstr cx_strstr_m_(cxmutstr haystack, cxstring needle) {
+ return cx_mutstrcast(cx_strstr_(cx_strcast(haystack), needle));
+}
/**
* Returns a substring starting at the location of the first occurrence of the
* returned.
*
* @param haystack the string to be scanned
- * @param needle string containing the sequence of characters to match
- * @return a substring starting at the first occurrence of
- * @p needle, or an empty string, if the sequence is not
- * contained
- * @see cx_strstr()
+ * @param needle string containing the sequence of characters to match
+ * @return (@c cxstring or @c cxmutstr) a substring starting at the first
+ * occurrence of @p needle, or an empty string, if the sequence is not contained
*/
-cx_attr_nodiscard
-CX_EXPORT cxmutstr cx_strstr_m(cxmutstr haystack, cxstring needle);
+#define cx_strstr(haystack, needle) _Generic(cx_strcast_m(haystack), \
+ cxstring: cx_strstr_,\
+ cxmutstr: cx_strstr_m_)(cx_strcast_m(haystack), cx_strcast(needle))
+#endif
/**
* Splits a given string using a delimiter string.
*
- * @note The resulting array contains strings that point to the source
- * @p string. Use cx_strdup() to get copies.
+ * Internal function - do not use.
*
* @param string the string to split
* @param delim the delimiter
* @param limit the maximum number of split items
- * @param output a preallocated array of at least @p limit length
+ * @param output the output array
* @return the actual number of split items
+ * @see cx_strsplit()
*/
-cx_attr_nodiscard cx_attr_nonnull cx_attr_access_w(4, 3)
-CX_EXPORT size_t cx_strsplit(cxstring string, cxstring delim,
+CX_EXTERN CX_NODISCARD CX_NONNULL CX_ACCESS_W(4, 3)
+size_t cx_strsplit_(cxstring string, cxstring delim,
size_t limit, cxstring *output);
/**
* Splits a given string using a delimiter string.
*
- * The array pointed to by @p output will be allocated by @p allocator.
- *
- * @note The resulting array contains strings that point to the source
- * @p string. Use cx_strdup() to get copies.
- *
- * @attention If allocation fails, the @c NULL pointer will be written to
- * @p output and the number returned will be zero.
+ * Internal function - do not use.
*
* @param allocator the allocator to use for allocating the resulting array
* @param string the string to split
* @param delim the delimiter
* @param limit the maximum number of split items
- * @param output a pointer where the address of the allocated array shall be
- * written to
+ * @param output the output array
* @return the actual number of split items
+ * @see cx_strsplit_a()
*/
-cx_attr_nodiscard cx_attr_nonnull cx_attr_access_w(5)
-CX_EXPORT size_t cx_strsplit_a(const CxAllocator *allocator,
+CX_EXTERN CX_NODISCARD CX_NONNULL CX_ACCESS_W(5)
+size_t cx_strsplit_a_(const CxAllocator *allocator,
cxstring string, cxstring delim,
size_t limit, cxstring **output);
/**
* Splits a given string using a delimiter string.
*
- * @note The resulting array contains strings that point to the source
- * @p string. Use cx_strdup() to get copies.
+ * Internal function - do not use.
*
* @param string the string to split
* @param delim the delimiter
* @param limit the maximum number of split items
- * @param output a preallocated array of at least @p limit length
+ * @param output the output array
* @return the actual number of split items
+ * @see cx_strsplit_m()
*/
-cx_attr_nodiscard cx_attr_nonnull cx_attr_access_w(4, 3)
-CX_EXPORT size_t cx_strsplit_m(cxmutstr string, cxstring delim,
+CX_EXTERN CX_NODISCARD CX_NONNULL CX_ACCESS_W(4, 3)
+size_t cx_strsplit_m_(cxmutstr string, cxstring delim,
size_t limit, cxmutstr *output);
+/**
+ * Splits a given string using a delimiter string.
+ *
+ * Internal function - do not use.
+ *
+ * @param allocator the allocator to use for allocating the resulting array
+ * @param string the string to split
+ * @param delim the delimiter
+ * @param limit the maximum number of split items
+ * @param output the output array
+ * @return the actual number of split items
+ * @see cx_strsplit_ma()
+ */
+CX_EXTERN CX_NODISCARD CX_NONNULL CX_ACCESS_W(5)
+size_t cx_strsplit_ma_(const CxAllocator *allocator,
+ cxmutstr string, cxstring delim, size_t limit,
+ cxmutstr **output);
+
+#ifdef __cplusplus
+CX_CPPDECL size_t cx_strsplit_cpp_(cxstring string, cxstring delim,
+ size_t limit, cxstring *output) {
+ return cx_strsplit_(string, delim, limit, output);
+}
+CX_CPPDECL size_t cx_strsplit_cpp_(cxmutstr string, cxstring delim,
+ size_t limit, cxmutstr *output) {
+ return cx_strsplit_m_(string, delim, limit, output);
+}
+CX_CPPDECL size_t cx_strsplit_a_cpp_(const CxAllocator *allocator,
+ cxstring string, cxstring delim, size_t limit, cxstring **output) {
+ return cx_strsplit_a_(allocator, string, delim, limit, output);
+}
+CX_CPPDECL size_t cx_strsplit_a_cpp_(const CxAllocator *allocator,
+ cxmutstr string, cxstring delim, size_t limit, cxmutstr **output) {
+ return cx_strsplit_ma_(allocator, string, delim, limit, output);
+}
+#define cx_strsplit(string, delim, limit, output) \
+ cx_strsplit_cpp_(cx_strcast_m(string), cx_strcast(delim), limit, output)
+#define cx_strsplit_a(allocator, string, delim, limit, output) \
+ cx_strsplit_a_cpp_(allocator, cx_strcast_m(string), cx_strcast(delim), limit, output)
+#else
+/**
+ * Splits a given string using a delimiter string.
+ *
+ * @note The resulting array contains strings that point to the source
+ * @p string. Use cx_strdup() to get copies.
+ *
+ * @param string the string to split
+ * @param delim the delimiter
+ * @param limit (@c size_t) the maximum number of split items
+ * @param output (@c cxstring* or @c cxmutstr*) a preallocated array of at
+ * least @p limit length
+ * @return the actual number of split items
+ */
+#define cx_strsplit(string, delim, limit, output) \
+ _Generic(cx_strcast_m(string), \
+ cxstring: cx_strsplit_, \
+ cxmutstr: cx_strsplit_m_)\
+ (cx_strcast_m(string), cx_strcast(delim), limit, output)
+
/**
* Splits a given string using a delimiter string.
*
* @attention If allocation fails, the @c NULL pointer will be written to
* @p output and the number returned will be zero.
*
- * @param allocator the allocator to use for allocating the resulting array
+ * @param allocator (@c CxAllocator*) the allocator to use for allocating the resulting array
* @param string the string to split
* @param delim the delimiter
- * @param limit the maximum number of split items
- * @param output a pointer where the address of the allocated array shall be
- * written to
+ * @param limit (@c size_t) the maximum number of split items
+ * @param output (@c cxstring** or @c cxmutstr**) a pointer where the address
+ * of the allocated array shall be written to
* @return the actual number of split items
*/
-cx_attr_nodiscard cx_attr_nonnull cx_attr_access_w(5)
-CX_EXPORT size_t cx_strsplit_ma(const CxAllocator *allocator,
- cxmutstr string, cxstring delim, size_t limit,
- cxmutstr **output);
+#define cx_strsplit_a(allocator, string, delim, limit, output) \
+ _Generic(cx_strcast_m(string), \
+ cxstring: cx_strsplit_a_, \
+ cxmutstr: cx_strsplit_ma_)\
+ (allocator, cx_strcast_m(string), cx_strcast(delim), limit, output)
+#endif
/**
* Compares two strings.
* @return negative if @p s1 is smaller than @p s2, positive if @p s1 is larger
* than @p s2, zero if both strings equal
*/
-cx_attr_nodiscard
-CX_EXPORT int cx_strcmp_(cxstring s1, cxstring s2);
+CX_EXTERN CX_NODISCARD
+int cx_strcmp_(cxstring s1, cxstring s2);
/**
* Compares two strings.
* @return negative if @p s1 is smaller than @p s2, positive if @p s1 is larger
* than @p s2, zero if both strings equal ignoring case
*/
-cx_attr_nodiscard
-CX_EXPORT int cx_strcasecmp_(cxstring s1, cxstring s2);
+CX_EXTERN CX_NODISCARD
+int cx_strcasecmp_(cxstring s1, cxstring s2);
/**
* Compares two strings ignoring case.
* @return negative if @p s1 is smaller than @p s2, positive if @p s1 is larger
* than @p s2, zero if both strings equal
*/
-cx_attr_nodiscard cx_attr_nonnull
-CX_EXPORT int cx_strcmp_p(const void *s1, const void *s2);
+CX_EXTERN CX_NODISCARD CX_NONNULL
+int cx_strcmp_p(const void *s1, const void *s2);
/**
* Compares two strings ignoring case.
* @return negative if @p s1 is smaller than @p s2, positive if @p s1 is larger
* than @p s2, zero if both strings equal ignoring case
*/
-cx_attr_nodiscard cx_attr_nonnull
-CX_EXPORT int cx_strcasecmp_p(const void *s1, const void *s2);
-
+CX_EXTERN CX_NODISCARD CX_NONNULL
+int cx_strcasecmp_p(const void *s1, const void *s2);
/**
* Creates a duplicate of the specified string.
* @return a duplicate of the string
* @see cx_strdup()
*/
-cx_attr_nodiscard cx_attr_nonnull
-CX_EXPORT cxmutstr cx_strdup_a_(const CxAllocator *allocator, cxstring string);
+CX_EXTERN CX_NODISCARD CX_NONNULL
+cxmutstr cx_strdup_a_(const CxAllocator *allocator, cxstring string);
/**
* Creates a duplicate of the specified string.
#define cx_strdup(string) cx_strdup_a(cxDefaultAllocator, string)
/**
- * Omits leading and trailing spaces.
- *
- * @note the returned string references the same memory, thus you
- * must @em not free the returned memory.
- *
- * @param string the string that shall be trimmed
- * @return the trimmed string
+ * Trims a string.
+ * Internal function - do not use.
+ * @param string
+ * @return
*/
-cx_attr_nodiscard
-CX_EXPORT cxstring cx_strtrim(cxstring string);
+CX_EXTERN CX_NODISCARD
+cxstring cx_strtrim_(cxstring string);
+
+#ifdef __cplusplus
+CX_CPPDECL cxstring cx_strtrim_cpp_(cxstring string) {
+ return cx_strtrim_(string);
+}
+CX_CPPDECL cxmutstr cx_strtrim_cpp_(cxmutstr string) {
+ return cx_mutstrcast(cx_strtrim_(cx_strcast(string)));
+}
+#define cx_strtrim(string) cx_strtrim_cpp_(cx_strcast_m(string))
+#else
+/**
+ * Internal conversion function.
+ * @param string
+ * @return
+ */
+CX_INLINE cxmutstr cx_strtrim_m_(cxmutstr string) {
+ return cx_mutstrcast(cx_strtrim_(cx_strcast(string)));
+}
/**
* Omits leading and trailing spaces.
* must @em not free the returned memory.
*
* @param string the string that shall be trimmed
- * @return the trimmed string
+ * @return (@c cxstring or @c cxmutstr) the trimmed string
*/
-cx_attr_nodiscard
-CX_EXPORT cxmutstr cx_strtrim_m(cxmutstr string);
+#define cx_strtrim(string) _Generic(cx_strcast_m(string), \
+ cxstring: cx_strtrim_, \
+ cxmutstr: cx_strtrim_m_)(cx_strcast_m(string))
+#endif
/**
* Checks if a string has a specific prefix.
* @return @c true, if and only if the string has the specified prefix,
* @c false otherwise
*/
-cx_attr_nodiscard
-CX_EXPORT bool cx_strprefix_(cxstring string, cxstring prefix);
+CX_EXTERN CX_NODISCARD
+bool cx_strprefix_(cxstring string, cxstring prefix);
/**
* Checks if a string has a specific prefix.
* @return @c true, if and only if the string has the specified suffix,
* @c false otherwise
*/
-cx_attr_nodiscard
-CX_EXPORT bool cx_strsuffix_(cxstring string, cxstring suffix);
+CX_EXTERN CX_NODISCARD
+bool cx_strsuffix_(cxstring string, cxstring suffix);
/**
* Checks if a string has a specific suffix.
* @return @c true, if and only if the string has the specified prefix,
* @c false otherwise
*/
-cx_attr_nodiscard
-CX_EXPORT bool cx_strcaseprefix_(cxstring string, cxstring prefix);
+CX_EXTERN CX_NODISCARD
+bool cx_strcaseprefix_(cxstring string, cxstring prefix);
/**
* Checks if a string has a specific prefix, ignoring the case.
* @return @c true, if and only if the string has the specified suffix,
* @c false otherwise
*/
-cx_attr_nodiscard
-CX_EXPORT bool cx_strcasesuffix_(cxstring string, cxstring suffix);
+CX_EXTERN CX_NODISCARD
+bool cx_strcasesuffix_(cxstring string, cxstring suffix);
/**
* Checks, if a string has a specific suffix, ignoring the case.
*/
#define cx_strcasesuffix(string, suffix) cx_strcasesuffix_(cx_strcast(string), cx_strcast(suffix))
+/**
+ * Replaces a string with another string.
+ *
+ * Internal function - do not use.
+ *
+ * @param allocator
+ * @param str
+ * @param search
+ * @param replacement
+ * @param replmax
+ * @return
+ * @see cx_strreplace_a()
+ * @see cx_strreplace()
+ * @see cx_strreplacen_a()
+ * @see cx_strreplacen()
+ */
+CX_EXTERN CX_NODISCARD CX_NONNULL
+cxmutstr cx_strreplace_(const CxAllocator *allocator,
+ cxstring str, cxstring search, cxstring replacement, size_t replmax);
+
/**
* Replaces a string with another string.
*
* If allocation fails, or the input string is empty,
* the returned string will be empty.
*
- * @param allocator the allocator to use
+ * @param allocator (@c CxAllocator*) the allocator to use
* @param str the string where replacements should be applied
* @param search the string to search for
* @param replacement the replacement string
- * @param replmax maximum number of replacements
- * @return the resulting string after applying the replacements
+ * @param replmax (@c size_t) maximum number of replacements
+ * @return (@c cxmutstr) the resulting string after applying the replacements
*/
-cx_attr_nodiscard cx_attr_nonnull
-CX_EXPORT cxmutstr cx_strreplacen_a(const CxAllocator *allocator,
- cxstring str, cxstring search, cxstring replacement, size_t replmax);
+#define cx_strreplacen_a(allocator, str, search, replacement, replmax) \
+ cx_strreplace_(allocator, cx_strcast(str), cx_strcast(search), cx_strcast(replacement), replmax)
/**
* Replaces a string with another string.
* If allocation fails, or the input string is empty,
* the returned string will be empty.
*
- * @param str (@c cxstring) the string where replacements should be applied
- * @param search (@c cxstring) the string to search for
- * @param replacement (@c cxstring) the replacement string
+ * @param str the string where replacements should be applied
+ * @param search the string to search for
+ * @param replacement the replacement string
* @param replmax (@c size_t) maximum number of replacements
* @return (@c cxmutstr) the resulting string after applying the replacements
*/
* the returned string will be empty.
*
* @param allocator (@c CxAllocator*) the allocator to use
- * @param str (@c cxstring) the string where replacements should be applied
- * @param search (@c cxstring) the string to search for
- * @param replacement (@c cxstring) the replacement string
+ * @param str the string where replacements should be applied
+ * @param search the string to search for
+ * @param replacement the replacement string
* @return (@c cxmutstr) the resulting string after applying the replacements
*/
#define cx_strreplace_a(allocator, str, search, replacement) \
* If allocation fails, or the input string is empty,
* the returned string will be empty.
*
- * @param str (@c cxstring) the string where replacements should be applied
- * @param search (@c cxstring) the string to search for
- * @param replacement (@c cxstring) the replacement string
+ * @param str the string where replacements should be applied
+ * @param search the string to search for
+ * @param replacement the replacement string
* @return (@c cxmutstr) the resulting string after applying the replacements
*/
#define cx_strreplace(str, search, replacement) \
* @param limit the maximum number of tokens that shall be returned
* @return a new string tokenization context
*/
-cx_attr_nodiscard
-CX_EXPORT CxStrtokCtx cx_strtok_(cxstring str, cxstring delim, size_t limit);
+CX_EXTERN CX_NODISCARD
+CxStrtokCtx cx_strtok_(cxstring str, cxstring delim, size_t limit);
/**
* Creates a string tokenization context.
* @return true if successful, false if the limit or the end of the string
* has been reached
*/
-cx_attr_nonnull cx_attr_nodiscard cx_attr_access_w(2)
-CX_EXPORT bool cx_strtok_next(CxStrtokCtx *ctx, cxstring *token);
+CX_EXTERN CX_NONNULL CX_NODISCARD CX_ACCESS_W(2)
+bool cx_strtok_next_(CxStrtokCtx *ctx, cxstring *token);
+#ifdef __cplusplus
+CX_CPPDECL cx_strtok_next(CxStrtokCtx *ctx, cxstring *token) {
+ return cx_strtok_next_(ctx, token);
+}
+CX_CPPDECL cx_strtok_next(CxStrtokCtx *ctx, cxmutstr *token) {
+ // Note: this is actually UB - fixed with start_lifetime_as() in C++23
+ // but it works on all supported platforms
+ return cx_strtok_next_(ctx, reinterpret_cast<cxstring*>(token));
+}
+#else // ! __cplusplus
/**
- * Returns the next token of a mutable string.
+ * Returns the next token.
*
* The token will point to the source string.
*
- * @attention
- * If the context was not initialized over a mutable string, modifying
- * the data of the returned token is undefined behavior.
- *
- * @param ctx the tokenization context
- * @param token a pointer to memory where the next token shall be stored
+ * @param ctx (@c CxStrtokCtx*) the tokenization context
+ * @param token a pointer to either a @c cxstring or @c cxmutstr
+ * where the next token shall be stored
* @return true if successful, false if the limit or the end of the string
* has been reached
*/
-cx_attr_nonnull cx_attr_nodiscard cx_attr_access_w(2)
-CX_EXPORT bool cx_strtok_next_m(CxStrtokCtx *ctx, cxmutstr *token);
+#define cx_strtok_next(ctx, token) _Generic((token), \
+ cxstring*: cx_strtok_next_, \
+ cxmutstr*: cx_strtok_next_)(ctx, (cxstring*)token)
+#endif
/**
* Defines an array of more delimiters for the specified tokenization context.
* @param delim array of more delimiters
* @param count number of elements in the array
*/
-cx_attr_nonnull cx_attr_access_r(2, 3)
-CX_EXPORT void cx_strtok_delim(CxStrtokCtx *ctx, const cxstring *delim, size_t count);
+CX_EXTERN CX_NONNULL CX_ACCESS_R(2, 3)
+void cx_strtok_delim(CxStrtokCtx *ctx, const cxstring *delim, size_t count);
/* ------------------------------------------------------------------------- *
* string to number conversion functions *
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtos_lc_(cxstring str, short *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtos_lc_(cxstring str, short *output, int base, const char *groupsep);
/**
* Converts a string to a number.
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtoi_lc_(cxstring str, int *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtoi_lc_(cxstring str, int *output, int base, const char *groupsep);
/**
* Converts a string to a number.
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtol_lc_(cxstring str, long *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtol_lc_(cxstring str, long *output, int base, const char *groupsep);
/**
* Converts a string to a number.
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtoll_lc_(cxstring str, long long *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtoll_lc_(cxstring str, long long *output, int base, const char *groupsep);
/**
* Converts a string to a number.
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtoi8_lc_(cxstring str, int8_t *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtoi8_lc_(cxstring str, int8_t *output, int base, const char *groupsep);
/**
* Converts a string to a number.
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtoi16_lc_(cxstring str, int16_t *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtoi16_lc_(cxstring str, int16_t *output, int base, const char *groupsep);
/**
* Converts a string to a number.
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtoi32_lc_(cxstring str, int32_t *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtoi32_lc_(cxstring str, int32_t *output, int base, const char *groupsep);
/**
* Converts a string to a number.
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtoi64_lc_(cxstring str, int64_t *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtoi64_lc_(cxstring str, int64_t *output, int base, const char *groupsep);
/**
* Converts a string to a number.
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtous_lc_(cxstring str, unsigned short *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtous_lc_(cxstring str, unsigned short *output, int base, const char *groupsep);
/**
* Converts a string to a number.
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtou_lc_(cxstring str, unsigned int *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtou_lc_(cxstring str, unsigned int *output, int base, const char *groupsep);
/**
* Converts a string to a number.
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtoul_lc_(cxstring str, unsigned long *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtoul_lc_(cxstring str, unsigned long *output, int base, const char *groupsep);
/**
* Converts a string to a number.
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtoull_lc_(cxstring str, unsigned long long *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtoull_lc_(cxstring str, unsigned long long *output, int base, const char *groupsep);
/**
* Converts a string to a number.
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtou8_lc_(cxstring str, uint8_t *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtou8_lc_(cxstring str, uint8_t *output, int base, const char *groupsep);
/**
* Converts a string to a number.
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtou16_lc_(cxstring str, uint16_t *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtou16_lc_(cxstring str, uint16_t *output, int base, const char *groupsep);
/**
* Converts a string to a number.
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtou32_lc_(cxstring str, uint32_t *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtou32_lc_(cxstring str, uint32_t *output, int base, const char *groupsep);
/**
* Converts a string to a number.
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtou64_lc_(cxstring str, uint64_t *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtou64_lc_(cxstring str, uint64_t *output, int base, const char *groupsep);
/**
* Converts a string to a number.
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtoz_lc_(cxstring str, size_t *output, int base, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtoz_lc_(cxstring str, size_t *output, int base, const char *groupsep);
/**
* Converts a string to a single precision floating-point number.
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtof_lc_(cxstring str, float *output, char decsep, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtof_lc_(cxstring str, float *output, char decsep, const char *groupsep);
/**
* Converts a string to a double precision floating-point number.
* @retval zero success
* @retval non-zero conversion was not possible
*/
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
-CX_EXPORT int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupsep);
+CX_EXTERN CX_ACCESS_W(2) CX_NONNULL_ARG(2)
+int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupsep);
/**
* Converts a string to a number.
*/
#define cx_strtod(str, output) cx_strtod_lc_(cx_strcast(str), output, '.', ",")
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
#endif //UCX_STRING_H
#include <string.h>
#include <setjmp.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
-
#ifndef __FUNCTION__
/**
* Alias for the <code>__func__</code> preprocessor macro.
* @param name optional name of the suite
* @return a new test suite
*/
-cx_attr_nonnull cx_attr_nodiscard cx_attr_cstr_arg(1) cx_attr_malloc
+CX_NONNULL CX_NODISCARD CX_CSTR_ARG(1) CX_MALLOC
static inline CxTestSuite* cx_test_suite_new(const char *name) {
CxTestSuite* suite = (CxTestSuite*) malloc(sizeof(CxTestSuite));
if (suite != NULL) {
* @retval zero success
* @retval non-zero failure
*/
-cx_attr_nonnull
+CX_NONNULL
CX_INLINE int cx_test_register(CxTestSuite* suite, CxTest test) {
CxTestSet *t = (CxTestSet*) malloc(sizeof(CxTestSet));
if (t) {
* @param out_target the target buffer or file to write the output to
* @param out_writer the write function writing to @p out_target
*/
-cx_attr_nonnull
+CX_NONNULL
CX_INLINE void cx_test_run(CxTestSuite *suite, void *out_target, cx_write_func out_writer) {
if (suite->name == NULL) {
out_writer("*** Test Suite ***\n", 1, 19, out_target);
char total[80];
int len = snprintf(
total, 80,
- " Total: %u\n Success: %u\n Failure: %u\n\n",
+ " Tests: %5u\n Success: %5u\n Failure: %5u\n\n",
suite->success + suite->failure, suite->success, suite->failure
);
out_writer(total, 1, len, out_target);
#define CX_TEST_CALL_SUBROUTINE(name,...) \
name(_suite_,_output_,_writefnc_,_env_,__VA_ARGS__)
-#ifdef __cplusplus
-}
-#endif
-
#endif /* UCX_TEST_H */
#include "collection.h"
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * A depth-first tree iterator.
- *
- * This iterator is not position-aware in a strict sense, as it does not assume
- * a particular order of elements in the tree. However, the iterator keeps track
- * of the number of nodes it has passed in a counter-variable.
- * Each node, regardless of the number of passes, is counted only once.
- *
- * @note Objects that are pointed to by an iterator are mutable through that
- * iterator. However, if the
- * underlying data structure is mutated by other means than this iterator (e.g.,
- * elements added or removed), the iterator becomes invalid (regardless of what
- * cxIteratorValid() returns).
- *
- * @see CxIterator
- */
-typedef struct cx_tree_iterator_s {
- /**
- * Base members.
- */
- CX_ITERATOR_BASE;
- /**
- * Indicates whether the subtree below the current node shall be skipped.
- */
- bool skip;
- /**
- * Set to true, when the iterator shall visit a node again
- * when all its children have been processed.
- */
- bool visit_on_exit;
- /**
- * True, if this iterator is currently leaving the node.
- */
- bool exiting;
- /**
- * Offset in the node struct for the children linked list.
- */
- ptrdiff_t loc_children;
- /**
- * Offset in the node struct for the next pointer.
- */
- ptrdiff_t loc_next;
- /**
- * The total number of distinct nodes that have been passed so far.
- */
- size_t counter;
- /**
- * The currently observed node.
- *
- * This is the same what cxIteratorCurrent() would return.
- */
- void *node;
- /**
- * Stores a copy of the pointer to the successor of the visited node.
- * Allows freeing a node on exit without corrupting the iteration.
- */
- void *node_next;
- /**
- * Internal stack.
- * Will be automatically freed once the iterator becomes invalid.
- *
- * If you want to discard the iterator before, you need to manually
- * call cxTreeIteratorDispose().
- */
- void **stack;
- /**
- * Internal capacity of the stack.
- */
- size_t stack_capacity;
- union {
- /**
- * Internal stack size.
- */
- size_t stack_size;
- /**
- * The current depth in the tree.
- * The node with which the iteration starts has depth 1.
- */
- size_t depth;
- };
-} CxTreeIterator;
-
/**
* An element in a visitor queue.
*/
};
/**
- * A breadth-first tree iterator.
- *
- * This iterator needs to maintain a visitor queue that will be automatically
- * freed once the iterator becomes invalid.
- * If you want to discard the iterator before, you MUST manually call
- * cxTreeVisitorDispose().
- *
- * This iterator is not position-aware in a strict sense, as it does not assume
- * a particular order of elements in the tree. However, the iterator keeps track
- * of the number of nodes it has passed in a counter-variable.
- * Each node, regardless of the number of passes, is counted only once.
- *
- * @note Objects that are pointed to by an iterator are mutable through that
- * iterator. However, if the
- * underlying data structure is mutated by other means than this iterator (e.g.,
- * elements added or removed), the iterator becomes invalid (regardless of what
- * cxIteratorValid() returns).
- *
- * @see CxIterator
+ * An iterator (DFS) or visitor (BFS) for a tree.
*/
-typedef struct cx_tree_visitor_s {
+typedef struct cx_tree_combined_iterator_s {
/**
* Base members.
*/
CX_ITERATOR_BASE;
- /**
- * Indicates whether the subtree below the current node shall be skipped.
- */
- bool skip;
/**
* Offset in the node struct for the children linked list.
*/
ptrdiff_t loc_next;
/**
* The total number of distinct nodes that have been passed so far.
+ * This includes the currently visited node.
*/
size_t counter;
+ /**
+ * The current depth in the tree.
+ */
+ size_t depth;
/**
* The currently observed node.
*
*/
void *node;
/**
- * The current depth in the tree.
+ * Memory for BFS or DFS-specific data.
*/
- size_t depth;
+ union {
+ struct {
+ /**
+ * Stores a copy of the pointer to the successor of the visited node.
+ * Allows freeing a node on exit without corrupting the iteration.
+ */
+ void *node_next;
+ /**
+ * Internal stack.
+ * Will be automatically freed once the iterator becomes invalid.
+ *
+ * If you want to discard the iterator before, you need to manually
+ * call cxTreeIteratorDispose().
+ */
+ void **stack;
+ /**
+ * Internal capacity of the stack.
+ */
+ size_t stack_capacity;
+ };
+ struct {
+ /**
+ * The next element in the visitor queue.
+ */
+ struct cx_tree_visitor_queue_s *queue_next;
+ /**
+ * The last element in the visitor queue.
+ */
+ struct cx_tree_visitor_queue_s *queue_last;
+ };
+ };
/**
- * The next element in the visitor queue.
+ * Indicates whether the subtree below the current node shall be skipped.
*/
- struct cx_tree_visitor_queue_s *queue_next;
+ bool skip;
+ /**
+ * Set to true, when the iterator shall visit a node again
+ * when all its children have been processed.
+ */
+ bool visit_on_exit;
+ /**
+ * True, if this iterator is currently leaving the node.
+ */
+ bool exiting;
/**
- * The last element in the visitor queue.
+ * Indicates whether the @c iterator (true) or the @c visitor (false) aspect is active.
*/
- struct cx_tree_visitor_queue_s *queue_last;
-} CxTreeVisitor;
+ bool use_dfs;
+} CxTreeIterator;
/**
* Releases internal memory of the given tree iterator.
* @param iter the iterator
*/
-cx_attr_nonnull
-CX_EXPORT void cxTreeIteratorDispose(CxTreeIterator *iter);
-
-/**
- * Releases internal memory of the given tree visitor.
- * @param visitor the visitor
- */
-cx_attr_nonnull
-CX_EXPORT void cxTreeVisitorDispose(CxTreeVisitor *visitor);
+CX_EXTERN CX_NONNULL
+void cxTreeIteratorDispose(CxTreeIterator *iter);
/**
* Advises the iterator to skip the subtree below the current node and
*/
#define cxTreeIteratorContinue(iterator) (iterator).skip = true; continue
-/**
- * Advises the visitor to skip the subtree below the current node and
- * also continues the current loop.
- *
- * @param visitor (@c CxTreeVisitor) the visitor
- */
-#define cxTreeVisitorContinue(visitor) cxTreeIteratorContinue(visitor)
-
/**
* Links a node to a (new) parent.
*
* the last child in the linked list (negative if there is no such pointer)
* @param loc_prev optional offset in the node struct for the prev pointer
* @param loc_next offset in the node struct for the next pointer
- * @see cx_tree_unlink()
+ * @see cx_tree_remove()
*/
-cx_attr_nonnull
-CX_EXPORT void cx_tree_link(void *parent, void *node,
+CX_EXTERN CX_NONNULL
+void cx_tree_add(void *parent, void *node,
ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
ptrdiff_t loc_prev, ptrdiff_t loc_next);
* the last child in the linked list (negative if there is no such pointer)
* @param loc_prev optional offset in the node struct for the prev pointer
* @param loc_next offset in the node struct for the next pointer
- * @see cx_tree_link()
+ * @see cx_tree_add()
*/
-cx_attr_nonnull
-CX_EXPORT void cx_tree_unlink(void *node,
+CX_EXTERN CX_NONNULL
+void cx_tree_remove(void *node,
ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
ptrdiff_t loc_prev, ptrdiff_t loc_next);
* positive if one of the children might contain the data,
* negative if neither the node nor the children contains the data
*/
-typedef int (*cx_tree_search_data_func)(const void *node, const void *data);
-
-
-/**
- * Function pointer for a search function.
- *
- * A function of this kind shall check if the specified @p node
- * contains the same @p data as @p new_node or if one of the children might
- * contain the data.
- *
- * The function should use the returned integer to indicate how close the
- * match is, where a negative number means that it does not match at all.
- * Zero means exact match and a positive number is an implementation defined
- * measure for the distance to an exact match.
- *
- * For example, consider a tree that stores file path information.
- * A node which is describing a parent directory of a searched file shall
- * return a positive number to indicate that a child node might contain the
- * searched item. On the other hand, if the node denotes a path that is not a
- * prefix of the searched filename, the function would return -1 to indicate
- * that the search does not need to be continued in that branch.
- *
- * @param node the node that is currently investigated
- * @param new_node a new node with the information which is searched
- *
- * @return 0 if @p node contains the same data as @p new_node,
- * positive if one of the children might contain the data,
- * negative if neither the node nor the children contains the data
- */
-typedef int (*cx_tree_search_func)(const void *node, const void *new_node);
+typedef int (*cx_tree_search_func)(const void *node, const void *data);
/**
* Searches for data in a tree.
*
* When the data cannot be found exactly, the search function might return the
* closest result, which might be a good starting point for adding a new node
- * to the tree (see also #cx_tree_add()).
+ * to the tree.
*
* Depending on the tree structure, it is not necessarily guaranteed that the
* "closest" match is uniquely defined. This function will search for a node
* node matching the criteria is returned.
*
* @param root the root node
- * @param depth the maximum depth (zero=indefinite, one=just root)
+ * @param max_depth the maximum depth (zero=indefinite, one=just root)
* @param data the data to search for
* @param sfunc the search function
* @param result where the result shall be stored
* could contain the node (but doesn't right now), negative if the tree does not
* contain any node that might be related to the searched data
*/
-cx_attr_nonnull cx_attr_access_w(5)
-CX_EXPORT int cx_tree_search_data(const void *root, size_t depth,
- const void *data, cx_tree_search_data_func sfunc,
- void **result, ptrdiff_t loc_children, ptrdiff_t loc_next);
-
-/**
- * Searches for a node in a tree.
- *
- * When no node with the same data can be found, the search function might
- * return the closest result, which might be a good starting point for adding the
- * new node to the tree (see also #cx_tree_add()).
- *
- * Depending on the tree structure, it is not necessarily guaranteed that the
- * "closest" match is uniquely defined. This function will search for a node
- * with the best match according to the @p sfunc (meaning: the return value of
- * @p sfunc which is closest to zero). If that is also ambiguous, an arbitrary
- * node matching the criteria is returned.
- *
- * @param root the root node
-* @param depth the maximum depth (zero=indefinite, one=just root)
- * @param node the node to search for
- * @param sfunc the search function
- * @param result where the result shall be stored
- * @param loc_children offset in the node struct for the children linked list
- * @param loc_next offset in the node struct for the next pointer
- * @return zero if the node was found exactly, positive if a node was found that
- * could contain the node (but doesn't right now), negative if the tree does not
- * contain any node that might be related to the searched data
- */
-cx_attr_nonnull cx_attr_access_w(5)
-CX_EXPORT int cx_tree_search(const void *root, size_t depth,
- const void *node, cx_tree_search_func sfunc,
- void **result, ptrdiff_t loc_children, ptrdiff_t loc_next);
+CX_EXTERN CX_NONNULL_ARG(4, 5) CX_ACCESS_W(5)
+int cx_tree_search(const void *root, size_t max_depth,
+ const void *data, cx_tree_search_func sfunc, void **result,
+ ptrdiff_t loc_children, ptrdiff_t loc_next);
/**
* Creates a depth-first iterator for a tree with the specified root node.
* @return the new tree iterator
* @see cxTreeIteratorDispose()
*/
-cx_attr_nodiscard
-CX_EXPORT CxTreeIterator cx_tree_iterator(void *root, bool visit_on_exit,
+CX_EXTERN CX_NODISCARD
+CxTreeIterator cx_tree_iterator(void *root, bool visit_on_exit,
ptrdiff_t loc_children, ptrdiff_t loc_next);
/**
* is allocated using the cxDefaultAllocator.
* When the visitor becomes invalid, this memory is automatically released.
* However, if you wish to cancel the iteration before the visitor becomes
- * invalid by itself, you MUST call cxTreeVisitorDispose() manually to release
+ * invalid by itself, you MUST call cxTreeIteratorDispose() manually to release
* the memory.
*
* @remark The returned iterator does not support cxIteratorFlagRemoval().
* @param loc_children offset in the node struct for the children linked list
* @param loc_next offset in the node struct for the next pointer
* @return the new tree visitor
- * @see cxTreeVisitorDispose()
+ * @see cxTreeIteratorDispose()
*/
-cx_attr_nodiscard
-CX_EXPORT CxTreeVisitor cx_tree_visitor(void *root,
+CX_EXTERN CX_NODISCARD
+CxTreeIterator cx_tree_visitor(void *root,
ptrdiff_t loc_children, ptrdiff_t loc_next);
-/**
- * Describes a function that creates a tree node from the specified data.
- * The first argument points to the data the node shall contain, and
- * the second argument may be used for additional data (e.g., an allocator).
- * Functions of this type shall either return a new pointer to a newly
- * created node or @c NULL when allocation fails.
- *
- * @note the function may leave the node pointers in the struct uninitialized.
- * The caller is responsible to set them according to the intended use case.
- */
-typedef void *(*cx_tree_node_create_func)(const void *, void *);
-
-/**
- * The local search depth for a new subtree when adding multiple elements.
- * The default value is 3.
- * This variable is used by #cx_tree_add_array() and #cx_tree_add_iter() to
- * implement optimized insertion of multiple elements into a tree.
- */
-CX_EXPORT extern unsigned int cx_tree_add_look_around_depth;
-
-/**
- * Adds multiple elements efficiently to a tree.
- *
- * Once an element cannot be added to the tree, this function returns, leaving
- * the iterator in a valid state pointing to the element that could not be
- * added.
- * Also, the pointer of the created node will be stored to @p failed.
- * The integer returned by this function denotes the number of elements obtained
- * from the @p iter that have been successfully processed.
- * When all elements could be processed, a @c NULL pointer will be written to
- * @p failed.
- *
- * The advantage of this function compared to multiple invocations of
- * #cx_tree_add() is that the search for the insert locations is not always
- * started from the root node.
- * Instead, the function checks #cx_tree_add_look_around_depth many parent nodes
- * of the current insert location before starting from the root node again.
- * When the variable is set to zero, only the last found location is checked
- * again.
- *
- * Refer to the documentation of #cx_tree_add() for more details.
- *
- * @param iter a pointer to an arbitrary iterator
- * @param num the maximum number of elements to obtain from the iterator
- * @param sfunc a search function
- * @param cfunc a node creation function
- * @param cdata optional additional data
- * @param root the root node of the tree
- * @param failed location where the pointer to a failed node shall be stored
- * @param loc_parent offset in the node struct for the parent pointer
- * @param loc_children offset in the node struct for the children linked list
- * @param loc_last_child optional offset in the node struct for the pointer to
- * the last child in the linked list (negative if there is no such pointer)
- * @param loc_prev optional offset in the node struct for the prev pointer
- * @param loc_next offset in the node struct for the next pointer
- * @return the number of nodes created and added
- * @see cx_tree_add()
- */
-cx_attr_nonnull_arg(1, 3, 4, 6, 7) cx_attr_access_w(6)
-CX_EXPORT size_t cx_tree_add_iter(struct cx_iterator_base_s *iter, size_t num,
- cx_tree_search_func sfunc, cx_tree_node_create_func cfunc,
- void *cdata, void **failed, void *root,
- ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
- ptrdiff_t loc_prev, ptrdiff_t loc_next);
-
-/**
- * Adds multiple elements efficiently to a tree.
- *
- * Once an element cannot be added to the tree, this function returns, storing
- * the pointer of the created node to @p failed.
- * The integer returned by this function denotes the number of elements from
- * the @p src array that have been successfully processed.
- * When all elements could be processed, a @c NULL pointer will be written to
- * @p failed.
- *
- * The advantage of this function compared to multiple invocations of
- * #cx_tree_add() is that the search for the insert locations is not always
- * started from the root node.
- * Instead, the function checks #cx_tree_add_look_around_depth many parent nodes
- * of the current insert location before starting from the root node again.
- * When the variable is set to zero, only the last found location is checked
- * again.
- *
- * Refer to the documentation of #cx_tree_add() for more details.
- *
- * @param src a pointer to the source data array
- * @param num the number of elements in the @p src array
- * @param elem_size the size of each element in the @p src array
- * @param sfunc a search function
- * @param cfunc a node creation function
- * @param cdata optional additional data
- * @param failed location where the pointer to a failed node shall be stored
- * @param root the root node of the tree
- * @param loc_parent offset in the node struct for the parent pointer
- * @param loc_children offset in the node struct for the children linked list
- * @param loc_last_child optional offset in the node struct for the pointer to
- * the last child in the linked list (negative if there is no such pointer)
- * @param loc_prev optional offset in the node struct for the prev pointer
- * @param loc_next offset in the node struct for the next pointer
- * @return the number of array elements successfully processed
- * @see cx_tree_add()
- */
-cx_attr_nonnull_arg(1, 4, 5, 7, 8) cx_attr_access_w(7)
-CX_EXPORT size_t cx_tree_add_array(const void *src, size_t num, size_t elem_size,
- cx_tree_search_func sfunc, cx_tree_node_create_func cfunc,
- void *cdata, void **failed, void *root,
- ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
- ptrdiff_t loc_prev, ptrdiff_t loc_next);
-
-/**
- * Adds data to a tree.
- *
- * An adequate location where to add the new tree node is searched with the
- * specified @p sfunc.
- *
- * When a location is found, the @p cfunc will be invoked with @p cdata.
- *
- * The node returned by @p cfunc will be linked into the tree.
- * When @p sfunc returns a positive integer, the new node will be linked as a
- * child. The other children (now siblings of the new node) are then checked
- * with @p sfunc, whether they could be children of the new node and re-linked
- * accordingly.
- *
- * When @p sfunc returns zero and the found node has a parent, the new
- * node will be added as a sibling - otherwise, the new node will be added
- * as a child.
- *
- * When @p sfunc returns a negative value, the new node will not be added to
- * the tree, and this function returns a non-zero value.
- * The caller should check if @p cnode contains a node pointer and deal with the
- * node that could not be added.
- *
- * This function also returns a non-zero value when @p cfunc tries to allocate
- * a new node but fails to do so. In that case, the pointer stored to @p cnode
- * will be @c NULL.
- *
- * Multiple elements can be added more efficiently with
- * #cx_tree_add_array() or #cx_tree_add_iter().
- *
- * @param src a pointer to the data
- * @param sfunc a search function
- * @param cfunc a node creation function
- * @param cdata optional additional data
- * @param cnode the location where a pointer to the new node is stored
- * @param root the root node of the tree
- * @param loc_parent offset in the node struct for the parent pointer
- * @param loc_children offset in the node struct for the children linked list
- * @param loc_last_child optional offset in the node struct for the pointer to
- * the last child in the linked list (negative if there is no such pointer)
- * @param loc_prev optional offset in the node struct for the prev pointer
- * @param loc_next offset in the node struct for the next pointer
- * @return zero when a new node was created and added to the tree,
- * non-zero otherwise
- */
-cx_attr_nonnull_arg(1, 2, 3, 5, 6) cx_attr_access_w(5)
-CX_EXPORT int cx_tree_add(const void *src,
- cx_tree_search_func sfunc, cx_tree_node_create_func cfunc,
- void *cdata, void **cnode, void *root,
- ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
- ptrdiff_t loc_prev, ptrdiff_t loc_next);
-
-
-/**
- * Tree class type.
- */
-typedef struct cx_tree_class_s cx_tree_class;
-
/**
* Base structure that can be used for tree nodes in a #CxTree.
*/
/**
* Structure for holding the base data of a tree.
*/
-struct cx_tree_s {
- /**
- * The tree class definition.
- */
- const cx_tree_class *cl;
-
- /**
- * Allocator to allocate new nodes.
- */
- const CxAllocator *allocator;
-
+typedef struct cx_tree_s {
+ /** Base attributes. */
+ CX_COLLECTION_BASE;
/**
* A pointer to the root node.
*
void *root;
/**
- * A function to create new nodes.
- *
- * Invocations to this function will receive a pointer to this tree
- * structure as the second argument.
- *
- * Nodes MAY use #cx_tree_node_base_s as the base layout, but do not need to.
- */
- cx_tree_node_create_func node_create;
-
- /**
- * An optional simple destructor for the tree nodes.
- */
- cx_destructor_func simple_destructor;
-
- /**
- * An optional advanced destructor for the tree nodes.
- */
- cx_destructor_func2 advanced_destructor;
-
- /**
- * The pointer to additional data that is passed to the advanced destructor.
- */
- void *destructor_data;
-
- /**
- * A function to compare two nodes.
- */
- cx_tree_search_func search;
-
- /**
- * A function to compare a node with data.
+ * The size of the node structure.
*/
- cx_tree_search_data_func search_data;
-
- /**
- * The number of currently stored elements.
- */
- size_t size;
+ size_t node_size;
/**
* Offset in the node struct for the parent pointer.
* Offset in the node struct for the next sibling pointer.
*/
ptrdiff_t loc_next;
-};
+
+ /**
+ * Offset in the node struct where the payload is located.
+ */
+ ptrdiff_t loc_data;
+} CxTree;
/**
* Macro to roll out the #cx_tree_node_base_s structure with a custom
*
* @param type the data type for the nodes
*/
-#define CX_TREE_NODE_BASE(type) \
+#define CX_TREE_NODE(type) \
type *parent; \
type *children;\
type *last_child;\
type *next
/**
- * Macro for specifying the layout of a base node tree.
+ * Macro for specifying the layout of a tree node.
*
- * When your tree uses #CX_TREE_NODE_BASE, you can use this
+ * When your tree uses #CX_TREE_NODE, you can use this
* macro in all tree functions that expect the layout parameters
* @c loc_parent, @c loc_children, @c loc_last_child, @c loc_prev,
* and @c loc_next.
+ * @param struct_name the name of the node structure
*/
-#define cx_tree_node_base_layout \
- offsetof(struct cx_tree_node_base_s, parent),\
- offsetof(struct cx_tree_node_base_s, children),\
- offsetof(struct cx_tree_node_base_s, last_child),\
- offsetof(struct cx_tree_node_base_s, prev), \
- offsetof(struct cx_tree_node_base_s, next)
-
-/**
- * The class definition for arbitrary trees.
- */
-struct cx_tree_class_s {
- /**
- * Member function for inserting a single element.
- *
- * Implementations SHALL NOT simply invoke @p insert_many as this comes
- * with too much overhead.
- */
- int (*insert_element)(struct cx_tree_s *tree, const void *data);
-
- /**
- * Member function for inserting multiple elements.
- *
- * Implementations SHALL avoid performing a full search in the tree for
- * every element even though the source data MAY be unsorted.
- */
- size_t (*insert_many)(struct cx_tree_s *tree, struct cx_iterator_base_s *iter, size_t n);
-
- /**
- * Member function for finding a node.
- */
- void *(*find)(struct cx_tree_s *tree, const void *subtree, const void *data, size_t depth);
-};
-
-/**
- * Common type for all tree implementations.
- */
-typedef struct cx_tree_s CxTree;
-
+#define cx_tree_node_layout(struct_name) \
+ offsetof(struct_name, parent),\
+ offsetof(struct_name, children),\
+ offsetof(struct_name, last_child),\
+ offsetof(struct_name, prev), \
+ offsetof(struct_name, next)
/**
* Destroys a node and its subtree.
* and would therefore result in a double-free.
*
* @param tree the tree
- * @param node the node to remove
+ * @param node the node being the root of the subtree to remove
* @see cxTreeFree()
*/
-cx_attr_nonnull
-CX_EXPORT void cxTreeDestroySubtree(CxTree *tree, void *node);
+CX_EXTERN CX_NONNULL
+void cxTreeDestroySubtree(CxTree *tree, void *node);
/**
* @param tree the tree
* @see cxTreeDestroySubtree()
*/
-#define cxTreeClear(tree) cxTreeDestroySubtree(tree, tree->root)
+CX_INLINE
+void cxTreeClear(CxTree *tree) {
+ if (tree->root != NULL) {
+ cxTreeDestroySubtree(tree, tree->root);
+ }
+}
/**
* Deallocates the tree structure.
*
* @param tree the tree to free
*/
-CX_EXPORT void cxTreeFree(CxTree *tree);
+CX_EXTERN
+void cxTreeFree(CxTree *tree);
/**
- * Creates a new tree structure based on the specified layout.
+ * Creates a new tree.
*
* The specified @p allocator will be used for creating the tree struct
- * and SHALL be used by @p create_func to allocate memory for the nodes.
- *
- * @note This function will also register an advanced destructor which
- * will free the nodes with the allocator's free() method.
- *
- * @param allocator the allocator that shall be used
- * (if @c NULL, the cxDefaultAllocator will be used)
- * @param create_func a function that creates new nodes
- * @param search_func a function that compares two nodes
- * @param search_data_func a function that compares a node with data
- * @param loc_parent offset in the node struct for the parent pointer
- * @param loc_children offset in the node struct for the children linked list
- * @param loc_last_child optional offset in the node struct for the pointer to
- * the last child in the linked list (negative if there is no such pointer)
- * @param loc_prev optional offset in the node struct for the prev pointer
- * @param loc_next offset in the node struct for the next pointer
- * @return the new tree
- * @see cxTreeCreateSimple()
- * @see cxTreeCreateWrapped()
- */
-cx_attr_nonnull_arg(2, 3, 4) cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxTreeFree, 1)
-CX_EXPORT CxTree *cxTreeCreate(const CxAllocator *allocator, cx_tree_node_create_func create_func,
- cx_tree_search_func search_func, cx_tree_search_data_func search_data_func,
- ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
- ptrdiff_t loc_prev, ptrdiff_t loc_next);
-
-/**
- * Creates a new tree structure based on a default layout.
- *
- * Nodes created by @p create_func MUST contain #cx_tree_node_base_s as the first
- * member (or at least respect the default offsets specified in the tree
- * struct), and they MUST be allocated with the specified allocator.
+ * @em and the nodes.
*
- * @note This function will also register an advanced destructor which
- * will free the nodes with the allocator's free() method.
+ * When you do not specify an existing @p root, the tree will be created
+ * empty. Additionally, cxFree() is registered as the advanced destructor for
+ * the tree so that all nodes that you will add to the tree are automatically
+ * freed together with the tree.
+ * When @p root is not @c NULL, no destructors are registered automatically.
*
- * @param allocator (@c CxAllocator*) the allocator that shall be used
- * @param create_func (@c cx_tree_node_create_func) a function that creates new nodes
- * @param search_func (@c cx_tree_search_func) a function that compares two nodes
- * @param search_data_func (@c cx_tree_search_data_func) a function that compares a node with data
- * @return (@c CxTree*) the new tree
- * @see cxTreeCreate()
- */
-#define cxTreeCreateSimple(allocator, create_func, search_func, search_data_func) \
- cxTreeCreate(allocator, create_func, search_func, search_data_func, cx_tree_node_base_layout)
-
-/**
- * Creates a new tree structure based on an existing tree.
- *
- * The specified @p allocator will be used for creating the tree struct.
- *
- * @attention This function will create an incompletely defined tree structure
- * where neither the create function, the search function, nor a destructor
- * will be set. If you wish to use any of this functionality for the wrapped
- * tree, you need to specify those functions afterward.
- *
- * @param allocator the allocator that was used for nodes of the wrapped tree
+ * @param allocator the allocator to use
* (if @c NULL, the cxDefaultAllocator is assumed)
- * @param root the root node of the tree that shall be wrapped
+ * @param node_size the complete size of one node
+ * @param elem_size the size of the payload stored in the node
+ * (@c CX_STORE_POINTERS is also supported)
+ * @param root an optional already existing root node
+ * @param loc_data optional offset in the node struct for the payload
+ * (when negative, cxTreeAddData() is not supported)
* @param loc_parent offset in the node struct for the parent pointer
* @param loc_children offset in the node struct for the children linked list
* @param loc_last_child optional offset in the node struct for the pointer to
* @return the new tree
* @see cxTreeCreate()
*/
-cx_attr_nonnull_arg(2) cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxTreeFree, 1)
-CX_EXPORT CxTree *cxTreeCreateWrapped(const CxAllocator *allocator, void *root,
+CX_EXTERN CX_NODISCARD CX_MALLOC CX_DEALLOC(cxTreeFree, 1)
+CxTree *cxTreeCreate(const CxAllocator *allocator,
+ size_t node_size, size_t elem_size, void *root, ptrdiff_t loc_data,
ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
ptrdiff_t loc_prev, ptrdiff_t loc_next);
/**
- * Inserts data into the tree.
- *
- * @remark For this function to work, the tree needs specified search and
- * create functions, which might not be available for wrapped trees
- * (see #cxTreeCreateWrapped()).
- *
- * @param tree the tree
- * @param data the data to insert
- * @retval zero success
- * @retval non-zero failure
- */
-cx_attr_nonnull
-CX_EXPORT int cxTreeInsert(CxTree *tree, const void *data);
-
-/**
- * Inserts elements provided by an iterator efficiently into the tree.
+ * Searches the data in the specified subtree.
*
- * @remark For this function to work, the tree needs specified search and
- * create functions, which might not be available for wrapped trees
- * (see #cxTreeCreateWrapped()).
+ * When @p max_depth is zero, the depth is not limited.
+ * The @p subtree_root itself is on depth 1 and its children have depth 2.
*
- * @param tree the tree
- * @param iter the iterator providing the elements
- * @param n the maximum number of elements to insert
- * @return the number of elements that could be successfully inserted
- */
-cx_attr_nonnull
-CX_EXPORT size_t cxTreeInsertIter(CxTree *tree, CxIteratorBase *iter, size_t n);
-
-/**
- * Inserts an array of data efficiently into the tree.
+ * @note When @p subtree_root is not @c NULL and not part of the @p tree,
+ * the behavior is undefined.
*
- * @remark For this function to work, the tree needs specified search and
- * create functions, which might not be available for wrapped trees
- * (see #cxTreeCreateWrapped()).
+ * @attention When @p loc_data is not available, this function immediately returns
+ * @c NULL.
*
* @param tree the tree
- * @param data the array of data to insert
- * @param elem_size the size of each element in the array
- * @param n the number of elements in the array
- * @return the number of elements that could be successfully inserted
+ * @param data the data to search for
+ * @param subtree_root the node where to start (@c NULL to start from root)
+ * @param max_depth the maximum search depth
+ * @param use_dfs @c true when depth-first search should be used;
+ * @c false when breadth-first search should be used
+ * @return the first matching node, or @c NULL when the data cannot be found
+ * @see cxTreeFind()
*/
-cx_attr_nonnull
-CX_EXPORT size_t cxTreeInsertArray(CxTree *tree, const void *data, size_t elem_size, size_t n);
+CX_EXTERN CX_NONNULL_ARG(1, 2) CX_NODISCARD
+void *cxTreeFindInSubtree(CxTree *tree, const void *data, void *subtree_root,
+ size_t max_depth, bool use_dfs);
/**
* Searches the data in the specified tree.
*
- * @remark For this function to work, the tree needs a specified @c search_data
- * function, which might not be available wrapped trees
- * (see #cxTreeCreateWrapped()).
+ * @attention When @p loc_data is not available, this function immediately returns
+ * @c NULL.
*
* @param tree the tree
* @param data the data to search for
+ * @param use_dfs @c true when depth-first search should be used;
+ * @c false when breadth-first search should be used
* @return the first matching node, or @c NULL when the data cannot be found
+ * @see cxTreeFindInSubtree()
+ * @see cxTreeFindFast()
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT void *cxTreeFind(CxTree *tree, const void *data);
+CX_INLINE CX_NONNULL CX_NODISCARD
+void *cxTreeFind(CxTree *tree, const void *data, bool use_dfs) {
+ if (tree->root == NULL) return NULL;
+ return cxTreeFindInSubtree(tree, data, tree->root, 0, use_dfs);
+}
/**
- * Searches the data in the specified subtree.
+ * Performs an efficient depth-first search in a branch of the specified tree.
*
* When @p max_depth is zero, the depth is not limited.
* The @p subtree_root itself is on depth 1 and its children have depth 2.
*
- * @note When @p subtree_root is not part of the @p tree, the behavior is
- * undefined.
- *
- * @remark For this function to work, the tree needs a specified @c search_data
- * function, which might not be the case for wrapped trees
- * (see #cxTreeCreateWrapped()).
+ * @note When @p subtree_root is not @c NULL and not part of the @p tree,
+ * the behavior is undefined.
*
* @param tree the tree
* @param data the data to search for
- * @param subtree_root the node where to start
+ * @param sfunc the search function
+ * @param subtree_root the node where to start (@c NULL to start from root)
* @param max_depth the maximum search depth
* @return the first matching node, or @c NULL when the data cannot be found
+ * @see cxTreeFindInSubtree()
+ */
+CX_EXTERN CX_NONNULL CX_NODISCARD
+void *cxTreeFindFastInSubtree(CxTree *tree, const void *data,
+ cx_tree_search_func sfunc, void *subtree_root, size_t max_depth);
+
+/**
+ * Performs an efficient depth-first search in the tree.
+ *
+ * @param tree the tree
+ * @param data the data to search for
+ * @param sfunc the search function
+ * @return the first matching node, or @c NULL when the data cannot be found
+ * @see cxTreeFind()
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT void *cxTreeFindInSubtree(CxTree *tree, const void *data, void *subtree_root, size_t max_depth);
+CX_INLINE CX_NONNULL CX_NODISCARD
+void *cxTreeFindFast(CxTree *tree, const void *data, cx_tree_search_func sfunc) {
+ return cxTreeFindFastInSubtree(tree, data, sfunc, tree->root, 0);
+}
/**
* Determines the size of the specified subtree.
* @param subtree_root the root node of the subtree
* @return the number of nodes in the specified subtree
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT size_t cxTreeSubtreeSize(CxTree *tree, void *subtree_root);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+size_t cxTreeSubtreeSize(CxTree *tree, void *subtree_root);
/**
* Determines the depth of the specified subtree.
* @param subtree_root the root node of the subtree
* @return the tree depth including the @p subtree_root
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT size_t cxTreeSubtreeDepth(CxTree *tree, void *subtree_root);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+size_t cxTreeSubtreeDepth(CxTree *tree, void *subtree_root);
/**
* Determines the size of the entire tree.
* @param tree the tree
* @return the tree size, counting the root as one
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT size_t cxTreeSize(CxTree *tree);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+size_t cxTreeSize(CxTree *tree);
/**
* Determines the depth of the entire tree.
* @param tree the tree
* @return the tree depth, counting the root as one
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT size_t cxTreeDepth(CxTree *tree);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+size_t cxTreeDepth(CxTree *tree);
/**
* Creates a depth-first iterator for the specified tree starting in @p node.
* @return a tree iterator (depth-first)
* @see cxTreeVisit()
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT CxTreeIterator cxTreeIterateSubtree(CxTree *tree, void *node, bool visit_on_exit);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+CxTreeIterator cxTreeIterateSubtree(CxTree *tree, void *node, bool visit_on_exit);
/**
* Creates a breadth-first iterator for the specified tree starting in @p node.
* @return a tree visitor (a.k.a. breadth-first iterator)
* @see cxTreeIterate()
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT CxTreeVisitor cxTreeVisitSubtree(CxTree *tree, void *node);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+CxTreeIterator cxTreeVisitSubtree(CxTree *tree, void *node);
/**
* Creates a depth-first iterator for the specified tree.
* @return a tree iterator (depth-first)
* @see cxTreeVisit()
*/
-cx_attr_nonnull cx_attr_nodiscard
-CX_EXPORT CxTreeIterator cxTreeIterate(CxTree *tree, bool visit_on_exit);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+CxTreeIterator cxTreeIterate(CxTree *tree, bool visit_on_exit);
/**
* Creates a breadth-first iterator for the specified tree.
* @return a tree visitor (a.k.a. breadth-first iterator)
* @see cxTreeIterate()
*/
-cx_attr_nonnull cx_attr_nodiscard
-CxTreeVisitor cxTreeVisit(CxTree *tree);
+CX_EXTERN CX_NONNULL CX_NODISCARD
+CxTreeIterator cxTreeVisit(CxTree *tree);
/**
* Sets the (new) parent of the specified child.
*
* If the @p child is not already a member of the tree, this function behaves
- * as #cxTreeAddChildNode().
+ * as #cxTreeAddNode().
*
* @param tree the tree
* @param parent the (new) parent of the child
* @param child the node to add
- * @see cxTreeAddChildNode()
+ * @see cxTreeAddNode()
*/
-cx_attr_nonnull
-CX_EXPORT void cxTreeSetParent(CxTree *tree, void *parent, void *child);
+CX_EXTERN CX_NONNULL
+void cxTreeSetParent(CxTree *tree, void *parent, void *child);
/**
* Adds a new node to the tree.
* Use #cxTreeSetParent() if you want to move a subtree to another location.
*
* @attention The node may be externally created, but MUST obey the same rules
- * as if it was created by the tree itself with #cxTreeAddChild() (e.g., use
- * the same allocator).
+ * as if it was created by the tree itself with #cxTreeAddData() (e.g., use
+ * the tree's allocator).
*
* @param tree the tree
* @param parent the parent of the node to add
* @param child the node to add
* @see cxTreeSetParent()
+ * @see cxTreeAddData()
*/
-cx_attr_nonnull
-CX_EXPORT void cxTreeAddChildNode(CxTree *tree, void *parent, void *child);
+CX_EXTERN CX_NONNULL
+void cxTreeAddNode(CxTree *tree, void *parent, void *child);
/**
* Creates a new node and adds it to the tree.
*
- * With this function you can decide where exactly the new node shall be added.
- * If you specified an appropriate search function, you may want to consider
- * leaving this task to the tree by using #cxTreeInsert().
- *
- * Be aware that adding nodes at arbitrary locations in the tree might cause
- * wrong or undesired results when subsequently invoking #cxTreeInsert(), and
- * the invariant imposed by the search function does not hold any longer.
- *
* @param tree the tree
* @param parent the parent node of the new node
* @param data the data that will be submitted to the create function
- * @return zero when the new node was created, non-zero on allocation failure
- * @see cxTreeInsert()
+ * @return the added node or @c NULL when the allocation failed
+ * @see cxTreeAdd()
*/
-cx_attr_nonnull
-CX_EXPORT int cxTreeAddChild(CxTree *tree, void *parent, const void *data);
+CX_EXTERN CX_NONNULL
+void *cxTreeAddData(CxTree *tree, void *parent, const void *data);
+
+/**
+ * Creates a new node and adds it to the tree.
+ *
+ * @param tree the tree
+ * @param parent the parent node of the new node
+ * @return the added node or @c NULL when the allocation failed
+ */
+CX_EXTERN CX_NODISCARD CX_NONNULL
+void *cxTreeCreateNode(CxTree *tree, void *parent);
+
+/**
+ * Creates a new root node or returns the existing root node.
+ *
+ * @param tree the tree
+ * @return the new root node, the existing root node, or @c NULL when the allocation failed
+ * @see cxTreeCreateRootData()
+ */
+CX_EXTERN CX_NODISCARD CX_NONNULL
+void *cxTreeCreateRoot(CxTree *tree);
+
+/**
+ * Creates a new root node or uses the existing root node and writes the specified data to that node.
+ *
+ * @note This function immediately returns @c NULL when @c loc_data was not initialized with a positive value.
+ *
+ * @param tree the tree
+ * @param data the data for the root node
+ * @return the new root node, the existing root node,
+ * or @c NULL when the allocation failed or the data location is not known
+ * @see cxTreeCreateRoot()
+ */
+CX_EXTERN CX_NODISCARD CX_NONNULL
+void *cxTreeCreateRootData(CxTree *tree, const void *data);
+
+/**
+ * Exchanges the root of the tree.
+ *
+ * @attention The old tree nodes might need to be deallocated by the caller.
+ * On the other hand, when the tree has destructors registered, keep in mind
+ * that they will be applied to the new tree.
+ * In particular, using cxTreeCreate() with a @c NULL root and setting the
+ * root with this function is @em not equivalent to creating the tree with
+ * a reference to an existing root because trees created without a root will
+ * have destructors registered.
+ *
+ * @param tree the tree
+ * @param new_root the new root node
+ * @return the old root node (or @c NULL when the tree was empty)
+ */
+CX_EXTERN CX_NONNULL_ARG(1) CX_NODISCARD
+void *cxTreeSetRoot(CxTree *tree, void *new_root);
/**
* A function that is invoked when a node needs to be re-linked to a new parent.
* node
* @return zero on success, non-zero if @p node is the root node of the tree
*/
-cx_attr_nonnull_arg(1, 2)
-CX_EXPORT int cxTreeRemoveNode(CxTree *tree, void *node, cx_tree_relink_func relink_func);
+CX_EXTERN CX_NONNULL_ARG(1, 2)
+int cxTreeRemoveNode(CxTree *tree, void *node, cx_tree_relink_func relink_func);
/**
* Removes a node and its subtree from the tree.
* @param tree the tree
* @param node the node to remove
*/
-cx_attr_nonnull
-CX_EXPORT void cxTreeRemoveSubtree(CxTree *tree, void *node);
+CX_EXTERN CX_NONNULL
+void cxTreeRemoveSubtree(CxTree *tree, void *node);
/**
* Destroys a node and re-links its children to its former parent.
* node
* @return zero on success, non-zero if @p node is the root node of the tree
*/
-cx_attr_nonnull_arg(1, 2)
-CX_EXPORT int cxTreeDestroyNode(CxTree *tree, void *node, cx_tree_relink_func relink_func);
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
+CX_EXTERN CX_NONNULL_ARG(1, 2)
+int cxTreeDestroyNode(CxTree *tree, void *node, cx_tree_relink_func relink_func);
#endif //UCX_TREE_H
switch (len) {
case 3:
h ^= (data[i + 2] & 0xFF) << 16;
- cx_attr_fallthrough;
+ CX_FALLTHROUGH;
case 2:
h ^= (data[i + 1] & 0xFF) << 8;
- cx_attr_fallthrough;
+ CX_FALLTHROUGH;
case 1:
h ^= (data[i + 0] & 0xFF);
h *= m;
- cx_attr_fallthrough;
+ CX_FALLTHROUGH;
default: // do nothing
;
}
key->hash = h;
}
-
-uint32_t cx_hash_u32(uint32_t x) {
- x = ((x >> 16) ^ x) * 0x45d9f3bu;
- x = ((x >> 16) ^ x) * 0x45d9f3bu;
- x = (x >> 16) ^ x;
- return x;
-}
-
-uint64_t cx_hash_u64(uint64_t x) {
- x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
- x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb);
- x = x ^ (x >> 31);
- return x;
-}
-
-CxHashKey cx_hash_key_str(const char *str) {
- CxHashKey key;
- key.data = str;
- key.len = str == NULL ? 0 : strlen(str);
- cx_hash_murmur(&key);
- return key;
-}
-
-CxHashKey cx_hash_key_ustr(unsigned const char *str) {
- CxHashKey key;
- key.data = str;
- key.len = str == NULL ? 0 : strlen((const char*)str);
- cx_hash_murmur(&key);
- return key;
-}
-
-CxHashKey cx_hash_key_cxstr(cxstring str) {
- return cx_hash_key(str.ptr, str.length);
-}
-
-CxHashKey cx_hash_key_mutstr(cxmutstr str) {
- return cx_hash_key(str.ptr, str.length);
-}
-
-CxHashKey cx_hash_key_bytes(
- const unsigned char *bytes,
- size_t len
-) {
- CxHashKey key;
- key.data = bytes;
- key.len = len;
- cx_hash_murmur(&key);
- return key;
-}
-
CxHashKey cx_hash_key(
const void *obj,
size_t len
return key;
}
-CxHashKey cx_hash_key_u32(uint32_t x) {
- CxHashKey key;
- key.data = NULL;
- key.len = 0;
- key.hash = cx_hash_u32(x);
- return key;
-}
-
-CxHashKey cx_hash_key_u64(uint64_t x) {
- CxHashKey key;
- key.data = NULL;
- key.len = 0;
- key.hash = cx_hash_u64(x);
- return key;
-}
-
-int cx_hash_key_cmp(const CxHashKey *left, const CxHashKey *right) {
+int cx_hash_key_cmp(const void *l, const void *r) {
+ const CxHashKey *left = l;
+ const CxHashKey *right = r;
int d;
d = cx_vcmp_uint64(left->hash, right->hash);
if (d != 0) return d;
if (left->len == 0) return 0;
return memcmp(left->data, right->data, left->len);
}
+
+cxstring cx_hash_key_as_string(const CxHashKey *key) {
+ return cx_strn(key->data, key->len);
+}
cxFree(map->collection.allocator, map);
}
-static void *cx_hash_map_put(
+static CxMapEntry cx_hash_map_put(
CxMap *map,
CxHashKey key,
void *value
struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map;
const CxAllocator *allocator = map->collection.allocator;
- unsigned hash = key.hash;
+ uint64_t hash = key.hash;
if (hash == 0) {
cx_hash_murmur(&key);
hash = key.hash;
allocator,
sizeof(struct cx_hash_map_element_s) + map->collection.elem_size
);
- if (e == NULL) return NULL;
+ if (e == NULL) return (CxMapEntry){NULL, NULL}; // LCOV_EXCL_LINE
// write the value
if (value == NULL) {
// copy the key
void *kd = cxMalloc(allocator, key.len);
- if (kd == NULL) {
+ if (kd == NULL) { // LCOV_EXCL_START
cxFree(allocator, e);
- return NULL;
- }
+ return (CxMapEntry){NULL, NULL};
+ } // LCOV_EXCL_STOP
memcpy(kd, key.data, key.len);
e->key.data = kd;
e->key.len = key.len;
map->collection.size++;
}
- // return pointer to the element
- return elm->data;
+ // return the entry
+ return (CxMapEntry){&elm->key, elm->data};
}
static void cx_hash_map_unlink(
) {
struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map;
- unsigned hash = key.hash;
+ uint64_t hash = key.hash;
if (hash == 0) {
cx_hash_murmur(&key);
hash = key.hash;
cx_hash_map_iterator,
};
+static int cx_map_cmpfunc2_safe_memcmp(const void *a, const void *b, void *c) {
+ // it is not safe to store a pointer to the size in the list
+ // because the entire list structure might get reallocated
+ size_t elem_size = (size_t)(uintptr_t)c;
+ return memcmp(a, b, elem_size);
+}
+
CxMap *cxHashMapCreate(
const CxAllocator *allocator,
size_t itemsize,
buckets = 16;
}
- struct cx_hash_map_s *map = cxCalloc(allocator, 1,
- sizeof(struct cx_hash_map_s));
+ struct cx_hash_map_s *map = cxZalloc(allocator, sizeof(struct cx_hash_map_s));
if (map == NULL) return NULL;
// initialize hash map members
if (itemsize > 0) {
map->base.collection.elem_size = itemsize;
+ map->base.collection.advanced_cmp = cx_map_cmpfunc2_safe_memcmp;
+ map->base.collection.cmp_data = (void*)(uintptr_t)itemsize;
} else {
map->base.collection.elem_size = sizeof(void *);
map->base.collection.store_pointer = true;
+ map->base.collection.simple_cmp = cx_cmp_ptr;
}
return (CxMap *) map;
size_t new_bucket_count = (map->collection.size * 5) >> 1;
if (new_bucket_count < hash_map->bucket_count) {
+ // LCOV_EXCL_START
errno = EOVERFLOW;
return 1;
- }
+ } // LCOV_EXCL_STOP
struct cx_hash_map_element_s **new_buckets = cxCalloc(
map->collection.allocator,
new_bucket_count, sizeof(struct cx_hash_map_element_s *)
);
- if (new_buckets == NULL) return 1;
+ if (new_buckets == NULL) return 1; // LCOV_EXCL_LINE
// iterate through the elements and assign them to their new slots
for (size_t slot = 0; slot < hash_map->bucket_count; slot++) {
#include "cx/iterator.h"
#include <string.h>
+#include <assert.h>
static bool cx_iter_valid(const void *it) {
const struct cx_iterator_s *iter = it;
return *(void**)iter->elem_handle;
}
-static void cx_iter_next_fast(void *it) {
+static void cx_iter_next(void *it) {
struct cx_iterator_s *iter = it;
- if (iter->base.remove) {
- iter->base.remove = false;
- iter->elem_count--;
- // only move the last element when we are not currently aiming
- // at the last element already
- if (iter->index < iter->elem_count) {
- void *last = ((char *) iter->src_handle)
- + iter->elem_count * iter->elem_size;
- memcpy(iter->elem_handle, last, iter->elem_size);
- }
- } else {
- iter->index++;
- iter->elem_handle = ((char *) iter->elem_handle) + iter->elem_size;
- }
+ assert(!iter->base.remove);
+ iter->index++;
+ iter->elem_handle = ((char *) iter->elem_handle) + iter->elem_size;
}
-static void cx_iter_next_slow(void *it) {
- struct cx_iterator_s *iter = it;
- if (iter->base.remove) {
- iter->base.remove = false;
- iter->elem_count--;
-
- // number of elements to move
- size_t remaining = iter->elem_count - iter->index;
- if (remaining > 0) {
- memmove(
- iter->elem_handle,
- ((char *) iter->elem_handle) + iter->elem_size,
- remaining * iter->elem_size
- );
- }
- } else {
- iter->index++;
- iter->elem_handle = ((char *) iter->elem_handle) + iter->elem_size;
- }
-}
-
-CxIterator cxIterator(
- const void *array,
- size_t elem_size,
- size_t elem_count,
- bool remove_keeps_order
-) {
+CxIterator cxIterator(const void *array, size_t elem_size, size_t elem_count) {
CxIterator iter;
iter.index = 0;
iter.elem_count = array == NULL ? 0 : elem_count;
iter.base.valid = cx_iter_valid;
iter.base.current = cx_iter_current;
- iter.base.next = remove_keeps_order ? cx_iter_next_slow : cx_iter_next_fast;
+ iter.base.next = cx_iter_next;
+ iter.base.valid_impl = NULL;
+ iter.base.current_impl = NULL;
+ iter.base.next_impl = NULL;
iter.base.remove = false;
iter.base.allow_remove = true;
return iter;
}
-CxIterator cxIteratorPtr(
- const void *array,
- size_t elem_count,
- bool remove_keeps_order
-) {
- CxIterator iter = cxIterator(array, sizeof(void*), elem_count, remove_keeps_order);
+CxIterator cxIteratorPtr(const void *array, size_t elem_count) {
+ CxIterator iter = cxIterator(array, sizeof(void*), elem_count);
iter.base.current = cx_iter_current_ptr;
return iter;
}
*/
#include "cx/json.h"
+#include "cx/kv_list.h"
#include <string.h>
#include <assert.h>
static CxJsonValue cx_json_value_nothing = {.type = CX_JSON_NOTHING};
-static int json_cmp_objvalue(const void *l, const void *r) {
- const CxJsonObjValue *left = l;
- const CxJsonObjValue *right = r;
- return cx_strcmp(cx_strcast(left->name), cx_strcast(right->name));
-}
-
-static size_t json_find_objvalue(const CxJsonValue *obj, cxstring name) {
- assert(obj->type == CX_JSON_OBJECT);
- CxJsonObjValue kv_dummy;
- kv_dummy.name = cx_mutstrn((char*) name.ptr, name.length);
- return cx_array_binary_search(
- obj->value.object.values,
- obj->value.object.values_size,
- sizeof(CxJsonObjValue),
- &kv_dummy,
- json_cmp_objvalue
- );
-}
-
-static int json_add_objvalue(CxJsonValue *objv, CxJsonObjValue member) {
- assert(objv->type == CX_JSON_OBJECT);
- const CxAllocator * const al = objv->allocator;
- CxJsonObject *obj = &(objv->value.object);
-
- // determine the index where we need to insert the new member
- size_t index = cx_array_binary_search_sup(
- obj->values,
- obj->values_size,
- sizeof(CxJsonObjValue),
- &member, json_cmp_objvalue
- );
-
- // is the name already present?
- if (index < obj->values_size && 0 == json_cmp_objvalue(&member, &obj->values[index])) {
- // free the original value
- cx_strfree_a(al, &obj->values[index].name);
- cxJsonValueFree(obj->values[index].value);
- // replace the item
- obj->values[index] = member;
-
- // nothing more to do
- return 0;
- }
-
- // determine the old capacity and reserve for one more element
- CxArrayReallocator arealloc = cx_array_reallocator(al, NULL);
- size_t oldcap = obj->values_capacity;
- if (cx_array_simple_reserve_a(&arealloc, obj->values, 1)) return 1;
-
- // check the new capacity, if we need to realloc the index array
- size_t newcap = obj->values_capacity;
- if (newcap > oldcap) {
- if (cxReallocateArray(al, &obj->indices, newcap, sizeof(size_t))) {
- return 1;
- }
- }
-
- // check if append or insert
- if (index < obj->values_size) {
- // move the other elements
- memmove(
- &obj->values[index+1],
- &obj->values[index],
- (obj->values_size - index) * sizeof(CxJsonObjValue)
- );
- // increase indices for the moved elements
- for (size_t i = 0; i < obj->values_size ; i++) {
- if (obj->indices[i] >= index) {
- obj->indices[i]++;
- }
- }
- }
-
- // insert the element and set the index
- obj->values[index] = member;
- obj->indices[obj->values_size] = index;
- obj->values_size++;
-
- return 0;
-}
-
static void token_destroy(CxJsonToken *token) {
if (token->allocated) {
cx_strfree(&token->content);
+ token->allocated = false;
}
}
static CxJsonToken token_create(CxJson *json, bool isstring, size_t start, size_t end) {
cxmutstr str = cx_mutstrn(json->buffer.space + start, end - start);
bool allocated = false;
- if (json->uncompleted.tokentype != CX_JSON_NO_TOKEN) {
+ if (json->uncompleted_tokentype != CX_JSON_NO_TOKEN) {
allocated = true;
- str = cx_strcat_m(json->uncompleted.content, 1, str);
- if (str.ptr == NULL) { // LCOV_EXCL_START
- return (CxJsonToken){CX_JSON_NO_TOKEN, false, {NULL, 0}};
- } // LCOV_EXCL_STOP
+ str = cx_strcat(json->uncompleted_content, 1, str);
+ if (str.ptr == NULL) {
+ return (CxJsonToken){CX_JSON_NO_TOKEN, false, CX_NULLSTR}; // LCOV_EXCL_LINE
+ }
+ json->uncompleted_content = CX_NULLSTR;
+ json->uncompleted_tokentype = CX_JSON_NO_TOKEN;
}
- json->uncompleted = (CxJsonToken){0};
CxJsonTokenType ttype;
if (isstring) {
ttype = CX_JSON_TOKEN_STRING;
} else {
- cxstring s = cx_strcast(str);
- if (!cx_strcmp(s, CX_STR("true")) || !cx_strcmp(s, CX_STR("false"))
- || !cx_strcmp(s, CX_STR("null"))) {
+ if (!cx_strcmp(str, "true") || !cx_strcmp(str, "false")
+ || !cx_strcmp(str, "null")) {
ttype = CX_JSON_TOKEN_LITERAL;
} else {
ttype = token_numbertype(str.ptr, str.length);
if (allocated) {
cx_strfree(&str);
}
- return (CxJsonToken){CX_JSON_TOKEN_ERROR, false, {NULL, 0}};
+ return (CxJsonToken){CX_JSON_TOKEN_ERROR, false, CX_NULLSTR};
}
return (CxJsonToken){ttype, allocated, str};
}
static enum cx_json_status token_parse_next(CxJson *json, CxJsonToken *result) {
// check if there is data in the buffer
if (cxBufferEof(&json->buffer)) {
- return json->uncompleted.tokentype == CX_JSON_NO_TOKEN ?
+ return json->uncompleted_tokentype == CX_JSON_NO_TOKEN ?
CX_JSON_NO_DATA : CX_JSON_INCOMPLETE_DATA;
}
// current token type and start index
- CxJsonTokenType ttype = json->uncompleted.tokentype;
+ CxJsonTokenType ttype = json->uncompleted_tokentype;
size_t token_part_start = json->buffer.pos;
bool escape_end_of_string = ttype == CX_JSON_TOKEN_STRING
- && json->uncompleted.content.ptr[json->uncompleted.content.length-1] == '\\';
+ && cx_strat(json->uncompleted_content, -1) == '\\';
for (size_t i = json->buffer.pos; i < json->buffer.size; i++) {
char c = json->buffer.space[i];
} else if (ctype != CX_JSON_NO_TOKEN) {
// single-char token
json->buffer.pos = i + 1;
- *result = (CxJsonToken){ctype, false, {NULL, 0}};
+ *result = (CxJsonToken){ctype, false, CX_NULLSTR};
return CX_JSON_NO_ERROR;
} else {
ttype = CX_JSON_TOKEN_LITERAL; // number or literal
}
}
- if (ttype != CX_JSON_NO_TOKEN) {
+ if (ttype == CX_JSON_NO_TOKEN) {
+ return CX_JSON_NO_DATA;
+ } else {
// uncompleted token
- size_t uncompleted_len = json->buffer.size - token_part_start;
- if (json->uncompleted.tokentype == CX_JSON_NO_TOKEN) {
- // current token is uncompleted
- // save current token content
- CxJsonToken uncompleted = {
- ttype, true,
- cx_strdup(cx_strn(json->buffer.space + token_part_start, uncompleted_len))
- };
- if (uncompleted.content.ptr == NULL) {
+ cxstring uncompleted = cx_strn(json->buffer.space + token_part_start, json->buffer.size - token_part_start);
+ if (json->uncompleted_tokentype == CX_JSON_NO_TOKEN) {
+ assert(json->uncompleted_content.ptr == NULL);
+ json->uncompleted_content = cx_strdup(uncompleted);
+ if (json->uncompleted_content.ptr == NULL) {
return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE
}
- json->uncompleted = uncompleted;
+ json->uncompleted_tokentype = ttype;
} else {
- // previously we also had an uncompleted token
+ // previously we already had an uncompleted token
// combine the uncompleted token with the current token
- assert(json->uncompleted.allocated);
- cxmutstr str = cx_strcat_m(json->uncompleted.content, 1,
- cx_strn(json->buffer.space + token_part_start, uncompleted_len));
- if (str.ptr == NULL) {
+ cxmutstr s = cx_strcat(json->uncompleted_content, 1, uncompleted);
+ if (s.ptr == NULL) {
return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE
}
- json->uncompleted.content = str;
+ json->uncompleted_content = s;
}
// advance the buffer position - we saved the stuff in the uncompleted token
- json->buffer.pos += uncompleted_len;
+ json->buffer.pos += uncompleted.length;
+ return CX_JSON_INCOMPLETE_DATA;
}
-
- return CX_JSON_INCOMPLETE_DATA;
}
// converts a Unicode codepoint to utf8
} else if (c == 'u') {
char utf8buf[4];
unsigned utf8len = unescape_unicode_string(
- cx_strn(str.ptr + i - 1, str.length + 1 - i),
+ cx_strn(str.ptr + i - 1, str.length - i),
utf8buf
);
if(utf8len > 0) {
} else {
// TODO: discuss the behavior for unrecognized escape sequences
// most parsers throw an error here - we just ignore it
- result.ptr[result.length++] = '\\';
+ result.ptr[result.length++] = '\\'; // LCOV_EXCL_LINE
}
result.ptr[result.length++] = c;
return result;
}
-static cxmutstr escape_string(cxmutstr str, bool escape_slash) {
+static cxmutstr escape_string(cxstring str, bool escape_slash) {
// note: this function produces the string without enclosing quotes
// the reason is that we don't want to allocate memory just for that
CxBuffer buf = {0};
size_t capa = str.length + 32;
char *space = cxMallocDefault(capa);
if (space == NULL) return cx_mutstrn(NULL, 0);
- cxBufferInit(&buf, space, capa, NULL, CX_BUFFER_AUTO_EXTEND);
+ cxBufferInit(&buf, NULL, space, capa, CX_BUFFER_AUTO_EXTEND);
cxBufferWrite(str.ptr, 1, i, &buf);
all_printable = false;
}
cxBufferPut(&buf, c);
}
}
- if (!all_printable) {
- str = cx_mutstrn(buf.space, buf.size);
+ cxmutstr ret;
+ if (all_printable) {
+ // don't copy the string when we don't need to escape anything
+ ret = cx_mutstrn((char*)str.ptr, str.length);
+ } else {
+ ret = cx_mutstrn(buf.space, buf.size);
}
cxBufferDestroy(&buf);
- return str;
+ return ret;
+}
+
+static CxJsonObject json_create_object_map(const CxAllocator *allocator) {
+ CxMap *map = cxKvListCreateAsMap(allocator, CX_STORE_POINTERS);
+ if (map == NULL) return NULL; // LCOV_EXCL_LINE
+ cxSetCompareFunc(map, cxJsonCompare);
+ cxSetDestructor(map, cxJsonValueFree);
+ return map;
+}
+
+static void json_free_object_map(CxJsonObject obj) {
+ cxMapFree(obj);
}
static CxJsonValue* json_create_value(CxJson *json, CxJsonValueType type) {
v->type = type;
v->allocator = json->allocator;
if (type == CX_JSON_ARRAY) {
- cx_array_initialize_a(json->allocator, v->value.array.array, 16);
- if (v->value.array.array == NULL) goto create_json_value_exit_error; // LCOV_EXCL_LINE
- } else if (type == CX_JSON_OBJECT) {
- cx_array_initialize_a(json->allocator, v->value.object.values, 16);
- v->value.object.indices = cxCalloc(json->allocator, 16, sizeof(size_t));
- if (v->value.object.values == NULL ||
- v->value.object.indices == NULL)
+ if (cx_array_init_a(json->allocator, v->array, 16)) {
goto create_json_value_exit_error; // LCOV_EXCL_LINE
+ }
+ } else if (type == CX_JSON_OBJECT) {
+ v->object = json_create_object_map(json->allocator);
+ if (v->object == NULL) goto create_json_value_exit_error; // LCOV_EXCL_LINE
}
// add the new value to a possible parent
- if (json->vbuf_size > 0) {
- CxJsonValue *parent = json->vbuf[json->vbuf_size - 1];
+ if (json->vbuf.size > 0) {
+ CxJsonValue *parent = json->vbuf.data[json->vbuf.size - 1];
assert(parent != NULL);
if (parent->type == CX_JSON_ARRAY) {
- CxArrayReallocator value_realloc = cx_array_reallocator(json->allocator, NULL);
- if (cx_array_simple_add_a(&value_realloc, parent->value.array.array, v)) {
+ if (cx_array_add_a(json->allocator, parent->array, v)) {
goto create_json_value_exit_error; // LCOV_EXCL_LINE
}
} else if (parent->type == CX_JSON_OBJECT) {
// the member was already created after parsing the name
- assert(json->uncompleted_member.name.ptr != NULL);
- json->uncompleted_member.value = v;
- if (json_add_objvalue(parent, json->uncompleted_member)) {
+ // store the pointer of the uncompleted value in the map
+ assert(json->uncompleted_member_name.ptr != NULL);
+ if (cxMapPut(parent->object, json->uncompleted_member_name, v)) {
goto create_json_value_exit_error; // LCOV_EXCL_LINE
}
- json->uncompleted_member.name = (cxmutstr) {NULL, 0};
+ cx_strfree_a(json->allocator, &json->uncompleted_member_name);
} else {
assert(false); // LCOV_EXCL_LINE
}
// add the new value to the stack, if it is an array or object
if (type == CX_JSON_ARRAY || type == CX_JSON_OBJECT) {
- CxArrayReallocator vbuf_realloc = cx_array_reallocator(NULL, json->vbuf_internal);
- if (cx_array_simple_add_a(&vbuf_realloc, json->vbuf, v)) {
- goto create_json_value_exit_error; // LCOV_EXCL_LINE
+ if (json->vbuf.size >= json->vbuf.capacity) {
+ int alloc_error;
+ if (json->vbuf.data == json->vbuf_internal) {
+ alloc_error = cx_array_copy_to_new(json->vbuf, json->vbuf.size+1);
+ } else {
+ alloc_error = cx_array_reserve(json->vbuf, json->vbuf.size+1);
+ }
+ if (alloc_error) {
+ goto create_json_value_exit_error; // LCOV_EXCL_LINE
+ }
}
+ json->vbuf.data[json->vbuf.size] = v;
+ json->vbuf.size++;
}
// if currently no value is parsed, this is now the value of interest
memset(json, 0, sizeof(CxJson));
json->allocator = allocator;
- json->states = json->states_internal;
- json->states_capacity = cx_nmemb(json->states_internal);
- json->states[0] = JP_STATE_VALUE_BEGIN;
- json->states_size = 1;
-
- json->vbuf = json->vbuf_internal;
- json->vbuf_capacity = cx_nmemb(json->vbuf_internal);
+ cx_array_init_fixed(json->states, json->states_internal, 1);
+ json->states.data[0] = JP_STATE_VALUE_BEGIN;
+ cx_array_init_fixed(json->vbuf, json->vbuf_internal, 0);
}
void cxJsonDestroy(CxJson *json) {
cxBufferDestroy(&json->buffer);
- if (json->states != json->states_internal) {
- cxFreeDefault(json->states);
+ if (json->states.data != json->states_internal) {
+ cx_array_free(json->states);
}
- if (json->vbuf != json->vbuf_internal) {
- cxFreeDefault(json->vbuf);
+ if (json->vbuf.data != json->vbuf_internal) {
+ cx_array_free(json->vbuf);
}
cxJsonValueFree(json->parsed);
json->parsed = NULL;
- if (json->uncompleted_member.name.ptr != NULL) {
- cx_strfree_a(json->allocator, &json->uncompleted_member.name);
- json->uncompleted_member = (CxJsonObjValue){{NULL, 0}, NULL};
- }
+ json->uncompleted_tokentype = CX_JSON_NO_TOKEN;
+ cx_strfree(&json->uncompleted_content);
+ cx_strfree_a(json->allocator, &json->uncompleted_member_name);
}
void cxJsonReset(CxJson *json) {
if (cxBufferEof(&json->buffer)) {
// reinitialize the buffer
cxBufferDestroy(&json->buffer);
- cxBufferInit(&json->buffer, (char*) buf, size,
- NULL, CX_BUFFER_AUTO_EXTEND | CX_BUFFER_COPY_ON_WRITE);
+ if (buf == NULL) buf = ""; // buffer must not be initialized with NULL
+ cxBufferInit(&json->buffer, NULL, (char*) buf,
+ size, CX_BUFFER_AUTO_EXTEND | CX_BUFFER_COPY_ON_WRITE);
json->buffer.size = size;
return 0;
} else {
}
static void json_add_state(CxJson *json, int state) {
- // we have guaranteed the necessary space with cx_array_simple_reserve()
+ // we have guaranteed the necessary space
// therefore, we can safely add the state in the simplest way possible
- json->states[json->states_size++] = state;
+ json->states.data[json->states.size++] = state;
}
#define return_rec(code) \
}
// pop the current state
- assert(json->states_size > 0);
- int state = json->states[--json->states_size];
-
- // guarantee that at least two more states fit on the stack
- CxArrayReallocator state_realloc = cx_array_reallocator(NULL, json->states_internal);
- if (cx_array_simple_reserve_a(&state_realloc, json->states, 2)) {
- return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE
+ assert(json->states.size > 0);
+ int state = json->states.data[--json->states.size];
+
+ // guarantee that at least two more states fit into the array
+ const size_t required_states_depth = json->states.size + 2;
+ if (required_states_depth >= json->states.capacity) {
+ int alloc_error;
+ if (json->states.data == json->states_internal) {
+ alloc_error = cx_array_copy_to_new(json->states, required_states_depth);
+ } else {
+ alloc_error = cx_array_reserve(json->states, required_states_depth);
+ }
+ if (alloc_error) {
+ return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE
+ }
}
json_add_state(json, JP_STATE_OBJ_NAME_OR_CLOSE);
return_rec(CX_JSON_NO_ERROR);
}
+ case CX_JSON_TOKEN_END_ARRAY: {
+ if (state == JP_STATE_VALUE_BEGIN_AR) {
+ // discard the array from the value buffer
+ json->vbuf.size--;
+ json->states.size--;
+ return_rec(CX_JSON_NO_ERROR);
+ } else {
+ return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN);
+ }
+ }
case CX_JSON_TOKEN_STRING: {
if ((vbuf = json_create_value(json, CX_JSON_STRING)) == NULL) {
return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE
if (str.ptr == NULL) {
return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE
}
- vbuf->value.string = str;
+ vbuf->string = str;
return_rec(CX_JSON_NO_ERROR);
}
case CX_JSON_TOKEN_INTEGER:
return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE
}
if (type == CX_JSON_INTEGER) {
- if (cx_strtoi64(token.content, &vbuf->value.integer, 10)) {
+ if (cx_strtoi64(token.content, &vbuf->integer, 10)) {
return_rec(CX_JSON_FORMAT_ERROR_NUMBER);
}
} else {
- if (cx_strtod(token.content, &vbuf->value.number)) {
- return_rec(CX_JSON_FORMAT_ERROR_NUMBER);
+ if (cx_strtod(token.content, &vbuf->number)) {
+ // TODO: at the moment this is unreachable, because the tokenizer is already stricter than cx_strtod()
+ return_rec(CX_JSON_FORMAT_ERROR_NUMBER); // LCOV_EXCL_LINE
}
}
return_rec(CX_JSON_NO_ERROR);
if ((vbuf = json_create_value(json, CX_JSON_LITERAL)) == NULL) {
return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE
}
- if (0 == cx_strcmp(cx_strcast(token.content), cx_str("true"))) {
- vbuf->value.literal = CX_JSON_TRUE;
- } else if (0 == cx_strcmp(cx_strcast(token.content), cx_str("false"))) {
- vbuf->value.literal = CX_JSON_FALSE;
+ if (0 == cx_strcmp(token.content, "true")) {
+ vbuf->literal = CX_JSON_TRUE;
+ } else if (0 == cx_strcmp(token.content, "false")) {
+ vbuf->literal = CX_JSON_FALSE;
} else {
- vbuf->value.literal = CX_JSON_NULL;
+ vbuf->literal = CX_JSON_NULL;
}
return_rec(CX_JSON_NO_ERROR);
}
return_rec(CX_JSON_NO_ERROR);
} else if (token.tokentype == CX_JSON_TOKEN_END_ARRAY) {
// discard the array from the value buffer
- json->vbuf_size--;
+ json->vbuf.size--;
return_rec(CX_JSON_NO_ERROR);
} else {
return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN);
} else if (state == JP_STATE_OBJ_NAME_OR_CLOSE || state == JP_STATE_OBJ_NAME) {
if (state == JP_STATE_OBJ_NAME_OR_CLOSE && token.tokentype == CX_JSON_TOKEN_END_OBJECT) {
// discard the obj from the value buffer
- json->vbuf_size--;
+ json->vbuf.size--;
return_rec(CX_JSON_NO_ERROR);
} else {
// expect string
if (name.ptr == NULL) {
return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE
}
- assert(json->uncompleted_member.name.ptr == NULL);
- json->uncompleted_member.name = name;
- assert(json->vbuf_size > 0);
+ assert(json->uncompleted_member_name.ptr == NULL);
+ json->uncompleted_member_name = name;
+ assert(json->vbuf.size > 0);
// next state
json_add_state(json, JP_STATE_OBJ_COLON);
return_rec(CX_JSON_NO_ERROR);
} else if (token.tokentype == CX_JSON_TOKEN_END_OBJECT) {
// discard the obj from the value buffer
- json->vbuf_size--;
+ json->vbuf.size--;
return_rec(CX_JSON_NO_ERROR);
} else {
return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN);
} else {
// should be unreachable
assert(false);
- return_rec(-1);
+ return_rec(-1); // LCOV_EXCL_LINE
}
}
CxJsonStatus cxJsonNext(CxJson *json, CxJsonValue **value) {
- // check if buffer has been filled
+ // initialize output value
+ *value = &cx_json_value_nothing;
+
+ // check if the buffer has been filled
if (json->buffer.space == NULL) {
return CX_JSON_NULL_DATA;
}
- // initialize output value
- *value = &cx_json_value_nothing;
-
// parse data
CxJsonStatus result;
do {
result = json_parse(json);
- if (result == CX_JSON_NO_ERROR && json->states_size == 1) {
+ if (result == CX_JSON_NO_ERROR && json->states.size == 1) {
// final state reached
- assert(json->states[0] == JP_STATE_VALUE_END);
- assert(json->vbuf_size == 0);
+ assert(json->states.data[0] == JP_STATE_VALUE_END);
+ assert(json->vbuf.size == 0);
// write output value
*value = json->parsed;
json->parsed = NULL;
// re-initialize state machine
- json->states[0] = JP_STATE_VALUE_BEGIN;
+ json->states.data[0] = JP_STATE_VALUE_BEGIN;
return CX_JSON_NO_ERROR;
}
// the parser might think there is no data
// but when we did not reach the final state,
// we know that there must be more to come
- if (result == CX_JSON_NO_DATA && json->states_size > 1) {
+ if (result == CX_JSON_NO_DATA && json->states.size > 1) {
return CX_JSON_INCOMPLETE_DATA;
}
return result;
}
+CxJsonStatus cx_json_from_string(const CxAllocator *allocator,
+ cxstring str, CxJsonValue **value) {
+ *value = &cx_json_value_nothing;
+ CxJson parser;
+ cxJsonInit(&parser, allocator);
+ if (cxJsonFill(&parser, str)) {
+ // LCOV_EXCL_START
+ cxJsonDestroy(&parser);
+ return CX_JSON_BUFFER_ALLOC_FAILED;
+ // LCOV_EXCL_STOP
+ }
+ CxJsonStatus status = cxJsonNext(&parser, value);
+ // check if we consume the total string
+ CxJsonValue *chk_value = NULL;
+ CxJsonStatus chk_status = CX_JSON_NO_DATA;
+ if (status == CX_JSON_NO_ERROR) {
+ chk_status = cxJsonNext(&parser, &chk_value);
+ }
+ cxJsonDestroy(&parser);
+ if (chk_status == CX_JSON_NO_DATA) {
+ return status;
+ } else {
+ cxJsonValueFree(*value);
+ // if chk_value is nothing, the free is harmless
+ cxJsonValueFree(chk_value);
+ *value = &cx_json_value_nothing;
+ return CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN;
+ }
+
+}
+
void cxJsonValueFree(CxJsonValue *value) {
if (value == NULL || value->type == CX_JSON_NOTHING) return;
switch (value->type) {
case CX_JSON_OBJECT: {
- CxJsonObject obj = value->value.object;
- for (size_t i = 0; i < obj.values_size; i++) {
- cxJsonValueFree(obj.values[i].value);
- cx_strfree_a(value->allocator, &obj.values[i].name);
- }
- cxFree(value->allocator, obj.values);
- cxFree(value->allocator, obj.indices);
+ json_free_object_map(value->object);
break;
}
case CX_JSON_ARRAY: {
- CxJsonArray array = value->value.array;
- for (size_t i = 0; i < array.array_size; i++) {
- cxJsonValueFree(array.array[i]);
+ for (size_t i = 0; i < value->array.size; i++) {
+ cxJsonValueFree(value->array.data[i]);
}
- cxFree(value->allocator, array.array);
+ cx_array_free_a(value->allocator, value->array);
break;
}
case CX_JSON_STRING: {
- cxFree(value->allocator, value->value.string.ptr);
+ cxFree(value->allocator, value->string.ptr);
break;
}
default: {
if (v == NULL) return NULL;
v->allocator = allocator;
v->type = CX_JSON_OBJECT;
- cx_array_initialize_a(allocator, v->value.object.values, 16);
- if (v->value.object.values == NULL) { // LCOV_EXCL_START
- cxFree(allocator, v);
- return NULL;
- // LCOV_EXCL_STOP
- }
- v->value.object.indices = cxCalloc(allocator, 16, sizeof(size_t));
- if (v->value.object.indices == NULL) { // LCOV_EXCL_START
- cxFree(allocator, v->value.object.values);
+ v->object = json_create_object_map(allocator);
+ if (v->object == NULL) { // LCOV_EXCL_START
cxFree(allocator, v);
return NULL;
// LCOV_EXCL_STOP
return v;
}
-CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator) {
+CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator, size_t capacity) {
if (allocator == NULL) allocator = cxDefaultAllocator;
CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue));
if (v == NULL) return NULL;
v->allocator = allocator;
v->type = CX_JSON_ARRAY;
- cx_array_initialize_a(allocator, v->value.array.array, 16);
- if (v->value.array.array == NULL) { cxFree(allocator, v); return NULL; }
+ if (capacity > 0) {
+ if (cx_array_init_a(allocator, v->array, capacity)) {
+ // LCOV_EXCL_START
+ cxFree(allocator, v);
+ return NULL;
+ // LCOV_EXCL_STOP
+ }
+ } else {
+ v->array.data = NULL;
+ v->array.size = v->array.capacity = 0;
+ }
return v;
}
if (v == NULL) return NULL;
v->allocator = allocator;
v->type = CX_JSON_NUMBER;
- v->value.number = num;
+ v->number = num;
return v;
}
if (v == NULL) return NULL;
v->allocator = allocator;
v->type = CX_JSON_INTEGER;
- v->value.integer = num;
+ v->integer = num;
return v;
}
-CxJsonValue* cxJsonCreateString(const CxAllocator* allocator, const char* str) {
- return cxJsonCreateCxString(allocator, cx_str(str));
-}
-
-CxJsonValue* cxJsonCreateCxString(const CxAllocator* allocator, cxstring str) {
+CxJsonValue* cx_json_create_string(const CxAllocator* allocator, cxstring str) {
if (allocator == NULL) allocator = cxDefaultAllocator;
CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue));
if (v == NULL) return NULL;
v->type = CX_JSON_STRING;
cxmutstr s = cx_strdup_a(allocator, str);
if (s.ptr == NULL) { cxFree(allocator, v); return NULL; }
- v->value.string = s;
+ v->string = s;
return v;
}
if (v == NULL) return NULL;
v->allocator = allocator;
v->type = CX_JSON_LITERAL;
- v->value.literal = lit;
+ v->literal = lit;
return v;
}
CxJsonValue** values = cxCallocDefault(count, sizeof(CxJsonValue*));
if (values == NULL) return -1;
for (size_t i = 0; i < count; i++) {
- values[i] = cxJsonCreateCxString(arr->allocator, str[i]);
+ values[i] = cxJsonCreateString(arr->allocator, str[i]);
if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; }
}
int ret = cxJsonArrAddValues(arr, values, count);
}
int cxJsonArrAddValues(CxJsonValue* arr, CxJsonValue* const* val, size_t count) {
- CxArrayReallocator value_realloc = cx_array_reallocator(arr->allocator, NULL);
assert(arr->type == CX_JSON_ARRAY);
- return cx_array_simple_copy_a(&value_realloc,
- arr->value.array.array,
- arr->value.array.array_size,
- val, count
- );
-}
-
-int cxJsonObjPut(CxJsonValue* obj, cxstring name, CxJsonValue* child) {
- cxmutstr k = cx_strdup_a(obj->allocator, name);
- if (k.ptr == NULL) return -1;
- CxJsonObjValue kv = {k, child};
- if (json_add_objvalue(obj, kv)) {
- cx_strfree_a(obj->allocator, &k);
- return 1;
- } else {
- return 0;
- }
+ return cx_array_add_array_a(arr->allocator, arr->array, val, count);
}
-CxJsonValue* cxJsonObjPutObj(CxJsonValue* obj, cxstring name) {
+int cx_json_obj_put(CxJsonValue* obj, cxstring name, CxJsonValue* child) {
+ return cxMapPut(obj->object, name, child);
+}
+
+CxJsonValue* cx_json_obj_put_obj(CxJsonValue* obj, cxstring name) {
CxJsonValue* v = cxJsonCreateObj(obj->allocator);
if (v == NULL) return NULL;
if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; }
return v;
}
-CxJsonValue* cxJsonObjPutArr(CxJsonValue* obj, cxstring name) {
- CxJsonValue* v = cxJsonCreateArr(obj->allocator);
+CxJsonValue* cx_json_obj_put_arr(CxJsonValue* obj, cxstring name, size_t capacity) {
+ CxJsonValue* v = cxJsonCreateArr(obj->allocator, capacity);
if (v == NULL) return NULL;
if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; }
return v;
}
-CxJsonValue* cxJsonObjPutNumber(CxJsonValue* obj, cxstring name, double num) {
+CxJsonValue* cx_json_obj_put_number(CxJsonValue* obj, cxstring name, double num) {
CxJsonValue* v = cxJsonCreateNumber(obj->allocator, num);
if (v == NULL) return NULL;
if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; }
return v;
}
-CxJsonValue* cxJsonObjPutInteger(CxJsonValue* obj, cxstring name, int64_t num) {
+CxJsonValue* cx_json_obj_put_integer(CxJsonValue* obj, cxstring name, int64_t num) {
CxJsonValue* v = cxJsonCreateInteger(obj->allocator, num);
if (v == NULL) return NULL;
if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; }
return v;
}
-CxJsonValue* cxJsonObjPutString(CxJsonValue* obj, cxstring name, const char* str) {
+CxJsonValue* cx_json_obj_put_string(CxJsonValue* obj, cxstring name, cxstring str) {
CxJsonValue* v = cxJsonCreateString(obj->allocator, str);
if (v == NULL) return NULL;
if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; }
return v;
}
-CxJsonValue* cxJsonObjPutCxString(CxJsonValue* obj, cxstring name, cxstring str) {
- CxJsonValue* v = cxJsonCreateCxString(obj->allocator, str);
- if (v == NULL) return NULL;
- if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; }
- return v;
-}
-
-CxJsonValue* cxJsonObjPutLiteral(CxJsonValue* obj, cxstring name, CxJsonLiteral lit) {
+CxJsonValue* cx_json_obj_put_literal(CxJsonValue* obj, cxstring name, CxJsonLiteral lit) {
CxJsonValue* v = cxJsonCreateLiteral(obj->allocator, lit);
if (v == NULL) return NULL;
if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL;}
}
CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index) {
- if (index >= value->value.array.array_size) {
+ if (index >= value->array.size) {
return &cx_json_value_nothing;
}
- return value->value.array.array[index];
+ return value->array.data[index];
}
CxJsonValue *cxJsonArrRemove(CxJsonValue *value, size_t index) {
- if (index >= value->value.array.array_size) {
+ if (index >= value->array.size) {
return NULL;
}
- CxJsonValue *ret = value->value.array.array[index];
- // TODO: replace with a low level cx_array_remove()
- size_t count = value->value.array.array_size - index - 1;
- if (count > 0) {
- memmove(value->value.array.array + index, value->value.array.array + index + 1, count * sizeof(CxJsonValue*));
- }
- value->value.array.array_size--;
+ CxJsonValue *ret = value->array.data[index];
+ cx_array_remove(value->array, index);
return ret;
}
char *cxJsonAsString(const CxJsonValue *value) {
- return value->value.string.ptr;
+ return value->string.ptr;
}
cxstring cxJsonAsCxString(const CxJsonValue *value) {
- return cx_strcast(value->value.string);
+ return cx_strcast(value->string);
}
cxmutstr cxJsonAsCxMutStr(const CxJsonValue *value) {
- return value->value.string;
+ return value->string;
}
double cxJsonAsDouble(const CxJsonValue *value) {
if (value->type == CX_JSON_INTEGER) {
- return (double) value->value.integer;
+ return (double) value->integer;
} else {
- return value->value.number;
+ return value->number;
}
}
int64_t cxJsonAsInteger(const CxJsonValue *value) {
if (value->type == CX_JSON_INTEGER) {
- return value->value.integer;
+ return value->integer;
} else {
- return (int64_t) value->value.number;
+ return (int64_t) value->number;
}
}
CxIterator cxJsonArrIter(const CxJsonValue *value) {
- return cxIteratorPtr(
- value->value.array.array,
- value->value.array.array_size,
- true // arrays need to keep order
- );
+ return cx_array_iterator_ptr(value->array);
}
-CxIterator cxJsonObjIter(const CxJsonValue *value) {
- return cxIterator(
- value->value.object.values,
- sizeof(CxJsonObjValue),
- value->value.object.values_size,
- true // TODO: objects do not always need to keep order
- );
+CxMapIterator cxJsonObjIter(const CxJsonValue *value) {
+ return cxMapIterator(value->object);
}
CxJsonValue *cx_json_obj_get(const CxJsonValue *value, cxstring name) {
- size_t index = json_find_objvalue(value, name);
- if (index >= value->value.object.values_size) {
+ CxJsonValue *v = cxMapGet(value->object, name);
+ if (v == NULL) {
return &cx_json_value_nothing;
} else {
- return value->value.object.values[index].value;
+ return v;
}
}
CxJsonValue *cx_json_obj_remove(CxJsonValue *value, cxstring name) {
- size_t index = json_find_objvalue(value, name);
- if (index >= value->value.object.values_size) {
- return NULL;
- } else {
- CxJsonObjValue kv = value->value.object.values[index];
- cx_strfree_a(value->allocator, &kv.name);
- // TODO: replace with cx_array_remove() / cx_array_remove_fast()
- value->value.object.values_size--;
- memmove(value->value.object.values + index, value->value.object.values + index + 1, (value->value.object.values_size - index) * sizeof(CxJsonObjValue));
- return kv.value;
- }
+ CxJsonValue *v = NULL;
+ cxMapRemoveAndGet(value->object, name, &v);
+ return v;
}
CxJsonWriter cxJsonWriterCompact(void) {
return (CxJsonWriter) {
false,
- true,
6,
false,
4,
CxJsonWriter cxJsonWriterPretty(bool use_spaces) {
return (CxJsonWriter) {
- true,
true,
6,
use_spaces,
expected++;
}
depth++;
- size_t elem_count = value->value.object.values_size;
- for (size_t look_idx = 0; look_idx < elem_count; look_idx++) {
- // get the member either via index array or directly
- size_t elem_idx = settings->sort_members
- ? look_idx
- : value->value.object.indices[look_idx];
- CxJsonObjValue *member = &value->value.object.values[elem_idx];
- if (settings->sort_members) {
- depth++;depth--;
- }
-
+ CxMapIterator member_iter = cxJsonObjIter(value);
+ cx_foreach(const CxMapEntry *, member, member_iter) {
// possible indentation
if (settings->pretty) {
if (cx_json_writer_indent(target, wfunc, settings, depth)) {
// the name
actual += wfunc("\"", 1, 1, target);
- cxmutstr name = escape_string(member->name, settings->escape_slash);
+ cxstring key = cx_hash_key_as_string(member->key);
+ cxmutstr name = escape_string(key, settings->escape_slash);
actual += wfunc(name.ptr, 1, name.length, target);
- if (name.ptr != member->name.ptr) {
- cx_strfree(&name);
- }
actual += wfunc("\"", 1, 1, target);
const char *obj_name_sep = ": ";
if (settings->pretty) {
actual += wfunc(obj_name_sep, 1, 2, target);
- expected += 4 + member->name.length;
+ expected += 4 + name.length;
} else {
actual += wfunc(obj_name_sep, 1, 1, target);
- expected += 3 + member->name.length;
+ expected += 3 + name.length;
+ }
+ if (name.ptr != key.ptr) {
+ cx_strfree(&name);
}
// the value
if (cx_json_write_rec(target, member->value, wfunc, settings, depth)) return 1;
// end of object-value
- if (look_idx < elem_count - 1) {
+ if (member_iter.index < member_iter.elem_count - 1) {
const char *obj_value_sep = ",\n";
if (settings->pretty) {
actual += wfunc(obj_value_sep, 1, 2, target);
if (cx_json_write_rec(
target, element,
wfunc, settings, depth)
- ) return 1;
+ ) {
+ return 1; // LCOV_EXCL_LINE
+ }
if (iter.index < iter.elem_count - 1) {
const char *arr_value_sep = ", ";
}
case CX_JSON_STRING: {
actual += wfunc("\"", 1, 1, target);
- cxmutstr str = escape_string(value->value.string, settings->escape_slash);
+ cxmutstr str = escape_string(cx_strcast(value->string),
+ settings->escape_slash);
actual += wfunc(str.ptr, 1, str.length, target);
- if (str.ptr != value->value.string.ptr) {
+ actual += wfunc("\"", 1, 1, target);
+ expected += 2 + str.length;
+ if (str.ptr != value->string.ptr) {
cx_strfree(&str);
}
- actual += wfunc("\"", 1, 1, target);
- expected += 2 + value->value.string.length;
break;
}
case CX_JSON_NUMBER: {
// because of the way how %g is defined, we need to
// double the precision and truncate ourselves
precision = 1 + (precision > 15 ? 30 : 2 * precision);
- snprintf(numbuf, 40, "%.*g", precision, value->value.number);
+ snprintf(numbuf, 40, "%.*g", precision, value->number);
char *dot, *exp;
unsigned char max_digits;
// find the decimal separator and hope that it's one of . or ,
break;
}
case CX_JSON_INTEGER: {
- snprintf(numbuf, 32, "%" PRIi64, value->value.integer);
+ snprintf(numbuf, 32, "%" PRIi64, value->integer);
size_t len = strlen(numbuf);
actual += wfunc(numbuf, 1, len, target);
expected += len;
break;
}
case CX_JSON_LITERAL: {
- if (value->value.literal == CX_JSON_TRUE) {
+ if (value->literal == CX_JSON_TRUE) {
actual += wfunc("true", 1, 4, target);
expected += 4;
- } else if (value->value.literal == CX_JSON_FALSE) {
+ } else if (value->literal == CX_JSON_FALSE) {
actual += wfunc("false", 1, 5, target);
expected += 5;
} else {
}
return cx_json_write_rec(target, value, wfunc, settings, 0);
}
+
+static cxmutstr cx_json_to_string(CxJsonValue *value, const CxAllocator *allocator, CxJsonWriter *writer) {
+ if (allocator == NULL) allocator = cxDefaultAllocator;
+ CxBuffer buffer;
+ if (cxBufferInit(&buffer, allocator, NULL, 128,
+ CX_BUFFER_AUTO_EXTEND | CX_BUFFER_DO_NOT_FREE)) {
+ return CX_NULLSTR; // LCOV_EXCL_LINE
+ }
+ if (cx_json_write_rec(&buffer, value, cxBufferWriteFunc, writer, 0)
+ || cxBufferTerminate(&buffer)) {
+ // LCOV_EXCL_START
+ buffer.flags &= ~CX_BUFFER_DO_NOT_FREE;
+ cxBufferDestroy(&buffer);
+ return CX_NULLSTR;
+ // LCOV_EXCL_STOP
+ } else {
+ cxmutstr str = cx_bstr_m(&buffer);
+ cxBufferDestroy(&buffer);
+ return str;
+ }
+
+}
+
+cxmutstr cxJsonToString(const CxAllocator *allocator, CxJsonValue *value) {
+ CxJsonWriter writer = cxJsonWriterCompact();
+ return cx_json_to_string(value, allocator, &writer);
+}
+
+cxmutstr cxJsonToPrettyString(const CxAllocator *allocator, CxJsonValue *value) {
+ CxJsonWriter writer = cxJsonWriterPretty(true);
+ return cx_json_to_string(value, allocator, &writer);
+}
+
+int cxJsonCompare(const CxJsonValue *json, const CxJsonValue *other) {
+ if (json == other) return 0;
+ if (json == NULL || other == NULL) return -1;
+ if (json->type != other->type) {
+ if (!cxJsonIsNumber(json)) return -1;
+ if (!cxJsonIsNumber(other)) return -1;
+ }
+ switch (json->type) {
+ case CX_JSON_NOTHING:
+ return 0;
+ case CX_JSON_OBJECT:
+ return cxMapCompare(json->object, other->object);
+ case CX_JSON_ARRAY:
+ if (json->array.size != other->array.size) return -1;
+ for (size_t i = 0; i < json->array.size; i++) {
+ const int d = cxJsonCompare(json->array.data[i], other->array.data[i]);
+ if (d != 0) return d;
+ }
+ return 0;
+ case CX_JSON_STRING:
+ return cx_strcmp(json->string, other->string);
+ case CX_JSON_INTEGER:
+ if (other->type == CX_JSON_INTEGER) {
+ return cx_vcmp_int64(json->integer, other->integer);
+ } else {
+ return cx_vcmp_double(cxJsonAsDouble(json), other->number);
+ }
+ case CX_JSON_NUMBER:
+ return cx_vcmp_double(json->number, cxJsonAsDouble(other));
+ case CX_JSON_LITERAL:
+ return json->literal == other->literal ? 0 : -1;
+ default: // LCOV_EXCL_START
+ // unreachable
+ assert(false);
+ return -1;
+ // LCOV_EXCL_STOP
+ }
+}
+
+CxJsonValue* cxJsonClone(const CxJsonValue* value, const CxAllocator* allocator) {
+ return cx_json_clone_func(NULL, value, allocator, NULL);
+}
+
+CxJsonValue* cx_json_clone_func(CxJsonValue* target, const CxJsonValue* source,
+ const CxAllocator* allocator, CX_UNUSED void *data) {
+ if (source == NULL || source->type == CX_JSON_NOTHING) {
+ return &cx_json_value_nothing;
+ }
+ if (allocator == NULL) allocator = cxDefaultAllocator;
+
+#define return_value(v) { \
+ CxJsonValue *ret = v; \
+ if (target == NULL) { \
+ return ret; \
+ } else { \
+ *target = *ret; \
+ ret->type = CX_JSON_UNINITIALIZED; \
+ cxJsonValueFree(ret); \
+ return target; \
+ } \
+ }
+
+ switch (source->type) {
+ case CX_JSON_OBJECT: {
+ CxJsonValue *obj = cxJsonCreateObj(allocator);
+ if (obj == NULL) return NULL; // LCOV_EXCL_LINE
+ if (cxMapClone(obj->object, source->object, cxJsonCloneFunc, allocator, NULL)) {
+ // LCOV_EXCL_START
+ cxJsonValueFree(obj);
+ return NULL;
+ // LCOV_EXCL_STOP
+ }
+ return_value(obj);
+ }
+ case CX_JSON_ARRAY: {
+ const size_t elem_count = source->array.size;
+ CxJsonValue *arr = cxJsonCreateArr(allocator, elem_count);
+ if (arr == NULL) return NULL; // LCOV_EXCL_LINE
+ arr->array.size = elem_count;
+ for (size_t i = 0 ; i < elem_count ; i++) {
+ CxJsonValue *e = cx_json_clone_func(NULL, source->array.data[i], allocator, NULL);
+ if (e == NULL) { // LCOV_EXCL_START
+ cxJsonValueFree(arr);
+ return NULL;
+ // LCOV_EXCL_STOP
+ }
+ arr->array.data[i] = e;
+ }
+ return_value(arr);
+ }
+ case CX_JSON_STRING:
+ return_value(cxJsonCreateString(allocator, source->string));
+ case CX_JSON_INTEGER:
+ return_value(cxJsonCreateInteger(allocator, source->integer));
+ case CX_JSON_NUMBER:
+ return_value(cxJsonCreateNumber(allocator, source->number));
+ case CX_JSON_LITERAL:
+ return_value(cxJsonCreateLiteral(allocator, source->literal));
+ default: // LCOV_EXCL_START
+ // unreachable
+ assert(false);
+ return NULL;
+ // LCOV_EXCL_STOP
+ }
+#undef return_value
+}
}
static CxHashKey *cx_kv_list_loc_key(cx_kv_list *list, void *node_data) {
- return (CxHashKey*)((char*)node_data + list->list.base.collection.elem_size);
+ return (CxHashKey*)((char*)node_data - list->list.loc_data + list->list.loc_extra);
}
static void cx_kvl_deallocate(struct cx_list_s *list) {
size_t index;
cx_linked_list *ll = &kv_list->list;
- char *node = cx_linked_list_find(
+ char *node = cx_linked_list_find_c(
ll->begin,
ll->loc_next, ll->loc_data,
- list->collection.cmpfunc, elem,
- &index
+ elem, &index,
+ cx_list_compare_wrapper,
+ list
);
if (node == NULL) {
return list->collection.size;
static void cx_kvl_map_deallocate(struct cx_map_s *map) {
cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list;
+ cx_kv_list_update_destructors(kv_list);
kv_list->map_methods->deallocate(map);
kv_list->list_methods->deallocate(&kv_list->list.base);
}
kv_list->map_methods->clear(map);
}
-static void *cx_kvl_map_put(CxMap *map, CxHashKey key, void *value) {
- cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list;
- // if the hash has not yet been computed, do it now
- if (key.hash == 0) {
- cx_hash_murmur(&key);
- }
-
- // reserve memory in the map first
- void **map_data = kv_list->map_methods->put(map, key, NULL);
- if (map_data == NULL) return NULL; // LCOV_EXCL_LINE
-
- // insert the data into the list (which most likely destroys the sorted property)
- kv_list->list.base.collection.sorted = false;
- void *node_data = kv_list->list_methods->insert_element(
- &kv_list->list.base, kv_list->list.base.collection.size,
- kv_list->list.base.collection.store_pointer ? &value : value);
- if (node_data == NULL) { // LCOV_EXCL_START
- // non-destructively remove the key again
- kv_list->map_methods->remove(&kv_list->map->map_base.base, key, &map_data);
- return NULL;
- } // LCOV_EXCL_STOP
-
- // write the node pointer to the map entry
- *map_data = node_data;
-
- // copy the key to the node data
- CxHashKey *key_ptr = cx_kv_list_loc_key(kv_list, node_data);
- *key_ptr = key;
-
- // we must return node_data here and not map_data,
- // because the node_data is the actual element of this collection
- return node_data;
-}
-
-void *cx_kvl_map_get(const CxMap *map, CxHashKey key) {
+static void *cx_kvl_map_get(const CxMap *map, CxHashKey key) {
cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list;
void *node_data = kv_list->map_methods->get(map, key);
if (node_data == NULL) return NULL; // LCOV_EXCL_LINE
return kv_list->list.base.collection.store_pointer ? *(void**)node_data : node_data;
}
-int cx_kvl_map_remove(CxMap *map, CxHashKey key, void *targetbuf) {
+static int cx_kvl_map_remove(CxMap *map, CxHashKey key, void *targetbuf) {
cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list;
void *node_data;
return 0;
}
+static CxMapEntry cx_kvl_map_put(CxMap *map, CxHashKey key, void *value) {
+ cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)map)->list;
+ // if the hash has not yet been computed, do it now
+ if (key.hash == 0) {
+ cx_hash_murmur(&key);
+ }
+
+ // remove any existing element first
+ cx_kvl_map_remove(map, key, NULL);
+
+ // now reserve new memory in the map
+ CxMapEntry map_entry = kv_list->map_methods->put(map, key, NULL);
+ if (map_entry.key == NULL) return (CxMapEntry){NULL, NULL}; // LCOV_EXCL_LINE
+
+ // insert the data into the list (which most likely destroys the sorted property)
+ kv_list->list.base.collection.sorted = false;
+ void *node_data = kv_list->list_methods->insert_element(
+ &kv_list->list.base, kv_list->list.base.collection.size,
+ kv_list->list.base.collection.store_pointer ? &value : value);
+ if (node_data == NULL) { // LCOV_EXCL_START
+ // non-destructively remove the key again
+ void *dummy;
+ kv_list->map_methods->remove(&kv_list->map->map_base.base, key, &dummy);
+ return (CxMapEntry){NULL, NULL};
+ } // LCOV_EXCL_STOP
+
+ // write the node pointer to the map entry
+ *(void**)map_entry.value = node_data;
+
+ // copy the key to the node data
+ CxHashKey *key_ptr = cx_kv_list_loc_key(kv_list, node_data);
+ *key_ptr = *map_entry.key;
+
+ // we must return an entry that points to the node data!
+ return (CxMapEntry ){key_ptr, node_data};
+}
+
static void *cx_kvl_iter_current_entry(const void *it) {
const CxMapIterator *iter = it;
return (void*)&iter->entry;
return iter->elem != NULL;
}
-CxMapIterator cx_kvl_map_iterator(const CxMap *map, enum cx_map_iterator_type type) {
+static CxMapIterator cx_kvl_map_iterator(const CxMap *map, enum cx_map_iterator_type type) {
CxMapIterator iter = {0};
iter.type = type;
return iter;
}
+static int cx_kvl_change_capacity(struct cx_list_s *list,
+ CX_UNUSED size_t cap) {
+ // since our backing list is a linked list, we don't need to do much here,
+ // but rehashing the map is quite useful
+ cx_kv_list *kv_list = (cx_kv_list*)list;
+ cxMapRehash(&kv_list->map->map_base.base);
+ return 0;
+}
+
static cx_list_class cx_kv_list_class = {
cx_kvl_deallocate,
cx_kvl_insert_element,
cx_kvl_sort,
NULL,
cx_kvl_reverse,
+ cx_kvl_change_capacity,
cx_kvl_iterator,
};
CxList *cxKvListCreate(
const CxAllocator *allocator,
- cx_compare_func comparator,
size_t elem_size
) {
if (allocator == NULL) {
}
// create a normal linked list and a normal hash map, first
- CxList *list = cxLinkedListCreate(allocator, comparator, elem_size);
+ CxList *list = cxLinkedListCreate(allocator, elem_size);
if (list == NULL) return NULL; // LCOV_EXCL_LINE
cx_linked_list *ll = (cx_linked_list*)list;
- ll->extra_data_len = sizeof(CxHashKey);
+ cx_linked_list_extra_data(ll, sizeof(CxHashKey));
CxMap *map = cxHashMapCreate(allocator, CX_STORE_POINTERS, 0);
if (map == NULL) { // LCOV_EXCL_START
cxListFree(list);
// remember the base methods and override them
kv_list->map_methods = map->cl;
map->cl = &cx_kv_map_class;
- if (list->climpl == NULL) {
- kv_list->list_methods = list->cl;
- list->cl = &cx_kv_list_class;
- } else {
- kv_list->list_methods = list->climpl;
- list->climpl = &cx_kv_list_class;
- }
+ kv_list->list_methods = list->cl;
+ list->cl = &cx_kv_list_class;
return list;
}
CxMap *cxKvListCreateAsMap(
const CxAllocator *allocator,
- cx_compare_func comparator,
size_t elem_size
) {
- CxList *list = cxKvListCreate(allocator, comparator, elem_size);
+ CxList *list = cxKvListCreate(allocator, elem_size);
return list == NULL ? NULL : cxKvListAsMap(list);
}
return 1;
}
- // add the key to the map;
- if (NULL == kv_list->map_methods->put(&kv_list->map->map_base.base, key, node_data)) {
- return 1; // LCOV_EXCL_LINE
- }
+ // add the key to the map
+ const CxMapEntry entry = kv_list->map_methods->put(
+ &kv_list->map->map_base.base, key, node_data);
+ if (entry.key == NULL) return 1; // LCOV_EXCL_LINE
// write the key to the list's node
CxHashKey *loc_key = cx_kv_list_loc_key(kv_list, node_data);
- *loc_key = key;
+ *loc_key = *entry.key;
return 0;
}
cx_kv_list *kv_list = (cx_kv_list*)list;
// reserve memory in the map
- void **map_data = kv_list->map_methods->put(&kv_list->map->map_base.base, key, NULL);
- if (map_data == NULL) return 1; // LCOV_EXCL_LINE
+ CxMapEntry map_entry = kv_list->map_methods->put(&kv_list->map->map_base.base, key, NULL);
+ if (map_entry.key == NULL) return 1; // LCOV_EXCL_LINE
// insert the node
void *node_data = kv_list->list_methods->insert_element(&kv_list->list.base, index,
kv_list->list.base.collection.store_pointer ? &value : value);
if (node_data == NULL) { // LCOV_EXCL_START
// non-destructively remove the key again
- kv_list->map_methods->remove(&kv_list->map->map_base.base, key, &map_data);
+ void *dummy;
+ kv_list->map_methods->remove(&kv_list->map->map_base.base, key, &dummy);
return 1;
} // LCOV_EXCL_STOP
- *map_data = node_data;
+ *(void**)map_entry.value = node_data;
// write the key to the node
CxHashKey *loc_key = cx_kv_list_loc_key(kv_list, node_data);
- *loc_key = key;
+ *loc_key = *map_entry.key;
return 0;
}
#include <string.h>
#include <assert.h>
+#if __STDC_VERSION__ < 202311L
+// we cannot simply include stdalign.h
+// because Solaris is not entirely C11 complaint
+#ifndef __alignof_is_defined
+#define alignof _Alignof
+#define __alignof_is_defined 1
+#endif
+#endif
+
// LOW LEVEL LINKED LIST FUNCTIONS
#define CX_LL_PTR(cur, off) (*(void**)(((char*)(cur))+(off)))
return (void *) cur;
}
-void *cx_linked_list_find(
+void *cx_linked_list_find_c(
const void *start,
ptrdiff_t loc_advance,
ptrdiff_t loc_data,
- cx_compare_func cmp_func,
const void *elem,
- size_t *found_index
+ size_t *found_index,
+ cx_compare_func2 cmp_func,
+ void *context
) {
assert(start != NULL);
assert(loc_advance >= 0);
size_t index = 0;
do {
void *current = ll_data(node);
- if (cmp_func(current, elem) == 0) {
+ if (cmp_func(current, elem, context) == 0) {
if (found_index != NULL) {
*found_index = index;
}
return NULL;
}
+void *cx_linked_list_find(
+ const void *start,
+ ptrdiff_t loc_advance,
+ ptrdiff_t loc_data,
+ const void *elem,
+ size_t *found_index,
+ cx_compare_func cmp_func
+) {
+ cx_compare_func_wrapper wrapper = {cmp_func};
+ return cx_linked_list_find_c(start, loc_advance, loc_data,
+ elem, found_index, cx_cmp_wrap, &wrapper);
+}
+
void *cx_linked_list_first(
const void *node,
ptrdiff_t loc_prev
}
}
-void cx_linked_list_insert_sorted(
- void **begin,
- void **end,
- ptrdiff_t loc_prev,
- ptrdiff_t loc_next,
- void *new_node,
- cx_compare_func cmp_func
-) {
- assert(ll_next(new_node) == NULL);
- cx_linked_list_insert_sorted_chain(
- begin, end, loc_prev, loc_next, new_node, cmp_func);
-}
-
static void *cx_linked_list_insert_sorted_chain_impl(
void **begin,
void **end,
ptrdiff_t loc_prev,
ptrdiff_t loc_next,
void *insert_begin,
- cx_compare_func cmp_func,
+ cx_compare_func2 cmp_func,
+ void *context,
bool allow_duplicates
) {
assert(begin != NULL);
// determine the new start
{
- int d = source_original == NULL ? 1 : cmp_func(source_original, source_argument);
+ int d = source_original == NULL ? 1 : cmp_func(source_original, source_argument, context);
if (d <= 0) {
// the new chain starts with the original chain
new_begin = new_end = source_original;
// now successively compare the elements and add them to the correct chains
while (source_original != NULL && source_argument != NULL) {
- int d = cmp_func(source_original, source_argument);
+ int d = cmp_func(source_original, source_argument, context);
if (d <= 0) {
// the original is not larger, add it to the chain
cx_linked_list_link(new_end, source_original, loc_prev, loc_next);
} else {
// the original is larger, append the source argument to the chain
// check if we must discard the source argument as duplicate
- if (!allow_duplicates && cmp_func(new_end, source_argument) == 0) {
+ if (!allow_duplicates && cmp_func(new_end, source_argument, context) == 0) {
if (dup_end == NULL) {
dup_begin = dup_end = source_argument;
} else {
} else {
// otherwise we must check one-by-one
while (source_argument != NULL) {
- if (cmp_func(new_end, source_argument) == 0) {
+ if (cmp_func(new_end, source_argument, context) == 0) {
if (dup_end == NULL) {
dup_begin = dup_end = source_argument;
} else {
return dup_begin;
}
+void cx_linked_list_insert_sorted(
+ void **begin,
+ void **end,
+ ptrdiff_t loc_prev,
+ ptrdiff_t loc_next,
+ void *new_node,
+ cx_compare_func cmp_func
+) {
+ assert(ll_next(new_node) == NULL);
+ cx_linked_list_insert_sorted_chain(
+ begin, end, loc_prev, loc_next, new_node, cmp_func);
+}
+
void cx_linked_list_insert_sorted_chain(
void **begin,
void **end,
void *insert_begin,
cx_compare_func cmp_func
) {
+ cx_compare_func_wrapper wrapper = {cmp_func};
cx_linked_list_insert_sorted_chain_impl(
begin, end, loc_prev, loc_next,
- insert_begin, cmp_func, true);
+ insert_begin, cx_cmp_wrap, &wrapper, true);
}
int cx_linked_list_insert_unique(
ptrdiff_t loc_next,
void *insert_begin,
cx_compare_func cmp_func
+) {
+ cx_compare_func_wrapper wrapper = {cmp_func};
+ return cx_linked_list_insert_sorted_chain_impl(
+ begin, end, loc_prev, loc_next,
+ insert_begin, cx_cmp_wrap, &wrapper, false);
+}
+
+void cx_linked_list_insert_sorted_c(
+ void **begin,
+ void **end,
+ ptrdiff_t loc_prev,
+ ptrdiff_t loc_next,
+ void *new_node,
+ cx_compare_func2 cmp_func,
+ void *context
+) {
+ assert(ll_next(new_node) == NULL);
+ cx_linked_list_insert_sorted_chain_c(
+ begin, end, loc_prev, loc_next, new_node, cmp_func, context);
+}
+
+void cx_linked_list_insert_sorted_chain_c(
+ void **begin,
+ void **end,
+ ptrdiff_t loc_prev,
+ ptrdiff_t loc_next,
+ void *insert_begin,
+ cx_compare_func2 cmp_func,
+ void *context
+) {
+ cx_linked_list_insert_sorted_chain_impl(
+ begin, end, loc_prev, loc_next,
+ insert_begin, cmp_func, context, true);
+}
+
+int cx_linked_list_insert_unique_c(
+ void **begin,
+ void **end,
+ ptrdiff_t loc_prev,
+ ptrdiff_t loc_next,
+ void *new_node,
+ cx_compare_func2 cmp_func,
+ void *context
+) {
+ assert(ll_next(new_node) == NULL);
+ return NULL != cx_linked_list_insert_unique_chain_c(
+ begin, end, loc_prev, loc_next, new_node, cmp_func, context);
+}
+
+void *cx_linked_list_insert_unique_chain_c(
+ void **begin,
+ void **end,
+ ptrdiff_t loc_prev,
+ ptrdiff_t loc_next,
+ void *insert_begin,
+ cx_compare_func2 cmp_func,
+ void *context
) {
return cx_linked_list_insert_sorted_chain_impl(
begin, end, loc_prev, loc_next,
- insert_begin, cmp_func, false);
+ insert_begin, cmp_func, context, false);
}
size_t cx_linked_list_remove_chain(
#endif
static void cx_linked_list_sort_merge(
+ void **begin,
+ void **end,
ptrdiff_t loc_prev,
ptrdiff_t loc_next,
ptrdiff_t loc_data,
void *ls,
void *le,
void *re,
- cx_compare_func cmp_func,
- void **begin,
- void **end
+ cx_compare_func2 cmp_func,
+ void *context
) {
void *sbo[CX_LINKED_LIST_SORT_SBO_SIZE];
void **sorted = length >= CX_LINKED_LIST_SORT_SBO_SIZE ?
cxMallocDefault(sizeof(void *) * length) : sbo;
- if (sorted == NULL) abort();
+ if (sorted == NULL) abort(); // LCOV_EXCL_LINE
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) {
+ if (cmp_func(ll_data(lc), ll_data(rc), context) <= 0) {
sorted[n] = lc;
lc = ll_next(lc);
} else {
}
}
-void cx_linked_list_sort( // NOLINT(misc-no-recursion) - purposely recursive function
+void cx_linked_list_sort_c( // NOLINT(misc-no-recursion) - purposely recursive function
void **begin,
void **end,
ptrdiff_t loc_prev,
ptrdiff_t loc_next,
ptrdiff_t loc_data,
- cx_compare_func cmp_func
+ cx_compare_func2 cmp_func,
+ void *context
) {
assert(begin != NULL);
assert(loc_next >= 0);
// check how many elements are already sorted
lc = ls;
size_t ln = 1;
- while (ll_next(lc) != NULL && cmp_func(ll_data(ll_next(lc)), ll_data(lc)) > 0) {
+ while (ll_next(lc) != NULL && cmp_func(ll_data(ll_next(lc)), ll_data(lc), context) > 0) {
lc = ll_next(lc);
ln++;
}
size_t rn = 1;
rc = le;
// skip already sorted elements
- while (ll_next(rc) != NULL && cmp_func(ll_data(ll_next(rc)), ll_data(rc)) > 0) {
+ while (ll_next(rc) != NULL && cmp_func(ll_data(ll_next(rc)), ll_data(rc), context) > 0) {
rc = ll_next(rc);
rn++;
}
// {ls,...,le->prev} and {rs,...,re->prev} are sorted - merge them
void *sorted_begin, *sorted_end;
- cx_linked_list_sort_merge(loc_prev, loc_next, loc_data,
+ cx_linked_list_sort_merge(&sorted_begin, &sorted_end,
+ loc_prev, loc_next, loc_data,
ln + rn, ls, le, re, cmp_func,
- &sorted_begin, &sorted_end);
+ context);
// Something left? Sort it!
size_t remainder_length = cx_linked_list_size(re, loc_next);
if (remainder_length > 0) {
void *remainder = re;
- cx_linked_list_sort(&remainder, NULL, loc_prev, loc_next, loc_data, cmp_func);
+ cx_linked_list_sort_c(&remainder, NULL, loc_prev, loc_next, loc_data, cmp_func, context);
// merge sorted list with (also sorted) remainder
- cx_linked_list_sort_merge(loc_prev, loc_next, loc_data,
+ cx_linked_list_sort_merge(&sorted_begin, &sorted_end,
+ loc_prev, loc_next, loc_data,
ln + rn + remainder_length,
sorted_begin, remainder, NULL, cmp_func,
- &sorted_begin, &sorted_end);
+ context);
}
*begin = sorted_begin;
if (end) *end = sorted_end;
}
}
-int cx_linked_list_compare(
+void cx_linked_list_sort(
+ void **begin,
+ void **end,
+ ptrdiff_t loc_prev,
+ ptrdiff_t loc_next,
+ ptrdiff_t loc_data,
+ cx_compare_func cmp_func
+) {
+ cx_compare_func_wrapper wrapper = {cmp_func};
+ cx_linked_list_sort_c(begin, end, loc_prev, loc_next, loc_data, cx_cmp_wrap, &wrapper);
+}
+
+int cx_linked_list_compare_c(
const void *begin_left,
const void *begin_right,
ptrdiff_t loc_advance,
ptrdiff_t loc_data,
- cx_compare_func cmp_func
+ cx_compare_func2 cmp_func,
+ void *context
) {
const void *left = begin_left, *right = begin_right;
while (left != NULL && right != NULL) {
const void *left_data = ll_data(left);
const void *right_data = ll_data(right);
- int result = cmp_func(left_data, right_data);
+ int result = cmp_func(left_data, right_data, context);
if (result != 0) return result;
left = ll_advance(left);
right = ll_advance(right);
else { return 0; }
}
+int cx_linked_list_compare(
+ const void *begin_left,
+ const void *begin_right,
+ ptrdiff_t loc_advance,
+ ptrdiff_t loc_data,
+ cx_compare_func cmp_func
+) {
+ cx_compare_func_wrapper wrapper = {cmp_func};
+ return cx_linked_list_compare_c(begin_left, begin_right,
+ loc_advance, loc_data, cx_cmp_wrap, &wrapper);
+}
+
void cx_linked_list_reverse(
void **begin,
void **end,
}
static void *cx_ll_malloc_node(const cx_linked_list *list) {
- return cxZalloc(list->base.collection.allocator,
- list->loc_data + list->base.collection.elem_size + list->extra_data_len);
+ size_t n;
+ if (list->extra_data_len == 0) {
+ n = list->loc_data + list->base.collection.elem_size;
+ } else {
+ n = list->loc_extra + list->extra_data_len;
+ }
+ return cxZalloc(list->base.collection.allocator, n);
}
static int cx_ll_insert_at(
}
}
-static _Thread_local cx_compare_func cx_ll_insert_sorted_cmp_func;
-static _Thread_local off_t cx_ll_insert_sorted_loc_data;
-
-static int cx_ll_insert_sorted_cmp_helper(const void *l, const void *r) {
- const char *left = (const char*)l + cx_ll_insert_sorted_loc_data;
- const char *right = (const char*)r + cx_ll_insert_sorted_loc_data;
- return cx_ll_insert_sorted_cmp_func(left, right);
+static int cx_ll_insert_sorted_cmp_helper(const void *l, const void *r, void *c) {
+ cx_linked_list *list = c;
+ const char *left = (const char*)l + list->loc_data;
+ const char *right = (const char*)r + list->loc_data;
+ return cx_list_compare_wrapper(left, right, list);
}
static size_t cx_ll_insert_sorted_impl(
}
CX_LL_PTR(prev, ll->loc_next) = NULL;
- // invoke the low level function
- cx_ll_insert_sorted_cmp_func = list->collection.cmpfunc;
- cx_ll_insert_sorted_loc_data = ll->loc_data;
- if (allow_duplicates) {
- cx_linked_list_insert_sorted_chain(
- &ll->begin,
- &ll->end,
- ll->loc_prev,
- ll->loc_next,
- chain,
- cx_ll_insert_sorted_cmp_helper
- );
- list->collection.size += inserted;
- } else {
- void *duplicates = cx_linked_list_insert_unique_chain(
- &ll->begin,
- &ll->end,
- ll->loc_prev,
- ll->loc_next,
- chain,
- cx_ll_insert_sorted_cmp_helper
- );
- list->collection.size += inserted;
+ // invoke the low-level function
+ void *duplicates = cx_linked_list_insert_sorted_chain_impl(
+ &ll->begin,
+ &ll->end,
+ ll->loc_prev,
+ ll->loc_next,
+ chain,
+ cx_ll_insert_sorted_cmp_helper,
+ list,
+ allow_duplicates
+ );
+ list->collection.size += inserted;
+ if (!allow_duplicates) {
// free the nodes that did not make it into the list
while (duplicates != NULL) {
void *next = CX_LL_PTR(duplicates, ll->loc_next);
size_t index;
cx_linked_list *ll = (cx_linked_list *) list;
- char *node = cx_linked_list_find(
+ char *node = cx_linked_list_find_c(
ll->begin,
ll->loc_next, ll->loc_data,
- list->collection.cmpfunc, elem,
- &index
- );
+ elem, &index,
+ cx_list_compare_wrapper,
+ list);
if (node == NULL) {
return list->collection.size;
}
static void cx_ll_sort(struct cx_list_s *list) {
cx_linked_list *ll = (cx_linked_list *) list;
- cx_linked_list_sort(&ll->begin, &ll->end,
+ cx_linked_list_sort_c(&ll->begin, &ll->end,
ll->loc_prev, ll->loc_next, ll->loc_data,
- list->collection.cmpfunc);
+ cx_list_compare_wrapper, list);
}
static void cx_ll_reverse(struct cx_list_s *list) {
cx_linked_list *right = (cx_linked_list *) other;
assert(left->loc_next == right->loc_next);
assert(left->loc_data == right->loc_data);
- return cx_linked_list_compare(left->begin, right->begin,
+ return cx_linked_list_compare_c(left->begin, right->begin,
left->loc_next, left->loc_data,
- list->collection.cmpfunc);
+ cx_list_compare_wrapper, (void*)list);
}
static bool cx_ll_iter_valid(const void *it) {
return result;
} else {
if (cx_ll_insert_element(list, list->collection.size, elem) == NULL) {
- return 1;
+ return 1; // LCOV_EXCL_LINE
}
iter->elem_count++;
iter->index = list->collection.size;
cx_ll_sort,
cx_ll_compare,
cx_ll_reverse,
+ NULL, // no overallocation supported
cx_ll_iterator,
};
CxList *cxLinkedListCreate(
const CxAllocator *allocator,
- cx_compare_func comparator,
size_t elem_size
) {
if (allocator == NULL) {
cx_linked_list *list = cxCalloc(allocator, 1, sizeof(cx_linked_list));
if (list == NULL) return NULL;
- list->extra_data_len = 0;
list->loc_prev = 0;
list->loc_next = sizeof(void*);
list->loc_data = sizeof(void*)*2;
+ list->loc_extra = -1;
+ list->extra_data_len = 0;
cx_list_init((CxList*)list, &cx_linked_list_class,
- allocator, comparator, elem_size);
+ allocator, elem_size);
return (CxList *) list;
}
+
+void cx_linked_list_extra_data(cx_linked_list *list, size_t len) {
+ list->extra_data_len = len;
+
+ off_t loc_extra = list->loc_data + (off_t) list->base.collection.elem_size;
+ size_t alignment = alignof(void*);
+ size_t padding = alignment - ((size_t)loc_extra % alignment);
+ list->loc_extra = loc_extra + (off_t) padding;
+}
#include <string.h>
#include <assert.h>
-// <editor-fold desc="Store Pointers Functionality">
-
-static _Thread_local cx_compare_func cx_pl_cmpfunc_impl;
-
-static int cx_pl_cmpfunc(
- const void *l,
- const void *r
-) {
- // l and r are guaranteed to be non-NULL pointing to the list's memory
- void *const *lptr = l;
- void *const *rptr = r;
- const void *left = *lptr;
- const void *right = *rptr;
- if (left == NULL) {
- // NULL is smaller than any value except NULL
- return right == NULL ? 0 : -1;
- } else if (right == NULL) {
- // any value is larger than NULL
- return 1;
+// we don't want to include the full array_list.h.
+// therefore, we only forward declare the one function we want to use
+CX_EXPORT void cx_array_qsort_c(void *array, size_t nmemb, size_t size,
+ cx_compare_func2 fn, void *context);
+
+
+int cx_list_compare_wrapper(const void *l, const void *r, void *c) {
+ CxList *list = c;
+ const void *left;
+ const void *right;
+ if (cxCollectionStoresPointers(list)) {
+ left = *(void**)l;
+ right = *(void**)r;
+ // for historic reasons, we are handling the NULL case here
+ // because every UCX compare function does not support NULL arguments
+ if (left == NULL) {
+ if (right == NULL) return 0;
+ return -1;
+ } else if (right == NULL) {
+ return 1;
+ }
+ } else {
+ left = l;
+ right = r;
}
- return cx_pl_cmpfunc_impl(left, right);
-}
-
-static void cx_pl_hack_cmpfunc(const struct cx_list_s *list) {
- // cast away const - this is the hacky thing
- struct cx_collection_s *l = (struct cx_collection_s*) &list->collection;
- cx_pl_cmpfunc_impl = l->cmpfunc;
- l->cmpfunc = cx_pl_cmpfunc;
-}
-
-static void cx_pl_unhack_cmpfunc(const struct cx_list_s *list) {
- // cast away const - this is the hacky thing
- struct cx_collection_s *l = (struct cx_collection_s*) &list->collection;
- l->cmpfunc = cx_pl_cmpfunc_impl;
-}
-
-static void cx_pl_destructor(struct cx_list_s *list) {
- list->climpl->deallocate(list);
-}
-
-static void *cx_pl_insert_element(
- struct cx_list_s *list,
- size_t index,
- const void *element
-) {
- return list->climpl->insert_element(list, index, &element);
-}
-
-static size_t cx_pl_insert_array(
- struct cx_list_s *list,
- size_t index,
- const void *array,
- size_t n
-) {
- return list->climpl->insert_array(list, index, array, n);
-}
-
-static size_t cx_pl_insert_sorted(
- struct cx_list_s *list,
- const void *array,
- size_t n
-) {
- cx_pl_hack_cmpfunc(list);
- size_t result = list->climpl->insert_sorted(list, array, n);
- cx_pl_unhack_cmpfunc(list);
- return result;
-}
-
-static size_t cx_pl_insert_unique(
- struct cx_list_s *list,
- const void *array,
- size_t n
-) {
- cx_pl_hack_cmpfunc(list);
- size_t result = list->climpl->insert_unique(list, array, n);
- cx_pl_unhack_cmpfunc(list);
- return result;
-}
-
-static int cx_pl_insert_iter(
- struct cx_iterator_s *iter,
- const void *elem,
- int prepend
-) {
- struct cx_list_s *list = iter->src_handle;
- return list->climpl->insert_iter(iter, &elem, prepend);
+ return cx_invoke_compare_func(list, left, right);
}
-static size_t cx_pl_remove(
- struct cx_list_s *list,
- size_t index,
- size_t num,
- void *targetbuf
-) {
- return list->climpl->remove(list, index, num, targetbuf);
-}
-
-static void cx_pl_clear(struct cx_list_s *list) {
- list->climpl->clear(list);
-}
-
-static int cx_pl_swap(
- struct cx_list_s *list,
- size_t i,
- size_t j
-) {
- return list->climpl->swap(list, i, j);
-}
-
-static void *cx_pl_at(
- const struct cx_list_s *list,
- size_t index
-) {
- void **ptr = list->climpl->at(list, index);
- return ptr == NULL ? NULL : *ptr;
-}
-
-static size_t cx_pl_find_remove(
- struct cx_list_s *list,
- const void *elem,
- bool remove
-) {
- cx_pl_hack_cmpfunc(list);
- size_t ret = list->climpl->find_remove(list, &elem, remove);
- cx_pl_unhack_cmpfunc(list);
- return ret;
-}
-
-static void cx_pl_sort(struct cx_list_s *list) {
- cx_pl_hack_cmpfunc(list);
- list->climpl->sort(list);
- cx_pl_unhack_cmpfunc(list);
-}
-
-static int cx_pl_compare(
- const struct cx_list_s *list,
- const struct cx_list_s *other
-) {
- cx_pl_hack_cmpfunc(list);
- int ret = list->climpl->compare(list, other);
- cx_pl_unhack_cmpfunc(list);
- return ret;
-}
-
-static void cx_pl_reverse(struct cx_list_s *list) {
- list->climpl->reverse(list);
-}
-
-static void *cx_pl_iter_current(const void *it) {
- const struct cx_iterator_s *iter = it;
- void **ptr = iter->base.current_impl(it);
- return ptr == NULL ? NULL : *ptr;
-}
-
-static struct cx_iterator_s cx_pl_iterator(
- const struct cx_list_s *list,
- size_t index,
- bool backwards
-) {
- struct cx_iterator_s iter = list->climpl->iterator(list, index, backwards);
- iter.base.current_impl = iter.base.current;
- iter.base.current = cx_pl_iter_current;
- return iter;
-}
-
-static cx_list_class cx_pointer_list_class = {
- cx_pl_destructor,
- cx_pl_insert_element,
- cx_pl_insert_array,
- cx_pl_insert_sorted,
- cx_pl_insert_unique,
- cx_pl_insert_iter,
- cx_pl_remove,
- cx_pl_clear,
- cx_pl_swap,
- cx_pl_at,
- cx_pl_find_remove,
- cx_pl_sort,
- cx_pl_compare,
- cx_pl_reverse,
- cx_pl_iterator,
-};
-// </editor-fold>
+#define cx_list_compare_wrapper(l, r, c) cx_list_compare_wrapper(l, r, (void*)c)
// <editor-fold desc="empty list implementation">
-static void cx_emptyl_noop(cx_attr_unused CxList *list) {
+static void cx_emptyl_noop(CX_UNUSED CxList *list) {
// this is a noop, but MUST be implemented
}
static void *cx_emptyl_at(
- cx_attr_unused const struct cx_list_s *list,
- cx_attr_unused size_t index
+ CX_UNUSED const struct cx_list_s *list,
+ CX_UNUSED size_t index
) {
return NULL;
}
static size_t cx_emptyl_find_remove(
- cx_attr_unused struct cx_list_s *list,
- cx_attr_unused const void *elem,
- cx_attr_unused bool remove
+ CX_UNUSED struct cx_list_s *list,
+ CX_UNUSED const void *elem,
+ CX_UNUSED bool remove
) {
return 0;
}
-static bool cx_emptyl_iter_valid(cx_attr_unused const void *iter) {
+static bool cx_emptyl_iter_valid(CX_UNUSED const void *iter) {
return false;
}
static CxIterator cx_emptyl_iterator(
const struct cx_list_s *list,
size_t index,
- cx_attr_unused bool backwards
+ CX_UNUSED bool backwards
) {
CxIterator iter = {0};
iter.src_handle = (void*) list;
cx_emptyl_noop,
NULL,
cx_emptyl_noop,
+ NULL,
cx_emptyl_iterator,
};
CxList cx_empty_list = {
{
- NULL,
NULL,
0,
0,
NULL,
NULL,
NULL,
+ NULL,
+ NULL,
+ NULL,
false,
true,
},
&cx_empty_list_class,
- NULL
};
CxList *const cxEmptyList = &cx_empty_list;
// </editor-fold>
-#define invoke_list_func(name, list, ...) \
- ((list)->climpl == NULL ? (list)->cl->name : (list)->climpl->name) \
- (list, __VA_ARGS__)
-
size_t cx_list_default_insert_array(
struct cx_list_s *list,
size_t index,
const char *src = data;
size_t i = 0;
for (; i < n; i++) {
- if (NULL == invoke_list_func(
- insert_element, list, index + i, src)
- ) {
+ if (NULL == list->cl->insert_element(list, index + i, src)) {
return i; // LCOV_EXCL_LINE
}
if (src != NULL) {
if (n == 0) return 0;
size_t elem_size = list->collection.elem_size;
- cx_compare_func cmp = list->collection.cmpfunc;
const char *src = sorted_data;
// track indices and number of inserted items
// search the list for insertion points
while (di < list->collection.size) {
- const void *list_elm = invoke_list_func(at, list, di);
+ const void *list_elm = list->cl->at(list, di);
// compare the current list element with the first source element
// if less, skip the list elements
// if equal, skip the list elements and optionally the source elements
{
- int d = cmp(list_elm, src);
+ int d = cx_list_compare_wrapper(list_elm, src, list);
if (d <= 0) {
if (!allow_duplicates && d == 0) {
src += elem_size;
si++;
processed++; // we also count duplicates for the return value
- while (si < n && cmp(list_elm, src) == 0) {
+ while (si < n && cx_list_compare_wrapper(list_elm, src, list) == 0) {
src += elem_size;
si++;
processed++;
while (++si < n) {
if (!allow_duplicates) {
// skip duplicates within the source
- if (cmp(next, next + elem_size) == 0) {
+ if (cx_list_compare_wrapper(next, next + elem_size, list) == 0) {
next += elem_size;
skip++;
continue;
}
next += elem_size;
// once we become larger than the list elem, break
- if (cmp(list_elm, next) <= 0) {
+ if (cx_list_compare_wrapper(list_elm, next, list) <= 0) {
break;
}
// otherwise, we can insert one more
// insert the elements at location si
if (ins == 1) {
- if (NULL == invoke_list_func(insert_element, list, di, src)) {
+ if (NULL == list->cl->insert_element(list, di, src)) {
return processed; // LCOV_EXCL_LINE
}
} else {
- size_t r = invoke_list_func(insert_array, list, di, src, ins);
+ size_t r = list->cl->insert_array(list, di, src, ins);
if (r < ins) {
return processed + r; // LCOV_EXCL_LINE
}
// insert remaining items
if (si < n) {
if (allow_duplicates) {
- processed += invoke_list_func(insert_array, list, di, src, n - si);
+ processed += list->cl->insert_array(list, di, src, n - si);
} else {
- const void *last = di == 0 ? NULL : invoke_list_func(at, list, di - 1);
+ const void *last = di == 0 ? NULL : list->cl->at(list, di - 1);
for (; si < n; si++) {
// skip duplicates within the source
- if (last == NULL || cmp(last, src) != 0) {
- if (NULL == invoke_list_func(insert_element, list, di, src)) {
+ if (last == NULL || cx_list_compare_wrapper(last, src, list) != 0) {
+ if (NULL == list->cl->insert_element(list, di, src)) {
return processed; // LCOV_EXCL_LINE
}
last = src;
// copy elements from source array
char *loc = tmp;
for (size_t i = 0; i < list_size; i++) {
- void *src = invoke_list_func(at, list, i);
+ void *src = list->cl->at(list, i);
memcpy(loc, src, elem_size);
loc += elem_size;
}
// qsort
- qsort(tmp, list_size, elem_size,
- list->collection.cmpfunc);
+ cx_array_qsort_c(tmp, list_size, elem_size, cx_list_compare_wrapper, list);
// copy elements back
loc = tmp;
for (size_t i = 0; i < list_size; i++) {
- void *dest = invoke_list_func(at, list, i);
+ void *dest = list->cl->at(list, i);
memcpy(dest, loc, elem_size);
loc += elem_size;
}
void *tmp = cxMallocDefault(elem_size);
if (tmp == NULL) return 1; // LCOV_EXCL_LINE
- void *ip = invoke_list_func(at, list, i);
- void *jp = invoke_list_func(at, list, j);
+ void *ip = list->cl->at(list, i);
+ void *jp = list->cl->at(list, j);
memcpy(tmp, ip, elem_size);
memcpy(ip, jp, elem_size);
return 0;
}
+static int cx_list_cmpfunc2_safe_memcmp(const void *a, const void *b, void *c) {
+ // it is not safe to store a pointer to the size in the list
+ // because the entire list structure might get reallocated
+ size_t elem_size = (size_t)(uintptr_t)c;
+ return memcmp(a, b, elem_size);
+}
+
void cx_list_init(
struct cx_list_s *list,
struct cx_list_class_s *cl,
const struct cx_allocator_s *allocator,
- cx_compare_func comparator,
size_t elem_size
) {
list->cl = cl;
list->collection.allocator = allocator;
- list->collection.cmpfunc = comparator;
+ list->collection.size = 0;
+ list->collection.sorted = false; // should be set by the implementation
if (elem_size > 0) {
list->collection.elem_size = elem_size;
+ list->collection.simple_cmp = NULL;
+ list->collection.advanced_cmp = cx_list_cmpfunc2_safe_memcmp;
+ list->collection.cmp_data = (void*)(uintptr_t)list->collection.elem_size;
+ list->collection.store_pointer = false;
} else {
list->collection.elem_size = sizeof(void *);
- if (list->collection.cmpfunc == NULL) {
- list->collection.cmpfunc = cx_cmp_ptr;
- }
+ list->collection.simple_cmp = cx_cmp_ptr;
+ list->collection.advanced_cmp = NULL;
+ list->collection.cmp_data = NULL;
list->collection.store_pointer = true;
- list->climpl = list->cl;
- list->cl = &cx_pointer_list_class;
}
}
const CxList *list,
const CxList *other
) {
+ // check if we cannot use the list internal function
bool cannot_optimize = false;
// if one is storing pointers but the other is not
cannot_optimize |= list->collection.store_pointer ^ other->collection.store_pointer;
- // if one class is wrapped but the other is not
- cannot_optimize |= (list->climpl == NULL) ^ (other->climpl == NULL);
-
- // if the compare functions do not match or both are NULL
- if (!cannot_optimize) {
- cx_compare_func list_cmp = (cx_compare_func) (list->climpl != NULL ?
- list->climpl->compare : list->cl->compare);
- cx_compare_func other_cmp = (cx_compare_func) (other->climpl != NULL ?
- other->climpl->compare : other->cl->compare);
- cannot_optimize |= list_cmp != other_cmp;
- cannot_optimize |= list_cmp == NULL;
- }
+ // check if the lists are incompatible or this list does not implement compare
+ cx_compare_func list_cmp = (cx_compare_func) list->cl->compare;
+ cx_compare_func other_cmp = (cx_compare_func) other->cl->compare;
+ cannot_optimize |= list_cmp != other_cmp;
+ cannot_optimize |= list_cmp == NULL;
if (cannot_optimize) {
// lists are definitely different - cannot use internal compare function
if (list->collection.size == other->collection.size) {
- CxIterator left = list->cl->iterator(list, 0, false);
- CxIterator right = other->cl->iterator(other, 0, false);
+ CxIterator left = cxListIterator(list);
+ CxIterator right = cxListIterator(other);
for (size_t i = 0; i < list->collection.size; i++) {
void *leftValue = cxIteratorCurrent(left);
void *rightValue = cxIteratorCurrent(right);
- int d = list->collection.cmpfunc(leftValue, rightValue);
+ // values are already unwrapped, invoke immediately
+ int d = cx_invoke_compare_func(list, leftValue, rightValue);
if (d != 0) {
return d;
}
int cxListAdd(CxList *list, const void *elem) {
list->collection.sorted = false;
- return list->cl->insert_element(list, list->collection.size, elem) == NULL;
+ return list->cl->insert_element(list, list->collection.size, cx_ref(list, elem)) == NULL;
}
size_t cxListAddArray(CxList *list, const void *array, size_t n) {
int cxListInsert(CxList *list, size_t index, const void *elem) {
list->collection.sorted = false;
- return list->cl->insert_element(list, index, elem) == NULL;
+ return list->cl->insert_element(list, index, cx_ref(list, elem)) == NULL;
}
void *cxListEmplaceAt(CxList *list, size_t index) {
iter.index = 0;
// replace the valid function to abort iteration when c is reached
iter.base.valid = cx_list_emplace_iterator_valid;
- // if we are storing pointers, we want to return the pure pointers.
- // therefore, we must unwrap the "current" method
- if (list->collection.store_pointer) {
- iter.base.current = iter.base.current_impl;
- }
return iter;
}
int cxListInsertSorted(CxList *list, const void *elem) {
assert(cxCollectionSorted(list));
list->collection.sorted = true;
- const void *data = list->collection.store_pointer ? &elem : elem;
- return list->cl->insert_sorted(list, data, 1) == 0;
+ return list->cl->insert_sorted(list, cx_ref(list, elem), 1) == 0;
}
int cxListInsertUnique(CxList *list, const void *elem) {
if (cxCollectionSorted(list)) {
list->collection.sorted = true;
- const void *data = list->collection.store_pointer ? &elem : elem;
- return list->cl->insert_unique(list, data, 1) == 0;
+ return list->cl->insert_unique(list, cx_ref(list, elem), 1) == 0;
} else {
if (cxListContains(list, elem)) {
return 0;
const char *source = array;
for (size_t i = 0 ; i < n; i++) {
// note: this also checks elements added in a previous iteration
- const void *data = list->collection.store_pointer ?
- *((const void**)source) : source;
+ const void *data = cx_deref(list, source);
if (!cxListContains(list, data)) {
if (cxListAdd(list, data)) {
return i; // LCOV_EXCL_LINE
}
int cxListInsertAfter(CxIterator *iter, const void *elem) {
- CxList* list = (CxList*)iter->src_handle;
+ CxList* list = iter->src_handle;
list->collection.sorted = false;
- return list->cl->insert_iter(iter, elem, 0);
+ return list->cl->insert_iter(iter, cx_ref(list, elem), 0);
}
int cxListInsertBefore(CxIterator *iter, const void *elem) {
- CxList* list = (CxList*)iter->src_handle;
+ CxList* list = iter->src_handle;
list->collection.sorted = false;
- return list->cl->insert_iter(iter, elem, 1);
+ return list->cl->insert_iter(iter, cx_ref(list, elem), 1);
}
int cxListRemove(CxList *list, size_t index) {
}
void *cxListAt(const CxList *list, size_t index) {
- return list->cl->at(list, index);
+ void *result = list->cl->at(list, index);
+ if (result == NULL) return NULL;
+ return cx_deref(list, result);
}
void *cxListFirst(const CxList *list) {
- return list->cl->at(list, 0);
+ return cxListAt(list, 0);
}
void *cxListLast(const CxList *list) {
- return list->cl->at(list, list->collection.size - 1);
+ return cxListAt(list, list->collection.size - 1);
}
int cxListSet(CxList *list, size_t index, const void *elem) {
}
if (list->collection.store_pointer) {
- // For pointer collections, always use climpl
- void **target = list->climpl->at(list, index);
+ void **target = list->cl->at(list, index);
*target = (void *)elem;
} else {
void *target = list->cl->at(list, index);
return 0;
}
+static void *cx_pl_iter_current(const void *it) {
+ const struct cx_iterator_s *iter = it;
+ void **ptr = iter->base.current_impl(it);
+ return ptr == NULL ? NULL : *ptr;
+}
+
+CX_INLINE CxIterator cx_pl_iter_wrap(const CxList *list, CxIterator iter) {
+ if (cxCollectionStoresPointers(list)) {
+ iter.base.current_impl = iter.base.current;
+ iter.base.current = cx_pl_iter_current;
+ return iter;
+ } else {
+ return iter;
+ }
+}
+
CxIterator cxListIteratorAt(const CxList *list, size_t index) {
if (list == NULL) list = cxEmptyList;
- return list->cl->iterator(list, index, false);
+ return cx_pl_iter_wrap(list, list->cl->iterator(list, index, false));
}
CxIterator cxListBackwardsIteratorAt(const CxList *list, size_t index) {
if (list == NULL) list = cxEmptyList;
- return list->cl->iterator(list, index, true);
+ return cx_pl_iter_wrap(list, list->cl->iterator(list, index, true));
}
CxIterator cxListIterator(const CxList *list) {
if (list == NULL) list = cxEmptyList;
- return list->cl->iterator(list, 0, false);
+ return cx_pl_iter_wrap(list, list->cl->iterator(list, 0, false));
}
CxIterator cxListBackwardsIterator(const CxList *list) {
if (list == NULL) list = cxEmptyList;
- return list->cl->iterator(list, list->collection.size - 1, true);
+ return cx_pl_iter_wrap(list, list->cl->iterator(list, list->collection.size - 1, true));
}
size_t cxListFind(const CxList *list, const void *elem) {
- return list->cl->find_remove((CxList*)list, elem, false);
+ return list->cl->find_remove((CxList*)list, cx_ref(list, elem), false);
}
bool cxListContains(const CxList* list, const void* elem) {
- return list->cl->find_remove((CxList*)list, elem, false) < list->collection.size;
+ return list->cl->find_remove((CxList*)list, cx_ref(list, elem), false) < list->collection.size;
}
bool cxListIndexValid(const CxList *list, size_t index) {
}
size_t cxListFindRemove(CxList *list, const void *elem) {
- return list->cl->find_remove(list, elem, true);
+ return list->cl->find_remove(list, cx_ref(list, elem), true);
}
void cxListSort(CxList *list) {
if (list == NULL) return;
list->cl->deallocate(list);
}
+
+static void cx_list_pop_uninitialized_elements(CxList *list, size_t n) {
+ cx_destructor_func destr_bak = list->collection.simple_destructor;
+ cx_destructor_func2 destr2_bak = list->collection.advanced_destructor;
+ list->collection.simple_destructor = NULL;
+ list->collection.advanced_destructor = NULL;
+ if (n == 1) {
+ cxListRemove(list, list->collection.size - 1);
+ } else {
+ cxListRemoveArray(list,list->collection.size - n, n);
+ }
+ list->collection.simple_destructor = destr_bak;
+ list->collection.advanced_destructor = destr2_bak;
+}
+
+static void* cx_list_shallow_clone_func(void *dst, const void *src, const CxAllocator *al, void *data) {
+ size_t elem_size = *(size_t*)data;
+ if (dst == NULL) dst = cxMalloc(al, elem_size);
+ if (dst != NULL) memcpy(dst, src, elem_size);
+ return dst;
+}
+
+#define use_shallow_clone_func(list) cx_list_shallow_clone_func, NULL, (void*)&((list)->collection.elem_size)
+
+int cxListClone(CxList *dst, const CxList *src, cx_clone_func clone_func,
+ const CxAllocator *clone_allocator, void *data) {
+ if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator;
+
+ // remember the original size
+ size_t orig_size = dst->collection.size;
+
+ // first, try to allocate the memory in the new list
+ CxIterator empl_iter = cxListEmplaceArray(dst, src->collection.size);
+
+ // get an iterator over the source elements
+ CxIterator src_iter = cxListIterator(src);
+
+ // now clone the elements
+ size_t cloned = empl_iter.elem_count;
+ for (size_t i = 0 ; i < empl_iter.elem_count; i++) {
+ void *src_elem = cxIteratorCurrent(src_iter);
+ void **dest_memory = cxIteratorCurrent(empl_iter);
+ void *target = cxCollectionStoresPointers(dst) ? NULL : dest_memory;
+ void *dest_ptr = clone_func(target, src_elem, clone_allocator, data);
+ if (dest_ptr == NULL) {
+ cloned = i;
+ break;
+ }
+ if (cxCollectionStoresPointers(dst)) {
+ *dest_memory = dest_ptr;
+ }
+ cxIteratorNext(src_iter);
+ cxIteratorNext(empl_iter);
+ }
+
+ // if we could not clone everything, free the allocated memory
+ // (disable the destructors!)
+ if (cloned < src->collection.size) {
+ cx_list_pop_uninitialized_elements(dst,
+ dst->collection.size - cloned - orig_size);
+ return 1;
+ }
+
+ // set the sorted flag when we know it's sorted
+ if (orig_size == 0 && src->collection.sorted) {
+ dst->collection.sorted = true;
+ }
+
+ return 0;
+}
+
+int cxListDifference(CxList *dst,
+ const CxList *minuend, const CxList *subtrahend,
+ cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) {
+ if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator;
+
+ // optimize for sorted collections
+ if (cxCollectionSorted(minuend) && cxCollectionSorted(subtrahend)) {
+ bool dst_was_empty = cxCollectionSize(dst) == 0;
+
+ CxIterator min_iter = cxListIterator(minuend);
+ CxIterator sub_iter = cxListIterator(subtrahend);
+ while (cxIteratorValid(min_iter)) {
+ void *min_elem = cxIteratorCurrent(min_iter);
+ void *sub_elem;
+ int d;
+ if (cxIteratorValid(sub_iter)) {
+ sub_elem = cxIteratorCurrent(sub_iter);
+ d = cx_list_compare_wrapper(sub_elem, min_elem, subtrahend);
+ } else {
+ // no more elements in the subtrahend,
+ // i.e., the min_elem is larger than any elem of the subtrahend
+ d = 1;
+ }
+ if (d == 0) {
+ // is contained, so skip it
+ cxIteratorNext(min_iter);
+ } else if (d < 0) {
+ // subtrahend is smaller than minuend,
+ // check the next element
+ cxIteratorNext(sub_iter);
+ } else {
+ // subtrahend is larger than the dst element,
+ // clone the minuend and advance
+ void **dst_mem = cxListEmplace(dst);
+ void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;
+ void* dst_ptr = clone_func(target, min_elem, clone_allocator, data);
+ if (dst_ptr == NULL) {
+ cx_list_pop_uninitialized_elements(dst, 1);
+ return 1;
+ }
+ if (cxCollectionStoresPointers(dst)) {
+ *dst_mem = dst_ptr;
+ }
+ cxIteratorNext(min_iter);
+ }
+ }
+
+ // if dst was empty, it is now guaranteed to be sorted
+ dst->collection.sorted = dst_was_empty;
+ } else {
+ CxIterator min_iter = cxListIterator(minuend);
+ cx_foreach(void *, elem, min_iter) {
+ if (cxListContains(subtrahend, elem)) {
+ continue;
+ }
+ void **dst_mem = cxListEmplace(dst);
+ void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;
+ void* dst_ptr = clone_func(target, elem, clone_allocator, data);
+ if (dst_ptr == NULL) {
+ cx_list_pop_uninitialized_elements(dst, 1);
+ return 1;
+ }
+ if (cxCollectionStoresPointers(dst)) {
+ *dst_mem = dst_ptr;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int cxListIntersection(CxList *dst,
+ const CxList *src, const CxList *other,
+ cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) {
+ if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator;
+
+ // optimize for sorted collections
+ if (cxCollectionSorted(src) && cxCollectionSorted(other)) {
+ bool dst_was_empty = cxCollectionSize(dst) == 0;
+
+ CxIterator src_iter = cxListIterator(src);
+ CxIterator other_iter = cxListIterator(other);
+ while (cxIteratorValid(src_iter) && cxIteratorValid(other_iter)) {
+ void *src_elem = cxIteratorCurrent(src_iter);
+ void *other_elem = cxIteratorCurrent(other_iter);
+ int d = cx_list_compare_wrapper(src_elem, other_elem, src);
+ if (d == 0) {
+ // is contained, clone it
+ void **dst_mem = cxListEmplace(dst);
+ void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;
+ void* dst_ptr = clone_func(target, src_elem, clone_allocator, data);
+ if (dst_ptr == NULL) {
+ cx_list_pop_uninitialized_elements(dst, 1);
+ return 1;
+ }
+ if (cxCollectionStoresPointers(dst)) {
+ *dst_mem = dst_ptr;
+ }
+ cxIteratorNext(src_iter);
+ } else if (d < 0) {
+ // the other element is larger, skip the source element
+ cxIteratorNext(src_iter);
+ } else {
+ // the source element is larger, try to find it in the other list
+ cxIteratorNext(other_iter);
+ }
+ }
+
+ // if dst was empty, it is now guaranteed to be sorted
+ dst->collection.sorted = dst_was_empty;
+ } else {
+ CxIterator src_iter = cxListIterator(src);
+ cx_foreach(void *, elem, src_iter) {
+ if (!cxListContains(other, elem)) {
+ continue;
+ }
+ void **dst_mem = cxListEmplace(dst);
+ void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;
+ void* dst_ptr = clone_func(target, elem, clone_allocator, data);
+ if (dst_ptr == NULL) {
+ cx_list_pop_uninitialized_elements(dst, 1);
+ return 1;
+ }
+ if (cxCollectionStoresPointers(dst)) {
+ *dst_mem = dst_ptr;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int cxListUnion(CxList *dst,
+ const CxList *src, const CxList *other,
+ cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) {
+ if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator;
+
+ // optimize for sorted collections
+ if (cxCollectionSorted(src) && cxCollectionSorted(other)) {
+ bool dst_was_empty = cxCollectionSize(dst) == 0;
+
+ CxIterator src_iter = cxListIterator(src);
+ CxIterator other_iter = cxListIterator(other);
+ while (cxIteratorValid(src_iter) || cxIteratorValid(other_iter)) {
+ void *src_elem = NULL, *other_elem = NULL;
+ int d;
+ if (!cxIteratorValid(src_iter)) {
+ other_elem = cxIteratorCurrent(other_iter);
+ d = 1;
+ } else if (!cxIteratorValid(other_iter)) {
+ src_elem = cxIteratorCurrent(src_iter);
+ d = -1;
+ } else {
+ src_elem = cxIteratorCurrent(src_iter);
+ other_elem = cxIteratorCurrent(other_iter);
+ d = cx_list_compare_wrapper(src_elem, other_elem, src);
+ }
+ void *clone_from;
+ if (d < 0) {
+ // source element is smaller clone it
+ clone_from = src_elem;
+ cxIteratorNext(src_iter);
+ } else if (d == 0) {
+ // both elements are equal, clone from the source, skip other
+ clone_from = src_elem;
+ cxIteratorNext(src_iter);
+ cxIteratorNext(other_iter);
+ } else {
+ // the other element is smaller, clone it
+ clone_from = other_elem;
+ cxIteratorNext(other_iter);
+ }
+ void **dst_mem = cxListEmplace(dst);
+ void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;
+ void* dst_ptr = clone_func(target, clone_from, clone_allocator, data);
+ if (dst_ptr == NULL) {
+ cx_list_pop_uninitialized_elements(dst, 1);
+ return 1;
+ }
+ if (cxCollectionStoresPointers(dst)) {
+ *dst_mem = dst_ptr;
+ }
+ }
+
+ // if dst was empty, it is now guaranteed to be sorted
+ dst->collection.sorted = dst_was_empty;
+ } else {
+ if (cxListClone(dst, src, clone_func, clone_allocator, data)) {
+ return 1;
+ }
+ CxIterator other_iter = cxListIterator(other);
+ cx_foreach(void *, elem, other_iter) {
+ if (cxListContains(src, elem)) {
+ continue;
+ }
+ void **dst_mem = cxListEmplace(dst);
+ void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;
+ void* dst_ptr = clone_func(target, elem, clone_allocator, data);
+ if (dst_ptr == NULL) {
+ cx_list_pop_uninitialized_elements(dst, 1);
+ return 1;
+ }
+ if (cxCollectionStoresPointers(dst)) {
+ *dst_mem = dst_ptr;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int cxListCloneShallow(CxList *dst, const CxList *src) {
+ return cxListClone(dst, src, use_shallow_clone_func(src));
+}
+
+int cxListDifferenceShallow(CxList *dst, const CxList *minuend, const CxList *subtrahend) {
+ return cxListDifference(dst, minuend, subtrahend, use_shallow_clone_func(minuend));
+}
+
+int cxListIntersectionShallow(CxList *dst, const CxList *src, const CxList *other) {
+ return cxListIntersection(dst, src, other, use_shallow_clone_func(src));
+}
+
+int cxListUnionShallow(CxList *dst, const CxList *src, const CxList *other) {
+ return cxListUnion(dst, src, other, use_shallow_clone_func(src));
+}
+
+int cxListReserve(CxList *list, size_t capacity) {
+ if (list->cl->change_capacity == NULL) {
+ return 0;
+ }
+ if (capacity <= cxCollectionSize(list)) {
+ return 0;
+ }
+ return list->cl->change_capacity(list, capacity);
+}
+
+int cxListShrink(CxList *list) {
+ if (list->cl->change_capacity == NULL) {
+ return 0;
+ }
+ return list->cl->change_capacity(list, cxCollectionSize(list));
+}
#include "cx/map.h"\r
#include <string.h>\r
\r
+#include "cx/list.h"\r
+\r
// <editor-fold desc="empty map implementation">\r
\r
-static void cx_empty_map_noop(cx_attr_unused CxMap *map) {\r
+static void cx_empty_map_noop(CX_UNUSED CxMap *map) {\r
// this is a noop, but MUST be implemented\r
}\r
\r
static void *cx_empty_map_get(\r
- cx_attr_unused const CxMap *map,\r
- cx_attr_unused CxHashKey key\r
+ CX_UNUSED const CxMap *map,\r
+ CX_UNUSED CxHashKey key\r
) {\r
return NULL;\r
}\r
\r
-static bool cx_empty_map_iter_valid(cx_attr_unused const void *iter) {\r
+static bool cx_empty_map_iter_valid(CX_UNUSED const void *iter) {\r
return false;\r
}\r
\r
static CxMapIterator cx_empty_map_iterator(\r
const struct cx_map_s *map,\r
- cx_attr_unused enum cx_map_iterator_type type\r
+ CX_UNUSED enum cx_map_iterator_type type\r
) {\r
CxMapIterator iter = {0};\r
iter.map = (CxMap*) map;\r
\r
CxMap cx_empty_map = {\r
{\r
- NULL,\r
NULL,\r
0,\r
0,\r
NULL,\r
NULL,\r
NULL,\r
+ NULL,\r
+ NULL,\r
+ NULL,\r
false,\r
true\r
},\r
}\r
\r
int cx_map_put(CxMap *map, CxHashKey key, void *value) {\r
- return map->cl->put(map, key, value) == NULL;\r
+ return map->cl->put(map, key, value).key == NULL;\r
}\r
\r
void *cx_map_emplace(CxMap *map, CxHashKey key) {\r
- return map->cl->put(map, key, NULL);\r
+ const CxMapEntry entry = map->cl->put(map, key, NULL);\r
+ if (entry.key == NULL) return NULL;\r
+ return entry.value;\r
}\r
\r
void *cx_map_get(const CxMap *map, CxHashKey key) {\r
if (map == NULL) return;\r
map->cl->deallocate(map);\r
}\r
+\r
+static void cx_map_remove_uninitialized_entry(CxMap *map, CxHashKey key) {\r
+ cx_destructor_func destr_bak = map->collection.simple_destructor;\r
+ cx_destructor_func2 destr2_bak = map->collection.advanced_destructor;\r
+ map->collection.simple_destructor = NULL;\r
+ map->collection.advanced_destructor = NULL;\r
+ cxMapRemove(map, key);\r
+ map->collection.simple_destructor = destr_bak;\r
+ map->collection.advanced_destructor = destr2_bak;\r
+}\r
+\r
+static void* cx_map_shallow_clone_func(void *dst, const void *src, const CxAllocator *al, void *data) {\r
+ size_t elem_size = *(size_t*)data;\r
+ if (dst == NULL) dst = cxMalloc(al, elem_size);\r
+ if (dst != NULL) memcpy(dst, src, elem_size);\r
+ return dst;\r
+}\r
+\r
+#define use_shallow_clone_func(map) cx_map_shallow_clone_func, NULL, (void*)&((map)->collection.elem_size)\r
+\r
+int cxMapClone(CxMap *dst, const CxMap *src, cx_clone_func clone_func,\r
+ const CxAllocator *clone_allocator, void *data) {\r
+ if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator;\r
+ CxMapIterator src_iter = cxMapIterator(src);\r
+ for (size_t i = 0; i < cxMapSize(src); i++) {\r
+ const CxMapEntry *entry = cxIteratorCurrent(src_iter);\r
+ void **dst_mem = cxMapEmplace(dst, *(entry->key));\r
+ if (dst_mem == NULL) {\r
+ return 1; // LCOV_EXCL_LINE\r
+ }\r
+ void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;\r
+ void *dst_ptr = clone_func(target, entry->value, clone_allocator, data);\r
+ if (dst_ptr == NULL) {\r
+ cx_map_remove_uninitialized_entry(dst, *(entry->key));\r
+ return 1;\r
+ }\r
+ if (cxCollectionStoresPointers(dst)) {\r
+ *dst_mem = dst_ptr;\r
+ }\r
+ cxIteratorNext(src_iter);\r
+ }\r
+ return 0;\r
+}\r
+\r
+int cxMapDifference(CxMap *dst, const CxMap *minuend, const CxMap *subtrahend,\r
+ cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) {\r
+ if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator;\r
+\r
+ CxMapIterator src_iter = cxMapIterator(minuend);\r
+ cx_foreach(const CxMapEntry *, entry, src_iter) {\r
+ if (cxMapContains(subtrahend, *entry->key)) {\r
+ continue;\r
+ }\r
+ void** dst_mem = cxMapEmplace(dst, *entry->key);\r
+ if (dst_mem == NULL) {\r
+ return 1; // LCOV_EXCL_LINE\r
+ }\r
+ void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;\r
+ void* dst_ptr = clone_func(target, entry->value, clone_allocator, data);\r
+ if (dst_ptr == NULL) {\r
+ cx_map_remove_uninitialized_entry(dst, *(entry->key));\r
+ return 1;\r
+ }\r
+ if (cxCollectionStoresPointers(dst)) {\r
+ *dst_mem = dst_ptr;\r
+ }\r
+ }\r
+ return 0;\r
+}\r
+\r
+int cxMapListDifference(CxMap *dst, const CxMap *src, const CxList *keys,\r
+ cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) {\r
+ if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator;\r
+\r
+ CxMapIterator src_iter = cxMapIterator(src);\r
+ cx_foreach(const CxMapEntry *, entry, src_iter) {\r
+ if (cxListContains(keys, entry->key)) {\r
+ continue;\r
+ }\r
+ void** dst_mem = cxMapEmplace(dst, *entry->key);\r
+ if (dst_mem == NULL) {\r
+ return 1; // LCOV_EXCL_LINE\r
+ }\r
+ void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;\r
+ void* dst_ptr = clone_func(target, entry->value, clone_allocator, data);\r
+ if (dst_ptr == NULL) {\r
+ cx_map_remove_uninitialized_entry(dst, *(entry->key));\r
+ return 1;\r
+ }\r
+ if (cxCollectionStoresPointers(dst)) {\r
+ *dst_mem = dst_ptr;\r
+ }\r
+ }\r
+ return 0;\r
+}\r
+\r
+int cxMapIntersection(CxMap *dst, const CxMap *src, const CxMap *other,\r
+ cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) {\r
+ if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator;\r
+\r
+ CxMapIterator src_iter = cxMapIterator(src);\r
+ cx_foreach(const CxMapEntry *, entry, src_iter) {\r
+ if (!cxMapContains(other, *entry->key)) {\r
+ continue;\r
+ }\r
+ void** dst_mem = cxMapEmplace(dst, *entry->key);\r
+ if (dst_mem == NULL) {\r
+ return 1; // LCOV_EXCL_LINE\r
+ }\r
+ void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;\r
+ void* dst_ptr = clone_func(target, entry->value, clone_allocator, data);\r
+ if (dst_ptr == NULL) {\r
+ cx_map_remove_uninitialized_entry(dst, *(entry->key));\r
+ return 1;\r
+ }\r
+ if (cxCollectionStoresPointers(dst)) {\r
+ *dst_mem = dst_ptr;\r
+ }\r
+ }\r
+ return 0;\r
+}\r
+\r
+int cxMapListIntersection(CxMap *dst, const CxMap *src, const CxList *keys,\r
+ cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) {\r
+ if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator;\r
+\r
+ CxMapIterator src_iter = cxMapIterator(src);\r
+ cx_foreach(const CxMapEntry *, entry, src_iter) {\r
+ if (!cxListContains(keys, entry->key)) {\r
+ continue;\r
+ }\r
+ void** dst_mem = cxMapEmplace(dst, *entry->key);\r
+ if (dst_mem == NULL) {\r
+ return 1; // LCOV_EXCL_LINE\r
+ }\r
+ void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;\r
+ void* dst_ptr = clone_func(target, entry->value, clone_allocator, data);\r
+ if (dst_ptr == NULL) {\r
+ cx_map_remove_uninitialized_entry(dst, *(entry->key));\r
+ return 1;\r
+ }\r
+ if (cxCollectionStoresPointers(dst)) {\r
+ *dst_mem = dst_ptr;\r
+ }\r
+ }\r
+ return 0;\r
+}\r
+\r
+int cxMapUnion(CxMap *dst, const CxMap *src,\r
+ cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) {\r
+ if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator;\r
+\r
+ CxMapIterator src_iter = cxMapIterator(src);\r
+ cx_foreach(const CxMapEntry *, entry, src_iter) {\r
+ if (cxMapContains(dst, *entry->key)) {\r
+ continue;\r
+ }\r
+ void** dst_mem = cxMapEmplace(dst, *entry->key);\r
+ if (dst_mem == NULL) {\r
+ return 1; // LCOV_EXCL_LINE\r
+ }\r
+ void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;\r
+ void* dst_ptr = clone_func(target, entry->value, clone_allocator, data);\r
+ if (dst_ptr == NULL) {\r
+ cx_map_remove_uninitialized_entry(dst, *(entry->key));\r
+ return 1;\r
+ }\r
+ if (cxCollectionStoresPointers(dst)) {\r
+ *dst_mem = dst_ptr;\r
+ }\r
+ }\r
+ return 0;\r
+}\r
+\r
+int cxMapCloneShallow(CxMap *dst, const CxMap *src) {\r
+ return cxMapClone(dst, src, use_shallow_clone_func(src));\r
+}\r
+\r
+int cxMapDifferenceShallow(CxMap *dst, const CxMap *minuend, const CxMap *subtrahend) {\r
+ return cxMapDifference(dst, minuend, subtrahend, use_shallow_clone_func(minuend));\r
+}\r
+\r
+int cxMapListDifferenceShallow(CxMap *dst, const CxMap *src, const CxList *keys) {\r
+ return cxMapListDifference(dst, src, keys, use_shallow_clone_func(src));\r
+}\r
+\r
+int cxMapIntersectionShallow(CxMap *dst, const CxMap *src, const CxMap *other) {\r
+ return cxMapIntersection(dst, src, other, use_shallow_clone_func(src));\r
+}\r
+\r
+int cxMapListIntersectionShallow(CxMap *dst, const CxMap *src, const CxList *keys) {\r
+ return cxMapListIntersection(dst, src, keys, use_shallow_clone_func(src));\r
+}\r
+\r
+int cxMapUnionShallow(CxMap *dst, const CxMap *src) {\r
+ return cxMapUnion(dst, src, use_shallow_clone_func(src));\r
+}\r
+\r
+int cxMapCompare(const CxMap *map, const CxMap *other) {\r
+ // compare map sizes\r
+ const size_t size_left = cxMapSize(map);\r
+ const size_t size_right = cxMapSize(other);\r
+ if (size_left < size_right) {\r
+ return -1;\r
+ } else if (size_left > size_right) {\r
+ return 1;\r
+ }\r
+\r
+ // iterate through the first map\r
+ CxMapIterator iter = cxMapIterator(map);\r
+ cx_foreach(const CxMapEntry *, entry, iter) {\r
+ const void *value_left = entry->value;\r
+ const void *value_right = cxMapGet(other, *entry->key);\r
+ // if the other map does not have the key, we are done\r
+ if (value_right == NULL) {\r
+ return -1;\r
+ }\r
+ // compare the values\r
+ const int d = cx_invoke_compare_func(map, value_left, value_right);\r
+ if (d != 0) {\r
+ return d;\r
+ }\r
+ }\r
+\r
+ return 0;\r
+}\r
if (!ptr) return;
struct cx_mempool_s *pool = p;
+ cx_destructor_func destr = pool->destr;
+ cx_destructor_func2 destr2 = pool->destr2;
+
struct cx_mempool_memory_s *mem =
(void*) ((char *) ptr - sizeof(struct cx_mempool_memory_s));
if (mem->destructor) {
mem->destructor(mem->c);
}
- if (pool->destr) {
- pool->destr(mem->c);
+ if (destr != NULL) {
+ destr(mem->c);
}
- if (pool->destr2) {
- pool->destr2(pool->destr2_data, mem->c);
+ if (destr2 != NULL) {
+ destr2(pool->destr2_data, mem->c);
}
cxFree(pool->base_allocator, mem);
size_t last_index = pool->size - 1;
}
static void cx_mempool_free_all_simple(const struct cx_mempool_s *pool) {
- const bool has_destr = pool->destr;
- const bool has_destr2 = pool->destr2;
+ cx_destructor_func destr = pool->destr;
+ cx_destructor_func2 destr2 = pool->destr2;
for (size_t i = 0; i < pool->size; i++) {
struct cx_mempool_memory_s *mem = pool->data[i];
if (mem->destructor) {
mem->destructor(mem->c);
}
- if (has_destr) {
- pool->destr(mem->c);
+ if (destr != NULL) {
+ destr(mem->c);
}
- if (has_destr2) {
- pool->destr2(pool->destr2_data, mem->c);
+ if (destr2 != NULL) {
+ destr2(pool->destr2_data, mem->c);
}
cxFree(pool->base_allocator, mem);
}
if (!ptr) return;
struct cx_mempool_s *pool = p;
+ cx_destructor_func destr = pool->destr;
+ cx_destructor_func2 destr2 = pool->destr2;
+
struct cx_mempool_memory2_s *mem =
(void*) ((char *) ptr - sizeof(struct cx_mempool_memory2_s));
if (mem->destructor) {
mem->destructor(mem->data, mem->c);
}
- if (pool->destr) {
- pool->destr(mem->c);
+ if (destr != NULL) {
+ destr(mem->c);
}
- if (pool->destr2) {
- pool->destr2(pool->destr2_data, mem->c);
+ if (destr2 != NULL) {
+ destr2(pool->destr2_data, mem->c);
}
cxFree(pool->base_allocator, mem);
size_t last_index = pool->size - 1;
}
static void cx_mempool_free_all_advanced(const struct cx_mempool_s *pool) {
- const bool has_destr = pool->destr;
- const bool has_destr2 = pool->destr2;
+ cx_destructor_func destr = pool->destr;
+ cx_destructor_func2 destr2 = pool->destr2;
for (size_t i = 0; i < pool->size; i++) {
struct cx_mempool_memory2_s *mem = pool->data[i];
if (mem->destructor) {
mem->destructor(mem->data, mem->c);
}
- if (has_destr) {
- pool->destr(mem->c);
+ if (destr != NULL) {
+ destr(mem->c);
}
- if (has_destr2) {
- pool->destr2(pool->destr2_data, mem->c);
+ if (destr2 != NULL) {
+ destr2(pool->destr2_data, mem->c);
}
cxFree(pool->base_allocator, mem);
}
if (!ptr) return;
struct cx_mempool_s *pool = p;
+ cx_destructor_func destr = pool->destr;
+ cx_destructor_func2 destr2 = pool->destr2;
+
for (size_t i = 0; i < pool->size; i++) {
if (ptr == pool->data[i]) {
- if (pool->destr) {
- pool->destr(ptr);
+ if (destr != NULL) {
+ destr(ptr);
}
- if (pool->destr2) {
- pool->destr2(pool->destr2_data, ptr);
+ if (destr2 != NULL) {
+ destr2(pool->destr2_data, ptr);
}
cxFree(pool->base_allocator, ptr);
size_t last_index = pool->size - 1;
}
static void cx_mempool_free_all_pure(const struct cx_mempool_s *pool) {
- const bool has_destr = pool->destr;
- const bool has_destr2 = pool->destr2;
+ cx_destructor_func destr = pool->destr;
+ cx_destructor_func2 destr2 = pool->destr2;
for (size_t i = 0; i < pool->size; i++) {
void *mem = pool->data[i];
- if (has_destr) {
- pool->destr(mem);
+ if (destr != NULL) {
+ destr(mem);
}
- if (has_destr2) {
- pool->destr2(pool->destr2_data, mem);
+ if (destr2 != NULL) {
+ destr2(pool->destr2_data, mem);
}
cxFree(pool->base_allocator, mem);
}
va_copy(ap2, ap);
int ret = vsnprintf(buf, CX_PRINTF_SBO_SIZE, fmt, ap);
if (ret < 0) {
+ // LCOV_EXCL_START
va_end(ap2);
return ret;
+ // LCOV_EXCL_STOP
} else if (ret < CX_PRINTF_SBO_SIZE) {
va_end(ap2);
return (int) wfc(buf, 1, ret, stream);
if (s.ptr) {
ret = vsnprintf(s.ptr, len, fmt, ap2);
if (ret < 0) {
+ // LCOV_EXCL_START
cxFree(a, s.ptr);
s.ptr = NULL;
+ // LCOV_EXCL_STOP
} else {
s.length = (size_t) ret;
}
if (ptr) {
int newret = vsnprintf(ptr, newlen, fmt, ap2);
if (newret < 0) {
- cxFree(alloc, ptr);
+ cxFree(alloc, ptr); // LCOV_EXCL_LINE
} else {
*len = newlen;
*str = ptr;
if (ptr) {
int newret = vsnprintf(ptr, newlen, fmt, ap2);
if (newret < 0) {
- cxFree(alloc, ptr);
+ cxFree(alloc, ptr); // LCOV_EXCL_LINE
} else {
*len = newlen;
*str = ptr;
#include "cx/properties.h"
#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
const CxPropertiesConfig cx_properties_config_default = {
- '=',
- '#',
- '\0',
- '\0',
+ '=',
+ '#',
+ '\0',
+ '\0',
'\\',
};
if (cxBufferEof(&prop->input)) {
// destroy a possible previously initialized buffer
cxBufferDestroy(&prop->input);
- cxBufferInit(&prop->input, (void*) buf, len,
- NULL, CX_BUFFER_COPY_ON_WRITE | CX_BUFFER_AUTO_EXTEND);
+ cxBufferInit(&prop->input, NULL, (void*) buf,
+ len, CX_BUFFER_COPY_ON_WRITE | CX_BUFFER_AUTO_EXTEND);
prop->input.size = len;
} else {
if (cxBufferAppend(buf, 1, len, &prop->input) < len) return -1;
char *buf,
size_t capacity
) {
- cxBufferInit(&prop->buffer, buf, capacity, NULL, CX_BUFFER_COPY_ON_EXTEND);
+ cxBufferInit(&prop->buffer, NULL, buf, capacity, CX_BUFFER_COPY_ON_EXTEND);
}
CxPropertiesStatus cxPropertiesNext(
// a pointer to the buffer we want to read from
CxBuffer *current_buffer = &prop->input;
-
+
+ char comment1 = prop->config.comment1;
+ char comment2 = prop->config.comment2;
+ char comment3 = prop->config.comment3;
+ char delimiter = prop->config.delimiter;
+ char continuation = prop->config.continuation;
+
// check if we have rescued data
if (!cxBufferEof(&prop->buffer)) {
// check if we can now get a complete line
cxstring input = cx_strn(prop->input.space + prop->input.pos,
prop->input.size - prop->input.pos);
cxstring nl = cx_strchr(input, '\n');
+ while (nl.length > 0) {
+ // check for line continuation
+ char previous = nl.ptr > input.ptr ? nl.ptr[-1] : cx_strat(cx_bstr(&prop->buffer), -1);
+ if (previous == continuation) {
+ // this nl is a line continuation, check the next newline
+ nl = cx_strchr(cx_strsubs(nl, 1), '\n');
+ } else {
+ break;
+ }
+ }
+
if (nl.length > 0) {
// we add as much data to the rescue buffer as we need
// to complete the line
if (cxBufferAppend(input.ptr, 1,
len_until_nl, &prop->buffer) < len_until_nl) {
- return CX_PROPERTIES_BUFFER_ALLOC_FAILED;
+ return CX_PROPERTIES_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE
}
// advance the position in the input buffer
// still not enough data, copy input buffer to internal buffer
if (cxBufferAppend(input.ptr, 1,
input.length, &prop->buffer) < input.length) {
- return CX_PROPERTIES_BUFFER_ALLOC_FAILED;
+ return CX_PROPERTIES_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE
}
// reset the input buffer (make way for a re-fill)
cxBufferReset(&prop->input);
return CX_PROPERTIES_INCOMPLETE_DATA;
}
}
-
- char comment1 = prop->config.comment1;
- char comment2 = prop->config.comment2;
- char comment3 = prop->config.comment3;
- char delimiter = prop->config.delimiter;
-
+
// get one line and parse it
while (!cxBufferEof(current_buffer)) {
const char *buf = current_buffer->space + current_buffer->pos;
size_t delimiter_index = 0;
size_t comment_index = 0;
bool has_comment = false;
+ bool has_continuation = false;
size_t i = 0;
char c = 0;
if (delimiter_index == 0 && !has_comment) {
delimiter_index = i;
}
+ } else if (delimiter_index > 0 && c == continuation && i+1 < len && buf[i+1] == '\n') {
+ has_continuation = true;
+ i++;
} else if (c == '\n') {
break;
}
assert(cxBufferEof(&prop->buffer));
if (prop->buffer.space == NULL) {
// initialize a rescue buffer, if the user did not provide one
- cxBufferInit(&prop->buffer, NULL, 256, NULL, CX_BUFFER_AUTO_EXTEND);
+ cxBufferInit(&prop->buffer, NULL, NULL, 256, CX_BUFFER_AUTO_EXTEND);
} else {
// from a previous rescue there might be already read data
// reset the buffer to avoid unnecessary buffer extension
cxBufferReset(&prop->buffer);
}
if (cxBufferAppend(buf, 1, len, &prop->buffer) < len) {
- return CX_PROPERTIES_BUFFER_ALLOC_FAILED;
+ return CX_PROPERTIES_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE
}
// reset the input buffer (make way for a re-fill)
cxBufferReset(&prop->input);
k = cx_strtrim(k);
val = cx_strtrim(val);
if (k.length > 0) {
+ current_buffer->pos += i + 1;
+ assert(current_buffer->pos <= current_buffer->size);
+ assert(current_buffer != &prop->buffer || current_buffer->pos == current_buffer->size);
+
+ if (has_continuation) {
+ char *ptr = (char*)val.ptr;
+ if (current_buffer != &prop->buffer) {
+ // move value to the rescue buffer
+ if (prop->buffer.space == NULL) {
+ cxBufferInit(&prop->buffer, NULL, NULL, 256, CX_BUFFER_AUTO_EXTEND);
+ }
+ prop->buffer.size = 0;
+ prop->buffer.pos = 0;
+ if (cxBufferWrite(val.ptr, 1, val.length, &prop->buffer) != val.length) {
+ return CX_PROPERTIES_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE
+ }
+ val.ptr = prop->buffer.space;
+ ptr = prop->buffer.space;
+ }
+ // value.ptr is now inside the rescue buffer and we can
+ // remove the continuation character from the value
+ bool trim = false;
+ size_t x = 0;
+ for(size_t j=0;j<val.length;j++) {
+ c = ptr[j];
+ if (j+1 < val.length && c == '\\' && ptr[j+1] == '\n') {
+ // skip continuation and newline character
+ j++;
+ trim = true; // enable trim in the next line
+ continue;
+ }
+ if (j > x) {
+ if (trim) {
+ if (isspace((unsigned char)c)) {
+ continue;
+ }
+ trim = false;
+ }
+ ptr[x] = c;
+ }
+ x++;
+ }
+ val.length = x;
+ }
*key = k;
*value = val;
- current_buffer->pos += i + 1;
- assert(current_buffer->pos <= current_buffer->size);
+
return CX_PROPERTIES_NO_ERROR;
} else {
return CX_PROPERTIES_INVALID_EMPTY_KEY;
return CX_PROPERTIES_NO_DATA;
}
-static int cx_properties_sink_map(
- cx_attr_unused CxProperties *prop,
- CxPropertiesSink *sink,
- cxstring key,
- cxstring value
-) {
- CxMap *map = sink->sink;
- CxAllocator *alloc = sink->data;
- cxmutstr v = cx_strdup_a(alloc, value);
- int r = cxMapPut(map, key, v.ptr);
- if (r != 0) cx_strfree_a(alloc, &v);
- return r;
-}
-
-CxPropertiesSink cxPropertiesMapSink(CxMap *map) {
- CxPropertiesSink sink;
- sink.sink = map;
- sink.data = (void*) cxDefaultAllocator;
- sink.sink_func = cx_properties_sink_map;
- return sink;
-}
-
-static int cx_properties_read_string(
- CxProperties *prop,
- CxPropertiesSource *src,
- cxstring *target
-) {
- if (prop->input.space == src->src) {
- // when the input buffer already contains the string
- // we have nothing more to provide
- target->length = 0;
- } else {
- target->ptr = src->src;
- target->length = src->data_size;
+#ifndef CX_PROPERTIES_LOAD_FILL_SIZE
+#define CX_PROPERTIES_LOAD_FILL_SIZE 1024
+#endif
+const unsigned cx_properties_load_fill_size = CX_PROPERTIES_LOAD_FILL_SIZE;
+#ifndef CX_PROPERTIES_LOAD_BUF_SIZE
+#define CX_PROPERTIES_LOAD_BUF_SIZE 256
+#endif
+const unsigned cx_properties_load_buf_size = CX_PROPERTIES_LOAD_BUF_SIZE;
+
+CxPropertiesStatus cx_properties_load(const CxAllocator *allocator,
+ cxstring filename, CxMap *target, CxPropertiesConfig config) {
+ if (allocator == NULL) {
+ allocator = cxDefaultAllocator;
}
- return 0;
-}
-
-static int cx_properties_read_file(
- cx_attr_unused CxProperties *prop,
- CxPropertiesSource *src,
- cxstring *target
-) {
- target->ptr = src->data_ptr;
- target->length = fread(src->data_ptr, 1, src->data_size, src->src);
- return ferror((FILE*)src->src);
-}
-static int cx_properties_read_init_file(
- cx_attr_unused CxProperties *prop,
- CxPropertiesSource *src
-) {
- src->data_ptr = cxMallocDefault(src->data_size);
- if (src->data_ptr == NULL) return 1;
- return 0;
-}
-
-static void cx_properties_read_clean_file(
- cx_attr_unused CxProperties *prop,
- CxPropertiesSource *src
-) {
- cxFreeDefault(src->data_ptr);
-}
-
-CxPropertiesSource cxPropertiesStringSource(cxstring str) {
- CxPropertiesSource src;
- src.src = (void*) str.ptr;
- src.data_size = str.length;
- src.data_ptr = NULL;
- src.read_func = cx_properties_read_string;
- src.read_init_func = NULL;
- src.read_clean_func = NULL;
- return src;
-}
-
-CxPropertiesSource cxPropertiesCstrnSource(const char *str, size_t len) {
- CxPropertiesSource src;
- src.src = (void*) str;
- src.data_size = len;
- src.data_ptr = NULL;
- src.read_func = cx_properties_read_string;
- src.read_init_func = NULL;
- src.read_clean_func = NULL;
- return src;
-}
-
-CxPropertiesSource cxPropertiesCstrSource(const char *str) {
- CxPropertiesSource src;
- src.src = (void*) str;
- src.data_size = strlen(str);
- src.data_ptr = NULL;
- src.read_func = cx_properties_read_string;
- src.read_init_func = NULL;
- src.read_clean_func = NULL;
- return src;
-}
-
-CxPropertiesSource cxPropertiesFileSource(FILE *file, size_t chunk_size) {
- CxPropertiesSource src;
- src.src = file;
- src.data_size = chunk_size;
- src.data_ptr = NULL;
- src.read_func = cx_properties_read_file;
- src.read_init_func = cx_properties_read_init_file;
- src.read_clean_func = cx_properties_read_clean_file;
- return src;
-}
+ // sanity check for the map
+ const bool use_cstring = cxCollectionStoresPointers(target);
+ if (!use_cstring && cxCollectionElementSize(target) != sizeof(cxmutstr)) {
+ return CX_PROPERTIES_MAP_ERROR;
+ }
-CxPropertiesStatus cxPropertiesLoad(
- CxProperties *prop,
- CxPropertiesSink sink,
- CxPropertiesSource source
-) {
- assert(source.read_func != NULL);
- assert(sink.sink_func != NULL);
+ // create a duplicate to guarantee zero-termination
+ cxmutstr fname = cx_strdup(filename);
+ if (fname.ptr == NULL) {
+ return CX_PROPERTIES_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE
+ }
- // initialize reader
- if (source.read_init_func != NULL) {
- if (source.read_init_func(prop, &source)) {
- return CX_PROPERTIES_READ_INIT_FAILED;
- }
+ // open the file
+ FILE *f = fopen(fname.ptr, "r");
+ if (f == NULL) {
+ cx_strfree(&fname);
+ return CX_PROPERTIES_FILE_ERROR;
}
- // transfer the data from the source to the sink
+ // initialize the parser
+ char linebuf[CX_PROPERTIES_LOAD_BUF_SIZE];
+ char fillbuf[CX_PROPERTIES_LOAD_FILL_SIZE];
CxPropertiesStatus status;
- CxPropertiesStatus kv_status = CX_PROPERTIES_NO_DATA;
- bool found = false;
+ CxProperties parser;
+ cxPropertiesInit(&parser, config);
+ cxPropertiesUseStack(&parser, linebuf, CX_PROPERTIES_LOAD_BUF_SIZE);
+
+ // read/fill/parse loop
+ status = CX_PROPERTIES_NO_DATA;
+ size_t keys_found = 0;
while (true) {
- // read input
- cxstring input;
- if (source.read_func(prop, &source, &input)) {
- status = CX_PROPERTIES_READ_FAILED;
+ size_t r = fread(fillbuf, 1, cx_properties_load_fill_size, f);
+ if (ferror(f)) {
+ // LCOV_EXCL_START
+ status = CX_PROPERTIES_FILE_ERROR;
break;
+ // LCOV_EXCL_STOP
}
-
- // no more data - break
- if (input.length == 0) {
- if (found) {
- // something was found, check the last kv_status
- if (kv_status == CX_PROPERTIES_INCOMPLETE_DATA) {
- status = CX_PROPERTIES_INCOMPLETE_DATA;
- } else {
- status = CX_PROPERTIES_NO_ERROR;
- }
- } else {
- // nothing found
- status = CX_PROPERTIES_NO_DATA;
- }
+ if (r == 0) {
break;
}
-
- // set the input buffer and read the k/v-pairs
- cxPropertiesFill(prop, input);
-
- do {
- cxstring key, value;
- kv_status = cxPropertiesNext(prop, &key, &value);
- if (kv_status == CX_PROPERTIES_NO_ERROR) {
- found = true;
- if (sink.sink_func(prop, &sink, key, value)) {
- kv_status = CX_PROPERTIES_SINK_FAILED;
+ if (cxPropertiesFilln(&parser, fillbuf, r)) {
+ // LCOV_EXCL_START
+ status = CX_PROPERTIES_BUFFER_ALLOC_FAILED;
+ break;
+ // LCOV_EXCL_STOP
+ }
+ cxstring key, value;
+ while (true) {
+ status = cxPropertiesNext(&parser, &key, &value);
+ if (status != CX_PROPERTIES_NO_ERROR) {
+ break;
+ } else {
+ cxmutstr v = cx_strdup_a(allocator, value);
+ // LCOV_EXCL_START
+ if (v.ptr == NULL) {
+ status = CX_PROPERTIES_MAP_ERROR;
+ break;
+ }
+ // LCOV_EXCL_STOP
+ void *mv = use_cstring ? (void*)v.ptr : &v;
+ if (cxMapPut(target, key, mv)) {
+ // LCOV_EXCL_START
+ cx_strfree(&v);
+ status = CX_PROPERTIES_MAP_ERROR;
+ break;
+ // LCOV_EXCL_STOP
}
+ keys_found++;
}
- } while (kv_status == CX_PROPERTIES_NO_ERROR);
-
- if (kv_status > CX_PROPERTIES_OK) {
- status = kv_status;
+ }
+ if (status > CX_PROPERTIES_OK) {
break;
}
}
- if (source.read_clean_func != NULL) {
- source.read_clean_func(prop, &source);
+ // cleanup and exit
+ fclose(f);
+ cxPropertiesDestroy(&parser);
+ cx_strfree(&fname);
+ if (status == CX_PROPERTIES_NO_DATA && keys_found > 0) {
+ return CX_PROPERTIES_NO_ERROR;
+ } else {
+ return status;
}
-
- return status;
}
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
-#ifdef MEMRCHR_NEED_GNU
+
+#ifdef WITH_MEMRCHR
#define _GNU_SOURCE
#endif
#include <ctype.h>
#ifdef _WIN32
-#define cx_strcasecmp_impl _strnicmp
+static int cx_fixed_strnicmp(const char* s1, const char* s2, size_t count) {
+ // Microsoft's implementation crashes when count == 0 and either string is NULL
+ if (count == 0) return 0;
+ return _strnicmp(s1, s2, count);
+}
+#define cx_strcasecmp_impl cx_fixed_strnicmp
#else
#include <strings.h>
#define cx_strcasecmp_impl strncasecmp
#endif
-cxmutstr cx_mutstr(char *cstring) {
- return (cxmutstr) {cstring, cstring == NULL ? 0 : strlen(cstring)};
-}
-
-cxmutstr cx_mutstrn(
- char *cstring,
- size_t length
-) {
- return (cxmutstr) {cstring, length};
-}
-
-cxstring cx_str(const char *cstring) {
- return (cxstring) {cstring, cstring == NULL ? 0 : strlen(cstring)};
-}
-
-cxstring cx_strn(
- const char *cstring,
- size_t length
-) {
- return (cxstring) {cstring, length};
-}
-
void cx_strfree(cxmutstr *str) {
if (str == NULL) return;
cxFreeDefault(str->ptr);
str->length = 0;
}
-int cx_strcpy_a(
+int cx_strcpy_a_(
const CxAllocator *alloc,
cxmutstr *dest,
cxstring src
) {
if (cxReallocate(alloc, &dest->ptr, src.length + 1)) {
- return 1;
+ return 1; // LCOV_EXCL_LINE
}
memcpy(dest->ptr, src.ptr, src.length);
return size;
}
-cxmutstr cx_strcat_ma(
+cxmutstr cx_strcat_a(
const CxAllocator *alloc,
cxmutstr str,
size_t count,
...
) {
- if (count == 0) return str;
+ if (count == 0) {
+ if (cxReallocate(alloc, &str.ptr, str.length + 1)) {
+ return CX_NULLSTR; // LCOV_EXCL_LINE
+ }
+ str.ptr[str.length] = '\0';
+ return str;
+ }
va_list ap;
va_start(ap, count);
va_list ap2;
size_t slen = str.length;
for (size_t i = 0; i < count; i++) {
cxstring s = va_arg(ap, cxstring);
- if (slen > SIZE_MAX - str.length) overflow = true;
+ if (slen > SIZE_MAX - s.length) overflow = true;
slen += s.length;
}
va_end(ap);
if (overflow) {
va_end(ap2);
errno = EOVERFLOW;
- return (cxmutstr) { NULL, 0 };
+ return CX_NULLSTR;
}
- // reallocate or create new string
- char *newstr;
- if (str.ptr == NULL) {
- newstr = cxMalloc(alloc, slen + 1);
- } else {
- newstr = cxRealloc(alloc, str.ptr, slen + 1);
- }
- if (newstr == NULL) {
+ // reallocate or create a new string
+ if (cxReallocate(alloc, &str.ptr, slen + 1)) {
+ // LCOV_EXCL_START
va_end(ap2);
- return (cxmutstr) {NULL, 0};
+ return CX_NULLSTR;
+ // LCOV_EXCL_STOP
}
- str.ptr = newstr;
// concatenate strings
size_t pos = str.length;
return str;
}
-cxstring cx_strsubs(
+cxstring cx_strsubs_(
cxstring string,
size_t start
) {
- return cx_strsubsl(string, start, string.length - start);
-}
-
-cxmutstr cx_strsubs_m(
- cxmutstr string,
- size_t start
-) {
- return cx_strsubsl_m(string, start, string.length - start);
+ return cx_strsubsl_(string, start, string.length);
}
-cxstring cx_strsubsl(
+cxstring cx_strsubsl_(
cxstring string,
size_t start,
size_t length
return (cxstring) {string.ptr + start, length};
}
-cxmutstr cx_strsubsl_m(
- cxmutstr string,
- size_t start,
- size_t length
-) {
- cxstring result = cx_strsubsl(cx_strcast(string), start, length);
- return (cxmutstr) {(char *) result.ptr, result.length};
-}
-
-cxstring cx_strchr(
+cxstring cx_strchr_(
cxstring string,
int chr
) {
return (cxstring) {ret, string.length - (ret - string.ptr)};
}
-cxmutstr cx_strchr_m(
- cxmutstr string,
- int chr
-) {
- cxstring result = cx_strchr(cx_strcast(string), chr);
- return (cxmutstr) {(char *) result.ptr, result.length};
-}
-
-cxstring cx_strrchr(
+cxstring cx_strrchr_(
cxstring string,
int chr
) {
#endif
}
-cxmutstr cx_strrchr_m(
- cxmutstr string,
- int chr
-) {
- cxstring result = cx_strrchr(cx_strcast(string), chr);
- return (cxmutstr) {(char *) result.ptr, result.length};
-}
-
#ifndef CX_STRSTR_SBO_SIZE
#define CX_STRSTR_SBO_SIZE 128
#endif
const unsigned cx_strstr_sbo_size = CX_STRSTR_SBO_SIZE;
-cxstring cx_strstr(
- cxstring haystack,
- cxstring needle
-) {
+cxstring cx_strstr_(cxstring haystack, cxstring needle) {
if (needle.length == 0) {
return haystack;
}
return result;
}
-cxmutstr cx_strstr_m(
- cxmutstr haystack,
- cxstring needle
-) {
- cxstring result = cx_strstr(cx_strcast(haystack), needle);
- return (cxmutstr) {(char *) result.ptr, result.length};
-}
-
-size_t cx_strsplit(
+size_t cx_strsplit_(
cxstring string,
cxstring delim,
size_t limit,
return n;
}
-size_t cx_strsplit_a(
+size_t cx_strsplit_a_(
const CxAllocator *allocator,
cxstring string,
cxstring delim,
}
}
*output = cxCalloc(allocator, n, sizeof(cxstring));
- return cx_strsplit(string, delim, n, *output);
+ return cx_strsplit_(string, delim, n, *output);
}
-size_t cx_strsplit_m(
+size_t cx_strsplit_m_(
cxmutstr string,
cxstring delim,
size_t limit,
cxmutstr *output
) {
- return cx_strsplit(cx_strcast(string),
+ return cx_strsplit_(cx_strcast(string),
delim, limit, (cxstring *) output);
}
-size_t cx_strsplit_ma(
+size_t cx_strsplit_ma_(
const CxAllocator *allocator,
cxmutstr string,
cxstring delim,
size_t limit,
cxmutstr **output
) {
- return cx_strsplit_a(allocator, cx_strcast(string),
+ return cx_strsplit_a_(allocator, cx_strcast(string),
delim, limit, (cxstring **) output);
}
cxMalloc(allocator, string.length + 1),
string.length
};
+ // LCOV_EXCL_START
if (result.ptr == NULL) {
result.length = 0;
return result;
}
+ // LCOV_EXCL_STOP
memcpy(result.ptr, string.ptr, string.length);
result.ptr[string.length] = '\0';
return result;
}
-cxstring cx_strtrim(cxstring string) {
+cxstring cx_strtrim_(cxstring string) {
cxstring result = string;
- while (result.length > 0 && isspace((unsigned char)(result.ptr[0]))) {
+ while (isspace((unsigned char)cx_strat(result, 0))) {
result.ptr++;
result.length--;
}
- while (result.length > 0 && isspace((unsigned char)result.ptr[result.length - 1])) {
+ while (isspace((unsigned char)cx_strat(result, -1))) {
result.length--;
}
return result;
}
-cxmutstr cx_strtrim_m(cxmutstr string) {
- cxstring result = cx_strtrim(cx_strcast(string));
- return (cxmutstr) {(char *) result.ptr, result.length};
-}
-
bool cx_strprefix_(
cxstring string,
cxstring prefix
#endif
}
-cxmutstr cx_strreplacen_a(
+cxmutstr cx_strreplace_(
const CxAllocator *allocator,
cxstring str,
cxstring search,
return ctx;
}
-bool cx_strtok_next(
+bool cx_strtok_next_(
CxStrtokCtx *ctx,
cxstring *token
) {
return true;
}
-bool cx_strtok_next_m(
- CxStrtokCtx *ctx,
- cxmutstr *token
-) {
- return cx_strtok_next(ctx, (cxstring *) token);
-}
-
void cx_strtok_delim(
CxStrtokCtx *ctx,
const cxstring *delim,
#include "cx/tree.h"
-#include "cx/array_list.h"
-
#include <assert.h>
+#include <string.h>
#define CX_TREE_PTR(cur, off) (*(void**)(((char*)(cur))+(off)))
#define tree_parent(node) CX_TREE_PTR(node, loc_parent)
#define tree_prev(node) CX_TREE_PTR(node, loc_prev)
#define tree_next(node) CX_TREE_PTR(node, loc_next)
-#define cx_tree_ptr_locations \
- loc_parent, loc_children, loc_last_child, loc_prev, loc_next
-
-#define cx_tree_node_layout(tree) \
+#define tree_layout(tree) \
(tree)->loc_parent,\
(tree)->loc_children,\
(tree)->loc_last_child,\
(tree)->loc_prev, \
(tree)->loc_next
-static void cx_tree_zero_pointers(
- void *node,
- ptrdiff_t loc_parent,
- ptrdiff_t loc_children,
- ptrdiff_t loc_last_child,
- ptrdiff_t loc_prev,
- ptrdiff_t loc_next
-) {
- tree_parent(node) = NULL;
- if (loc_prev >= 0) {
- tree_prev(node) = NULL;
- }
- tree_next(node) = NULL;
- tree_children(node) = NULL;
- if (loc_last_child >= 0) {
- tree_last_child(node) = NULL;
- }
-}
-
-void cx_tree_link(
+void cx_tree_add(
void *parent,
void *node,
ptrdiff_t loc_parent,
void *current_parent = tree_parent(node);
if (current_parent == parent) return;
if (current_parent != NULL) {
- cx_tree_unlink(node, cx_tree_ptr_locations);
+ cx_tree_remove(node, loc_parent, loc_children,
+ loc_last_child, loc_prev, loc_next);
}
if (tree_children(parent) == NULL) {
}
}
-void cx_tree_unlink(
+void cx_tree_remove(
void *node,
ptrdiff_t loc_parent,
ptrdiff_t loc_children,
}
}
-int cx_tree_search(
- const void *root,
- size_t depth,
- const void *node,
- cx_tree_search_func sfunc,
- void **result,
- ptrdiff_t loc_children,
- ptrdiff_t loc_next
-) {
+int cx_tree_search(const void *root, size_t max_depth,
+ const void *data, cx_tree_search_func sfunc, void **result,
+ ptrdiff_t loc_children, ptrdiff_t loc_next) {
// help avoiding bugs due to uninitialized memory
assert(result != NULL);
*result = NULL;
+ // NULL root? exit!
+ if (root == NULL) {
+ return -1;
+ }
+
// remember return value for best match
- int ret = sfunc(root, node);
+ int ret = sfunc(root, data);
if (ret < 0) {
// not contained, exit
return -1;
}
// when depth is one, we are already done
- if (depth == 1) {
+ if (max_depth == 1) {
return ret;
}
// special case: indefinite depth
- if (depth == 0) {
- depth = SIZE_MAX;
+ if (max_depth == 0) {
+ max_depth = SIZE_MAX;
}
// create an iterator
// loop through the remaining tree
cx_foreach(void *, elem, iter) {
// investigate the current node
- int ret_elem = sfunc(elem, node);
+ int ret_elem = sfunc(elem, data);
if (ret_elem == 0) {
// if found, exit the search
*result = elem;
}
// when we reached the max depth, skip the subtree
- if (iter.depth == depth) {
+ if (iter.depth == max_depth) {
cxTreeIteratorContinue(iter);
}
}
- // dispose the iterator as we might have exited the loop early
+ // dispose of the iterator as we might have exited the loop early
cxTreeIteratorDispose(&iter);
assert(ret < 0 || *result != NULL);
return ret;
}
-int cx_tree_search_data(
- const void *root,
- size_t depth,
- const void *data,
- cx_tree_search_data_func sfunc,
- void **result,
- ptrdiff_t loc_children,
- ptrdiff_t loc_next
-) {
- // it is basically the same implementation
- return cx_tree_search(
- root, depth, data,
- (cx_tree_search_func) sfunc,
- result,
- loc_children, loc_next);
-}
-
static bool cx_tree_iter_valid(const void *it) {
- const struct cx_tree_iterator_s *iter = it;
+ const CxTreeIterator *iter = it;
return iter->node != NULL;
}
static void *cx_tree_iter_current(const void *it) {
- const struct cx_tree_iterator_s *iter = it;
+ const CxTreeIterator *iter = it;
return iter->node;
}
static void cx_tree_iter_next(void *it) {
- struct cx_tree_iterator_s *iter = it;
+ CxTreeIterator *iter = it;
ptrdiff_t const loc_next = iter->loc_next;
ptrdiff_t const loc_children = iter->loc_children;
// protect us from misuse
}
} else {
// node has children, push the first child onto the stack and enter it
- cx_array_simple_add(iter->stack, children);
+ if (iter->depth >= iter->stack_capacity) {
+ const size_t newcap = iter->stack_capacity + 8;
+ if (cxReallocateArrayDefault(&iter->stack, newcap, sizeof(void*))) {
+ // we cannot return an error in this function
+ abort(); // LCOV_EXCL_LINE
+ }
+ iter->stack_capacity = newcap;
+ }
+ iter->stack[iter->depth] = children;
+ iter->depth++;
iter->node = children;
iter->counter++;
}
ptrdiff_t loc_children,
ptrdiff_t loc_next
) {
- CxTreeIterator iter;
- iter.loc_children = loc_children;
- iter.loc_next = loc_next;
- iter.visit_on_exit = visit_on_exit;
+ CxTreeIterator ret;
+ ret.use_dfs = true;
+ ret.loc_children = loc_children;
+ ret.loc_next = loc_next;
+ ret.visit_on_exit = visit_on_exit;
// initialize members
- iter.node_next = NULL;
- iter.exiting = false;
- iter.skip = false;
+ ret.node_next = NULL;
+ ret.exiting = false;
+ ret.skip = false;
// assign base iterator functions
- iter.base.allow_remove = false;
- iter.base.remove = false;
- iter.base.current_impl = NULL;
- iter.base.valid = cx_tree_iter_valid;
- iter.base.next = cx_tree_iter_next;
- iter.base.current = cx_tree_iter_current;
+ ret.base.allow_remove = false;
+ ret.base.remove = false;
+ ret.base.current_impl = NULL;
+ ret.base.valid = cx_tree_iter_valid;
+ ret.base.next = cx_tree_iter_next;
+ ret.base.current = cx_tree_iter_current;
// visit the root node
- iter.node = root;
+ ret.node = root;
if (root != NULL) {
- iter.stack_capacity = 16;
- iter.stack = cxMallocDefault(sizeof(void *) * 16);
- iter.stack[0] = root;
- iter.counter = 1;
- iter.depth = 1;
+ ret.stack_capacity = 16;
+ ret.stack = cxMallocDefault(sizeof(void *) * 16);
+ ret.stack[0] = root;
+ ret.counter = 1;
+ ret.depth = 1;
} else {
- iter.stack_capacity = 0;
- iter.stack = NULL;
- iter.counter = 0;
- iter.depth = 0;
+ ret.stack_capacity = 0;
+ ret.stack = NULL;
+ ret.counter = 0;
+ ret.depth = 0;
}
- return iter;
+ return ret;
}
static bool cx_tree_visitor_valid(const void *it) {
- const struct cx_tree_visitor_s *iter = it;
+ const CxTreeIterator *iter = it;
return iter->node != NULL;
}
static void *cx_tree_visitor_current(const void *it) {
- const struct cx_tree_visitor_s *iter = it;
+ const CxTreeIterator *iter = it;
return iter->node;
}
-cx_attr_nonnull
+CX_NONNULL
static void cx_tree_visitor_enqueue_siblings(
- struct cx_tree_visitor_s *iter, void *node, ptrdiff_t loc_next) {
+ CxTreeIterator *iter, void *node, ptrdiff_t loc_next) {
node = tree_next(node);
while (node != NULL) {
struct cx_tree_visitor_queue_s *q;
}
static void cx_tree_visitor_next(void *it) {
- struct cx_tree_visitor_s *iter = it;
+ CxTreeIterator *iter = it;
// protect us from misuse
if (!iter->base.valid(iter)) return;
iter->counter++;
}
-CxTreeVisitor cx_tree_visitor(
+CxTreeIterator cx_tree_visitor(
void *root,
ptrdiff_t loc_children,
ptrdiff_t loc_next
) {
- CxTreeVisitor iter;
- iter.loc_children = loc_children;
- iter.loc_next = loc_next;
+ CxTreeIterator ret;
+ ret.visit_on_exit = false;
+ ret.exiting = false;
+ ret.use_dfs = false;
+ ret.loc_children = loc_children;
+ ret.loc_next = loc_next;
// initialize members
- iter.skip = false;
- iter.queue_next = NULL;
- iter.queue_last = NULL;
+ ret.skip = false;
+ ret.queue_next = NULL;
+ ret.queue_last = NULL;
// assign base iterator functions
- iter.base.allow_remove = false;
- iter.base.remove = false;
- iter.base.current_impl = NULL;
- iter.base.valid = cx_tree_visitor_valid;
- iter.base.next = cx_tree_visitor_next;
- iter.base.current = cx_tree_visitor_current;
+ ret.base.allow_remove = false;
+ ret.base.remove = false;
+ ret.base.current_impl = NULL;
+ ret.base.valid = cx_tree_visitor_valid;
+ ret.base.next = cx_tree_visitor_next;
+ ret.base.current = cx_tree_visitor_current;
// visit the root node
- iter.node = root;
+ ret.node = root;
if (root != NULL) {
- iter.counter = 1;
- iter.depth = 1;
- } else {
- iter.counter = 0;
- iter.depth = 0;
- }
-
- return iter;
-}
-
-static void cx_tree_add_link_duplicate(
- void *original, void *duplicate,
- ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
- ptrdiff_t loc_prev, ptrdiff_t loc_next
-) {
- void *shared_parent = tree_parent(original);
- if (shared_parent == NULL) {
- cx_tree_link(original, duplicate, cx_tree_ptr_locations);
+ ret.counter = 1;
+ ret.depth = 1;
} else {
- cx_tree_link(shared_parent, duplicate, cx_tree_ptr_locations);
+ ret.counter = 0;
+ ret.depth = 0;
}
-}
-static void cx_tree_add_link_new(
- void *parent, void *node, cx_tree_search_func sfunc,
- ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
- ptrdiff_t loc_prev, ptrdiff_t loc_next
-) {
- // check the current children one by one,
- // if they could be children of the new node
- void *child = tree_children(parent);
- while (child != NULL) {
- void *next = tree_next(child);
-
- if (sfunc(node, child) > 0) {
- // the sibling could be a child -> re-link
- cx_tree_link(node, child, cx_tree_ptr_locations);
- }
-
- child = next;
- }
-
- // add new node as new child
- cx_tree_link(parent, node, cx_tree_ptr_locations);
+ return ret;
}
-int cx_tree_add(
- const void *src,
- cx_tree_search_func sfunc,
- cx_tree_node_create_func cfunc,
- void *cdata,
- void **cnode,
- void *root,
- ptrdiff_t loc_parent,
- ptrdiff_t loc_children,
- ptrdiff_t loc_last_child,
- ptrdiff_t loc_prev,
- ptrdiff_t loc_next
-) {
- *cnode = cfunc(src, cdata);
- if (*cnode == NULL) return 1;
- cx_tree_zero_pointers(*cnode, cx_tree_ptr_locations);
-
- void *match = NULL;
- int result = cx_tree_search(
- root,
- 0,
- *cnode,
- sfunc,
- &match,
- loc_children,
- loc_next
- );
-
- if (result < 0) {
- // node does not fit into the tree - return non-zero value
- return 1;
- } else if (result == 0) {
- // data already found in the tree, link duplicate
- cx_tree_add_link_duplicate(match, *cnode, cx_tree_ptr_locations);
- } else {
- // closest match found, add new node
- cx_tree_add_link_new(match, *cnode, sfunc, cx_tree_ptr_locations);
+size_t cx_tree_size(void *root, ptrdiff_t loc_children, ptrdiff_t loc_next) {
+ CxTreeIterator iter = cx_tree_iterator(root, false, loc_children, loc_next);
+ while (cxIteratorValid(iter)) {
+ cxIteratorNext(iter);
}
-
- return 0;
+ return iter.counter;
}
-unsigned int cx_tree_add_look_around_depth = 3;
-
-size_t cx_tree_add_iter(
- struct cx_iterator_base_s *iter,
- size_t num,
- cx_tree_search_func sfunc,
- cx_tree_node_create_func cfunc,
- void *cdata,
- void **failed,
- void *root,
- ptrdiff_t loc_parent,
- ptrdiff_t loc_children,
- ptrdiff_t loc_last_child,
- ptrdiff_t loc_prev,
- ptrdiff_t loc_next
-) {
- // erase the failed pointer
- *failed = NULL;
-
- // iter not valid? cancel...
- if (!iter->valid(iter)) return 0;
-
- size_t processed = 0;
- void *current_node = root;
- const void *elem;
-
- for (void **eptr; processed < num &&
- iter->valid(iter) && (eptr = iter->current(iter)) != NULL;
- iter->next(iter)) {
- elem = *eptr;
-
- // create the new node
- void *new_node = cfunc(elem, cdata);
- if (new_node == NULL) return processed;
- cx_tree_zero_pointers(new_node, cx_tree_ptr_locations);
-
- // start searching from current node
- void *match;
- int result;
- unsigned int look_around_retries = cx_tree_add_look_around_depth;
- cx_tree_add_look_around_retry:
- result = cx_tree_search(
- current_node,
- 0,
- new_node,
- sfunc,
- &match,
- loc_children,
- loc_next
- );
-
- if (result < 0) {
- // traverse upwards and try to find better parents
- void *parent = tree_parent(current_node);
- if (parent != NULL) {
- if (look_around_retries > 0) {
- look_around_retries--;
- current_node = parent;
- } else {
- // look around retries exhausted, start from the root
- current_node = root;
- }
- goto cx_tree_add_look_around_retry;
- } else {
- // no parents. so we failed
- *failed = new_node;
- return processed;
- }
- } else if (result == 0) {
- // data already found in the tree, link duplicate
- cx_tree_add_link_duplicate(match, new_node, cx_tree_ptr_locations);
- // but stick with the original match, in case we needed a new root
- current_node = match;
- } else {
- // closest match found, add new node as child
- cx_tree_add_link_new(match, new_node, sfunc,
- cx_tree_ptr_locations);
- current_node = match;
+size_t cx_tree_depth(void *root, ptrdiff_t loc_children, ptrdiff_t loc_next) {
+ CxTreeIterator iter = cx_tree_iterator(root, false, loc_children, loc_next);
+ size_t depth = 0;
+ while (cxIteratorValid(iter)) {
+ if (iter.depth > depth) {
+ depth = iter.depth;
}
-
- processed++;
+ cxIteratorNext(iter);
}
- return processed;
+ return depth;
}
-size_t cx_tree_add_array(
- const void *src,
- size_t num,
- size_t elem_size,
- cx_tree_search_func sfunc,
- cx_tree_node_create_func cfunc,
- void *cdata,
- void **failed,
- void *root,
- ptrdiff_t loc_parent,
- ptrdiff_t loc_children,
- ptrdiff_t loc_last_child,
- ptrdiff_t loc_prev,
- ptrdiff_t loc_next
-) {
- // erase failed pointer
- *failed = NULL;
-
- // super special case: zero elements
- if (num == 0) {
- return 0;
- }
-
- // special case: one element does not need an iterator
- if (num == 1) {
- void *node;
- if (0 == cx_tree_add(
- src, sfunc, cfunc, cdata, &node, root,
- loc_parent, loc_children, loc_last_child,
- loc_prev, loc_next)) {
- return 1;
- } else {
- *failed = node;
- return 0;
- }
- }
-
- // otherwise, create iterator and hand over to other function
- CxIterator iter = cxIterator(src, elem_size, num, false);
- return cx_tree_add_iter(cxIteratorRef(iter), num, sfunc,
- cfunc, cdata, failed, root,
- loc_parent, loc_children, loc_last_child,
- loc_prev, loc_next);
-}
+CxTree *cxTreeCreate(const CxAllocator *allocator,
+ size_t node_size, size_t elem_size, void *root, ptrdiff_t loc_data,
+ ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
+ ptrdiff_t loc_prev, ptrdiff_t loc_next) {
-static int cx_tree_default_insert_element(
- CxTree *tree,
- const void *data
-) {
- void *node;
- if (tree->root == NULL) {
- node = tree->node_create(data, tree);
- if (node == NULL) return 1;
- cx_tree_zero_pointers(node, cx_tree_node_layout(tree));
- tree->root = node;
- tree->size = 1;
- return 0;
- }
- int result = cx_tree_add(data, tree->search, tree->node_create,
- tree, &node, tree->root, cx_tree_node_layout(tree));
- if (0 == result) {
- tree->size++;
- } else {
- cxFree(tree->allocator, node);
+ if (allocator == NULL) {
+ allocator = cxDefaultAllocator;
}
- return result;
-}
-static size_t cx_tree_default_insert_many(
- CxTree *tree,
- CxIteratorBase *iter,
- size_t n
-) {
- size_t ins = 0;
- if (!iter->valid(iter)) return 0;
- if (tree->root == NULL) {
- // use the first element from the iter to create the root node
- void **eptr = iter->current(iter);
- void *node = tree->node_create(*eptr, tree);
- if (node == NULL) return 0;
- cx_tree_zero_pointers(node, cx_tree_node_layout(tree));
- tree->root = node;
- ins = 1;
- iter->next(iter);
- }
- void *failed;
- ins += cx_tree_add_iter(iter, n, tree->search, tree->node_create,
- tree, &failed, tree->root, cx_tree_node_layout(tree));
- tree->size += ins;
- if (ins < n) {
- cxFree(tree->allocator, failed);
- }
- return ins;
-}
+ CxTree *tree = cxZalloc(allocator, sizeof(CxTree));
+ if (tree == NULL) return NULL; // LCOV_EXCL_LINE
+ tree->collection.allocator = allocator;
-static void *cx_tree_default_find(
- CxTree *tree,
- const void *subtree,
- const void *data,
- size_t depth
-) {
- if (tree->root == NULL) return NULL;
-
- void *found;
- if (0 == cx_tree_search_data(
- subtree,
- depth,
- data,
- tree->search_data,
- &found,
- tree->loc_children,
- tree->loc_next
- )) {
- return found;
+ if (elem_size == CX_STORE_POINTERS) {
+ tree->collection.store_pointer = true;
+ tree->collection.elem_size = sizeof(void*);
} else {
- return NULL;
+ tree->collection.elem_size = elem_size;
}
-}
-static cx_tree_class cx_tree_default_class = {
- cx_tree_default_insert_element,
- cx_tree_default_insert_many,
- cx_tree_default_find
-};
-
-CxTree *cxTreeCreate(const CxAllocator *allocator,
- cx_tree_node_create_func create_func,
- cx_tree_search_func search_func,
- cx_tree_search_data_func search_data_func,
- ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
- ptrdiff_t loc_prev, ptrdiff_t loc_next
-) {
- if (allocator == NULL) {
- allocator = cxDefaultAllocator;
- }
- assert(create_func != NULL);
- assert(search_func != NULL);
- assert(search_data_func != NULL);
-
- CxTree *tree = cxMalloc(allocator, sizeof(CxTree));
- if (tree == NULL) return NULL;
-
- tree->cl = &cx_tree_default_class;
- tree->allocator = allocator;
- tree->node_create = create_func;
- tree->search = search_func;
- tree->search_data = search_data_func;
- tree->simple_destructor = NULL;
- tree->advanced_destructor = (cx_destructor_func2) cxFree;
- tree->destructor_data = (void *) allocator;
+ tree->root = root;
+ tree->node_size = node_size;
tree->loc_parent = loc_parent;
tree->loc_children = loc_children;
tree->loc_last_child = loc_last_child;
tree->loc_prev = loc_prev;
tree->loc_next = loc_next;
- tree->root = NULL;
- tree->size = 0;
+ tree->loc_data = loc_data;
+
+ if (root == NULL) {
+ cxSetAdvancedDestructor(tree, cxFree, (void*)allocator);
+ } else {
+ tree->collection.size = cx_tree_size(root, loc_children, loc_next);
+ }
return tree;
}
if (tree->root != NULL) {
cxTreeClear(tree);
}
- cxFree(tree->allocator, tree);
-}
-
-CxTree *cxTreeCreateWrapped(const CxAllocator *allocator, void *root,
- ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child,
- ptrdiff_t loc_prev, ptrdiff_t loc_next) {
- if (allocator == NULL) {
- allocator = cxDefaultAllocator;
- }
- assert(root != NULL);
-
- CxTree *tree = cxMalloc(allocator, sizeof(CxTree));
- if (tree == NULL) return NULL;
-
- tree->cl = &cx_tree_default_class;
- // set the allocator anyway, just in case...
- tree->allocator = allocator;
- tree->node_create = NULL;
- tree->search = NULL;
- tree->search_data = NULL;
- tree->simple_destructor = NULL;
- tree->advanced_destructor = NULL;
- tree->destructor_data = NULL;
- tree->loc_parent = loc_parent;
- tree->loc_children = loc_children;
- tree->loc_last_child = loc_last_child;
- tree->loc_prev = loc_prev;
- tree->loc_next = loc_next;
- tree->root = root;
- tree->size = cxTreeSubtreeSize(tree, root);
- return tree;
+ cxFree(tree->collection.allocator, tree);
}
void cxTreeSetParent(CxTree *tree, void *parent, void *child) {
size_t loc_parent = tree->loc_parent;
if (tree_parent(child) == NULL) {
- tree->size++;
+ tree->collection.size++;
}
- cx_tree_link(parent, child, cx_tree_node_layout(tree));
+ cx_tree_add(parent, child, tree_layout(tree));
}
-void cxTreeAddChildNode(CxTree *tree, void *parent, void *child) {
- cx_tree_link(parent, child, cx_tree_node_layout(tree));
- tree->size++;
+void cxTreeAddNode(CxTree *tree, void *parent, void *child) {
+ cx_tree_add(parent, child, tree_layout(tree));
+ tree->collection.size++;
}
-int cxTreeAddChild(CxTree *tree, void *parent, const void *data) {
- void *node = tree->node_create(data, tree);
- if (node == NULL) return 1;
- cx_tree_zero_pointers(node, cx_tree_node_layout(tree));
- cx_tree_link(parent, node, cx_tree_node_layout(tree));
- tree->size++;
- return 0;
+void *cxTreeCreateNode(CxTree *tree, void *parent) {
+ void *node = cxZalloc(tree->collection.allocator, tree->node_size);
+ if (node == NULL) return NULL; // LCOV_EXCL_LINE
+ cx_tree_add(parent, node, tree_layout(tree));
+ tree->collection.size++;
+ return node;
}
-int cxTreeInsert(CxTree *tree, const void *data) {
- return tree->cl->insert_element(tree, data);
+void *cxTreeAddData(CxTree *tree, void *parent, const void *data) {
+ if (tree->loc_data < 0) return NULL;
+
+ void *node = cxTreeCreateNode(tree, parent);
+ if (node == NULL) return NULL; // LCOV_EXCL_LINE
+
+ char *dst = node;
+ dst += tree->loc_data;
+ const void *src = cxCollectionStoresPointers(tree) ? (const void*)&data : data;
+ memcpy(dst, src, tree->collection.elem_size);
+
+ return node;
+}
+
+void *cxTreeCreateRoot(CxTree *tree) {
+ if (tree->root != NULL) {
+ return tree->root;
+ }
+
+ void *node = cxZalloc(tree->collection.allocator, tree->node_size);
+ if (node == NULL) return NULL; // LCOV_EXCL_LINE
+ tree->root = node;
+ tree->collection.size = 1;
+ return node;
}
-size_t cxTreeInsertIter(CxTree *tree, CxIteratorBase *iter, size_t n) {
- return tree->cl->insert_many(tree, iter, n);
+void *cxTreeCreateRootData(CxTree *tree, const void *data) {
+ if (tree->loc_data < 0) return NULL;
+
+ void *node = cxTreeCreateRoot(tree);
+ if (node == NULL) return NULL; // LCOV_EXCL_LINE
+
+ char *dst = node;
+ dst += tree->loc_data;
+ const void *src = cxCollectionStoresPointers(tree) ? (const void*)&data : data;
+ memcpy(dst, src, tree->collection.elem_size);
+
+ return node;
}
-size_t cxTreeInsertArray(CxTree *tree, const void *data, size_t elem_size, size_t n) {
- if (n == 0) return 0;
- if (n == 1) return 0 == cxTreeInsert(tree, data) ? 1 : 0;
- CxIterator iter = cxIterator(data, elem_size, n, false);
- return cxTreeInsertIter(tree, cxIteratorRef(iter), n);
+void *cxTreeSetRoot(CxTree *tree, void *new_root) {
+ void *old_root = tree->root;
+ tree->root = new_root;
+ return old_root;
}
-void *cxTreeFind( CxTree *tree, const void *data) {
- return tree->cl->find(tree, tree->root, data, 0);
+void *cxTreeFindInSubtree(CxTree *tree, const void *data,
+ void *subtree_root, size_t max_depth, bool use_dfs) {
+ if (tree->loc_data < 0 || subtree_root == NULL) {
+ return NULL;
+ }
+
+ CxTreeIterator iter = use_dfs
+ ? cx_tree_iterator(subtree_root, false, tree->loc_children, tree->loc_next)
+ : cx_tree_visitor(subtree_root, tree->loc_children, tree->loc_next);
+
+ cx_foreach(char*, node, iter) {
+ char *node_data = node + tree->loc_data;
+ if (cxCollectionStoresPointers(tree)) {
+ node_data = *(void**)node_data;
+ }
+ if (cx_invoke_compare_func(tree, node_data, data) == 0) {
+ cxTreeIteratorDispose(&iter);
+ return node;
+ }
+ if (iter.depth == max_depth) {
+ cxTreeIteratorContinue(iter);
+ }
+ }
+ return NULL;
}
-void *cxTreeFindInSubtree(CxTree *tree, const void *data, void *subtree_root, size_t max_depth) {
- return tree->cl->find(tree, subtree_root, data, max_depth);
+void *cxTreeFindFastInSubtree(CxTree *tree, const void *data,
+ cx_tree_search_func sfunc, void *root, size_t max_depth) {
+ void *result;
+ int ret = cx_tree_search(root, max_depth, data, sfunc, &result,
+ tree->loc_children, tree->loc_next);
+ if (ret == 0) {
+ return result;
+ } else {
+ return NULL;
+ }
}
size_t cxTreeSubtreeSize(CxTree *tree, void *subtree_root) {
- CxTreeVisitor visitor = cx_tree_visitor(
- subtree_root,
- tree->loc_children,
- tree->loc_next
- );
- while (cxIteratorValid(visitor)) {
- cxIteratorNext(visitor);
+ if (subtree_root == tree->root) {
+ return tree->collection.size;
}
- return visitor.counter;
+ return cx_tree_size(subtree_root, tree->loc_children, tree->loc_next);
}
size_t cxTreeSubtreeDepth(CxTree *tree, void *subtree_root) {
- CxTreeVisitor visitor = cx_tree_visitor(
- subtree_root,
- tree->loc_children,
- tree->loc_next
- );
- while (cxIteratorValid(visitor)) {
- cxIteratorNext(visitor);
- }
- return visitor.depth;
+ return cx_tree_depth(subtree_root, tree->loc_children, tree->loc_next);
}
size_t cxTreeSize(CxTree *tree) {
- return tree->size;
+ return tree->collection.size;
}
size_t cxTreeDepth(CxTree *tree) {
- CxTreeVisitor visitor = cx_tree_visitor(
- tree->root, tree->loc_children, tree->loc_next
- );
- while (cxIteratorValid(visitor)) {
- cxIteratorNext(visitor);
- }
- return visitor.depth;
+ return cx_tree_depth(tree->root, tree->loc_children, tree->loc_next);
}
int cxTreeRemoveNode(
void *new_parent = tree_parent(node);
// first, unlink from the parent
- cx_tree_unlink(node, cx_tree_node_layout(tree));
+ cx_tree_remove(node, tree_layout(tree));
// then relink each child
ptrdiff_t loc_children = tree->loc_children;
}
// link to new parent
- cx_tree_link(new_parent, child, cx_tree_node_layout(tree));
+ cx_tree_add(new_parent, child, tree_layout(tree));
// proceed to next child
child = tree_next(child);
if (loc_last_child >= 0) tree_last_child(node) = NULL;
// the tree now has one member less
- tree->size--;
+ tree->collection.size--;
return 0;
}
void cxTreeRemoveSubtree(CxTree *tree, void *node) {
if (node == tree->root) {
tree->root = NULL;
- tree->size = 0;
+ tree->collection.size = 0;
return;
}
size_t subtree_size = cxTreeSubtreeSize(tree, node);
- cx_tree_unlink(node, cx_tree_node_layout(tree));
- tree->size -= subtree_size;
+ cx_tree_remove(node, tree_layout(tree));
+ tree->collection.size -= subtree_size;
}
int cxTreeDestroyNode(
) {
int result = cxTreeRemoveNode(tree, node, relink_func);
if (result == 0) {
- if (tree->simple_destructor) {
- tree->simple_destructor(node);
- }
- if (tree->advanced_destructor) {
- tree->advanced_destructor(tree->destructor_data, node);
- }
+ cx_invoke_destructor_raw(tree, node);
return 0;
} else {
return result;
}
void cxTreeDestroySubtree(CxTree *tree, void *node) {
- cx_tree_unlink(node, cx_tree_node_layout(tree));
+ cx_tree_remove(node, tree_layout(tree));
CxTreeIterator iter = cx_tree_iterator(
node, true,
tree->loc_children, tree->loc_next
);
cx_foreach(void *, child, iter) {
if (iter.exiting) {
- if (tree->simple_destructor) {
- tree->simple_destructor(child);
- }
- if (tree->advanced_destructor) {
- tree->advanced_destructor(tree->destructor_data, child);
- }
+ // always call the destructors with the node!
+ cx_invoke_destructor_raw(tree, child);
}
}
- tree->size -= iter.counter;
+ tree->collection.size -= iter.counter;
if (node == tree->root) {
tree->root = NULL;
}
}
void cxTreeIteratorDispose(CxTreeIterator *iter) {
- cxFreeDefault(iter->stack);
- iter->stack = NULL;
-}
-
-void cxTreeVisitorDispose(CxTreeVisitor *visitor) {
- struct cx_tree_visitor_queue_s *q = visitor->queue_next;
- while (q != NULL) {
- struct cx_tree_visitor_queue_s *next = q->next;
- cxFreeDefault(q);
- q = next;
+ if (iter->use_dfs) {
+ cxFreeDefault(iter->stack);
+ iter->stack = NULL;
+ } else {
+ struct cx_tree_visitor_queue_s *q = iter->queue_next;
+ while (q != NULL) {
+ struct cx_tree_visitor_queue_s *next = q->next;
+ cxFreeDefault(q);
+ q = next;
+ }
+ iter->queue_next = iter->queue_last = NULL;
}
}
);
}
-CxTreeVisitor cxTreeVisitSubtree(CxTree *tree, void *node) {
+CxTreeIterator cxTreeVisitSubtree(CxTree *tree, void *node) {
return cx_tree_visitor(
node, tree->loc_children, tree->loc_next
);
return cxTreeIterateSubtree(tree, tree->root, visit_on_exit);
}
-CxTreeVisitor cxTreeVisit(CxTree *tree) {
+CxTreeIterator cxTreeVisit(CxTree *tree) {
return cxTreeVisitSubtree(tree, tree->root);
}
args->rbutton4 = strdup(label);
}
-void ui_dialogwindow_args_set_lbutton1_states(UiDialogWindowArgs *args, const int *states) {
- // TODO
+void ui_dialogwindow_args_set_lbutton1_states(UiDialogWindowArgs *args, const int *states, int numstates) {
+ args->lbutton1_states = calloc(numstates+1, sizeof(int));
+ memcpy((void*)args->lbutton1_states, states, numstates * sizeof(int));
+ ((int*)args->lbutton1_states)[numstates] = -1;
}
-void ui_dialogwindow_args_set_lbutton2_states(UiDialogWindowArgs *args, const int *states) {
- // TODO
+void ui_dialogwindow_args_set_lbutton2_states(UiDialogWindowArgs *args, const int *states, int numstates) {
+ args->lbutton2_states = calloc(numstates+1, sizeof(int));
+ memcpy((void*)args->lbutton2_states, states, numstates * sizeof(int));
+ ((int*)args->lbutton2_states)[numstates] = -1;
}
-void ui_dialogwindow_args_set_rbutton3_states(UiDialogWindowArgs *args, const int *states) {
- // TODO
+void ui_dialogwindow_args_set_rbutton3_states(UiDialogWindowArgs *args, const int *states, int numstates) {
+ args->rbutton3_states = calloc(numstates+1, sizeof(int));
+ memcpy((void*)args->rbutton3_states, states, numstates * sizeof(int));
+ ((int*)args->rbutton3_states)[numstates] = -1;
}
-void ui_dialogwindow_args_set_rbutton4_states(UiDialogWindowArgs *args, const int *states) {
- // TODO
+void ui_dialogwindow_args_set_rbutton4_states(UiDialogWindowArgs *args, const int *states, int numstates) {
+ args->rbutton4_states = calloc(numstates+1, sizeof(int));
+ memcpy((void*)args->rbutton4_states, states, numstates * sizeof(int));
+ ((int*)args->rbutton4_states)[numstates] = -1;
}
void ui_dialogwindow_args_set_default_button(UiDialogWindowArgs *args, int button) {
UIEXPORT void ui_dialogwindow_args_set_lbutton2(UiDialogWindowArgs *args, const char *label);
UIEXPORT void ui_dialogwindow_args_set_rbutton3(UiDialogWindowArgs *args, const char *label);
UIEXPORT void ui_dialogwindow_args_set_rbutton4(UiDialogWindowArgs *args, const char *label);
-UIEXPORT void ui_dialogwindow_args_set_lbutton1_states(UiDialogWindowArgs *args, const int *states);
-UIEXPORT void ui_dialogwindow_args_set_lbutton2_states(UiDialogWindowArgs *args, const int *states);
-UIEXPORT void ui_dialogwindow_args_set_rbutton3_states(UiDialogWindowArgs *args, const int *states);
-UIEXPORT void ui_dialogwindow_args_set_rbutton4_states(UiDialogWindowArgs *args, const int *states);
+UIEXPORT void ui_dialogwindow_args_set_lbutton1_states(UiDialogWindowArgs *args, const int *states, int numstates);
+UIEXPORT void ui_dialogwindow_args_set_lbutton2_states(UiDialogWindowArgs *args, const int *states, int numstates);
+UIEXPORT void ui_dialogwindow_args_set_rbutton3_states(UiDialogWindowArgs *args, const int *states, int numstates);
+UIEXPORT void ui_dialogwindow_args_set_rbutton4_states(UiDialogWindowArgs *args, const int *states, int numstates);
UIEXPORT void ui_dialogwindow_args_set_default_button(UiDialogWindowArgs *args, int button);
UIEXPORT void ui_dialogwindow_args_set_width(UiDialogWindowArgs *args, int width);
UIEXPORT void ui_dialogwindow_args_set_height(UiDialogWindowArgs *args, int height);
memset(ctx, 0, sizeof(UiContext));
ctx->mp = mp;
ctx->allocator = mp->allocator;
- ctx->destroy_handler = cxArrayListCreate(ctx->allocator, NULL, sizeof(UiDestroyHandler), 16);
+ ctx->destroy_handler = cxArrayListCreate(ctx->allocator, sizeof(UiDestroyHandler), 16);
ctx->obj = toplevel;
ctx->vars = cxHashMapCreate(mp->allocator, CX_STORE_POINTERS, 16);
- ctx->documents = cxLinkedListCreate(mp->allocator, cx_cmp_ptr, CX_STORE_POINTERS);
- ctx->state_widgets = cxLinkedListCreate(mp->allocator, cx_cmp_ptr, sizeof(UiStateWidget));
- ctx->states = cxArrayListCreate(mp->allocator, cx_cmp_int, sizeof(int), 32);
+ ctx->documents = cxLinkedListCreate(mp->allocator, CX_STORE_POINTERS);
+ ctx->state_widgets = cxLinkedListCreate(mp->allocator, sizeof(UiStateWidget));
+ ctx->states = cxArrayListCreate(mp->allocator, sizeof(int), 32);
+ cxSetCompareFunc(ctx->states, cx_cmp_int);
ctx->attach_document = uic_context_attach_document;
ctx->detach_document2 = uic_context_detach_document;
void uic_context_detach_all(UiContext *ctx) {
// copy list
- CxList *ls = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS);
+ CxList *ls = cxLinkedListCreate(cxDefaultAllocator, CX_STORE_POINTERS);
CxIterator i = cxListIterator(ctx->documents);
cx_foreach(void *, doc, i) {
cxListAdd(ls, doc);
enable = (ui_enablefunc)ui_set_enabled;
}
// get states
- CxList *states = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), 16);
+ CxList *states = cxArrayListCreate(cxDefaultAllocator, sizeof(int), 16);
va_list ap;
va_start(ap, enable);
int state;
if(enable == NULL) {
enable = (ui_enablefunc)ui_set_enabled;
}
- CxList *ls = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), nstates);
+ CxList *ls = cxArrayListCreate(cxDefaultAllocator, sizeof(int), nstates);
for(int i=0;i<nstates;i++) {
cxListAdd(ls, states+i);
}
cxMempoolSetDestructor(mem, (cx_destructor_func)destr);
}
+void ui_var_set_int(UiContext *ctx, const char *name, int64_t value) {
+ UiInteger *i = ui_get_int_var(ctx, name);
+ if(i) {
+ ui_set(i, value);
+ }
+}
+
+int64_t ui_var_get_int(UiContext *ctx, const char *name) {
+ UiInteger *i = ui_get_int_var(ctx, name);
+ if(i) {
+ return ui_get(i);
+ }
+ return 0;
+}
+
+void ui_var_set_double(UiContext *ctx, const char *name, double value) {
+ UiDouble *d = ui_get_double_var(ctx, name);
+ if(d) {
+ ui_set(d, value);
+ }
+}
+
+double ui_var_get_double(UiContext *ctx, const char *name) {
+ UiDouble *d = ui_get_double_var(ctx, name);
+ if(d) {
+ return ui_get(d);
+ }
+ return 0;
+}
+
+void ui_var_set_string(UiContext *ctx, const char *name, char *value) {
+ UiString *s = ui_get_string_var(ctx, name);
+ if(s) {
+ ui_set(s, value);
+ }
+}
+
+char* ui_var_get_string(UiContext *ctx, const char *name) {
+ UiString *s = ui_get_string_var(ctx, name);
+ if(s) {
+ return ui_get(s);
+ }
+ return NULL;
+}
+
+UIEXPORT void ui_var_add_observer(UiContext *ctx, const char *varname, ui_callback f, void *data) {
+ UiVar *var = uic_get_var(ctx, varname);
+ if(!var) {
+ return;
+ }
+
+ switch(var->type) {
+ case UI_VAR_INTEGER: {
+ UiInteger *v = var->value;
+ v->observers = ui_add_observer(v->observers, f, data);
+ break;
+ }
+ case UI_VAR_DOUBLE: {
+ UiDouble *v = var->value;
+ v->observers = ui_add_observer(v->observers, f, data);
+ break;
+ }
+ case UI_VAR_RANGE: {
+ UiRange *v = var->value;
+ v->observers = ui_add_observer(v->observers, f, data);
+ break;
+ }
+ case UI_VAR_STRING: {
+ UiString *v = var->value;
+ v->observers = ui_add_observer(v->observers, f, data);
+ break;
+ }
+ case UI_VAR_TEXT: {
+ UiText *v = var->value;
+ v->observers = ui_add_observer(v->observers, f, data);
+ break;
+ }
+ case UI_VAR_LIST: {
+ UiList *v = var->value;
+ v->observers = ui_add_observer(v->observers, f, data);
+ break;
+ }
+ case UI_VAR_GENERIC: break;
+ case UI_VAR_SPECIAL: break;
+ }
+}
+
+void ui_int_add_observer(UiInteger *i, ui_callback f, void *data) {
+ i->observers = ui_add_observer(i->observers, f, data);
+}
+
+void ui_double_add_observer(UiDouble *d, ui_callback f, void *data) {
+ d->observers = ui_add_observer(d->observers, f, data);
+}
+
+void ui_range_add_observer(UiRange *r, ui_callback f, void *data) {
+ r->observers = ui_add_observer(r->observers, f, data);
+}
+
+void ui_string_add_observer(UiString *s, ui_callback f, void *data) {
+ s->observers = ui_add_observer(s->observers, f, data);
+}
+
+void ui_text_add_observer(UiText *t, ui_callback f, void *data) {
+ t->observers = ui_add_observer(t->observers, f, data);
+}
+
+void ui_list_add_observer(UiList *l, ui_callback f, void *data) {
+ l->observers = ui_add_observer(l->observers, f, data);
+}
+
UiInteger* ui_get_int_var(UiContext *ctx, const char *name) {
UiVar *var = uic_get_var_t(ctx, name, UI_VAR_INTEGER);
return var ? var->value : NULL;
return var ? var->value : NULL;
}
+UIEXPORT UiList* ui_get_list_var(UiContext *ctx, const char *name) {
+ UiVar *var = uic_get_var_t(ctx, name, UI_VAR_LIST);
+ return var ? var->value : NULL;
+}
+
UiGeneric* ui_get_generic_var(UiContext *ctx, const char *name) {
UiVar *var = uic_get_var_t(ctx, name, UI_VAR_GENERIC);
return var ? var->value : NULL;
#elif UI_GTK2 || UI_GTK3
GtkAccelGroup *accel_group;
#endif
+#endif
+#ifdef UI_WIN32
+ CxMap *command_map; // key: int, value: UiCommand
+ uint64_t command_id_counter;
#endif
// allow only one document to be attached
}
void uic_menu_init(void) {
- global_builder.current = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS);
+ global_builder.current = cxLinkedListCreate(cxDefaultAllocator, CX_STORE_POINTERS);
current_builder = &global_builder;
}
UiMenuBuilder *builder = malloc(sizeof(UiMenuBuilder));
builder->menus_begin = NULL;
builder->menus_end = NULL;
- builder->current = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS);
+ builder->current = cxLinkedListCreate(cxDefaultAllocator, CX_STORE_POINTERS);
builder->ref = 1;
current_builder = builder;
*out_builder = builder;
handler->handler.callback = callback;
handler->in = in;
handler->out = out;
- handler->outbuf = cxBufferCreate(NULL, 4096, NULL, CX_BUFFER_FREE_CONTENTS | CX_BUFFER_AUTO_EXTEND);
+ handler->outbuf = cxBufferCreate(NULL, NULL, 4096, CX_BUFFER_FREE_CONTENTS | CX_BUFFER_AUTO_EXTEND);
handler->stop = 0;
pthread_mutex_init(&handler->queue_lock, NULL);
pthread_mutex_init(&handler->avlbl_lock, NULL);
void ui_register_object_creation_callback(ui_object_callback func, void *userdata) {
if(!creation_callbacks) {
- creation_callbacks = cxLinkedListCreateSimple(sizeof(objcallback));
+ creation_callbacks = cxLinkedListCreate(NULL, sizeof(objcallback));
}
objcallback cb = { func, userdata };
cxListAdd(creation_callbacks, &cb);
void ui_register_object_destruction_callback(ui_object_callback func, void *userdata) {
if(!destruction_callbacks) {
- destruction_callbacks = cxLinkedListCreateSimple(sizeof(objcallback));
+ destruction_callbacks = cxLinkedListCreate(NULL, sizeof(objcallback));
}
objcallback cb = { func, userdata };
cxListAdd(destruction_callbacks, &cb);
}
UiObject* uic_object_new_toplevel(void) {
- fflush(stdout);
CxMempool *mp = cxMempoolCreateSimple(256);
UiObject *obj = cxCalloc(mp->allocator, 1, sizeof(UiObjectPrivate));
- fflush(stdout);
obj->ctx = uic_context(obj, mp);
obj->ctx->parent = ui_global_context();
- fflush(stdout);
uic_object_created(obj);
- fflush(stdout);
return obj;
}
COMMON_OBJ += properties$(OBJ_EXT)
COMMON_OBJ += menu$(OBJ_EXT)
COMMON_OBJ += toolbar$(OBJ_EXT)
-COMMON_OBJ += ucx_properties$(OBJ_EXT)
COMMON_OBJ += threadpool$(OBJ_EXT)
COMMON_OBJ += condvar$(OBJ_EXT)
COMMON_OBJ += args$(OBJ_EXT)
#include <cx/buffer.h>
#include <cx/hash_map.h>
-#include "ucx_properties.h"
+#include <cx/properties.h>
static CxMap *application_properties;
static CxMap *language;
#endif
+static UiBool load_on_startup = TRUE;
+static void *properties_data = NULL;
+static size_t properties_data_length = 0;
+
+void ui_load_properties_file_on_startup(UiBool enable) {
+ load_on_startup = enable;
+}
+
+void ui_set_properties_data(const char *str, size_t len) {
+ if(str && len > 0) {
+ properties_data = malloc(len);
+ memcpy(properties_data, str, len);
+ } else {
+ properties_data = NULL;
+ properties_data_length = 0;
+ }
+}
char* ui_getappdir(void) {
if(ui_appname() == NULL) {
}
CxBuffer buf;
- cxBufferInit(&buf, NULL, 128, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ cxBufferInit(&buf, cxDefaultAllocator, NULL, 128, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
// add base dir
char *homeenv = getenv(UI_ENV_HOME);
cxBufferPutString(&buf, "Library/Application Support/");
#elif defined(_WIN32)
// on Windows the app dir is $USERPROFILE/AppData/Local/$APPNAME/
- cxBufferPutString(&buf, "AppData\\Local\\");
+ cxBufferPutString(&buf, "AppData\\Roaming\\");
#else
if(use_xdg_config_home) {
// app dir is $HOME/.config/$APPNAME/
#endif
}
+static int load_properties(FILE *file, CxMap *map) {
+ CxProperties prop;
+ cxPropertiesInitDefault(&prop);
+ char buf[8192];
+ size_t r;
+ CxPropertiesStatus status = CX_PROPERTIES_NO_ERROR;
+ while((r = fread(buf, 1, 8192, file)) > 0) {
+ cxPropertiesFilln(&prop, buf, r);
+ cxstring key;
+ cxstring value;
+ while((status = cxPropertiesNext(&prop, &key, &value)) == CX_PROPERTIES_NO_ERROR) {
+ cxMapPut(map, key, cx_strdup(value).ptr);
+ }
+ if(status > CX_PROPERTIES_OK) {
+ break;
+ }
+ }
+ return status <= CX_PROPERTIES_NO_DATA ? 0 : 1;
+}
+
void uic_load_app_properties() {
application_properties = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 128);
application_properties->collection.simple_destructor = free;
+ if(!load_on_startup) {
+ if(properties_data) {
+ CxProperties prop;
+ cxPropertiesInitDefault(&prop);
+
+ CxPropertiesStatus status = CX_PROPERTIES_NO_ERROR;
+ cxPropertiesFilln(&prop, properties_data, properties_data_length);
+
+ cxstring key;
+ cxstring value;
+ while((status = cxPropertiesNext(&prop, &key, &value)) == CX_PROPERTIES_NO_ERROR) {
+ cxMapPut(application_properties, key, cx_strdup(value).ptr);
+ }
+
+ if(status > CX_PROPERTIES_NO_DATA) {
+ fprintf(stderr, "Error: cannot load properties: %d\n", (int)status);
+ } else {
+ cxMapRehash(application_properties);
+ }
+ }
+ return;
+ }
+
if(!ui_appname()) {
// applications without name cannot load app properties
return;
// try creating the parent
int err = ui_mkdir(parent);
if(err) {
- fprintf(stderr, "Error: Cannot create directory %s: %s\n", strerror(errno));
+ fprintf(stderr, "Error: Cannot create directory %s: %s\n", parent, strerror(errno));
free(parent);
free(dir);
return;
return;
}
- if(ucx_properties_load(application_properties, file)) {
- fprintf(stderr, "Ui Error: Cannot load application properties.\n");
+ if(load_properties(file, application_properties)) {
+ fprintf(stderr, "Error: Cannot load application properties.\n");
}
fclose(file);
}
int ret = 0;
- if(ucx_properties_store(application_properties, file)) {
- fprintf(stderr, "Ui Error: Cannot store application properties.\n");
- ret = 1;
+ CxMapIterator i = cxMapIterator(application_properties);
+ cx_foreach(CxMapEntry *, entry, i) {
+ fprintf(file, "%.*s = %s\n", (int)entry->key->len, (char*)entry->key->data, (char*)entry->value);
}
+ cxMapRehash(application_properties);
+
fclose(file);
free(path);
static char* uic_concat_path(const char *base, const char *p, const char *ext) {
size_t baselen = strlen(base);
- CxBuffer *buf = cxBufferCreate(NULL, 32, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+ CxBuffer *buf = cxBufferCreate(cxDefaultAllocator, NULL, 32, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
if(baselen > 0) {
cxBufferWrite(base, 1, baselen, buf);
if(base[baselen - 1] != '/') {
return 1;
}
- if(ucx_properties_load(lang, file)) {
- fprintf(stderr, "Ui Error: Cannot parse language file: %s.\n", path);
+ if(load_properties(file, lang)) {
+ fprintf(stderr, "Error: Cannot parse language file: %s.\n", path);
}
fclose(file);
void uic_toolbar_init(void) {\r
toolbar_items = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);\r
for(int i=0;i<8;i++) {\r
- toolbar_defaults[i] = cxLinkedListCreateSimple(CX_STORE_POINTERS);\r
+ toolbar_defaults[i] = cxLinkedListCreate(NULL, CX_STORE_POINTERS);\r
}\r
}\r
\r
/* --------------------------- UiList --------------------------- */
void uic_ucx_list_init(UiContext *ctx, UiList *list, void *unused) {
- list->data = cxArrayListCreate(ctx->mp->allocator, NULL, CX_STORE_POINTERS, 32);
+ list->data = cxArrayListCreate(ctx->mp->allocator, CX_STORE_POINTERS, 32);
list->first = ui_list_first;
list->next = ui_list_next;
list->get = ui_list_get;
va_list ap;
va_start(ap, ctx);
- CxList *cols = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(UiColumn), 32);
+ CxList *cols = cxArrayListCreate(cxDefaultAllocator, sizeof(UiColumn), 32);
int type;
while((type = va_arg(ap, int)) != -1) {
char *name = va_arg(ap, char*);
UiPathElm* ui_default_pathelm_func(const char* full_path, size_t len, size_t* ret_nelm, void* data) {
cxstring *pathelms;
- size_t nelm = cx_strsplit_a(cxDefaultAllocator, cx_strn(full_path, len), CX_STR("/"), 4096, &pathelms);
+ size_t nelm = cx_strsplit_a(cxDefaultAllocator, cx_strn(full_path, len), cx_str("/"), 4096, &pathelms);
if (nelm == 0) {
*ret_nelm = 0;
size_t capa = str.length + 32;
char *space = cxMallocDefault(capa);
if (space == NULL) return cx_mutstrn(NULL, 0);
- cxBufferInit(&buf, space, capa, NULL, CX_BUFFER_AUTO_EXTEND);
+ cxBufferInit(&buf, NULL, space, capa, CX_BUFFER_AUTO_EXTEND);
cxBufferWrite(str.ptr, 1, i, &buf);
all_printable = false;
}
event->observers = NULL;
event->callback = NULL;
event->userdata = NULL;
+ event->customint1 = 0;
+ event->customint2 = 0;
g_signal_connect(
widget,
UiVarEventData *event)
{
GtkSwitch *sw = GTK_SWITCH (gobject);
- gboolean active = gtk_switch_get_active (sw);
+ gboolean active = gtk_switch_get_active(sw);
UiEvent e;
e.obj = event->obj;
e.window = e.obj->window;
e.eventdata = NULL;
e.eventdatatype = 0;
+ e.intval = active;
e.set = ui_get_setop();
if(event->callback) {
UiInteger *i = event->var->value;
ui_notify_evt(i->observers, &e);
}
+ if(event->customint1 > 0) {
+ if(active) {
+ ui_set_state(e.obj->ctx, event->customint1);
+ } else {
+ ui_unset_state(e.obj->ctx, event->customint1);
+ }
+ }
}
UIWIDGET ui_switch_create(UiObject* obj, UiToggleArgs *args) {
}
UiVarEventData *event = malloc(sizeof(UiVarEventData));
+ memset(event, 0, sizeof(UiVarEventData));
event->obj = obj;
event->callback = args->onchange;
event->userdata = args->onchangedata;
event->var = var;
event->observers = NULL;
+ event->customint1 = args->enable_state;
g_signal_connect(
widget,
"destroy",
G_CALLBACK(ui_destroy_vardata),
event);
-
+
UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
UiLayout layout = UI_ARGS2LAYOUT(args);
ct->add(ct, widget, &layout);
event->observers = NULL;
event->callback = NULL;
event->userdata = NULL;
+ event->customint1 = 0;
+ event->customint2 = 0;
UiRadioButtonData *rbdata = malloc(sizeof(UiRadioButtonData));
rbdata->value = rgroup;
static char* create_linkbutton_jsonvalue(const char *label, const char *uri, gboolean include_null, gboolean visited, gboolean set_visited) {
CxJsonValue *obj = cxJsonCreateObj(NULL);
if(label) {
- cxJsonObjPutString(obj, CX_STR("label"), label);
+ cxJsonObjPutString(obj, cx_str("label"), label);
} else if(include_null) {
- cxJsonObjPutLiteral(obj, CX_STR("label"), CX_JSON_NULL);
+ cxJsonObjPutLiteral(obj, cx_str("label"), CX_JSON_NULL);
}
if(uri) {
- cxJsonObjPutString(obj, CX_STR("uri"), uri);
+ cxJsonObjPutString(obj, cx_str("uri"), uri);
} else if(include_null) {
- cxJsonObjPutLiteral(obj, CX_STR("uri"), CX_JSON_NULL);
+ cxJsonObjPutLiteral(obj, cx_str("uri"), CX_JSON_NULL);
}
if(set_visited) {
- cxJsonObjPutLiteral(obj, CX_STR("visited"), visited ? CX_JSON_TRUE : CX_JSON_FALSE);
+ cxJsonObjPutLiteral(obj, cx_str("visited"), visited ? CX_JSON_TRUE : CX_JSON_FALSE);
}
CxJsonWriter writer = cxJsonWriterCompact();
CxBuffer buf;
- cxBufferInit(&buf, NULL, 128, NULL, CX_BUFFER_AUTO_EXTEND);
+ cxBufferInit(&buf, NULL, NULL, 128, CX_BUFFER_AUTO_EXTEND);
cxJsonWrite(&buf, obj, (cx_write_func)cxBufferWrite, &writer);
cxJsonValueFree(obj);
cxBufferTerminate(&buf);
#else
gtk_box_pack_start(GTK_BOX(ct->widget), widget, expand, fill, 0);
#endif
-
- ct->current = widget;
}
UiContainerX* ui_grid_container(
gtk_grid_attach(GTK_GRID(ct->widget), widget, grid->x, grid->y, colspan, rowspan);
grid->x += colspan;
-
- grid->container.current = widget;
}
#endif
#ifdef UI_GTK2
void ui_frame_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) {
widget = ui_gtk_set_margin(widget, layout->margin, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom);
FRAME_SET_CHILD(ct->widget, widget);
- ct->current = widget;
}
UiContainerX* ui_expander_container(UiObject *obj, GtkWidget *expander) {
void ui_expander_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) {
widget = ui_gtk_set_margin(widget, layout->margin, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom);
EXPANDER_SET_CHILD(ct->widget, widget);
- ct->current = widget;
}
void ui_scrolledwindow_container_add(UiContainerPrivate *ct, GtkWidget *widget, UiLayout *layout) {
widget = ui_gtk_set_margin(widget, layout->margin, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom);
// TODO: check if the widget implements GtkScrollable
SCROLLEDWINDOW_SET_CHILD(ct->widget, widget);
- ct->current = widget;
}
UiContainerX* ui_scrolledwindow_container(UiObject *obj, GtkWidget *scrolledwindow) {
}
widget = ui_gtk_set_margin(widget, layout->margin, layout->margin_left, layout->margin_right, layout->margin_top, layout->margin_bottom);
data->add_tab(ct->widget, -1, layout->label, widget);
-
- ct->current = widget;
}
#ifdef UI_GTK2
ct->orientation = orientation;
ct->max = max;
ct->initial_position = init;
- ct->children = cxArrayListCreateSimple(CX_STORE_POINTERS, 4);
+ ct->children = cxArrayListCreate(NULL, CX_STORE_POINTERS, 4);
return ct;
}
static void update_itemlist(UiList *list, int c) {
UiGtkItemListContainer *ct = list->obj;
- CxMap *new_items = cxHashMapCreateSimple(CX_STORE_POINTERS);
+ CxMap *new_items = cxHashMapCreate(NULL, CX_STORE_POINTERS, 32);
new_items->collection.advanced_destructor = remove_item;
new_items->collection.destructor_data = ct;
container->create_ui = args->create_ui;
container->userdata = args->userdata;
container->subcontainer = args->subcontainer;
- container->current_items = cxHashMapCreateSimple(CX_STORE_POINTERS);
+ container->current_items = cxHashMapCreate(NULL, CX_STORE_POINTERS, 32);
container->current_items->collection.advanced_destructor = remove_item;
container->current_items->collection.destructor_data = container;
container->margin = args->sub_margin;
UiContainerX container;
GtkWidget *widget;
UIMENU menu;
- GtkWidget *current; // TODO: remove
void (*add)(UiContainerPrivate*, GtkWidget*, UiLayout *layout);
UiLayout layout;
UiDnD* ui_create_dnd(void) {
UiDnD *dnd = malloc(sizeof(UiDnD));
memset(dnd, 0, sizeof(UiDnD));
- dnd->providers = cxArrayListCreateSimple(sizeof(void*), 16);
+ dnd->providers = cxArrayListCreate(NULL, sizeof(void*), 16);
dnd->selected_action = 0;
dnd->delete = FALSE;
return dnd;
}
UiVarEventData *event = malloc(sizeof(UiVarEventData));
+ memset(event, 0, sizeof(UiVarEventData));
event->obj = obj;
event->var = var;
event->observers = obs;
#define UI_HEADERBAR_SETTINGS(h)
#if GTK_MAJOR_VERSION >= 4
#define UI_HEADERBAR_SET_TITLE_WIDGET(h, w) gtk_header_bar_set_title_widget(GTK_HEADER_BAR(h), w)
-#define UI_HEADERBAR_SHOW_TITLE_WIDGET(h, b) gtk_header_bar_set_show_title(GTK_HEADER_BAR(h), b)
+#define UI_HEADERBAR_SHOW_TITLE_WIDGET(h, b)
#else
#define UI_HEADERBAR_SET_TITLE_WIDGET(h, w) gtk_header_bar_set_custom_title(GTK_HEADER_BAR(h), w)
-#define UI_HEADERBAR_SHOW_TITLE_WIDGET(h, b) gtk_header_bar_set_show_title(GTK_HEADER_BAR(h), b)
+#define UI_HEADERBAR_SHOW_TITLE_WIDGET(h, b)
#endif
#endif
#endif
void ui_image_init(void) {
- image_map = cxHashMapCreateSimple(CX_STORE_POINTERS);
+ image_map = cxHashMapCreate(NULL, CX_STORE_POINTERS, 16);
icon_theme = ICONTHEME_GET_DEFAULT();
}
view->selection.count = 0;
view->selection.rows = NULL;
- CX_ARRAY_DECLARE(int, newselection);
- cx_array_initialize(newselection, 8);
-
size_t nitems = g_list_model_get_n_items(G_LIST_MODEL(view->liststore));
+ int *newselection = calloc(nitems, sizeof(int));
+ int selection_size = 0;
for(size_t i=0;i<nitems;i++) {
if(gtk_selection_model_is_selected(view->selectionmodel, i)) {
int s = (int)i;
- cx_array_simple_add(newselection, s);
+ newselection[selection_size++] = s;
}
}
- if(newselection_size > 0) {
- view->selection.count = newselection_size;
+ if(selection_size > 0) {
+ view->selection.count = selection_size;
view->selection.rows = newselection;
} else {
free(newselection);
void *data = listview->getvalue(list, elm, row, c, listview->getvaluedata, &freevalue);
UiModelType type = model->types[i];
-
+ UiTextStyle cstyle = { 0, 0 };
if(getstyle) {
// in case the column is icon+text, only get a style for the text column
int style_col = c;
// Get the individual column style
// The column style overrides the row style, however if no column style
// is provided, we stick with the row style
- if(getstyle(list, elm, row, style_col, listview->getstyledata, &style)) {
+ if(getstyle(list, elm, row, style_col, listview->getstyledata, &cstyle)) {
style_set = TRUE;
+ cstyle.text_style |= style.text_style;
+ if(!cstyle.fg_set) {
+ cstyle.fg_set = style.fg_set;
+ cstyle.fg = style.fg;
+ }
+ } else {
+ cstyle = style;
}
}
}
break;
}
+ case UI_STRING_EDITABLE: {
+ g_value_init(&value, G_TYPE_STRING);
+ g_value_set_string(&value, data);
+ if(freevalue) {
+ free(data);
+ }
+ break;
+ }
+ case UI_BOOL_EDITABLE: {
+ g_value_init(&value, G_TYPE_BOOLEAN);
+ intptr_t b = (intptr_t)data;
+ g_value_set_boolean(&value, b != 0 ? TRUE : FALSE);
+ if(freevalue) {
+ free(data);
+ }
+ break;
+ }
}
gtk_list_store_set_value(store, iter, c, &value);
GValue style_weight_value = G_VALUE_INIT;
g_value_init(&style_weight_value, G_TYPE_INT);
- if(style.text_style & UI_TEXT_STYLE_BOLD) {
+ if(cstyle.text_style & UI_TEXT_STYLE_BOLD) {
g_value_set_int(&style_weight_value, 600);
} else {
g_value_set_int(&style_weight_value, 400);
GValue style_underline_value = G_VALUE_INIT;
g_value_init(&style_underline_value, G_TYPE_INT);
- if(style.text_style & UI_TEXT_STYLE_UNDERLINE) {
+ if(cstyle.text_style & UI_TEXT_STYLE_UNDERLINE) {
g_value_set_int(&style_underline_value, PANGO_UNDERLINE_SINGLE);
} else {
g_value_set_int(&style_underline_value, PANGO_UNDERLINE_NONE);
GValue style_italic_value = G_VALUE_INIT;
g_value_init(&style_italic_value, G_TYPE_INT);
- if(style.text_style & UI_TEXT_STYLE_ITALIC) {
+ if(cstyle.text_style & UI_TEXT_STYLE_ITALIC) {
g_value_set_int(&style_italic_value, PANGO_STYLE_ITALIC);
} else {
g_value_set_int(&style_italic_value, PANGO_STYLE_NORMAL);
GValue style_fgset_value = G_VALUE_INIT;
g_value_init(&style_fgset_value, G_TYPE_BOOLEAN);
- g_value_set_boolean(&style_fgset_value, style.fg_set);
+ g_value_set_boolean(&style_fgset_value, cstyle.fg_set);
gtk_list_store_set_value(store, iter, soff + 4, &style_fgset_value);
- if(style.fg_set) {
+ if(cstyle.fg_set) {
char buf[8];
- snprintf(buf, 8, "#%02X%02X%02X", (int)style.fg.red, (int)style.fg.green, (int)style.fg.blue);
+ snprintf(buf, 8, "#%02X%02X%02X", (int)cstyle.fg.red, (int)cstyle.fg.green, (int)cstyle.fg.blue);
GValue style_fg_value = G_VALUE_INIT;
g_value_init(&style_fg_value, G_TYPE_STRING);
types[c] = G_TYPE_OBJECT;
types[++c] = G_TYPE_STRING;
}
+ case UI_STRING_EDITABLE: types[c] = G_TYPE_STRING; break;
+ case UI_BOOL_EDITABLE: types[c] = G_TYPE_BOOLEAN; break;
}
}
int s = 0;
gtk_combo_box_set_active(GTK_COMBO_BOX(dropdown), index);
}
+static void table_cell_toggled(
+ GtkCellRendererToggle *renderer,
+ gchar *path,
+ gpointer user_data)
+{
+ printf("cell toggled\n");
+}
+
+static void table_cell_edited(
+ GtkCellRendererText *renderer,
+ gchar *path,
+ gchar *new_text,
+ gpointer user_data)
+{
+ printf("cell edited\n");
+}
+
UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) {
// create treeview
GtkWidget *view = gtk_tree_view_new();
"pixbuf",
i + addi,
NULL);
+ } else if (model->types[i] == UI_BOOL_EDITABLE) {
+ GtkCellRenderer *renderer = gtk_cell_renderer_toggle_new();
+ column = gtk_tree_view_column_new_with_attributes(
+ model->titles[i],
+ renderer,
+ "active",
+ i + addi,
+ NULL);
+ g_signal_connect(renderer, "toggled", G_CALLBACK(table_cell_toggled), NULL);
} else {
GtkCellRenderer *textrenderer = gtk_cell_renderer_text_new();
+ if(model->types[i] == UI_STRING_EDITABLE) {
+ g_object_set(textrenderer, "editable", TRUE, NULL);
+ }
column = gtk_tree_view_column_new_with_attributes(
model->titles[i],
textrenderer,
UiListSelection ui_listview_getselection(UiList *list) {
UiListView *view = list->obj;
- UiListSelection selection = ui_listview_selection(
+ UiListSelection selection = ui_listview_get_selection(
gtk_tree_view_get_selection(GTK_TREE_VIEW(view->widget)),
NULL);
return selection;
GtkTreeViewColumn *column,
UiTreeEventData *event)
{
- UiListSelection selection = ui_listview_selection(
+ UiListSelection selection = ui_listview_get_selection(
gtk_tree_view_get_selection(treeview),
event);
GtkTreeSelection *treeselection,
UiTreeEventData *event)
{
- UiListSelection selection = ui_listview_selection(treeselection, event);
+ UiListSelection selection = ui_listview_get_selection(treeselection, event);
UiEvent e;
e.obj = event->obj;
}
}
-UiListSelection ui_listview_selection(
+UiListSelection ui_listview_get_selection(
GtkTreeSelection *selection,
UiTreeEventData *event)
{
uisublist.numitems = 0;
uisublist.header = sublist->header ? strdup(sublist->header) : NULL;
uisublist.separator = sublist->separator;
- uisublist.widgets = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+ uisublist.widgets = cxLinkedListCreate(NULL, CX_STORE_POINTERS);
uisublist.listbox = uilistbox;
uisublist.userdata = sublist->userdata;
uisublist.index = cxListSize(sublists);
uilistbox->onactivatedata = args->onactivatedata;
uilistbox->onbuttonclick = args->onbuttonclick;
uilistbox->onbuttonclickdata = args->onbuttonclickdata;
- uilistbox->sublists = cxArrayListCreateSimple(sizeof(UiListBoxSubList), 4);
+ uilistbox->sublists = cxArrayListCreate(NULL, sizeof(UiListBoxSubList), 4);
uilistbox->sublists->collection.advanced_destructor = (cx_destructor_func2)sublist_destroy;
uilistbox->sublists->collection.destructor_data = obj;
uilistbox->first_row = NULL;
}
cxListFree(uilistbox->sublists);
- CxList *new_sublists = cxArrayListCreateSimple(sizeof(UiListBoxSubList), list->count(list));
+ CxList *new_sublists = cxArrayListCreate(NULL, sizeof(UiListBoxSubList), list->count(list));
uilistbox->sublists = new_sublists;
UiSubList *sublist = list->first(list);
UIMENU menu = data->customdata3;
g_object_set_data(G_OBJECT(button), "ui-button-popup", menu);
+#if GTK_CHECK_VERSION(4, 0, 0)
gtk_popover_popup(GTK_POPOVER(menu));
+#else
+ ui_contextmenu_popup(menu, button, 0, 0);
+#endif
}
}
event
);
gtk_widget_set_visible(button, FALSE);
+ WIDGET_NO_SHOW_ALL(button);
g_object_set_data(G_OBJECT(row), "ui-listbox-row-button", button);
LISTBOX_ROW_REMOVE_CHILD(row);
listbox_fill_row(listbox, GTK_WIDGET(row), sublist, &item, index);
+ LISTBOX_ROW_SHOW(row);
// cleanup
free(item.label);
#endif
listbox_fill_row(listbox, row, sublist, &item, index);
+ LISTBOX_ROW_SHOW(row);
if(index == first_index) {
// first row in the sublist, set ui_listbox data to the row
// which is then used by the headerfunc
void ui_listview_selection_event(
GtkTreeSelection *treeselection,
UiTreeEventData *event);
-UiListSelection ui_listview_selection(
+UiListSelection ui_listview_get_selection(
GtkTreeSelection *selection,
UiTreeEventData *event);
int ui_tree_path_list_index(GtkTreePath *path);
gtk_menu_shell_append(GTK_MENU_SHELL(parent), widget);
if(i->states) {
- CxList *states = cxArrayListCreateSimple(sizeof(int), i->nstates);
+ CxList *states = cxArrayListCreate(NULL, sizeof(int), i->nstates);
cxListAddArray(states, i->states, i->nstates);
uic_add_state_widget(obj->ctx, widget, (ui_enablefunc)ui_set_enabled, states);
cxListFree(states);
GtkWidget *widget = gtk_check_menu_item_new_with_mnemonic(ci->label);
gtk_menu_shell_append(GTK_MENU_SHELL(p), widget);
- if(ci->callback) {
- UiEventData *event = malloc(sizeof(UiEventData));
- event->obj = obj;
- event->userdata = ci->userdata;
- event->callback = ci->callback;
- event->value = 0;
- event->customdata = NULL;
-
- g_signal_connect(
- widget,
- "toggled",
- G_CALLBACK(ui_menu_event_toggled),
- event);
- g_signal_connect(
- widget,
- "destroy",
- G_CALLBACK(ui_destroy_userdata),
- event);
+ UiVarEventData *event = malloc(sizeof(UiVarEventData));
+ memset(event, 0, sizeof(UiVarEventData));
+ event->obj = obj;
+ event->userdata = ci->userdata;
+ event->callback = ci->callback;
+ event->var = uic_widget_var(obj->ctx, obj->ctx, NULL, ci->varname, UI_VAR_INTEGER);
+ if(event->var) {
+ UiInteger *v = event->var->value;
+ v->obj = widget;
+ v->get = ui_checkitem_get;
+ v->set = ui_checkitem_set;
+ if(v->value != 0) {
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), TRUE);
+ }
+ }
+
+ g_signal_connect(
+ widget,
+ "toggled",
+ G_CALLBACK(ui_menu_event_toggled),
+ event);
+ g_signal_connect(
+ widget,
+ "destroy",
+ G_CALLBACK(ui_destroy_userdata),
+ event);
+}
+
+static void ui_menu_event_radio_item_toggled(GtkRadioMenuItem *ri, UiVarEventData *event) {
+ UiInteger *i = event->var->value;
+
+ UiEvent evt;
+ evt.obj = event->obj;
+ evt.window = event->obj->window;
+ evt.document = event->obj->ctx->document;
+ evt.eventdata = i;
+ evt.eventdatatype = i ? UI_EVENT_DATA_INTEGER_VALUE : 0;
+ evt.intval = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(ri));
+
+ if(event->callback) {
+ event->callback(&evt, event->userdata);
+ }
+
+ if(evt.intval) {
+ evt.intval = ui_get(i);
+ ui_notify_evt(i->observers, &evt);
}
}
+static void ui_destroy_menu_radio_item(GtkWidget *unused, UiVarEventData *event) {
+ if(event->customint1) {
+ uic_unbind_var(event->var);
+ }
+ free(event);
+}
+
void add_radioitem_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObject *obj) {
- // TODO
+ UiMenuRadioItem *ri = (UiMenuRadioItem*)item;
+
+ UiVar *var = uic_widget_var(obj->ctx, obj->ctx, NULL, ri->varname, UI_VAR_INTEGER);
+ if(!var) {
+ fprintf(stderr, "Error: menu radioitem varname is null\n");
+ return;
+ }
+ int first = 0;
+ UiInteger *i = var->value;
+ GSList *group = i->obj;
+ GtkWidget *widget = gtk_radio_menu_item_new_with_label(group, ri->label);
+ gtk_menu_shell_append(GTK_MENU_SHELL(p), widget);
+ if(!group) {
+ i->get = ui_radioitem_get;
+ i->set = ui_radioitem_set;
+ first = 1;
+ }
+ i->obj = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(widget));
+
+ UiVarEventData *event = malloc(sizeof(UiVarEventData));
+ memset(event, 0, sizeof(UiVarEventData));
+ event->obj = obj;
+ event->var = var;
+ event->callback = ri->callback;
+ event->userdata = ri->userdata;
+ event->customint1 = first;
+
+ g_signal_connect(
+ widget,
+ "toggled",
+ G_CALLBACK(ui_menu_event_radio_item_toggled),
+ event);
+ g_signal_connect(
+ widget,
+ "destroy",
+ G_CALLBACK(ui_destroy_menu_radio_item),
+ event);
}
static void menuitem_list_remove_binding(void *obj) {
// binding object is a list of all connected UiActiveMenuItemList.
CxList *bindings = list->obj;
if(!bindings) {
- bindings = cxLinkedListCreate(var->from_ctx->mp->allocator, NULL, CX_STORE_POINTERS);
+ bindings = cxLinkedListCreate(var->from_ctx->mp->allocator, CX_STORE_POINTERS);
list->obj = bindings;
}
cxListAdd(bindings, ls);
uic_set_tmp_eventdata(NULL, 0);
}
-void ui_menu_event_toggled(GtkCheckMenuItem *ci, UiEventData *event) {
+void ui_menu_event_toggled(GtkCheckMenuItem *ci, UiVarEventData *event) {
+ UiInteger *i = event->var ? event->var->value : NULL;
+
UiEvent evt;
evt.obj = event->obj;
evt.window = event->obj->window;
evt.document = event->obj->ctx->document;
- evt.eventdata = NULL;
+ evt.eventdata = i;
+ evt.eventdatatype = i ? UI_EVENT_DATA_INTEGER_VALUE : 0;
evt.intval = gtk_check_menu_item_get_active(ci);
- event->callback(&evt, event->userdata);
+ if(event->callback) {
+ event->callback(&evt, event->userdata);
+ }
+
+ if(i) {
+ ui_notify_evt(i->observers, &evt);
+ }
}
int64_t ui_checkitem_get(UiInteger *i) {
void ui_checkitem_set(UiInteger *i, int64_t value) {
i->value = value;
- gtk_check_menu_item_set_active(i->obj, value);
+ gtk_check_menu_item_set_active(i->obj, (gboolean)value);
+}
+
+int64_t ui_radioitem_get(UiInteger *value) {
+ int selection = 0;
+ GSList *ls = value->obj;
+ guint len = g_slist_length(ls);
+ int i = 0;
+ while(ls) {
+ if(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(ls->data))) {
+ selection = len - i;
+ break;
+ }
+ ls = ls->next;
+ i++;
+ }
+
+ value->value = selection;
+ return selection;
+}
+
+void ui_radioitem_set(UiInteger *i, int64_t value) {
+ GSList *ls = i->obj;
+
+ int len = g_slist_length(ls);
+ if(value > len) {
+ value = len;
+ }
+ int s = len - value;
+ int j = 0;
+ int unset = 1;
+ while(ls) {
+ if(j == s) {
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(ls->data), TRUE);
+ unset = 0;
+ break;
+ }
+ ls = ls->next;
+ j++;
+ }
+
+ i->value = value;
}
g_object_unref(action);
if(i->states) {
- CxList *groups = cxArrayListCreateSimple(sizeof(int), i->nstates);
+ CxList *groups = cxArrayListCreate(NULL, sizeof(int), i->nstates);
cxListAddArray(groups, i->states, i->nstates);
uic_add_state_widget(obj->ctx, action, (ui_enablefunc)action_enable, groups);
cxListFree(groups);
}
static void stateful_action_notify_group(UiMenuRadioGroup *group, UiInteger *i) {
+ int intval = ui_get(i);
+
UiEvent event;
event.obj = group->obj;
event.window = event.obj->window;
event.document = event.obj->ctx->document;
event.eventdata = NULL;
event.eventdatatype = 0;
- event.intval = (int)i->value;
event.set = ui_get_setop();
CxIterator iter = cxListIterator(group->callbacks);
cx_foreach(UiCallbackData *, cb, iter) {
+ event.intval = intval == iter.index;
if(cb->callback) {
cb->callback(&event, cb->userdata);
}
}
+ event.intval = intval;
UiObserver *obs = i->observers;
while(obs) {
if(obs->callback) {
static UiMenuRadioGroup* create_radio_group(UiObject *obj, UiMenuRadioItem *item, GSimpleAction *action) {
UiMenuRadioGroup *group = cxZalloc(obj->ctx->allocator, sizeof(UiMenuRadioGroup));
- group->callbacks = cxArrayListCreate(obj->ctx->allocator, NULL, sizeof(UiCallbackData), 8);
+ group->callbacks = cxArrayListCreate(obj->ctx->allocator, sizeof(UiCallbackData), 8);
group->var = uic_create_var(ui_global_context(), item->varname, UI_VAR_INTEGER);
group->obj = obj;
group->action = action;
UiInteger *i = group->var->value;
CxList *bindings = i->obj;
if(!bindings) {
- bindings = cxLinkedListCreate(group->var->from_ctx->mp->allocator, NULL, CX_STORE_POINTERS);
+ bindings = cxLinkedListCreate(group->var->from_ctx->mp->allocator, CX_STORE_POINTERS);
i->obj = bindings;
i->set = ui_action_set_state;
i->get = ui_action_get_state;
// binding object is a list of all connected UiActiveMenuItemList.
CxList *bindings = list->obj;
if(!bindings) {
- bindings = cxLinkedListCreate(var->from_ctx->mp->allocator, NULL, CX_STORE_POINTERS);
+ bindings = cxLinkedListCreate(var->from_ctx->mp->allocator, CX_STORE_POINTERS);
list->obj = bindings;
}
cxListAdd(bindings, ls);
void ui_menulist_update(UiList *list, int ignored);
void ui_update_menuitem_list(UiActiveMenuItemList *list);
void ui_menu_event_wrapper(GtkMenuItem *item, UiEventData *event);
-void ui_menu_event_toggled(GtkCheckMenuItem *ci, UiEventData *event);
+void ui_menu_event_toggled(GtkCheckMenuItem *ci, UiVarEventData *event);
int64_t ui_checkitem_get(UiInteger *i);
void ui_checkitem_set(UiInteger *i, int64_t value);
+int64_t ui_radioitem_get(UiInteger *i);
+void ui_radioitem_set(UiInteger *i, int64_t value);
#endif /* GTK_MAJOR_VERSION <= 3 */
// TODO: move to common
static UiPathElm* default_pathelm_func(const char* full_path, size_t len, size_t* ret_nelm, void* data) {
cxstring *pathelms;
- size_t nelm = cx_strsplit_a(cxDefaultAllocator, cx_strn(full_path, len), CX_STR("/"), 4096, &pathelms);
+ size_t nelm = cx_strsplit_a(cxDefaultAllocator, cx_strn(full_path, len), cx_str("/"), 4096, &pathelms);
if (nelm == 0) {
*ret_nelm = 0;
gtk_box_append(GTK_BOX(pathtf->hbox), button);
- if(i+1 < pathtf->current_nelm && cx_strcmp(cx_strn(elm->name, elm->name_len), CX_STR("/"))) {
+ if(i+1 < pathtf->current_nelm && cx_strcmp(cx_strn(elm->name, elm->name_len), cx_str("/"))) {
GtkWidget *path_separator = gtk_label_new("/");
gtk_widget_add_css_class(path_separator, "pathbar-button-inactive");
gtk_box_append(GTK_BOX(pathtf->hbox), path_separator);
#if UI_GTK2 || UI_GTK3
-GtkWidget* ui_create_toolbar(UiObject *obj) {
+GtkWidget* ui_create_toolbar(UiObject *obj, UiBool sidebar) {
GtkWidget *toolbar = gtk_toolbar_new();
#ifdef UI_GTK3
gtk_style_context_add_class(
CxList *left_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_LEFT);
CxList *center_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_CENTER);
CxList *right_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHT);
+ CxList *sidebar_left = uic_get_toolbar_defaults(UI_TOOLBAR_SIDEBAR_LEFT);
+ CxList *sidebar_right = uic_get_toolbar_defaults(UI_TOOLBAR_SIDEBAR_RIGHT);
+ CxList *rightpanel_left = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHTPANEL_LEFT);
+ CxList *rightpanel_center = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHTPANEL_CENTER);
+ CxList *rightpanel_right = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHTPANEL_RIGHT);
+
+ if(sidebar) {
+ ui_toolbar_add_items(obj, toolbar, items, sidebar_left);
+ ui_toolbar_add_items(obj, toolbar, items, sidebar_right);
+ }
ui_toolbar_add_items(obj, toolbar, items, left_defaults);
ui_toolbar_add_items(obj, toolbar, items, center_defaults);
ui_toolbar_add_items(obj, toolbar, items, right_defaults);
- /*
- GtkToolbar *tb = GTK_TOOLBAR(toolbar);
- CxIterator i = cxListIterator(defaults);
- cx_foreach(char *, def, i) {
- UiToolItemI *item = cxMapGet(toolbar_items, def);
- if(item) {
- item->add_to(tb, item, obj);
- } else if(!strcmp(def, "@separator")) {
- gtk_toolbar_insert(tb, gtk_separator_tool_item_new(), -1);
- } else {
- fprintf(stderr, "UI Error: Unknown toolbar item: %s\n", def);
- }
- }
- */
+ ui_toolbar_add_items(obj, toolbar, items, rightpanel_left);
+ ui_toolbar_add_items(obj, toolbar, items, rightpanel_center);
+ ui_toolbar_add_items(obj, toolbar, items, rightpanel_right);
return toolbar;
}
}
}
- UiVarEventData *event = cxMalloc(
+ UiVarEventData *event = cxZalloc(
obj->ctx->allocator,
sizeof(UiVarEventData));
event->obj = obj;
void *userdata,
va_list ap);
-GtkWidget* ui_create_toolbar(UiObject *obj);
+GtkWidget* ui_create_toolbar(UiObject *obj, UiBool sidebar);
void ui_toolbar_add_items(UiObject *obj, GtkWidget *toolbar, CxMap *items, CxList *defaults);
gtk_widget_set_visible(widget, visible);
#else
if(visible) {
+ gtk_widget_set_no_show_all(widget, FALSE);
gtk_widget_show_all(widget);
} else {
- gtk_widget_set_no_show_all(widget, FALSE);
+ gtk_widget_set_no_show_all(widget, TRUE);
gtk_widget_hide(widget);
}
#endif
free(userdata);
}
-void ui_destroy_vardata(GtkWidget *object, UiVarEventData *data) {
+void ui_destroy_vardata(GtkWidget *unused, UiVarEventData *data) {
if(data->var) {
ui_destroy_boundvar(data->obj->ctx, data->var);
}
}
if(style_classes) {
cxstring *cls = NULL;
- size_t numClasses = cx_strsplit_a(cxDefaultAllocator, cx_str(style_classes), CX_STR(" "), 128, &cls);
+ size_t numClasses = cx_strsplit_a(cxDefaultAllocator, cx_str(style_classes), cx_str(" "), 128, &cls);
for(int i=0;i<numClasses;i++) {
cxmutstr m = cx_strdup(cls[i]);
#if GTK_MAJOR_VERSION >= 4
#define WINDOW_SHOW(window) gtk_window_present(GTK_WINDOW(window))
#define WINDOW_DESTROY(window) gtk_window_destroy(GTK_WINDOW(window))
#define WINDOW_SET_CONTENT(window, child) gtk_window_set_child(GTK_WINDOW(window), child)
+#define WIDGET_NO_SHOW_ALL(widget)
#define BOX_ADD(box, child) gtk_box_append(GTK_BOX(box), child)
#define BOX_ADD_EXPAND(box, child) gtk_widget_set_hexpand(child, TRUE); gtk_widget_set_vexpand(child, TRUE); gtk_box_append(GTK_BOX(box), child)
#define BOX_ADD_NO_EXPAND(box, child) gtk_box_append(GTK_BOX(box), child)
#define LISTBOX_REMOVE(listbox, row) gtk_list_box_remove(GTK_LIST_BOX(listbox), row)
#define LISTBOX_ROW_SET_CHILD(row, child) gtk_list_box_row_set_child(GTK_LIST_BOX_ROW(row), child)
#define LISTBOX_ROW_REMOVE_CHILD(row) gtk_list_box_row_set_child(GTK_LIST_BOX_ROW(row), NULL)
+#define LISTBOX_ROW_SHOW(row)
#define PANED_SET_CHILD1(paned, child) gtk_paned_set_start_child(GTK_PANED(paned), child)
#define PANED_SET_CHILD2(paned, child) gtk_paned_set_end_child(GTK_PANED(paned), child)
#else
#define WINDOW_SHOW(window) gtk_widget_show_all(window)
#define WINDOW_DESTROY(window) gtk_widget_destroy(window)
#define WINDOW_SET_CONTENT(window, child) gtk_container_add(GTK_CONTAINER(window), child)
+#define WIDGET_NO_SHOW_ALL(widget) gtk_widget_set_no_show_all(widget, TRUE)
#define BOX_ADD(box, child) gtk_container_add(GTK_CONTAINER(box), child)
#define BOX_ADD_EXPAND(box, child) gtk_box_pack_start(GTK_BOX(box), child, TRUE, TRUE, 0)
#define BOX_ADD_NO_EXPAND(box, child) gtk_box_pack_start(GTK_BOX(box), child, TRUE, FALSE, 0)
#define LISTBOX_REMOVE(listbox, row) gtk_container_remove(GTK_CONTAINER(listbox), row)
#define LISTBOX_ROW_SET_CHILD(row, child) gtk_container_add(GTK_CONTAINER(row), child)
#define LISTBOX_ROW_REMOVE_CHILD(row) gtk_container_remove(GTK_CONTAINER(row), gtk_bin_get_child(GTK_BIN(row)))
+#define LISTBOX_ROW_SHOW(row) gtk_widget_show_all(GTK_WIDGET(row))
#define PANED_SET_CHILD1(paned, child) gtk_paned_pack1(GTK_PANED(paned), child, TRUE, TRUE)
#define PANED_SET_CHILD2(paned, child) gtk_paned_pack2(GTK_PANED(paned), child, TRUE, TRUE)
#endif
UiObserver **observers;
ui_callback callback;
void *userdata;
+ int customint1;
+ int customint2;
} UiVarEventData;
typedef enum UiOrientation UiOrientation;
void ui_set_widget_nvisibility_states(UiContext *ctx, GtkWidget *widget, const int *states, size_t ngroups);
void ui_destroy_userdata(GtkWidget *object, void *userdata);
-void ui_destroy_vardata(GtkWidget *object, UiVarEventData *data);
+void ui_destroy_vardata(GtkWidget *unused, UiVarEventData *data);
void ui_destroy_widget_var(GtkWidget *object, UiVar *var);
void ui_destroy_boundvar(UiContext *ctx, UiVar *var);
if(!simple) {
ui_fill_headerbar(obj, headerbar_sidebar, headerbar_main, headerbar_right);
}
-#elif GTK_MAJOR_VERSION >= 4
- GtkWidget *content_box = ui_gtk_vbox_new(0);
- WINDOW_SET_CONTENT(obj->widget, vbox);
- if(!simple) {
- if(uic_get_menu_list()) {
- GtkWidget *mb = ui_create_menubar(obj);
- if(mb) {
- BOX_ADD(vbox, mb);
- }
- }
- }
- if(sidebar) {
- GtkWidget *paned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
- GtkWidget *sidebar_vbox = ui_gtk_vbox_new(0);
- gtk_paned_set_start_child(GTK_PANED(paned), sidebar_vbox);
- gtk_paned_set_end_child(GTK_PANED(paned), content_box);
- BOX_ADD_EXPAND(GTK_BOX(vbox), paned);
- g_object_set_data(G_OBJECT(obj->widget), "ui_sidebar", sidebar_vbox);
- } else {
- BOX_ADD_EXPAND(GTK_BOX(vbox), content_box);
- }
#else
if(!simple) {
// menu
if(uic_get_menu_list()) {
GtkWidget *mb = ui_create_menubar(obj);
if(mb) {
- gtk_box_pack_start(GTK_BOX(vbox), mb, FALSE, FALSE, 0);
+ BOX_ADD(vbox, mb);
}
}
// toolbar
+#if GTK_MAJOR_VERSION >= 4
+ // TODO: gtk4 toolbar
+#else
if(uic_toolbar_isenabled()) {
- GtkWidget *tb = ui_create_toolbar(obj);
+ GtkWidget *tb = ui_create_toolbar(obj, sidebar);
if(tb) {
- gtk_box_pack_start(GTK_BOX(vbox), tb, FALSE, FALSE, 0);
+ BOX_ADD(vbox, tb);
}
}
-
+#endif
//GtkWidget *hb = ui_create_headerbar(obj);
//gtk_window_set_titlebar(GTK_WINDOW(obj->widget), hb);
}
GtkWidget *paned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
if(sidebar) {
GtkWidget *sidebar_vbox = ui_gtk_vbox_new(0);
- gtk_paned_add1(GTK_PANED(paned), sidebar_vbox);
+ PANED_SET_CHILD1(paned, sidebar_vbox);
g_object_set_data(G_OBJECT(obj->widget), "ui_sidebar", sidebar_vbox);
gtk_paned_set_position(GTK_PANED(paned), 200);
}
if(splitview) {
GtkWidget *content_paned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
gtk_paned_set_position(GTK_PANED(content_paned), splitview_pos);
- gtk_paned_add2(GTK_PANED(paned), content_paned);
+ PANED_SET_CHILD2(paned, content_paned);
GtkWidget *right_content_box = ui_gtk_vbox_new(0);
- gtk_paned_add1(GTK_PANED(content_paned), content_box);
- gtk_paned_add2(GTK_PANED(content_paned), right_content_box);
+ PANED_SET_CHILD1(content_paned, content_box);
+ PANED_SET_CHILD2(content_paned, right_content_box);
g_object_set_data(G_OBJECT(obj->widget), "ui_window_splitview", content_paned);
g_object_set_data(G_OBJECT(obj->widget), "ui_left_panel", content_box);
g_object_set_data(G_OBJECT(obj->widget), "ui_right_panel", right_content_box);
} else {
- gtk_paned_add2(GTK_PANED(paned), content_box);
+ PANED_SET_CHILD2(paned, content_box);
}
BOX_ADD_EXPAND(GTK_BOX(vbox), paned);
UIEXPORT UIWIDGET ui_breadcrumbbar_create(UiObject *obj, UiListArgs *args);
UIEXPORT void ui_listview_select(UIWIDGET listview, int index);
+UIEXPORT int ui_listview_selection(UIWIDGET listview);
UIEXPORT void ui_dropdown_select(UIWIDGET dropdown, int index);
+UIEXPORT int ui_dropdown_selection(UIWIDGET dropdown);
UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs *args);
extern "C" {
#endif
+void ui_load_properties_file_on_startup(UiBool enable);
+void ui_set_properties_data(const char *str, size_t len);
+
const char* ui_get_property(const char *name);
void ui_set_property(const char *name, const char *value);
const char* ui_set_default_property(const char *name, const char *value);
UIEXPORT UiString* ui_get_string_var(UiContext *ctx, const char *name);
UIEXPORT UiText* ui_get_text_var(UiContext *ctx, const char *name);
UIEXPORT UiRange* ui_get_range_var(UiContext *ctx, const char *name);
+UIEXPORT UiList* ui_get_list_var(UiContext *ctx, const char *name);
UIEXPORT UiGeneric* ui_get_generic_var(UiContext *ctx, const char *name);
+UIEXPORT void ui_var_add_observer(UiContext *ctx, const char *varname, ui_callback f, void *data);
+
+UIEXPORT void ui_int_add_observer(UiInteger *i, ui_callback f, void *data);
+UIEXPORT void ui_double_add_observer(UiDouble *d, ui_callback f, void *data);
+UIEXPORT void ui_range_add_observer(UiRange *r, ui_callback f, void *data);
+UIEXPORT void ui_string_add_observer(UiString *s, ui_callback f, void *data);
+UIEXPORT void ui_text_add_observer(UiText *t, ui_callback f, void *data);
+UIEXPORT void ui_list_add_observer(UiList *l, ui_callback f, void *data);
+
UIEXPORT UiObserver* ui_observer_new(ui_callback f, void *data);
UIEXPORT UiObserver* ui_obsvlist_add(UiObserver *list, UiObserver *observer);
UIEXPORT UiObserver* ui_add_observer(UiObserver *list, ui_callback f, void *data);