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