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