snac2

Fork of https://codeberg.org/grunfink/snac2
git clone https://git.inz.fi/snac2
Log | Files | Refs | README | LICENSE

xs.h (38012B)


      1 /* copyright (c) 2022 - 2025 grunfink et al. / MIT license */
      2 
      3 #ifndef _XS_H
      4 
      5 #define _XS_H
      6 
      7 #include <stdio.h>
      8 #include <string.h>
      9 #include <stdlib.h>
     10 #include <ctype.h>
     11 #include <unistd.h>
     12 #include <stdarg.h>
     13 #include <signal.h>
     14 #include <errno.h>
     15 #include <stdint.h>
     16 
     17 typedef enum {
     18     XSTYPE_STRING = 0x02,       /* C string (\0 delimited) (NOT STORED) */
     19     XSTYPE_NUMBER = 0x17,       /* double in spirit, stored as a C string (\0 delimited) */
     20     XSTYPE_NULL   = 0x18,       /* Special NULL value */
     21     XSTYPE_TRUE   = 0x06,       /* Boolean */
     22     XSTYPE_FALSE  = 0x15,       /* Boolean */
     23     XSTYPE_LIST   = 0x1d,       /* Sequence of LITEMs up to EOM (with size) */
     24     XSTYPE_LITEM  = 0x1f,       /* Element of a list (any type) */
     25     XSTYPE_DICT   = 0x1c,       /* Sequence of KEYVALs up to EOM (with size) */
     26     XSTYPE_KEYVAL = 0x1e,       /* key + value (STRING key + any type) */
     27     XSTYPE_DATA   = 0x10        /* A block of anonymous data */
     28 } xstype;
     29 
     30 
     31 /* types */
     32 typedef char xs_val;
     33 typedef char xs_str;
     34 typedef char xs_list;
     35 typedef char xs_keyval;
     36 typedef char xs_dict;
     37 typedef char xs_number;
     38 typedef char xs_data;
     39 typedef struct {
     40     xs_str *data;
     41     int len;
     42 } xs_str_bld;
     43 
     44 /* size in bytes of the type size */
     45 #define _XS_TYPE_SIZE 4
     46 
     47 /* auto-destroyable strings */
     48 #define xs __attribute__ ((__cleanup__ (_xs_destroy))) xs_val
     49 
     50 /* not really all, just very much */
     51 #define XS_ALL 0xfffffff
     52 
     53 #ifndef xs_countof
     54 #define xs_countof(a) (sizeof((a)) / sizeof((*a)))
     55 #endif
     56 
     57 void *xs_free(void *ptr);
     58 void *_xs_realloc(void *ptr, size_t size, const char *file, int line, const char *func);
     59 #define xs_realloc(ptr, size) _xs_realloc(ptr, size, __FILE__, __LINE__, __func__)
     60 int _xs_blk_size(int sz);
     61 void _xs_destroy(char **var);
     62 #define xs_debug() raise(SIGTRAP)
     63 xstype xs_type(const xs_val *data);
     64 int xs_size(const xs_val *data);
     65 int xs_is_null(const xs_val *data);
     66 int xs_cmp(const xs_val *v1, const xs_val *v2);
     67 const xs_val *xs_or(const xs_val *v1, const xs_val *v2);
     68 xs_val *xs_dup(const xs_val *data);
     69 xs_val *xs_expand(xs_val *data, int offset, int size);
     70 xs_val *xs_collapse(xs_val *data, int offset, int size);
     71 xs_val *xs_insert_m(xs_val *data, int offset, const char *mem, int size);
     72 #define xs_insert(data, offset, data2) xs_insert_m(data, offset, data2, xs_size(data2))
     73 #define xs_append_m(data, mem, size) xs_insert_m(data, xs_size(data) - 1, mem, size)
     74 xs_val *xs_stock(int type);
     75 
     76 xs_str *xs_str_new(const char *str);
     77 xs_str *xs_str_new_sz(const char *mem, int sz);
     78 xs_str *xs_str_wrap_i(const char *prefix, xs_str *str, const char *suffix);
     79 #define xs_str_prepend_i(str, prefix) xs_str_wrap_i(prefix, str, NULL)
     80 xs_str *_xs_str_cat(xs_str *str, const char *strs[]);
     81 #define xs_str_cat(str, ...) _xs_str_cat(str, (const char *[]){ __VA_ARGS__, NULL })
     82 xs_str *xs_str_cat_fmt(xs_str *str, const char *fmt, ...);
     83 void xs_str_bld_cat(xs_str_bld *b, const char *str);
     84 void xs_str_bld_cat_fmt(xs_str_bld *b, const char *fmt, ...);
     85 xs_str *xs_replace_in(xs_str *str, const char *sfrom, const char *sto, int times);
     86 #define xs_replace_i(str, sfrom, sto) xs_replace_in(str, sfrom, sto, XS_ALL)
     87 #define xs_replace(str, sfrom, sto) xs_replace_in(xs_dup(str), sfrom, sto, XS_ALL)
     88 #define xs_replace_n(str, sfrom, sto, times) xs_replace_in(xs_dup(str), sfrom, sto, times)
     89 xs_str *xs_fmt(const char *fmt, ...);
     90 int xs_str_in(const char *haystack, const char *needle);
     91 int xs_between(const char *prefix, const char *str, const char *suffix);
     92 #define xs_startswith(str, prefix) xs_between(prefix, str, NULL)
     93 #define xs_endswith(str, suffix) xs_between(NULL, str, suffix)
     94 xs_str *xs_crop_i(xs_str *str, int start, int end);
     95 xs_str *xs_lstrip_chars_i(xs_str *str, const char *chars);
     96 xs_str *xs_rstrip_chars_i(xs_str *str, const char *chars);
     97 xs_str *xs_strip_chars_i(xs_str *str, const char *chars);
     98 #define xs_strip_i(str) xs_strip_chars_i(str, " \r\n\t\v\f")
     99 xs_str *xs_tolower_i(xs_str *str);
    100 
    101 xs_list *xs_list_new(void);
    102 xs_list *xs_list_append_m(xs_list *list, const char *mem, int dsz);
    103 xs_list *_xs_list_append(xs_list *list, const xs_val *vals[]);
    104 #define xs_list_append(list, ...) _xs_list_append(list, (const xs_val *[]){ __VA_ARGS__, NULL })
    105 int xs_list_iter(xs_list **list, const xs_val **value);
    106 int xs_list_next(const xs_list *list, const xs_val **value, int *ctxt);
    107 int xs_list_len(const xs_list *list);
    108 const xs_val *xs_list_get(const xs_list *list, int num);
    109 xs_list *xs_list_del(xs_list *list, int num);
    110 xs_list *xs_list_insert(xs_list *list, int num, const xs_val *data);
    111 xs_list *xs_list_set(xs_list *list, int num, const xs_val *data);
    112 xs_list *xs_list_dequeue(xs_list *list, xs_val **data, int last);
    113 #define xs_list_pop(list, data) xs_list_dequeue(list, data, 1)
    114 #define xs_list_shift(list, data) xs_list_dequeue(list, data, 0)
    115 int xs_list_in(const xs_list *list, const xs_val *val);
    116 xs_str *xs_join(const xs_list *list, const char *sep);
    117 xs_list *xs_split_n(const char *str, const char *sep, int times);
    118 #define xs_split(str, sep) xs_split_n(str, sep, XS_ALL)
    119 xs_list *xs_list_cat(xs_list *l1, const xs_list *l2);
    120 
    121 int xs_keyval_size(const xs_str *key, const xs_val *value);
    122 xs_str *xs_keyval_key(const xs_keyval *keyval);
    123 xs_val *xs_keyval_value(const xs_keyval *keyval);
    124 xs_keyval *xs_keyval_make(xs_keyval *keyval, const xs_str *key, const xs_val *value);
    125 
    126 xs_dict *xs_dict_new(void);
    127 xs_dict *xs_dict_append(xs_dict *dict, const xs_str *key, const xs_val *value);
    128 xs_dict *xs_dict_prepend(xs_dict *dict, const xs_str *key, const xs_val *value);
    129 int xs_dict_next(const xs_dict *dict, const xs_str **key, const xs_val **value, int *ctxt);
    130 const xs_val *xs_dict_get(const xs_dict *dict, const xs_str *key);
    131 #define xs_dict_get_def(dict, key, def) xs_or(xs_dict_get(dict, key), def)
    132 xs_dict *xs_dict_del(xs_dict *dict, const xs_str *key);
    133 xs_dict *xs_dict_set(xs_dict *dict, const xs_str *key, const xs_val *data);
    134 xs_dict *xs_dict_set_strnn(xs_dict *dict, const void *key, int klen, const void *val, int vlen);
    135 xs_dict *xs_dict_set_fmt(xs_dict *dict, const xs_str *key, const char *fmt, ...);
    136 xs_dict *xs_dict_gc(const xs_dict *dict);
    137 
    138 const xs_val *xs_dict_get_path_sep(const xs_dict *dict, const char *path, const char *sep);
    139 #define xs_dict_get_path(dict, path) xs_dict_get_path_sep(dict, path, ".")
    140 xs_dict *xs_dict_set_path_sep(xs_dict *dict, const char *path, const xs_val *value, const char *sep);
    141 #define xs_dict_set_path(dict, path, value) xs_dict_set_path_sep(dict, path, value, ".")
    142 
    143 xs_val *xs_val_new(xstype t);
    144 xs_number *xs_number_new(double f);
    145 double xs_number_get(const xs_number *v);
    146 const char *xs_number_str(const xs_number *v);
    147 
    148 xs_data *xs_data_new(const void *data, int size);
    149 int xs_data_size(const xs_data *value);
    150 void xs_data_get(void *data, const xs_data *value);
    151 
    152 void *xs_memmem(const char *haystack, int h_size, const char *needle, int n_size);
    153 
    154 unsigned int xs_hash_func(const char *data, int size);
    155 uint64_t xs_hash64_func(const char *data, int size);
    156 
    157 #ifdef XS_ASSERT
    158 #include <assert.h>
    159 #define XS_ASSERT_TYPE(v, t) assert(xs_type(v) == t)
    160 #define XS_ASSERT_TYPE_NULL(v, t) assert(v == NULL || xs_type(v) == t)
    161 #else
    162 #define XS_ASSERT_TYPE(v, t) (void)(0)
    163 #define XS_ASSERT_TYPE_NULL(v, t) (void)(0)
    164 #endif
    165 
    166 #define xs_return(v) xs_val *__r = v; v = NULL; return __r
    167 
    168 #define xs_is_true(v) (xs_type((v)) == XSTYPE_TRUE)
    169 #define xs_is_false(v) (xs_type((v)) == XSTYPE_FALSE)
    170 #define xs_not(v) xs_stock(xs_is_true((v)) ? XSTYPE_FALSE : XSTYPE_TRUE)
    171 #define xs_is_string(v) (xs_type((v)) == XSTYPE_STRING)
    172 #define xs_is_list(v) (xs_type((v)) == XSTYPE_LIST)
    173 #define xs_is_dict(v) (xs_type((v)) == XSTYPE_DICT)
    174 
    175 #define xs_list_foreach(l, v) for (int ct_##__LINE__ = 0; xs_list_next(l, &v, &ct_##__LINE__); )
    176 #define xs_dict_foreach(l, k, v) for (int ct_##__LINE__ = 0; xs_dict_next(l, &k, &v, &ct_##__LINE__); )
    177 
    178 
    179 #ifdef XS_IMPLEMENTATION
    180 
    181 void *_xs_realloc(void *ptr, size_t size, const char *file, int line, const char *func)
    182 {
    183     xs_val *ndata = realloc(ptr, size);
    184 
    185     if (ndata == NULL) {
    186         fprintf(stderr, "ERROR: out of memory at %s:%d: %s()\n", file, line, func);
    187         abort();
    188     }
    189 
    190 #ifdef XS_DEBUG
    191     if (ndata != ptr) {
    192         int n;
    193         FILE *f = fopen("xs_memory.out", "a");
    194 
    195         if (ptr != NULL)
    196             fprintf(f, "%p r\n", ptr);
    197 
    198         fprintf(f, "%p a %ld %s:%d: %s", ndata, size, file, line, func);
    199 
    200         if (ptr != NULL) {
    201             fprintf(f, " [");
    202             for (n = 0; n < 32 && ndata[n]; n++) {
    203                 if (ndata[n] >= 32 && ndata[n] <= 127)
    204                     fprintf(f, "%c", ndata[n]);
    205                 else
    206                     fprintf(f, "\\%02x", (unsigned char)ndata[n]);
    207             }
    208             fprintf(f, "]");
    209         }
    210 
    211         fprintf(f, "\n");
    212 
    213         fclose(f);
    214     }
    215 #else
    216     (void)file;
    217     (void)line;
    218     (void)func;
    219 #endif
    220 
    221     return ndata;
    222 }
    223 
    224 
    225 void *xs_free(void *ptr)
    226 {
    227 #ifdef XS_DEBUG
    228     if (ptr != NULL) {
    229         FILE *f = fopen("xs_memory.out", "a");
    230         fprintf(f, "%p b\n", ptr);
    231         fclose(f);
    232     }
    233 #endif
    234 
    235     free(ptr);
    236     return NULL;
    237 }
    238 
    239 
    240 void _xs_destroy(char **var)
    241 {
    242 /*
    243     if (_xs_debug)
    244         printf("_xs_destroy %p\n", var);
    245 */
    246     xs_free(*var);
    247 }
    248 
    249 
    250 int _xs_blk_size(int sz)
    251 /* calculates the block size */
    252 {
    253     int blk_size = 4096;
    254 
    255     if (sz < 256)
    256         blk_size = 32;
    257     else
    258     if (sz < 4096)
    259         blk_size = 256;
    260 
    261     return ((((sz) + blk_size - 1) / blk_size) * blk_size);
    262 }
    263 
    264 
    265 xstype xs_type(const xs_val *data)
    266 /* return the type of data */
    267 {
    268     xstype t;
    269 
    270     if (data == NULL)
    271         t = XSTYPE_NULL;
    272     else
    273     switch (data[0]) {
    274     case XSTYPE_NULL:
    275     case XSTYPE_TRUE:
    276     case XSTYPE_FALSE:
    277     case XSTYPE_LIST:
    278     case XSTYPE_LITEM:
    279     case XSTYPE_DICT:
    280     case XSTYPE_KEYVAL:
    281     case XSTYPE_NUMBER:
    282     case XSTYPE_DATA:
    283         t = data[0];
    284         break;
    285     default:
    286         t = XSTYPE_STRING;
    287         break;
    288     }
    289 
    290     return t;
    291 }
    292 
    293 
    294 void _xs_put_size(xs_val *ptr, int i)
    295 /* must match _XS_TYPE_SIZE */
    296 {
    297     memcpy(ptr + 1, &i, sizeof(i));
    298 }
    299 
    300 
    301 int _xs_get_size(const xs_val *ptr)
    302 /* must match _XS_TYPE_SIZE */
    303 {
    304     int i;
    305     memcpy(&i, ptr + 1, sizeof(i));
    306     return i;
    307 }
    308 
    309 
    310 int xs_size(const xs_val *data)
    311 /* returns the size of data in bytes */
    312 {
    313     int len = 0;
    314     const char *p;
    315 
    316     if (data == NULL)
    317         return 0;
    318 
    319     switch (xs_type(data)) {
    320     case XSTYPE_STRING:
    321         len = strlen(data) + 1;
    322         break;
    323 
    324     case XSTYPE_LIST:
    325     case XSTYPE_DICT:
    326     case XSTYPE_DATA:
    327         len = _xs_get_size(data);
    328 
    329         break;
    330 
    331     case XSTYPE_KEYVAL:
    332         /* calculate the size of the key and the value */
    333         p = data + 1;
    334         p += xs_size(p);
    335         p += xs_size(p);
    336 
    337         len = p - data;
    338 
    339         break;
    340 
    341     case XSTYPE_LITEM:
    342         /* it's the size of the item + 1 */
    343         p = data + 1;
    344         p += xs_size(p);
    345 
    346         len = p - data;
    347 
    348         break;
    349 
    350     case XSTYPE_NUMBER:
    351         len = 1 + xs_size(data + 1);
    352 
    353         break;
    354 
    355     default:
    356         len = 1;
    357     }
    358 
    359     return len;
    360 }
    361 
    362 
    363 int xs_is_null(const xs_val *data)
    364 /* checks for null */
    365 {
    366     return (xs_type(data) == XSTYPE_NULL);
    367 }
    368 
    369 
    370 int xs_cmp(const xs_val *v1, const xs_val *v2)
    371 /* compares two values */
    372 {
    373     if (xs_type(v1) == XSTYPE_STRING && xs_type(v2) == XSTYPE_STRING)
    374         return strcmp(v1, v2);
    375 
    376     int s1 = xs_size(v1);
    377     int s2 = xs_size(v2);
    378     int d = s1 - s2;
    379 
    380     return d == 0 ? memcmp(v1, v2, s1) : d;
    381 }
    382 
    383 
    384 const xs_val *xs_or(const xs_val *v1, const xs_val *v2)
    385 /* returns v1 if it's not NULL, else v2 */
    386 {
    387     return v1 == NULL ? v2 : v1;
    388 }
    389 
    390 
    391 xs_val *xs_dup(const xs_val *data)
    392 /* creates a duplicate of data */
    393 {
    394     xs_val *s = NULL;
    395 
    396     if (data) {
    397         int sz = xs_size(data);
    398         s = xs_realloc(NULL, _xs_blk_size(sz));
    399 
    400         memcpy(s, data, sz);
    401     }
    402 
    403     return s;
    404 }
    405 
    406 
    407 xs_val *xs_expand(xs_val *data, int offset, int size)
    408 /* opens a hole in data */
    409 {
    410     xstype type = xs_type(data);
    411     int sz = xs_size(data) + size;
    412 
    413     /* open room */
    414     data = xs_realloc(data, _xs_blk_size(sz));
    415 
    416     /* move up the rest of the data */
    417     memmove(data + offset + size, data + offset, sz - offset - size);
    418 
    419     if (type == XSTYPE_LIST ||
    420         type == XSTYPE_DICT ||
    421         type == XSTYPE_DATA)
    422         _xs_put_size(data, sz);
    423 
    424     return data;
    425 }
    426 
    427 
    428 xs_val *xs_collapse(xs_val *data, int offset, int size)
    429 /* shrinks data */
    430 {
    431     int sz = xs_size(data);
    432 
    433     /* don't try to delete beyond the limit */
    434     if (offset + size > sz)
    435         size = sz - offset;
    436 
    437     /* shrink total size */
    438     sz -= size;
    439 
    440     memmove(data + offset, data + offset + size, sz - offset);
    441 
    442     if (xs_type(data) == XSTYPE_LIST ||
    443         xs_type(data) == XSTYPE_DICT ||
    444         xs_type(data) == XSTYPE_DATA)
    445         _xs_put_size(data, sz);
    446 
    447     return xs_realloc(data, _xs_blk_size(sz));
    448 }
    449 
    450 
    451 xs_val *xs_insert_m(xs_val *data, int offset, const char *mem, int size)
    452 /* inserts a memory block */
    453 {
    454     data = xs_expand(data, offset, size);
    455     memcpy(data + offset, mem, size);
    456 
    457     return data;
    458 }
    459 
    460 
    461 xs_val *xs_stock(int type)
    462 /* returns stock values */
    463 {
    464     static xs_val stock_null[]  = { XSTYPE_NULL };
    465     static xs_val stock_true[]  = { XSTYPE_TRUE };
    466     static xs_val stock_false[] = { XSTYPE_FALSE };
    467     static xs_val stock_0[]     = { XSTYPE_NUMBER, '0', '\0' };
    468     static xs_val stock_1[]     = { XSTYPE_NUMBER, '1', '\0' };
    469     static xs_list *stock_list = NULL;
    470     static xs_dict *stock_dict = NULL;
    471 
    472     switch (type) {
    473     case 0:            return stock_0;
    474     case 1:            return stock_1;
    475     case XSTYPE_NULL:  return stock_null;
    476     case XSTYPE_TRUE:  return stock_true;
    477     case XSTYPE_FALSE: return stock_false;
    478 
    479     case XSTYPE_LIST:
    480         if (stock_list == NULL)
    481             stock_list = xs_list_new();
    482         return stock_list;
    483 
    484     case XSTYPE_DICT:
    485         if (stock_dict == NULL)
    486             stock_dict = xs_dict_new();
    487         return stock_dict;
    488     }
    489 
    490     return NULL;
    491 }
    492 
    493 
    494 /** strings **/
    495 
    496 xs_str *xs_str_new(const char *str)
    497 /* creates a new string */
    498 {
    499     return xs_insert(NULL, 0, str ? str : "");
    500 }
    501 
    502 
    503 xs_str *xs_str_new_sz(const char *mem, int sz)
    504 /* creates a new string from a memory block, adding an asciiz */
    505 {
    506     xs_str *s = xs_realloc(NULL, _xs_blk_size(sz + 1));
    507     memcpy(s, mem, sz);
    508     s[sz] = '\0';
    509 
    510     return s;
    511 }
    512 
    513 
    514 xs_str *xs_str_wrap_i(const char *prefix, xs_str *str, const char *suffix)
    515 /* wraps str with prefix and suffix */
    516 {
    517     XS_ASSERT_TYPE(str, XSTYPE_STRING);
    518 
    519     if (prefix)
    520         str = xs_insert_m(str, 0, prefix, strlen(prefix));
    521 
    522     if (suffix)
    523         str = xs_insert_m(str, strlen(str), suffix, strlen(suffix));
    524 
    525     return str;
    526 }
    527 
    528 xs_str *xs_str_cat_fmt(xs_str *str, const char *fmt, ...)
    529 {
    530     int o = strlen(str);
    531     int n;
    532     va_list ap;
    533 
    534     va_start(ap, fmt);
    535     n = vsnprintf(NULL, 0, fmt, ap);
    536     va_end(ap);
    537 
    538     if (n++ > 0) {
    539         str = xs_realloc(str, o + n);
    540         va_start(ap, fmt);
    541         vsnprintf(str + o, n, fmt, ap);
    542         va_end(ap);
    543     }
    544 
    545     return str;
    546 }
    547 
    548 void xs_str_bld_cat_fmt(xs_str_bld *b, const char *fmt, ...)
    549 {
    550     int n;
    551     va_list ap;
    552 
    553     va_start(ap, fmt);
    554     n = vsnprintf(NULL, 0, fmt, ap);
    555     va_end(ap);
    556 
    557     if (n > 0) {
    558         b->data = xs_realloc(b->data, _xs_blk_size(b->len + n + 1));
    559         va_start(ap, fmt);
    560         vsnprintf(b->data + b->len, n + 1, fmt, ap);
    561         va_end(ap);
    562 
    563         b->len += n;
    564     }
    565 }
    566 
    567 void xs_str_bld_cat(xs_str_bld *b, const xs_str *s)
    568 {
    569     int n = strlen(s);
    570     b->data = xs_realloc(b->data, _xs_blk_size(b->len + n + 1));
    571     memcpy(b->data + b->len, s, n + 1);
    572     b->len += n;
    573 }
    574 
    575 xs_str *_xs_str_cat(xs_str *str, const char *strs[])
    576 /* concatenates all strings after str */
    577 {
    578     int o = strlen(str);
    579 
    580     while (*strs) {
    581         int sz = strlen(*strs);
    582         str = xs_insert_m(str, o, *strs, sz);
    583         o += sz;
    584         strs++;
    585     }
    586 
    587     return str;
    588 }
    589 
    590 
    591 xs_str *xs_replace_in(xs_str *str, const char *sfrom, const char *sto, int times)
    592 /* replaces inline all sfrom with sto */
    593 {
    594     XS_ASSERT_TYPE(str, XSTYPE_STRING);
    595 
    596     int sfsz = strlen(sfrom);
    597     int stsz = strlen(sto);
    598     int diff = stsz - sfsz;
    599     char *ss;
    600     int offset = 0;
    601 
    602     while (times > 0 && (ss = strstr(str + offset, sfrom)) != NULL) {
    603         int n_offset = ss - str;
    604 
    605         if (diff < 0)
    606             str = xs_collapse(str, n_offset, -diff);
    607         else
    608         if (diff > 0)
    609             str = xs_expand(str, n_offset, diff);
    610 
    611         memcpy(str + n_offset, sto, stsz);
    612 
    613         offset = n_offset + stsz;
    614 
    615         times--;
    616     }
    617 
    618     return str;
    619 }
    620 
    621 
    622 xs_str *xs_fmt(const char *fmt, ...)
    623 /* formats a string with printf()-like marks */
    624 {
    625     int n;
    626     xs_str *s = NULL;
    627     va_list ap;
    628 
    629     va_start(ap, fmt);
    630     n = vsnprintf(s, 0, fmt, ap);
    631     va_end(ap);
    632 
    633     if (n > 0) {
    634         s = xs_realloc(NULL, _xs_blk_size(n + 1));
    635 
    636         va_start(ap, fmt);
    637         vsnprintf(s, n + 1, fmt, ap);
    638         va_end(ap);
    639     }
    640 
    641     return s;
    642 }
    643 
    644 
    645 int xs_str_in(const char *haystack, const char *needle)
    646 /* finds needle in haystack and returns the offset or -1 */
    647 {
    648     char *s;
    649     int r = -1;
    650 
    651     if ((s = strstr(haystack, needle)) != NULL)
    652         r = s - haystack;
    653 
    654     return r;
    655 }
    656 
    657 
    658 int xs_between(const char *prefix, const char *str, const char *suffix)
    659 /* returns true if str starts with prefix and ends with suffix */
    660 {
    661     int sz = strlen(str);
    662     int psz = prefix ? strlen(prefix) : 0;
    663     int ssz = suffix ? strlen(suffix) : 0;
    664 
    665     if (sz < psz || sz < ssz)
    666         return 0;
    667 
    668     if (prefix && memcmp(str, prefix, psz) != 0)
    669         return 0;
    670 
    671     if (suffix && memcmp(str + sz - ssz, suffix, ssz) != 0)
    672         return 0;
    673 
    674     return 1;
    675 }
    676 
    677 
    678 xs_str *xs_crop_i(xs_str *str, int start, int end)
    679 /* crops the string to be only from start to end */
    680 {
    681     int sz = strlen(str);
    682 
    683     if (end <= 0)
    684         end = sz + end;
    685 
    686     /* crop from the top */
    687     if (end >= 0 && end < sz)
    688         str[end] = '\0';
    689 
    690     /* crop from the bottom */
    691     str = xs_collapse(str, 0, start);
    692 
    693     return str;
    694 }
    695 
    696 
    697 xs_str *xs_lstrip_chars_i(xs_str *str, const char *chars)
    698 /* strips all chars from the start of str */
    699 {
    700     int n = strspn(str, chars);;
    701 
    702     if (n)
    703         str = xs_collapse(str, 0, n);
    704 
    705     return str;
    706 }
    707 
    708 
    709 xs_str *xs_rstrip_chars_i(xs_str *str, const char *chars)
    710 /* strips all chars from the end of str */
    711 {
    712     int n;
    713 
    714     for (n = strlen(str); n > 0 && strchr(chars, str[n - 1]); n--);
    715     str[n] = '\0';
    716 
    717     return str;
    718 }
    719 
    720 
    721 xs_str *xs_strip_chars_i(xs_str *str, const char *chars)
    722 /* strips the string of chars from the start and the end */
    723 {
    724     return xs_lstrip_chars_i(xs_rstrip_chars_i(str, chars), chars);
    725 }
    726 
    727 
    728 xs_str *xs_tolower_i(xs_str *str)
    729 /* convert to lowercase */
    730 {
    731     XS_ASSERT_TYPE(str, XSTYPE_STRING);
    732 
    733     int n;
    734 
    735     for (n = 0; str[n]; n++)
    736         str[n] = tolower(str[n]);
    737 
    738     return str;
    739 }
    740 
    741 
    742 /** lists **/
    743 
    744 xs_list *xs_list_new(void)
    745 /* creates a new list */
    746 {
    747     int sz = 1 + _XS_TYPE_SIZE + 1;
    748     xs_list *l = xs_realloc(NULL, sz);
    749     memset(l, '\0', sz);
    750 
    751     l[0] = XSTYPE_LIST;
    752     _xs_put_size(l, sz);
    753 
    754     return l;
    755 }
    756 
    757 
    758 xs_list *_xs_list_write_litem(xs_list *list, int offset, const char *mem, int dsz)
    759 /* writes a list item */
    760 {
    761     XS_ASSERT_TYPE(list, XSTYPE_LIST);
    762 
    763     if (mem == NULL) {
    764         mem = xs_stock(XSTYPE_NULL);
    765         dsz = xs_size(mem);
    766     }
    767 
    768     list = xs_expand(list, offset, dsz + 1);
    769 
    770     list[offset] = XSTYPE_LITEM;
    771     memcpy(list + offset + 1, mem, dsz);
    772 
    773     return list;
    774 }
    775 
    776 
    777 xs_list *xs_list_append_m(xs_list *list, const char *mem, int dsz)
    778 /* adds a memory block to the list */
    779 {
    780     XS_ASSERT_TYPE(list, XSTYPE_LIST);
    781 
    782     return _xs_list_write_litem(list, xs_size(list) - 1, mem, dsz);
    783 }
    784 
    785 
    786 xs_list *_xs_list_append(xs_list *list, const xs_val *vals[])
    787 /* adds several values to the list */
    788 {
    789     /* special case: if the first argument is NULL, just insert it */
    790     if (*vals == NULL)
    791         return xs_list_append_m(list, NULL, 0);
    792 
    793     while (*vals) {
    794         list = xs_list_append_m(list, *vals, xs_size(*vals));
    795         vals++;
    796     }
    797 
    798     return list;
    799 }
    800 
    801 xs_list *xs_list_append_nstr(xs_list *list, const char *str, int n)
    802 {
    803     list = xs_list_append_m(list, str, n + 1);
    804     list[xs_size(list) - 2] = '\0';
    805     return list;
    806 }
    807 
    808 
    809 int xs_list_iter(xs_list **list, const xs_val **value)
    810 /* iterates a list value */
    811 {
    812     int goon = 1;
    813 
    814     xs_val *p = *list;
    815 
    816     /* skip the start of the list */
    817     if (xs_type(p) == XSTYPE_LIST)
    818         p += 1 + _XS_TYPE_SIZE;
    819 
    820     /* an element? */
    821     if (xs_type(p) == XSTYPE_LITEM) {
    822         p++;
    823 
    824         *value = p;
    825 
    826         p += xs_size(*value);
    827     }
    828     else {
    829         /* end of list */
    830         goon = 0;
    831     }
    832 
    833     /* store back the pointer */
    834     *list = p;
    835 
    836     return goon;
    837 }
    838 
    839 
    840 int xs_list_next(const xs_list *list, const xs_val **value, int *ctxt)
    841 /* iterates a list, with context */
    842 {
    843     if (xs_type(list) != XSTYPE_LIST)
    844         return 0;
    845 
    846     int goon = 1;
    847 
    848     const char *p = list;
    849 
    850     /* skip the start of the list */
    851     if (*ctxt == 0)
    852         *ctxt = 1 + _XS_TYPE_SIZE;
    853 
    854     p += *ctxt;
    855 
    856     /* an element? */
    857     if (xs_type(p) == XSTYPE_LITEM) {
    858         p++;
    859 
    860         *value = p;
    861 
    862         p += xs_size(*value);
    863     }
    864     else {
    865         /* end of list */
    866         goon = 0;
    867     }
    868 
    869     /* update the context */
    870     *ctxt = p - list;
    871 
    872     return goon;
    873 }
    874 
    875 
    876 int xs_list_len(const xs_list *list)
    877 /* returns the number of elements in the list */
    878 {
    879     XS_ASSERT_TYPE_NULL(list, XSTYPE_LIST);
    880 
    881     int c = 0;
    882     const xs_val *v;
    883 
    884     xs_list_foreach(list, v)
    885         c++;
    886 
    887     return c;
    888 }
    889 
    890 
    891 const xs_val *xs_list_get(const xs_list *list, int num)
    892 /* returns the element #num */
    893 {
    894     XS_ASSERT_TYPE(list, XSTYPE_LIST);
    895 
    896     if (num < 0)
    897         num = xs_list_len(list) + num;
    898 
    899     int c = 0;
    900     const xs_val *v;
    901 
    902     xs_list_foreach(list, v) {
    903         if (c == num)
    904             return v;
    905 
    906         c++;
    907     }
    908 
    909     return NULL;
    910 }
    911 
    912 
    913 xs_list *xs_list_del(xs_list *list, int num)
    914 /* deletes element #num */
    915 {
    916     XS_ASSERT_TYPE(list, XSTYPE_LIST);
    917 
    918     const xs_val *v;
    919 
    920     if ((v = xs_list_get(list, num)) != NULL)
    921         list = xs_collapse(list, v - 1 - list, xs_size(v - 1));
    922 
    923     return list;
    924 }
    925 
    926 
    927 xs_list *xs_list_insert(xs_list *list, int num, const xs_val *data)
    928 /* inserts an element at #num position */
    929 {
    930     XS_ASSERT_TYPE(list, XSTYPE_LIST);
    931 
    932     const xs_val *v;
    933     int offset;
    934 
    935     if ((v = xs_list_get(list, num)) != NULL)
    936         offset = v - list;
    937     else
    938         offset = xs_size(list);
    939 
    940     return _xs_list_write_litem(list, offset - 1, data, xs_size(data));
    941 }
    942 
    943 
    944 xs_list *xs_list_set(xs_list *list, int num, const xs_val *data)
    945 /* sets the element at #num position */
    946 {
    947     XS_ASSERT_TYPE(list, XSTYPE_LIST);
    948 
    949     list = xs_list_del(list, num);
    950     list = xs_list_insert(list, num, data);
    951 
    952     return list;
    953 }
    954 
    955 
    956 xs_list *xs_list_dequeue(xs_list *list, xs_val **data, int last)
    957 /* gets a copy of the first or last element of a list, shrinking it */
    958 {
    959     XS_ASSERT_TYPE(list, XSTYPE_LIST);
    960 
    961     int ct = 0;
    962     const xs_val *v = NULL;
    963 
    964     if (!last) {
    965         /* get the first */
    966         xs_list_next(list, &v, &ct);
    967     }
    968     else {
    969         /* iterate to the end */
    970         while (xs_list_next(list, &v, &ct));
    971     }
    972 
    973     if (v != NULL) {
    974         *data = xs_dup(v);
    975 
    976         /* collapse from the address of the element */
    977         list = xs_collapse(list, v - 1 - list, xs_size(v - 1));
    978     }
    979 
    980     return list;
    981 }
    982 
    983 
    984 int xs_list_in(const xs_list *list, const xs_val *val)
    985 /* returns the position of val in list or -1 */
    986 {
    987     XS_ASSERT_TYPE_NULL(list, XSTYPE_LIST);
    988 
    989     int n = 0;
    990     const xs_val *v;
    991     int sz = xs_size(val);
    992 
    993     xs_list_foreach(list, v) {
    994         if (sz == xs_size(v) && memcmp(val, v, sz) == 0)
    995             return n;
    996 
    997         n++;
    998     }
    999 
   1000     return -1;
   1001 }
   1002 
   1003 
   1004 xs_str *xs_join(const xs_list *list, const char *sep)
   1005 /* joins a list into a string */
   1006 {
   1007     XS_ASSERT_TYPE(list, XSTYPE_LIST);
   1008 
   1009     xs_str *s = NULL;
   1010     const xs_val *v;
   1011     int c = 0;
   1012     int offset = 0;
   1013     int ssz = strlen(sep);
   1014 
   1015     xs_list_foreach(list, v) {
   1016         /* refuse to join non-string values */
   1017         if (xs_type(v) == XSTYPE_STRING) {
   1018             int sz;
   1019 
   1020             /* add the separator */
   1021             if (c != 0 && ssz) {
   1022                 s = xs_realloc(s, offset + ssz);
   1023                 memcpy(s + offset, sep, ssz);
   1024                 offset += ssz;
   1025             }
   1026 
   1027             /* add the element */
   1028             if ((sz = strlen(v)) > 0) {
   1029                 s = xs_realloc(s, offset + sz);
   1030                 memcpy(s + offset, v, sz);
   1031                 offset += sz;
   1032             }
   1033 
   1034             c++;
   1035         }
   1036     }
   1037 
   1038     /* null-terminate */
   1039     s = xs_realloc(s, _xs_blk_size(offset + 1));
   1040     s[offset] = '\0';
   1041 
   1042     return s;
   1043 }
   1044 
   1045 
   1046 xs_list *xs_split_n(const char *str, const char *sep, int times)
   1047 /* splits a string into a list upto n times */
   1048 {
   1049     xs_list *list = xs_list_new();
   1050 
   1051     if (!xs_is_string(str) || !xs_is_string(sep))
   1052         return list;
   1053 
   1054     int sz = strlen(sep);
   1055     char *ss;
   1056 
   1057     while (times > 0 && (ss = strstr(str, sep)) != NULL) {
   1058         /* create a new string with this slice and add it to the list */
   1059         xs *s = xs_str_new_sz(str, ss - str);
   1060 
   1061         if (xs_is_string(s))
   1062             list = xs_list_append(list, s);
   1063 
   1064         /* skip past the separator */
   1065         str = ss + sz;
   1066 
   1067         times--;
   1068     }
   1069 
   1070     /* add the rest of the string */
   1071     if (xs_is_string(str))
   1072         list = xs_list_append(list, str);
   1073 
   1074     return list;
   1075 }
   1076 
   1077 
   1078 xs_list *xs_list_cat(xs_list *l1, const xs_list *l2)
   1079 /* concatenates list l2 to l1 */
   1080 {
   1081     XS_ASSERT_TYPE(l1, XSTYPE_LIST);
   1082     XS_ASSERT_TYPE(l2, XSTYPE_LIST);
   1083 
   1084     /* inserts at the end of l1 the content of l2 (skipping header and footer) */
   1085     return xs_insert_m(l1, xs_size(l1) - 1,
   1086         l2 + 1 + _XS_TYPE_SIZE, xs_size(l2) - (1 + _XS_TYPE_SIZE + 1));
   1087 }
   1088 
   1089 
   1090 /** keyvals **/
   1091 
   1092 int xs_keyval_size(const xs_str *key, const xs_val *value)
   1093 /* returns the needed size for a keyval */
   1094 {
   1095     return 1 + xs_size(key) + xs_size(value);
   1096 }
   1097 
   1098 
   1099 xs_str *xs_keyval_key(const xs_keyval *keyval)
   1100 /* returns a pointer to the key of the keyval */
   1101 {
   1102     return (xs_str *)&keyval[1];
   1103 }
   1104 
   1105 
   1106 xs_val *xs_keyval_value(const xs_keyval *keyval)
   1107 /* returns a pointer to the value of the keyval */
   1108 {
   1109     return (xs_val *)&keyval[1 + xs_size(xs_keyval_key(keyval))];
   1110 }
   1111 
   1112 
   1113 xs_keyval *xs_keyval_make(xs_keyval *keyval, const xs_str *key, const xs_val *value)
   1114 /* builds a keyval into mem (should have enough size) */
   1115 {
   1116     keyval[0] = XSTYPE_KEYVAL;
   1117     memcpy(xs_keyval_key(keyval),   key,   xs_size(key));
   1118     memcpy(xs_keyval_value(keyval), value, xs_size(value));
   1119 
   1120     return keyval;
   1121 }
   1122 
   1123 
   1124 /** dicts **/
   1125 
   1126 typedef struct {
   1127     int value_offset;   /* offset to value (from dict start) */
   1128     int next;           /* next node in sequential scanning */
   1129     int child[4];       /* child nodes in hashed search */
   1130     char key[];         /* C string key */
   1131 } ditem_hdr;
   1132 
   1133 typedef struct {
   1134     int size;           /* size of full dict (_XS_TYPE_SIZE) */
   1135     int first;          /* first node for sequential scanning */
   1136     int last;           /* last node for sequential scanning */
   1137     int root;           /* root node for hashed search */
   1138     /* a bunch of ditem_hdr and value follows */
   1139 } dict_hdr;
   1140 
   1141 
   1142 xs_dict *xs_dict_new(void)
   1143 /* creates a new dict */
   1144 {
   1145     /* size of dict */
   1146     int sz = 1 + sizeof(dict_hdr);
   1147 
   1148     xs_dict *d = xs_realloc(NULL, sz);
   1149     memset(d, '\0', sz);
   1150 
   1151     d[0] = XSTYPE_DICT;
   1152     _xs_put_size(d, sz);
   1153 
   1154     return d;
   1155 }
   1156 
   1157 static int *_xs_dict_locate(const xs_dict *dict, const char *key, int ksz)
   1158 /* locates a ditem */
   1159 {
   1160     unsigned int h = xs_hash_func(key, ksz);
   1161 
   1162     /* start from the root */
   1163     dict_hdr *dh = (dict_hdr *)(dict + 1);
   1164     int *off = &dh->root;
   1165 
   1166     while (*off) {
   1167         /* pointer to ditem */
   1168         ditem_hdr *di = (ditem_hdr *)(dict + *off);
   1169 
   1170         /* pointer to the key */
   1171         const char *d_key = di->key;
   1172 
   1173         if (strcmp(key, d_key) == 0)
   1174             break;
   1175 
   1176         off = &di->child[h >> 30];
   1177         h <<= 2;
   1178     }
   1179 
   1180     return off;
   1181 }
   1182 
   1183 xs_dict *_xs_dict_ensure(xs_dict *dict, const xs_str *key, int klen, int vsz, int *offset)
   1184 {
   1185     if (xs_type(dict) == XSTYPE_DICT) {
   1186         int *o = _xs_dict_locate(dict, key, klen);
   1187         int end = xs_size(dict);
   1188 
   1189         if (!*o) {
   1190             /* ditem does not exist yet: append to the end */
   1191             *o = end;
   1192 
   1193             int dsz = sizeof(ditem_hdr) + klen + 1 + vsz;
   1194 
   1195             /* open room in the dict for the full ditem */
   1196             dict = xs_expand(dict, end, dsz);
   1197 
   1198             dict_hdr *dh = (dict_hdr *)(dict + 1);
   1199 
   1200             /* build the ditem */
   1201             ditem_hdr *di = (ditem_hdr *)(dict + end);
   1202             memset(di, '\0', dsz);
   1203 
   1204             /* set the offset to the value */
   1205             di->value_offset = end + sizeof(ditem_hdr) + klen + 1;
   1206 
   1207             /* copy the key */
   1208             memcpy(di->key, key, klen);
   1209             di->key[klen] = '\0';
   1210 
   1211             /* chain to the sequential list */
   1212             if (dh->first == 0)
   1213                 dh->first = end;
   1214             else {
   1215                 /* chain this new element to the last one */
   1216                 ditem_hdr *dil = (ditem_hdr *)(dict + dh->last);
   1217                 dil->next = end;
   1218             }
   1219 
   1220             dh->last = end;
   1221 
   1222             *offset = di->value_offset;
   1223         }
   1224         else {
   1225             /* ditem already exists */
   1226             ditem_hdr *di = (ditem_hdr *)(dict + *o);
   1227 
   1228             /* get pointer to the value offset */
   1229             int *i = &di->value_offset;
   1230 
   1231             /* deleted? recover offset */
   1232             if (*i < 0)
   1233                 *i *= -1;
   1234 
   1235             /* get old value */
   1236             xs_val *o_value = dict + *i;
   1237 
   1238             /* will new value fit over the old one? */
   1239             if (vsz > xs_size(o_value)) {
   1240                 /* not enough room: new value will live at the end of the dict */
   1241                 /* (old value is leaked inside the dict) */
   1242                 *i = end;
   1243                 *offset = *i;
   1244 
   1245                 dict = xs_expand(dict, end, vsz);
   1246             } else
   1247                 *offset = *i;
   1248         }
   1249     }
   1250     else {
   1251         *offset = -1;
   1252     }
   1253 
   1254     return dict;
   1255 }
   1256 
   1257 xs_dict *xs_dict_set_fmt(xs_dict *dict, const xs_str *key, const char *fmt, ...)
   1258 {
   1259     if (xs_type(dict) == XSTYPE_DICT) {
   1260         int o;
   1261         int vsz;
   1262         va_list ap;
   1263 
   1264         va_start(ap, fmt);
   1265         vsz = vsnprintf(NULL, 0, fmt, ap);
   1266         va_end(ap);
   1267 
   1268         if (vsz++ < 0)
   1269             return dict;
   1270 
   1271         dict = _xs_dict_ensure(dict, key, strlen(key), vsz, &o);
   1272 
   1273         va_start(ap, fmt);
   1274         vsnprintf(dict + o, vsz, fmt, ap);
   1275         va_end(ap);
   1276     }
   1277 
   1278     return dict;
   1279 }
   1280 
   1281 xs_dict *xs_dict_set(xs_dict *dict, const xs_str *key, const xs_val *value)
   1282 /* sets a key/value pair */
   1283 {
   1284     if (value == NULL)
   1285         value = xs_stock(XSTYPE_NULL);
   1286 
   1287     if (xs_type(dict) == XSTYPE_DICT) {
   1288         int vsz = xs_size(value);
   1289         int o;
   1290         dict = _xs_dict_ensure(dict, key, strlen(key), vsz, &o);
   1291         memcpy(dict + o, value, vsz);
   1292     }
   1293 
   1294     return dict;
   1295 }
   1296 
   1297 xs_dict *xs_dict_set_strnn(xs_dict *dict, const void *key, int klen, const void *val, int vlen)
   1298 /* sets a key/value pair */
   1299 {
   1300     if (xs_type(dict) == XSTYPE_DICT) {
   1301         int o;
   1302         dict = _xs_dict_ensure(dict, key, klen, vlen + 1, &o);
   1303         memcpy(dict + o, val, vlen);
   1304 	*(dict + o + vlen) = '\0';
   1305     }
   1306 
   1307     return dict;
   1308 }
   1309 
   1310 xs_dict *xs_dict_append(xs_dict *dict, const xs_str *key, const xs_val *value)
   1311 /* just an alias (for this implementation it's the same) */
   1312 {
   1313     return xs_dict_set(dict, key, value);
   1314 }
   1315 
   1316 
   1317 xs_dict *xs_dict_prepend(xs_dict *dict, const xs_str *key, const xs_val *value)
   1318 /* just an alias (for this implementation it's the same) */
   1319 {
   1320     return xs_dict_set(dict, key, value);
   1321 }
   1322 
   1323 
   1324 xs_dict *xs_dict_del(xs_dict *dict, const xs_str *key)
   1325 /* deletes a key/value pair */
   1326 {
   1327     if (xs_type(dict) == XSTYPE_DICT) {
   1328         int *o = _xs_dict_locate(dict, key, strlen(key));
   1329 
   1330         if (*o) {
   1331             /* found ditem */
   1332             ditem_hdr *di = (ditem_hdr *)(dict + *o);
   1333 
   1334             /* deleted ditems have a negative value offset */
   1335             di->value_offset *= -1;
   1336         }
   1337     }
   1338 
   1339     return dict;
   1340 }
   1341 
   1342 
   1343 const xs_val *xs_dict_get(const xs_dict *dict, const xs_str *key)
   1344 /* gets a value by key, or NULL */
   1345 {
   1346     if (xs_type(dict) == XSTYPE_DICT) {
   1347         int *o = _xs_dict_locate(dict, key, strlen(key));
   1348 
   1349         if (*o) {
   1350             /* found ditem */
   1351             ditem_hdr *di = (ditem_hdr *)(dict + *o);
   1352 
   1353             if (di->value_offset > 0)
   1354                 return dict + di->value_offset;
   1355         }
   1356     }
   1357 
   1358     return NULL;
   1359 }
   1360 
   1361 
   1362 int xs_dict_next(const xs_dict *dict, const xs_str **key, const xs_val **value, int *ctxt)
   1363 /* dict iterator, with context */
   1364 {
   1365     if (xs_type(dict) != XSTYPE_DICT)
   1366         return 0;
   1367 
   1368     if (*ctxt == 0) {
   1369         /* at the beginning: get the first sequential item */
   1370         const dict_hdr *dh = (dict_hdr *)(dict + 1);
   1371         *ctxt = dh->first;
   1372     }
   1373 
   1374     *value = NULL;
   1375 
   1376     while (*value == NULL && *ctxt > 0) {
   1377         const ditem_hdr *di = (ditem_hdr *)(dict + *ctxt);
   1378 
   1379         /* get value */
   1380         if (di->value_offset > 0) {
   1381             *value = (xs_val *)(dict + di->value_offset);
   1382 
   1383             /* get key */
   1384             *key = (xs_str *)&di->key;
   1385         }
   1386 
   1387         /* get offset to next ditem */
   1388         *ctxt = di->next ? di->next : -1;
   1389     }
   1390 
   1391     return *value != NULL;
   1392 }
   1393 
   1394 
   1395 xs_dict *xs_dict_gc(const xs_dict *dict)
   1396 /* creates a copy of dict, but garbage-collected */
   1397 {
   1398     xs_dict *nd = xs_dict_new();
   1399     const xs_str *k;
   1400     const xs_val *v;
   1401 
   1402     xs_dict_foreach(dict, k, v) {
   1403         if (xs_type(v) == XSTYPE_DICT) {
   1404             xs *sd = xs_dict_gc(v);
   1405             nd = xs_dict_set(nd, k, sd);
   1406         }
   1407         else
   1408             nd = xs_dict_set(nd, k, v);
   1409     }
   1410 
   1411     return nd;
   1412 }
   1413 
   1414 
   1415 const xs_val *xs_dict_get_path_sep(const xs_dict *dict, const char *path, const char *sep)
   1416 /* gets a value from dict given a path separated by sep */
   1417 {
   1418     /* split by the separator */
   1419     xs *l = xs_split_n(path, sep, 1);
   1420 
   1421     /* only one part? just get */
   1422     if (xs_list_len(l) == 1)
   1423         return xs_dict_get(dict, path);
   1424 
   1425     const char *prefix = xs_list_get(l, 0);
   1426     const char *rest   = xs_list_get(l, 1);
   1427     const xs_dict *sd  = xs_dict_get(dict, prefix);
   1428 
   1429     if (xs_type(sd) == XSTYPE_DICT)
   1430         return xs_dict_get_path_sep(sd, rest, sep);
   1431 
   1432     return NULL;
   1433 }
   1434 
   1435 
   1436 xs_dict *xs_dict_set_path_sep(xs_dict *dict, const char *path, const xs_val *value, const char *sep)
   1437 /* sets a value into dict given a path separated by sep;
   1438    intermediate dicts are created if needed */
   1439 {
   1440     /* split by the separator */
   1441     xs *l = xs_split_n(path, sep, 1);
   1442 
   1443     /* only one part? just set */
   1444     if (xs_list_len(l) == 1)
   1445         return xs_dict_set(dict, path, value);
   1446 
   1447     const char *prefix = xs_list_get(l, 0);
   1448     const char *rest   = xs_list_get(l, 1);
   1449 
   1450     xs *nd = NULL;
   1451 
   1452     /* does the first part of path exist? */
   1453     const xs_dict *cd = xs_dict_get(dict, prefix);
   1454 
   1455     if (xs_type(cd) == XSTYPE_DICT)
   1456         nd = xs_dup(cd);
   1457     else
   1458         nd = xs_dict_new();
   1459 
   1460     /* move down the path */
   1461     nd = xs_dict_set_path_sep(nd, rest, value, sep);
   1462 
   1463     /* set */
   1464     return xs_dict_set(dict, prefix, nd);
   1465 }
   1466 
   1467 
   1468 /** other values **/
   1469 
   1470 xs_val *xs_val_new(xstype t)
   1471 /* adds a new special value */
   1472 {
   1473     xs_val *v = xs_realloc(NULL, _xs_blk_size(1));
   1474 
   1475     v[0] = t;
   1476 
   1477     return v;
   1478 }
   1479 
   1480 
   1481 /** numbers */
   1482 
   1483 xs_number *xs_number_new(double f)
   1484 /* adds a new number value */
   1485 {
   1486     xs_number *v;
   1487     char tmp[64];
   1488 
   1489     snprintf(tmp, sizeof(tmp), "%.15lf", f);
   1490 
   1491     /* strip useless zeros */
   1492     if (strchr(tmp, '.') != NULL) {
   1493         char *ptr;
   1494 
   1495         for (ptr = tmp + strlen(tmp) - 1; *ptr == '0'; ptr--);
   1496 
   1497         if (*ptr != '.')
   1498             ptr++;
   1499 
   1500         *ptr = '\0';
   1501     }
   1502 
   1503     /* alloc for the marker and the full string */
   1504     v = xs_realloc(NULL, _xs_blk_size(1 + xs_size(tmp)));
   1505 
   1506     v[0] = XSTYPE_NUMBER;
   1507     memcpy(&v[1], tmp, xs_size(tmp));
   1508 
   1509     return v;
   1510 }
   1511 
   1512 
   1513 double xs_number_get(const xs_number *v)
   1514 /* gets the number as a double */
   1515 {
   1516     double f = 0.0;
   1517 
   1518     if (xs_type(v) == XSTYPE_NUMBER)
   1519         f = atof(&v[1]);
   1520     else
   1521     if (xs_type(v) == XSTYPE_STRING)
   1522         f = atof(v);
   1523 
   1524     return f;
   1525 }
   1526 
   1527 
   1528 const char *xs_number_str(const xs_number *v)
   1529 /* gets the number as a string */
   1530 {
   1531     const char *p = NULL;
   1532 
   1533     if (xs_type(v) == XSTYPE_NUMBER)
   1534         p = &v[1];
   1535 
   1536     return p;
   1537 }
   1538 
   1539 
   1540 /** raw data blocks **/
   1541 
   1542 xs_data *xs_data_new(const void *data, int size)
   1543 /* returns a new raw data value */
   1544 {
   1545     xs_data *v;
   1546 
   1547     /* add the overhead (data type + size) */
   1548     int total_size = size + 1 + _XS_TYPE_SIZE;
   1549 
   1550     v = xs_realloc(NULL, _xs_blk_size(total_size));
   1551     v[0] = XSTYPE_DATA;
   1552 
   1553     _xs_put_size(v, total_size);
   1554 
   1555     memcpy(&v[1 + _XS_TYPE_SIZE], data, size);
   1556 
   1557     return v;
   1558 }
   1559 
   1560 
   1561 int xs_data_size(const xs_data *value)
   1562 /* returns the size of the data stored inside value */
   1563 {
   1564     return _xs_get_size(value) - (1 + _XS_TYPE_SIZE);
   1565 }
   1566 
   1567 
   1568 void xs_data_get(void *data, const xs_data *value)
   1569 /* copies the raw data stored inside value into data */
   1570 {
   1571     memcpy(data, &value[1 + _XS_TYPE_SIZE], xs_data_size(value));
   1572 }
   1573 
   1574 
   1575 void *xs_memmem(const char *haystack, int h_size, const char *needle, int n_size)
   1576 /* clone of memmem */
   1577 {
   1578     char *p, *r = NULL;
   1579     int offset = 0;
   1580 
   1581     while (!r && h_size - offset > n_size &&
   1582            (p = memchr(haystack + offset, *needle, h_size - offset))) {
   1583         if (memcmp(p, needle, n_size) == 0)
   1584             r = p;
   1585         else
   1586             offset = p - haystack + 1;
   1587     }
   1588 
   1589     return r;
   1590 }
   1591 
   1592 
   1593 unsigned int xs_hash_func(const char *data, int size)
   1594 /* a general purpose hashing function */
   1595 {
   1596     unsigned int hash = 0x666;
   1597 
   1598     for (int n = 0; n < size; n++) {
   1599         hash ^= (unsigned char)data[n];
   1600         hash *= 111111111;
   1601     }
   1602 
   1603     return hash ^ hash >> 16;
   1604 }
   1605 
   1606 
   1607 uint64_t xs_hash64_func(const char *data, int size)
   1608 /* a general purpose hashing function (64 bit) */
   1609 {
   1610     uint64_t hash = 0x100;
   1611 
   1612     for (int n = 0; n < size; n++) {
   1613         hash ^= (unsigned char)data[n];
   1614         hash *= 1111111111111111111;
   1615     }
   1616 
   1617     return hash;
   1618 }
   1619 
   1620 
   1621 #endif /* XS_IMPLEMENTATION */
   1622 
   1623 #endif /* _XS_H */