2.69
xs.h
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
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_DATA = 0x10 /* A block of anonymous data */
27} xstype;
28
29
30/* types */
31typedef char xs_val;
32typedef char xs_str;
33typedef char xs_list;
34typedef char xs_keyval;
35typedef char xs_dict;
36typedef char xs_number;
37typedef char xs_data;
38
39/* size in bytes of the type size */
40#define _XS_TYPE_SIZE 4
41
42/* auto-destroyable strings */
43#define xs __attribute__ ((__cleanup__ (_xs_destroy))) xs_val
44
45/* not really all, just very much */
46#define XS_ALL 0xfffffff
47
48#ifndef xs_countof
49#define xs_countof(a) (sizeof((a)) / sizeof((*a)))
50#endif
51
52void *xs_free(void *ptr);
53void *_xs_realloc(void *ptr, size_t size, const char *file, int line, const char *func);
54#define xs_realloc(ptr, size) _xs_realloc(ptr, size, __FILE__, __LINE__, __func__)
55int _xs_blk_size(int sz);
56void _xs_destroy(char **var);
57#define xs_debug() raise(SIGTRAP)
58xstype xs_type(const xs_val *data);
59int xs_size(const xs_val *data);
60int xs_is_null(const xs_val *data);
61int xs_cmp(const xs_val *v1, const xs_val *v2);
62const xs_val *xs_or(const xs_val *v1, const xs_val *v2);
63xs_val *xs_dup(const xs_val *data);
64xs_val *xs_expand(xs_val *data, int offset, int size);
65xs_val *xs_collapse(xs_val *data, int offset, int size);
66xs_val *xs_insert_m(xs_val *data, int offset, const char *mem, int size);
67#define xs_insert(data, offset, data2) xs_insert_m(data, offset, data2, xs_size(data2))
68#define xs_append_m(data, mem, size) xs_insert_m(data, xs_size(data) - 1, mem, size)
69xs_val *xs_stock(int type);
70
71xs_str *xs_str_new(const char *str);
72xs_str *xs_str_new_sz(const char *mem, int sz);
73xs_str *xs_str_wrap_i(const char *prefix, xs_str *str, const char *suffix);
74#define xs_str_prepend_i(str, prefix) xs_str_wrap_i(prefix, str, NULL)
75xs_str *_xs_str_cat(xs_str *str, const char *strs[]);
76#define xs_str_cat(str, ...) _xs_str_cat(str, (const char *[]){ __VA_ARGS__, NULL })
77xs_str *xs_replace_in(xs_str *str, const char *sfrom, const char *sto, int times);
78#define xs_replace_i(str, sfrom, sto) xs_replace_in(str, sfrom, sto, XS_ALL)
79#define xs_replace(str, sfrom, sto) xs_replace_in(xs_dup(str), sfrom, sto, XS_ALL)
80#define xs_replace_n(str, sfrom, sto, times) xs_replace_in(xs_dup(str), sfrom, sto, times)
81xs_str *xs_fmt(const char *fmt, ...);
82int xs_str_in(const char *haystack, const char *needle);
83int xs_between(const char *prefix, const char *str, const char *suffix);
84#define xs_startswith(str, prefix) xs_between(prefix, str, NULL)
85#define xs_endswith(str, suffix) xs_between(NULL, str, suffix)
86xs_str *xs_crop_i(xs_str *str, int start, int end);
87xs_str *xs_lstrip_chars_i(xs_str *str, const char *chars);
88xs_str *xs_rstrip_chars_i(xs_str *str, const char *chars);
89xs_str *xs_strip_chars_i(xs_str *str, const char *chars);
90#define xs_strip_i(str) xs_strip_chars_i(str, " \r\n\t\v\f")
91xs_str *xs_tolower_i(xs_str *str);
92
93xs_list *xs_list_new(void);
94xs_list *xs_list_append_m(xs_list *list, const char *mem, int dsz);
95xs_list *_xs_list_append(xs_list *list, const xs_val *vals[]);
96#define xs_list_append(list, ...) _xs_list_append(list, (const xs_val *[]){ __VA_ARGS__, NULL })
97int xs_list_iter(xs_list **list, const xs_val **value);
98int xs_list_next(const xs_list *list, const xs_val **value, int *ctxt);
99int xs_list_len(const xs_list *list);
100const xs_val *xs_list_get(const xs_list *list, int num);
101xs_list *xs_list_del(xs_list *list, int num);
102xs_list *xs_list_insert(xs_list *list, int num, const xs_val *data);
103xs_list *xs_list_set(xs_list *list, int num, const xs_val *data);
104xs_list *xs_list_dequeue(xs_list *list, xs_val **data, int last);
105#define xs_list_pop(list, data) xs_list_dequeue(list, data, 1)
106#define xs_list_shift(list, data) xs_list_dequeue(list, data, 0)
107int xs_list_in(const xs_list *list, const xs_val *val);
108xs_str *xs_join(const xs_list *list, const char *sep);
109xs_list *xs_split_n(const char *str, const char *sep, int times);
110#define xs_split(str, sep) xs_split_n(str, sep, XS_ALL)
111xs_list *xs_list_cat(xs_list *l1, const xs_list *l2);
112
113int xs_keyval_size(const xs_str *key, const xs_val *value);
114xs_str *xs_keyval_key(const xs_keyval *keyval);
115xs_val *xs_keyval_value(const xs_keyval *keyval);
116xs_keyval *xs_keyval_make(xs_keyval *keyval, const xs_str *key, const xs_val *value);
117
118xs_dict *xs_dict_new(void);
119xs_dict *xs_dict_append(xs_dict *dict, const xs_str *key, const xs_val *value);
120xs_dict *xs_dict_prepend(xs_dict *dict, const xs_str *key, const xs_val *value);
121int xs_dict_next(const xs_dict *dict, const xs_str **key, const xs_val **value, int *ctxt);
122const xs_val *xs_dict_get(const xs_dict *dict, const xs_str *key);
123#define xs_dict_get_def(dict, key, def) xs_or(xs_dict_get(dict, key), def)
124xs_dict *xs_dict_del(xs_dict *dict, const xs_str *key);
125xs_dict *xs_dict_set(xs_dict *dict, const xs_str *key, const xs_val *data);
126xs_dict *xs_dict_gc(const xs_dict *dict);
127
128const xs_val *xs_dict_get_path_sep(const xs_dict *dict, const char *path, const char *sep);
129#define xs_dict_get_path(dict, path) xs_dict_get_path_sep(dict, path, ".")
130xs_dict *xs_dict_set_path_sep(xs_dict *dict, const char *path, const xs_val *value, const char *sep);
131#define xs_dict_set_path(dict, path, value) xs_dict_set_path_sep(dict, path, value, ".")
132
133xs_val *xs_val_new(xstype t);
134xs_number *xs_number_new(double f);
135double xs_number_get(const xs_number *v);
136const char *xs_number_str(const xs_number *v);
137
138xs_data *xs_data_new(const void *data, int size);
139int xs_data_size(const xs_data *value);
140void xs_data_get(void *data, const xs_data *value);
141
142void *xs_memmem(const char *haystack, int h_size, const char *needle, int n_size);
143
144unsigned int xs_hash_func(const char *data, int size);
145
146#ifdef XS_ASSERT
147#include <assert.h>
148#define XS_ASSERT_TYPE(v, t) assert(xs_type(v) == t)
149#define XS_ASSERT_TYPE_NULL(v, t) assert(v == NULL || xs_type(v) == t)
150#else
151#define XS_ASSERT_TYPE(v, t) (void)(0)
152#define XS_ASSERT_TYPE_NULL(v, t) (void)(0)
153#endif
154
155#define xs_return(v) xs_val *__r = v; v = NULL; return __r
156
157#define xs_is_true(v) (xs_type((v)) == XSTYPE_TRUE)
158#define xs_is_false(v) (xs_type((v)) == XSTYPE_FALSE)
159#define xs_not(v) xs_stock(xs_is_true((v)) ? XSTYPE_FALSE : XSTYPE_TRUE)
160#define xs_is_string(v) (xs_type((v)) == XSTYPE_STRING)
161#define xs_is_list(v) (xs_type((v)) == XSTYPE_LIST)
162#define xs_is_dict(v) (xs_type((v)) == XSTYPE_DICT)
163
164#define xs_list_foreach(l, v) for (int ct_##__LINE__ = 0; xs_list_next(l, &v, &ct_##__LINE__); )
165#define xs_dict_foreach(l, k, v) for (int ct_##__LINE__ = 0; xs_dict_next(l, &k, &v, &ct_##__LINE__); )
166
167
168#ifdef XS_IMPLEMENTATION
169
170void *_xs_realloc(void *ptr, size_t size, const char *file, int line, const char *func)
171{
172 xs_val *ndata = realloc(ptr, size);
173
174 if (ndata == NULL) {
175 fprintf(stderr, "ERROR: out of memory at %s:%d: %s()\n", file, line, func);
176 abort();
177 }
178
179#ifdef XS_DEBUG
180 if (ndata != ptr) {
181 int n;
182 FILE *f = fopen("xs_memory.out", "a");
183
184 if (ptr != NULL)
185 fprintf(f, "%p r\n", ptr);
186
187 fprintf(f, "%p a %ld %s:%d: %s", ndata, size, file, line, func);
188
189 if (ptr != NULL) {
190 fprintf(f, " [");
191 for (n = 0; n < 32 && ndata[n]; n++) {
192 if (ndata[n] >= 32 && ndata[n] <= 127)
193 fprintf(f, "%c", ndata[n]);
194 else
195 fprintf(f, "\\%02x", (unsigned char)ndata[n]);
196 }
197 fprintf(f, "]");
198 }
199
200 fprintf(f, "\n");
201
202 fclose(f);
203 }
204#else
205 (void)file;
206 (void)line;
207 (void)func;
208#endif
209
210 return ndata;
211}
212
213
214void *xs_free(void *ptr)
215{
216#ifdef XS_DEBUG
217 if (ptr != NULL) {
218 FILE *f = fopen("xs_memory.out", "a");
219 fprintf(f, "%p b\n", ptr);
220 fclose(f);
221 }
222#endif
223
224 free(ptr);
225 return NULL;
226}
227
228
229void _xs_destroy(char **var)
230{
231/*
232 if (_xs_debug)
233 printf("_xs_destroy %p\n", var);
234*/
235 xs_free(*var);
236}
237
238
239int _xs_blk_size(int sz)
240/* calculates the block size */
241{
242 int blk_size = 4096;
243
244 if (sz < 256)
245 blk_size = 32;
246 else
247 if (sz < 4096)
248 blk_size = 256;
249
250 return ((((sz) + blk_size) / blk_size) * blk_size);
251}
252
253
254xstype xs_type(const xs_val *data)
255/* return the type of data */
256{
257 xstype t;
258
259 if (data == NULL)
260 t = XSTYPE_NULL;
261 else
262 switch (data[0]) {
263 case XSTYPE_NULL:
264 case XSTYPE_TRUE:
265 case XSTYPE_FALSE:
266 case XSTYPE_LIST:
267 case XSTYPE_LITEM:
268 case XSTYPE_DICT:
269 case XSTYPE_KEYVAL:
270 case XSTYPE_NUMBER:
271 case XSTYPE_DATA:
272 t = data[0];
273 break;
274 default:
275 t = XSTYPE_STRING;
276 break;
277 }
278
279 return t;
280}
281
282
283void _xs_put_size(xs_val *ptr, int i)
284/* must match _XS_TYPE_SIZE */
285{
286 memcpy(ptr + 1, &i, sizeof(i));
287}
288
289
290int _xs_get_size(const xs_val *ptr)
291/* must match _XS_TYPE_SIZE */
292{
293 int i;
294 memcpy(&i, ptr + 1, sizeof(i));
295 return i;
296}
297
298
299int xs_size(const xs_val *data)
300/* returns the size of data in bytes */
301{
302 int len = 0;
303 const char *p;
304
305 if (data == NULL)
306 return 0;
307
308 switch (xs_type(data)) {
309 case XSTYPE_STRING:
310 len = strlen(data) + 1;
311 break;
312
313 case XSTYPE_LIST:
314 case XSTYPE_DICT:
315 case XSTYPE_DATA:
316 len = _xs_get_size(data);
317
318 break;
319
320 case XSTYPE_KEYVAL:
321 /* calculate the size of the key and the value */
322 p = data + 1;
323 p += xs_size(p);
324 p += xs_size(p);
325
326 len = p - data;
327
328 break;
329
330 case XSTYPE_LITEM:
331 /* it's the size of the item + 1 */
332 p = data + 1;
333 p += xs_size(p);
334
335 len = p - data;
336
337 break;
338
339 case XSTYPE_NUMBER:
340 len = 1 + xs_size(data + 1);
341
342 break;
343
344 default:
345 len = 1;
346 }
347
348 return len;
349}
350
351
352int xs_is_null(const xs_val *data)
353/* checks for null */
354{
355 return (xs_type(data) == XSTYPE_NULL);
356}
357
358
359int xs_cmp(const xs_val *v1, const xs_val *v2)
360/* compares two values */
361{
362 if (xs_type(v1) == XSTYPE_STRING && xs_type(v2) == XSTYPE_STRING)
363 return strcmp(v1, v2);
364
365 int s1 = xs_size(v1);
366 int s2 = xs_size(v2);
367 int d = s1 - s2;
368
369 return d == 0 ? memcmp(v1, v2, s1) : d;
370}
371
372
373const xs_val *xs_or(const xs_val *v1, const xs_val *v2)
374/* returns v1 if it's not NULL, else v2 */
375{
376 return v1 == NULL ? v2 : v1;
377}
378
379
380xs_val *xs_dup(const xs_val *data)
381/* creates a duplicate of data */
382{
383 xs_val *s = NULL;
384
385 if (data) {
386 int sz = xs_size(data);
387 s = xs_realloc(NULL, _xs_blk_size(sz));
388
389 memcpy(s, data, sz);
390 }
391
392 return s;
393}
394
395
396xs_val *xs_expand(xs_val *data, int offset, int size)
397/* opens a hole in data */
398{
399 int sz = xs_size(data);
400 int n;
401
402 sz += size;
403
404 /* open room */
405 data = xs_realloc(data, _xs_blk_size(sz));
406
407 /* move up the rest of the data */
408 for (n = sz - 1; n >= offset + size; n--)
409 data[n] = data[n - size];
410
411 if (xs_type(data) == XSTYPE_LIST ||
412 xs_type(data) == XSTYPE_DICT ||
413 xs_type(data) == XSTYPE_DATA)
414 _xs_put_size(data, sz);
415
416 return data;
417}
418
419
420xs_val *xs_collapse(xs_val *data, int offset, int size)
421/* shrinks data */
422{
423 int sz = xs_size(data);
424 int n;
425
426 /* don't try to delete beyond the limit */
427 if (offset + size > sz)
428 size = sz - offset;
429
430 /* shrink total size */
431 sz -= size;
432
433 for (n = offset; n < sz; n++)
434 data[n] = data[n + size];
435
436 if (xs_type(data) == XSTYPE_LIST ||
437 xs_type(data) == XSTYPE_DICT ||
438 xs_type(data) == XSTYPE_DATA)
439 _xs_put_size(data, sz);
440
441 return xs_realloc(data, _xs_blk_size(sz));
442}
443
444
445xs_val *xs_insert_m(xs_val *data, int offset, const char *mem, int size)
446/* inserts a memory block */
447{
448 data = xs_expand(data, offset, size);
449 memcpy(data + offset, mem, size);
450
451 return data;
452}
453
454
455xs_val *xs_stock(int type)
456/* returns stock values */
457{
458 static xs_val stock_null[] = { XSTYPE_NULL };
459 static xs_val stock_true[] = { XSTYPE_TRUE };
460 static xs_val stock_false[] = { XSTYPE_FALSE };
461 static xs_val stock_0[] = { XSTYPE_NUMBER, '0', '\0' };
462 static xs_val stock_1[] = { XSTYPE_NUMBER, '1', '\0' };
463 static xs_list *stock_list = NULL;
464 static xs_dict *stock_dict = NULL;
465
466 switch (type) {
467 case 0: return stock_0;
468 case 1: return stock_1;
469 case XSTYPE_NULL: return stock_null;
470 case XSTYPE_TRUE: return stock_true;
471 case XSTYPE_FALSE: return stock_false;
472
473 case XSTYPE_LIST:
474 if (stock_list == NULL)
475 stock_list = xs_list_new();
476 return stock_list;
477
478 case XSTYPE_DICT:
479 if (stock_dict == NULL)
480 stock_dict = xs_dict_new();
481 return stock_dict;
482 }
483
484 return NULL;
485}
486
487
488/** strings **/
489
490xs_str *xs_str_new(const char *str)
491/* creates a new string */
492{
493 return xs_insert(NULL, 0, str ? str : "");
494}
495
496
497xs_str *xs_str_new_sz(const char *mem, int sz)
498/* creates a new string from a memory block, adding an asciiz */
499{
500 xs_str *s = xs_realloc(NULL, _xs_blk_size(sz + 1));
501 memcpy(s, mem, sz);
502 s[sz] = '\0';
503
504 return s;
505}
506
507
508xs_str *xs_str_wrap_i(const char *prefix, xs_str *str, const char *suffix)
509/* wraps str with prefix and suffix */
510{
511 XS_ASSERT_TYPE(str, XSTYPE_STRING);
512
513 if (prefix)
514 str = xs_insert_m(str, 0, prefix, strlen(prefix));
515
516 if (suffix)
517 str = xs_insert_m(str, strlen(str), suffix, strlen(suffix));
518
519 return str;
520}
521
522
523xs_str *_xs_str_cat(xs_str *str, const char *strs[])
524/* concatenates all strings after str */
525{
526 int o = strlen(str);
527
528 while (*strs) {
529 int sz = strlen(*strs);
530 str = xs_insert_m(str, o, *strs, sz);
531 o += sz;
532 strs++;
533 }
534
535 return str;
536}
537
538
539xs_str *xs_replace_in(xs_str *str, const char *sfrom, const char *sto, int times)
540/* replaces inline all sfrom with sto */
541{
542 XS_ASSERT_TYPE(str, XSTYPE_STRING);
543
544 int sfsz = strlen(sfrom);
545 int stsz = strlen(sto);
546 int diff = stsz - sfsz;
547 char *ss;
548 int offset = 0;
549
550 while (times > 0 && (ss = strstr(str + offset, sfrom)) != NULL) {
551 int n_offset = ss - str;
552
553 if (diff < 0)
554 str = xs_collapse(str, n_offset, -diff);
555 else
556 if (diff > 0)
557 str = xs_expand(str, n_offset, diff);
558
559 memcpy(str + n_offset, sto, stsz);
560
561 offset = n_offset + stsz;
562
563 times--;
564 }
565
566 return str;
567}
568
569
570xs_str *xs_fmt(const char *fmt, ...)
571/* formats a string with printf()-like marks */
572{
573 int n;
574 xs_str *s = NULL;
575 va_list ap;
576
577 va_start(ap, fmt);
578 n = vsnprintf(s, 0, fmt, ap);
579 va_end(ap);
580
581 if (n > 0) {
582 s = xs_realloc(NULL, _xs_blk_size(n + 1));
583
584 va_start(ap, fmt);
585 vsnprintf(s, n + 1, fmt, ap);
586 va_end(ap);
587 }
588
589 return s;
590}
591
592
593int xs_str_in(const char *haystack, const char *needle)
594/* finds needle in haystack and returns the offset or -1 */
595{
596 char *s;
597 int r = -1;
598
599 if ((s = strstr(haystack, needle)) != NULL)
600 r = s - haystack;
601
602 return r;
603}
604
605
606int xs_between(const char *prefix, const char *str, const char *suffix)
607/* returns true if str starts with prefix and ends with suffix */
608{
609 int sz = strlen(str);
610 int psz = prefix ? strlen(prefix) : 0;
611 int ssz = suffix ? strlen(suffix) : 0;
612
613 if (sz < psz || sz < ssz)
614 return 0;
615
616 if (prefix && memcmp(str, prefix, psz) != 0)
617 return 0;
618
619 if (suffix && memcmp(str + sz - ssz, suffix, ssz) != 0)
620 return 0;
621
622 return 1;
623}
624
625
626xs_str *xs_crop_i(xs_str *str, int start, int end)
627/* crops the string to be only from start to end */
628{
629 int sz = strlen(str);
630
631 if (end <= 0)
632 end = sz + end;
633
634 /* crop from the top */
635 if (end > 0 && end < sz)
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, '\0', 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 scanning */
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 scanning */
1074 int last; /* last node for sequential scanning */
1075 int root; /* root node for hashed search */
1076 /* a bunch of ditem_hdr and value follows */
1077} dict_hdr;
1078
1079
1080xs_dict *xs_dict_new(void)
1081/* creates a new dict */
1082{
1083 /* size of dict */
1084 int sz = 1 + sizeof(dict_hdr);
1085
1086 xs_dict *d = xs_realloc(NULL, sz);
1087 memset(d, '\0', sz);
1088
1089 d[0] = XSTYPE_DICT;
1090 _xs_put_size(d, sz);
1091
1092 return d;
1093}
1094
1095static int *_xs_dict_locate(const xs_dict *dict, const char *key)
1096/* locates a ditem */
1097{
1098 unsigned int h = xs_hash_func(key, strlen(key));
1099
1100 /* start from the root */
1101 dict_hdr *dh = (dict_hdr *)(dict + 1);
1102 int *off = &dh->root;
1103
1104 while (*off) {
1105 /* pointer to ditem */
1106 ditem_hdr *di = (ditem_hdr *)(dict + *off);
1107
1108 /* pointer to the key */
1109 const char *d_key = di->key;
1110
1111 if (strcmp(key, d_key) == 0)
1112 break;
1113
1114 off = &di->child[h >> 30];
1115 h <<= 2;
1116 }
1117
1118 return off;
1119}
1120
1121
1122xs_dict *xs_dict_set(xs_dict *dict, const xs_str *key, const xs_val *value)
1123/* sets a key/value pair */
1124{
1125 if (value == NULL)
1126 value = xs_stock(XSTYPE_NULL);
1127
1128 if (xs_type(dict) == XSTYPE_DICT) {
1129 int *o = _xs_dict_locate(dict, key);
1130 int end = xs_size(dict);
1131
1132 if (!*o) {
1133 /* ditem does not exist yet: append to the end */
1134 *o = end;
1135
1136 int ksz = xs_size(key);
1137 int vsz = xs_size(value);
1138 int dsz = sizeof(ditem_hdr) + ksz + vsz;
1139
1140 /* open room in the dict for the full ditem */
1141 dict = xs_expand(dict, end, dsz);
1142
1143 dict_hdr *dh = (dict_hdr *)(dict + 1);
1144
1145 /* build the ditem */
1146 ditem_hdr *di = (ditem_hdr *)(dict + end);
1147 memset(di, '\0', dsz);
1148
1149 /* set the offset to the value */
1150 di->value_offset = end + sizeof(ditem_hdr) + ksz;
1151
1152 /* copy the key */
1153 memcpy(di->key, key, ksz);
1154
1155 /* copy the value */
1156 memcpy(dict + di->value_offset, value, vsz);
1157
1158 /* chain to the sequential list */
1159 if (dh->first == 0)
1160 dh->first = end;
1161 else {
1162 /* chain this new element to the last one */
1163 ditem_hdr *dil = (ditem_hdr *)(dict + dh->last);
1164 dil->next = end;
1165 }
1166
1167 dh->last = end;
1168 }
1169 else {
1170 /* ditem already exists */
1171 ditem_hdr *di = (ditem_hdr *)(dict + *o);
1172
1173 /* get pointer to the value offset */
1174 int *i = &di->value_offset;
1175
1176 /* deleted? recover offset */
1177 if (*i < 0)
1178 *i *= -1;
1179
1180 /* get old value */
1181 xs_val *o_value = dict + *i;
1182
1183 /* will new value fit over the old one? */
1184 if (xs_size(value) <= xs_size(o_value)) {
1185 /* just overwrite */
1186 /* (difference is leaked inside the dict) */
1187 memcpy(o_value, value, xs_size(value));
1188 }
1189 else {
1190 /* not enough room: new value will live at the end of the dict */
1191 /* (old value is leaked inside the dict) */
1192 *i = end;
1193
1194 dict = xs_insert(dict, end, value);
1195 }
1196 }
1197 }
1198
1199 return dict;
1200}
1201
1202
1203xs_dict *xs_dict_append(xs_dict *dict, const xs_str *key, const xs_val *value)
1204/* just an alias (for this implementation it's the same) */
1205{
1206 return xs_dict_set(dict, key, value);
1207}
1208
1209
1210xs_dict *xs_dict_prepend(xs_dict *dict, const xs_str *key, const xs_val *value)
1211/* just an alias (for this implementation it's the same) */
1212{
1213 return xs_dict_set(dict, key, value);
1214}
1215
1216
1217xs_dict *xs_dict_del(xs_dict *dict, const xs_str *key)
1218/* deletes a key/value pair */
1219{
1220 if (xs_type(dict) == XSTYPE_DICT) {
1221 int *o = _xs_dict_locate(dict, key);
1222
1223 if (*o) {
1224 /* found ditem */
1225 ditem_hdr *di = (ditem_hdr *)(dict + *o);
1226
1227 /* deleted ditems have a negative value offset */
1228 di->value_offset *= -1;
1229 }
1230 }
1231
1232 return dict;
1233}
1234
1235
1236const xs_val *xs_dict_get(const xs_dict *dict, const xs_str *key)
1237/* gets a value by key, or NULL */
1238{
1239 if (xs_type(dict) == XSTYPE_DICT) {
1240 int *o = _xs_dict_locate(dict, key);
1241
1242 if (*o) {
1243 /* found ditem */
1244 ditem_hdr *di = (ditem_hdr *)(dict + *o);
1245
1246 if (di->value_offset > 0)
1247 return dict + di->value_offset;
1248 }
1249 }
1250
1251 return NULL;
1252}
1253
1254
1255int xs_dict_next(const xs_dict *dict, const xs_str **key, const xs_val **value, int *ctxt)
1256/* dict iterator, with context */
1257{
1258 if (xs_type(dict) != XSTYPE_DICT)
1259 return 0;
1260
1261 if (*ctxt == 0) {
1262 /* at the beginning: get the first sequential item */
1263 const dict_hdr *dh = (dict_hdr *)(dict + 1);
1264 *ctxt = dh->first;
1265 }
1266
1267 *value = NULL;
1268
1269 while (*value == NULL && *ctxt > 0) {
1270 const ditem_hdr *di = (ditem_hdr *)(dict + *ctxt);
1271
1272 /* get value */
1273 if (di->value_offset > 0) {
1274 *value = (xs_val *)(dict + di->value_offset);
1275
1276 /* get key */
1277 *key = (xs_str *)&di->key;
1278 }
1279
1280 /* get offset to next ditem */
1281 *ctxt = di->next ? di->next : -1;
1282 }
1283
1284 return *value != NULL;
1285}
1286
1287
1288xs_dict *xs_dict_gc(const xs_dict *dict)
1289/* creates a copy of dict, but garbage-collected */
1290{
1291 xs_dict *nd = xs_dict_new();
1292 const xs_str *k;
1293 const xs_val *v;
1294
1295 xs_dict_foreach(dict, k, v) {
1296 if (xs_type(v) == XSTYPE_DICT) {
1297 xs *sd = xs_dict_gc(v);
1298 nd = xs_dict_set(nd, k, sd);
1299 }
1300 else
1301 nd = xs_dict_set(nd, k, v);
1302 }
1303
1304 return nd;
1305}
1306
1307
1308const xs_val *xs_dict_get_path_sep(const xs_dict *dict, const char *path, const char *sep)
1309/* gets a value from dict given a path separated by sep */
1310{
1311 /* split by the separator */
1312 xs *l = xs_split_n(path, sep, 1);
1313
1314 /* only one part? just get */
1315 if (xs_list_len(l) == 1)
1316 return xs_dict_get(dict, path);
1317
1318 const char *prefix = xs_list_get(l, 0);
1319 const char *rest = xs_list_get(l, 1);
1320 const xs_dict *sd = xs_dict_get(dict, prefix);
1321
1322 if (xs_type(sd) == XSTYPE_DICT)
1323 return xs_dict_get_path_sep(sd, rest, sep);
1324
1325 return NULL;
1326}
1327
1328
1329xs_dict *xs_dict_set_path_sep(xs_dict *dict, const char *path, const xs_val *value, const char *sep)
1330/* sets a value into dict given a path separated by sep;
1331 intermediate dicts are created if needed */
1332{
1333 /* split by the separator */
1334 xs *l = xs_split_n(path, sep, 1);
1335
1336 /* only one part? just set */
1337 if (xs_list_len(l) == 1)
1338 return xs_dict_set(dict, path, value);
1339
1340 const char *prefix = xs_list_get(l, 0);
1341 const char *rest = xs_list_get(l, 1);
1342
1343 xs *nd = NULL;
1344
1345 /* does the first part of path exist? */
1346 const xs_dict *cd = xs_dict_get(dict, prefix);
1347
1348 if (xs_type(cd) == XSTYPE_DICT)
1349 nd = xs_dup(cd);
1350 else
1351 nd = xs_dict_new();
1352
1353 /* move down the path */
1354 nd = xs_dict_set_path_sep(nd, rest, value, sep);
1355
1356 /* set */
1357 return xs_dict_set(dict, prefix, nd);
1358}
1359
1360
1361/** other values **/
1362
1363xs_val *xs_val_new(xstype t)
1364/* adds a new special value */
1365{
1366 xs_val *v = xs_realloc(NULL, _xs_blk_size(1));
1367
1368 v[0] = t;
1369
1370 return v;
1371}
1372
1373
1374/** numbers */
1375
1376xs_number *xs_number_new(double f)
1377/* adds a new number value */
1378{
1379 xs_number *v;
1380 char tmp[64];
1381
1382 snprintf(tmp, sizeof(tmp), "%.15lf", f);
1383
1384 /* strip useless zeros */
1385 if (strchr(tmp, '.') != NULL) {
1386 char *ptr;
1387
1388 for (ptr = tmp + strlen(tmp) - 1; *ptr == '0'; ptr--);
1389
1390 if (*ptr != '.')
1391 ptr++;
1392
1393 *ptr = '\0';
1394 }
1395
1396 /* alloc for the marker and the full string */
1397 v = xs_realloc(NULL, _xs_blk_size(1 + xs_size(tmp)));
1398
1399 v[0] = XSTYPE_NUMBER;
1400 memcpy(&v[1], tmp, xs_size(tmp));
1401
1402 return v;
1403}
1404
1405
1406double xs_number_get(const xs_number *v)
1407/* gets the number as a double */
1408{
1409 double f = 0.0;
1410
1411 if (xs_type(v) == XSTYPE_NUMBER)
1412 f = atof(&v[1]);
1413 else
1414 if (xs_type(v) == XSTYPE_STRING)
1415 f = atof(v);
1416
1417 return f;
1418}
1419
1420
1421const char *xs_number_str(const xs_number *v)
1422/* gets the number as a string */
1423{
1424 const char *p = NULL;
1425
1426 if (xs_type(v) == XSTYPE_NUMBER)
1427 p = &v[1];
1428
1429 return p;
1430}
1431
1432
1433/** raw data blocks **/
1434
1435xs_data *xs_data_new(const void *data, int size)
1436/* returns a new raw data value */
1437{
1438 xs_data *v;
1439
1440 /* add the overhead (data type + size) */
1441 int total_size = size + 1 + _XS_TYPE_SIZE;
1442
1443 v = xs_realloc(NULL, _xs_blk_size(total_size));
1444 v[0] = XSTYPE_DATA;
1445
1446 _xs_put_size(v, total_size);
1447
1448 memcpy(&v[1 + _XS_TYPE_SIZE], data, size);
1449
1450 return v;
1451}
1452
1453
1454int xs_data_size(const xs_data *value)
1455/* returns the size of the data stored inside value */
1456{
1457 return _xs_get_size(value) - (1 + _XS_TYPE_SIZE);
1458}
1459
1460
1461void xs_data_get(void *data, const xs_data *value)
1462/* copies the raw data stored inside value into data */
1463{
1464 memcpy(data, &value[1 + _XS_TYPE_SIZE], xs_data_size(value));
1465}
1466
1467
1468void *xs_memmem(const char *haystack, int h_size, const char *needle, int n_size)
1469/* clone of memmem */
1470{
1471 char *p, *r = NULL;
1472 int offset = 0;
1473
1474 while (!r && h_size - offset > n_size &&
1475 (p = memchr(haystack + offset, *needle, h_size - offset))) {
1476 if (memcmp(p, needle, n_size) == 0)
1477 r = p;
1478 else
1479 offset = p - haystack + 1;
1480 }
1481
1482 return r;
1483}
1484
1485
1486unsigned int xs_hash_func(const char *data, int size)
1487/* a general purpose hashing function */
1488{
1489 unsigned int hash = 0x666;
1490 int n;
1491
1492 for (n = 0; n < size; n++) {
1493 hash ^= (unsigned char)data[n];
1494 hash *= 111111111;
1495 }
1496
1497 return hash ^ hash >> 16;
1498}
1499
1500
1501#endif /* XS_IMPLEMENTATION */
1502
1503#endif /* _XS_H */