|
@@ -0,0 +1,169 @@
|
|
|
|
|
+/* copyright (c) 2022 - 2026 grunfink et al. / MIT license */
|
|
|
|
|
+
|
|
|
|
|
+#ifndef _XS_LIST_TOOLS_H
|
|
|
|
|
+
|
|
|
|
|
+#define _XS_LIST_TOOLS_H
|
|
|
|
|
+
|
|
|
|
|
+ xs_list *xs_list_insert_sorted(xs_list *list, const xs_val *nv);
|
|
|
|
|
+ xs_list *xs_list_reverse(const xs_list *l);
|
|
|
|
|
+ xs_val **xs_list_to_array(const xs_list *l, int *len);
|
|
|
|
|
+ int xs_list_sort_cmp(const void *p1, const void *p2);
|
|
|
|
|
+ int xs_list_sort_inv_cmp(const void *p1, const void *p2);
|
|
|
|
|
+ int xs_list_sort_dict_cmp(const char *field, const void *p1, const void *p2);
|
|
|
|
|
+ xs_list *xs_list_sort(const xs_list *l, int (*cmp)(const void *, const void *));
|
|
|
|
|
+ xs_list *xs_list_shuffle(const xs_list *l);
|
|
|
|
|
+
|
|
|
|
|
+#ifdef XS_IMPLEMENTATION
|
|
|
|
|
+
|
|
|
|
|
+#include "xs_random.h"
|
|
|
|
|
+
|
|
|
|
|
+xs_list *xs_list_insert_sorted(xs_list *list, const xs_val *nv)
|
|
|
|
|
+/* inserts a string in the list in its ordered position */
|
|
|
|
|
+{
|
|
|
|
|
+ XS_ASSERT_TYPE(list, XSTYPE_LIST);
|
|
|
|
|
+
|
|
|
|
|
+ int offset = xs_size(list);
|
|
|
|
|
+
|
|
|
|
|
+ const xs_val *v;
|
|
|
|
|
+ xs_list_foreach(list, v) {
|
|
|
|
|
+ /* if this element is greater or equal, insert here */
|
|
|
|
|
+ if (xs_cmp(v, nv) >= 0) {
|
|
|
|
|
+ offset = v - list;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return _xs_list_write_litem(list, offset - 1, nv, xs_size(nv));
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+xs_list *xs_list_reverse(const xs_list *l)
|
|
|
|
|
+/* creates a new list as a reverse version of l */
|
|
|
|
|
+{
|
|
|
|
|
+ xs_list *n = xs_dup(l);
|
|
|
|
|
+ const xs_val *v;
|
|
|
|
|
+
|
|
|
|
|
+ /* move to one byte before the EOM */
|
|
|
|
|
+ char *p = n + xs_size(n) - 1;
|
|
|
|
|
+
|
|
|
|
|
+ xs_list_foreach(l, v) {
|
|
|
|
|
+ /* size of v, plus the LITEM */
|
|
|
|
|
+ int z = xs_size(v) + 1;
|
|
|
|
|
+
|
|
|
|
|
+ p -= z;
|
|
|
|
|
+
|
|
|
|
|
+ /* copy v, including its LITEM */
|
|
|
|
|
+ memcpy(p, v - 1, z);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return n;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+xs_val **xs_list_to_array(const xs_list *l, int *len)
|
|
|
|
|
+/* converts a list to an array of values */
|
|
|
|
|
+/* must be freed after use */
|
|
|
|
|
+{
|
|
|
|
|
+ *len = xs_list_len(l);
|
|
|
|
|
+ xs_val **a = xs_realloc(NULL, *len * sizeof(xs_val *));
|
|
|
|
|
+ const xs_val *v;
|
|
|
|
|
+ int n = 0;
|
|
|
|
|
+
|
|
|
|
|
+ xs_list_foreach(l, v)
|
|
|
|
|
+ a[n++] = (xs_val *)v;
|
|
|
|
|
+
|
|
|
|
|
+ return a;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+int xs_list_sort_cmp(const void *p1, const void *p2)
|
|
|
|
|
+/* default list sorting function */
|
|
|
|
|
+{
|
|
|
|
|
+ const xs_val *v1 = *(xs_val **)p1;
|
|
|
|
|
+ const xs_val *v2 = *(xs_val **)p2;
|
|
|
|
|
+
|
|
|
|
|
+ return xs_cmp(v1, v2);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+int xs_list_sort_inv_cmp(const void *p1, const void *p2)
|
|
|
|
|
+/* default list inverse sorting function */
|
|
|
|
|
+{
|
|
|
|
|
+ const xs_val *v1 = *(xs_val **)p1;
|
|
|
|
|
+ const xs_val *v2 = *(xs_val **)p2;
|
|
|
|
|
+
|
|
|
|
|
+ return xs_cmp(v2, v1);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+int xs_list_sort_dict_cmp(const char *field, const void *p1, const void *p2)
|
|
|
|
|
+/* compare sorting function for a field an array of dicts */
|
|
|
|
|
+{
|
|
|
|
|
+ const xs_dict *d1 = *(xs_val **)p1;
|
|
|
|
|
+ const xs_dict *d2 = *(xs_val **)p2;
|
|
|
|
|
+
|
|
|
|
|
+ if (xs_type(d1) != XSTYPE_DICT || xs_type(d2) != XSTYPE_DICT)
|
|
|
|
|
+ return 0;
|
|
|
|
|
+
|
|
|
|
|
+ return xs_cmp(xs_dict_get_def(d1, field, ""),
|
|
|
|
|
+ xs_dict_get_def(d2, field, ""));
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+xs_list *xs_list_sort(const xs_list *l, int (*cmp)(const void *, const void *))
|
|
|
|
|
+/* returns a sorted copy of l. cmp can be null for standard sorting */
|
|
|
|
|
+{
|
|
|
|
|
+ int sz;
|
|
|
|
|
+ xs_val **a = xs_list_to_array(l, &sz);
|
|
|
|
|
+ xs_list *nl = xs_dup(l);
|
|
|
|
|
+ char *p = nl + 1 + _XS_TYPE_SIZE;
|
|
|
|
|
+
|
|
|
|
|
+ /* sort the array */
|
|
|
|
|
+ qsort(a, sz, sizeof(xs_val *), cmp ? cmp : xs_list_sort_cmp);
|
|
|
|
|
+
|
|
|
|
|
+ /* transfer the sorted list over the copy */
|
|
|
|
|
+ for (int n = 0; n < sz; n++) {
|
|
|
|
|
+ /* get the litem */
|
|
|
|
|
+ const char *e = a[n] - 1;
|
|
|
|
|
+ int z = xs_size(e);
|
|
|
|
|
+
|
|
|
|
|
+ memcpy(p, e, z);
|
|
|
|
|
+ p += z;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ xs_free(a);
|
|
|
|
|
+
|
|
|
|
|
+ return nl;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+xs_list *xs_list_shuffle(const xs_list *l)
|
|
|
|
|
+/* returns a shuffled list */
|
|
|
|
|
+{
|
|
|
|
|
+ int sz;
|
|
|
|
|
+ xs_val **a = xs_list_to_array(l, &sz);
|
|
|
|
|
+ xs_list *nl = xs_list_new();
|
|
|
|
|
+ unsigned int seed = 0;
|
|
|
|
|
+
|
|
|
|
|
+ xs_rnd_buf(&seed, sizeof(seed));
|
|
|
|
|
+
|
|
|
|
|
+ /* shuffle */
|
|
|
|
|
+ for (int n = sz - 1; n > 0; n--) {
|
|
|
|
|
+ int m = xs_rnd_int32_d(&seed) % n;
|
|
|
|
|
+ void *p = a[n];
|
|
|
|
|
+ a[n] = a[m];
|
|
|
|
|
+ a[m] = p;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ for (int n = 0; n < sz; n++)
|
|
|
|
|
+ nl = xs_list_append(nl, a[n]);
|
|
|
|
|
+
|
|
|
|
|
+ xs_free(a);
|
|
|
|
|
+
|
|
|
|
|
+ return nl;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+#endif /* XS_IMPLEMENTATION */
|
|
|
|
|
+
|
|
|
|
|
+#endif /* XS_LIST_TOOLS_H */
|