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