1 /* sdb - MIT - Copyright 2011-2018 - pancake */
2
3 #include "sdb.h"
4 #include <limits.h>
5
6 // TODO: Push should always prepend. do not make this configurable
7 #define PUSH_PREPENDS 1
8
9 // TODO: missing num_{inc/dec} functions
10
Aindexof(const char * str,int idx)11 static const char *Aindexof(const char *str, int idx) {
12 int len = 0;
13 const char *n, *p = str;
14 for (len = 0; ; len++) {
15 if (len == idx) {
16 return p;
17 }
18 if (!(n = strchr (p, SDB_RS))) {
19 break;
20 }
21 p = n + 1;
22 }
23 return NULL;
24 }
25
astrcmp(const char * a,const char * b)26 static int astrcmp(const char *a, const char *b) {
27 register char va = *a;
28 register char vb = *b;
29 for (;;) {
30 if (va == '\0' || va == SDB_RS) {
31 if (vb == '\0' || vb == SDB_RS) {
32 return 0;
33 }
34 return -1;
35 }
36 if (vb == '\0' || vb == SDB_RS) {
37 return 1;
38 }
39 if (va != vb) {
40 return (va > vb) ? 1 : -1;
41 }
42 va = *(++a);
43 vb = *(++b);
44 }
45 }
46
cstring_cmp(const void * a,const void * b)47 static inline int cstring_cmp(const void *a, const void *b) {
48 const char **va = (const char **)a;
49 const char **vb = (const char **)b;
50 return astrcmp (*va, *vb);
51 }
52
int_cmp(const void * a,const void * b)53 static inline int int_cmp(const void *a, const void *b) {
54 const ut64 va = *(const ut64 *)a;
55 const ut64 vb = *(const ut64 *)b;
56 if (va > vb) {
57 return 1;
58 }
59 if (va < vb) {
60 return -1;
61 }
62 return 0;
63 }
64
sdb_array_get_num(Sdb * s,const char * key,int idx,ut32 * cas)65 SDB_API ut64 sdb_array_get_num(Sdb *s, const char *key, int idx, ut32 *cas) {
66 int i;
67 const char *n, *str = sdb_const_get (s, key, cas);
68 if (!str || !*str) {
69 return 0LL;
70 }
71 if (idx) {
72 for (i = 0; i < idx; i++) {
73 n = strchr (str, SDB_RS);
74 if (!n) {
75 return 0LL;
76 }
77 str = n + 1;
78 }
79 }
80 return sdb_atoi (str);
81 }
82
sdb_array_get(Sdb * s,const char * key,int idx,ut32 * cas)83 SDB_API char *sdb_array_get(Sdb *s, const char *key, int idx, ut32 *cas) {
84 const char *str = sdb_const_get (s, key, cas);
85 const char *p = str;
86 char *o, *n;
87 int i, len;
88 if (!str || !*str) {
89 return NULL;
90 }
91 if (idx < 0) {
92 int len = sdb_alen (str);
93 idx = -idx;
94 if (idx > len) {
95 return NULL;
96 }
97 idx = len - idx;
98 }
99 if (!idx) {
100 n = strchr (str, SDB_RS);
101 if (!n) {
102 return strdup (str);
103 }
104 len = n - str;
105 o = malloc (len + 1);
106 if (!o) {
107 return NULL;
108 }
109 memcpy (o, str, len);
110 o[len] = 0;
111 return o;
112 }
113 for (i = 0; i < idx; i++) {
114 n = strchr (p, SDB_RS);
115 if (!n) return NULL;
116 p = n + 1;
117 }
118 n = strchr (p, SDB_RS);
119 if (!n) {
120 return strdup (p);
121 }
122 len = n - p;
123 o = malloc (len + 1);
124 if (o) {
125 memcpy (o, p, len);
126 o[len] = 0;
127 return o;
128 }
129 return NULL;
130 }
131
sdb_array_insert_num(Sdb * s,const char * key,int idx,ut64 val,ut32 cas)132 SDB_API int sdb_array_insert_num(Sdb *s, const char *key, int idx, ut64 val,
133 ut32 cas) {
134 char valstr[64];
135 return sdb_array_insert (s, key, idx,
136 sdb_itoa (val, valstr, SDB_NUM_BASE), cas);
137 }
138
139 // TODO: done, but there's room for improvement
sdb_array_insert(Sdb * s,const char * key,int idx,const char * val,ut32 cas)140 SDB_API int sdb_array_insert(Sdb *s, const char *key, int idx, const char *val,
141 ut32 cas) {
142 int lnstr, lstr;
143 size_t lval;
144 char *x, *ptr;
145 const char *str = sdb_const_get_len (s, key, &lstr, 0);
146 if (!str || !*str) {
147 return sdb_set (s, key, val, cas);
148 }
149 lval = strlen (val);
150 lstr--;
151 // XXX: lstr is wrongly computed in sdb_const_get_with an off-by-one
152 // we can optimize this by caching value len in memory . add
153 // sdb_const_get_size()
154 lstr = strlen (str);
155
156 // When removing strlen this conversion should be checked
157 size_t lstr_tmp = lstr;
158 if (SZT_ADD_OVFCHK (lval, lstr_tmp) || SZT_ADD_OVFCHK (lval + lstr_tmp, 2)) {
159 return false;
160 }
161 x = malloc (lval + lstr_tmp + 2);
162 if (!x) {
163 return false;
164 }
165
166 if (idx == -1) {
167 memcpy (x, str, lstr);
168 x[lstr] = SDB_RS;
169 memcpy (x + lstr + 1, val, lval + 1);
170 } else if (!idx) {
171 memcpy (x, val, lval);
172 x[lval] = SDB_RS;
173 memcpy (x + lval + 1, str, lstr + 1);
174 } else {
175 char *nstr = malloc (lstr + 1);
176 if (!nstr) {
177 free (x);
178 return false;
179 }
180 memcpy (nstr, str, lstr + 1);
181 ptr = (char *)Aindexof (nstr, idx);
182 if (ptr) {
183 int lptr = (nstr + lstr + 1) - ptr;
184 char *p_1 = ptr > nstr? ptr - 1: ptr;
185 *p_1 = 0;
186 lnstr = ptr - nstr - 1;
187 memcpy (x, nstr, lnstr);
188 x[lnstr] = SDB_RS;
189 memcpy (x + lnstr + 1, val, lval);
190 x[lnstr + lval + 1] = SDB_RS;
191 // TODO: this strlen hurts performance
192 memcpy (x + lval + 2 + lnstr, ptr, lptr); //strlen (ptr)+1);
193 free (nstr);
194 } else {
195 // this is not efficient
196 free (nstr);
197 free (x);
198 // fallback for empty buckets
199 return sdb_array_set (s, key, idx, val, cas);
200 }
201 }
202 return sdb_set_owned (s, key, x, cas);
203 }
204
sdb_array_set_num(Sdb * s,const char * key,int idx,ut64 val,ut32 cas)205 SDB_API int sdb_array_set_num(Sdb *s, const char *key, int idx, ut64 val,
206 ut32 cas) {
207 char valstr[SDB_NUM_BUFSZ];
208 return sdb_array_set (s, key, idx, sdb_itoa (val, valstr, SDB_NUM_BASE),
209 cas);
210 }
211
sdb_array_add_num(Sdb * s,const char * key,ut64 val,ut32 cas)212 SDB_API int sdb_array_add_num(Sdb *s, const char *key, ut64 val, ut32 cas) {
213 char buf[SDB_NUM_BUFSZ];
214 char *v = sdb_itoa (val, buf, SDB_NUM_BASE);
215 if (!sdb_array_contains (s, key, v, NULL)) {
216 if (val < 256) {
217 char *v = sdb_itoa (val, buf, 10);
218 return sdb_array_add (s, key, v, cas);
219 }
220 }
221 return sdb_array_add (s, key, v, cas);
222 }
223
224 // XXX: index should be supressed here? if its a set we shouldnt change the index
sdb_array_add(Sdb * s,const char * key,const char * val,ut32 cas)225 SDB_API int sdb_array_add(Sdb *s, const char *key, const char *val, ut32 cas) {
226 if (sdb_array_contains (s, key, val, NULL)) {
227 return 0;
228 }
229 return sdb_array_insert (s, key, -1, val, cas);
230 }
231
sdb_array_add_sorted(Sdb * s,const char * key,const char * val,ut32 cas)232 SDB_API int sdb_array_add_sorted(Sdb *s, const char *key, const char *val, ut32 cas) {
233 int lstr, lval, i, j;
234 const char *str_e, *str_lp, *str_p, *str = sdb_const_get_len (s, key, &lstr, 0);
235 char *nstr, *nstr_p, **vals;
236 const char null = '\0';
237 if (!str || !*str) {
238 str = &null;
239 lstr = 0;
240 }
241 str_e = str + lstr;
242 str_lp = str_p = str;
243 if (!val || !*val) {
244 return 1;
245 }
246 lval = strlen (val);
247 vals = sdb_fmt_array (val);
248 for (i = 0; vals[i]; i++) {
249 /* empty */
250 }
251 if (i > 1) {
252 qsort (vals, i, sizeof (ut64*), cstring_cmp);
253 }
254 nstr_p = nstr = malloc (lstr + lval + 3);
255 if (!nstr) {
256 return 1;
257 }
258 for (i = 0; vals[i]; i++) {
259 while (str_p < str_e) {
260 if (astrcmp (vals[i], str_p) < 0) {
261 break;
262 }
263 str_p = sdb_const_anext (str_p);
264 if (!str_p) {
265 str_p = str_e;
266 }
267 }
268 memcpy (nstr_p, str_lp, str_p - str_lp);
269 nstr_p += str_p - str_lp;
270 if (str_p == str_e && str_lp != str_e) {
271 *(nstr_p++) = SDB_RS;
272 }
273 str_lp = str_p;
274 j = strlen (vals[i]);
275 memcpy (nstr_p, vals[i], j);
276 nstr_p += j;
277 *(nstr_p++) = SDB_RS;
278 }
279 if (str_lp < str_e) {
280 memcpy (nstr_p, str_lp, str_e - str_lp);
281 nstr_p += str_e - str_lp;
282 *(nstr_p) = '\0';
283 } else {
284 *(--nstr_p) = '\0';
285 }
286 sdb_set_owned (s, key, nstr, cas);
287 free (vals);
288 return 0;
289 }
290
sdb_array_add_sorted_num(Sdb * s,const char * key,ut64 val,ut32 cas)291 SDB_API int sdb_array_add_sorted_num(Sdb *s, const char *key, ut64 val,
292 ut32 cas) {
293 int i;
294 char valstr[SDB_NUM_BUFSZ];
295 const char *str = sdb_const_get (s, key, 0);
296 const char *n = str;
297 if (!str || !*str) {
298 return sdb_set (s, key, sdb_itoa (val, valstr, SDB_NUM_BASE), cas);
299 }
300 for (i = 0; n; i++) {
301 if (val <= sdb_atoi (n)) {
302 break;
303 }
304 n = sdb_const_anext (n);
305 }
306 return sdb_array_insert_num (s, key, n? i: -1, val, cas);
307 }
308
sdb_array_unset(Sdb * s,const char * key,int idx,ut32 cas)309 SDB_API int sdb_array_unset(Sdb *s, const char *key, int idx, ut32 cas) {
310 return sdb_array_set (s, key, idx, "", cas);
311 }
312
sdb_array_append(Sdb * s,const char * key,const char * val,ut32 cas)313 SDB_API bool sdb_array_append(Sdb *s, const char *key, const char *val,
314 ut32 cas) {
315 #if SLOW
316 return sdb_array_set (s, key, -1, val, cas);
317 #else
318 int str_len = 0;
319 ut32 kas = cas;
320 const char *str = sdb_const_get_len (s, key, &str_len, &kas);
321 if (!val || (cas && cas != kas)) {
322 return false;
323 }
324 cas = kas;
325 if (str && *str && str_len > 0) {
326 int val_len = strlen (val);
327 char *newval = malloc (str_len + val_len + 2);
328 if (!newval) {
329 return false;
330 }
331 memcpy (newval, str, str_len);
332 newval[str_len] = SDB_RS;
333 memcpy (newval+str_len+1, val, val_len);
334 newval[str_len+val_len+1] = 0;
335 sdb_set_owned (s, key, newval, cas);
336 } else {
337 sdb_set (s, key, val, cas);
338 }
339 return true;
340 #endif
341 }
342
sdb_array_append_num(Sdb * s,const char * key,ut64 val,ut32 cas)343 SDB_API bool sdb_array_append_num(Sdb *s, const char *key, ut64 val, ut32 cas) {
344 return sdb_array_set_num (s, key, -1, val, cas);
345 }
346
sdb_array_set(Sdb * s,const char * key,int idx,const char * val,ut32 cas)347 SDB_API int sdb_array_set(Sdb *s, const char *key, int idx, const char *val,
348 ut32 cas) {
349 int lstr, lval, len;
350 const char *usr, *str = sdb_const_get_len (s, key, &lstr, 0);
351 char *ptr;
352
353 if (!str || !*str) {
354 return sdb_set (s, key, val, cas);
355 }
356 // XXX: should we cache sdb_alen value inside kv?
357 len = sdb_alen (str);
358 lstr--;
359 if (idx < 0 || idx == len) { // append
360 return sdb_array_insert (s, key, -1, val, cas);
361 }
362 lval = strlen (val);
363 if (idx > len) {
364 int ret, i, ilen = idx-len;
365 char *newkey = malloc (ilen + lval + 1);
366 if (!newkey) {
367 return 0;
368 }
369 for (i = 0; i < ilen; i++) {
370 newkey [i] = SDB_RS;
371 }
372 memcpy (newkey + i, val, lval + 1);
373 ret = sdb_array_insert (s, key, -1, newkey, cas);
374 free (newkey);
375 return ret;
376 }
377 //lstr = strlen (str);
378 ptr = (char*)Aindexof (str, idx);
379 if (ptr) {
380 int diff = ptr - str;
381 char *nstr = malloc (lstr + lval + 2);
382 if (!nstr) {
383 return false;
384 }
385 ptr = nstr + diff;
386 //memcpy (nstr, str, lstr+1);
387 memcpy (nstr, str, diff);
388 memcpy (ptr, val, lval + 1);
389 usr = Aindexof (str, idx + 1);
390 if (usr) {
391 ptr[lval] = SDB_RS;
392 strcpy (ptr + lval + 1, usr);
393 }
394 return sdb_set_owned (s, key, nstr, 0);
395 }
396 return 0;
397 }
398
sdb_array_remove_num(Sdb * s,const char * key,ut64 val,ut32 cas)399 SDB_API int sdb_array_remove_num(Sdb *s, const char *key, ut64 val, ut32 cas) {
400 const char *n, *p, *str = sdb_const_get (s, key, 0);
401 int idx = 0;
402 ut64 num;
403 if (str) {
404 for (p = str; ; idx++) {
405 num = sdb_atoi (p);
406 if (num == val) {
407 return sdb_array_delete (s, key, idx, cas);
408 }
409 n = strchr (p, SDB_RS);
410 if (!n) {
411 break;
412 }
413 p = n + 1;
414 }
415 }
416 return 0;
417 }
418
419 /* get array index of given value */
sdb_array_indexof(Sdb * s,const char * key,const char * val,ut32 cas)420 SDB_API int sdb_array_indexof(Sdb *s, const char *key, const char *val,
421 ut32 cas) {
422 const char *str = sdb_const_get (s, key, 0);
423 const char *n, *p = str;
424 int i;
425 for (i = 0; ; i++) {
426 if (!p) {
427 break;
428 }
429 if (!astrcmp (p, val)) {
430 return i;
431 }
432 n = strchr (p, SDB_RS);
433 if (!n) break;
434 p = n + 1;
435 }
436 return -1;
437 }
438
439 // previously named del_str... pair with _add
sdb_array_remove(Sdb * s,const char * key,const char * val,ut32 cas)440 SDB_API int sdb_array_remove (Sdb *s, const char *key, const char *val,
441 ut32 cas) {
442 const char *str = sdb_const_get (s, key, 0);
443 const char *n, *p = str;
444 int idx;
445 if (p) {
446 for (idx = 0; ; idx++) {
447 if (!astrcmp (p, val)) {
448 return sdb_array_delete (s, key, idx, cas);
449 }
450 n = strchr (p, SDB_RS);
451 if (!n) {
452 break;
453 }
454 p = n + 1;
455 }
456 }
457 return 0;
458 }
459
sdb_array_delete(Sdb * s,const char * key,int idx,ut32 cas)460 SDB_API int sdb_array_delete(Sdb *s, const char *key, int idx, ut32 cas) {
461 int i;
462 char *p, *n, *str = sdb_get (s, key, 0);
463 p = str;
464 if (!str || !*str) {
465 free (str);
466 return 0;
467 }
468 if (idx < 0) {
469 idx = sdb_alen (str);
470 if (idx) idx--;
471 }
472 for (i = 0; i < idx; i++) {
473 if ( (n = strchr (p, SDB_RS)) ) {
474 p = n + 1;
475 } else {
476 free (str);
477 return 0;
478 }
479 }
480 n = strchr (p, SDB_RS);
481 if (n) {
482 memmove (p, n + 1, strlen (n));
483 } else {
484 if (p != str)
485 p--; // remove tailing SDB_RS
486 *p = 0;
487 p[1] = 0;
488 }
489 sdb_set_owned (s, key, str, cas);
490 return 1;
491 }
492
493 // XXX Doesnt work if numbers are stored in different base
sdb_array_contains_num(Sdb * s,const char * key,ut64 num,ut32 * cas)494 SDB_API bool sdb_array_contains_num(Sdb *s, const char *key, ut64 num, ut32 *cas) {
495 char val[SDB_NUM_BUFSZ];
496 char *nval = sdb_itoa (num, val, SDB_NUM_BASE);
497 return sdb_array_contains (s, key, nval, cas);
498 }
499
sdb_array_contains(Sdb * s,const char * key,const char * val,ut32 * cas)500 SDB_API bool sdb_array_contains(Sdb *s, const char *key, const char *val, ut32 *cas) {
501 if (!s || !key || !val) {
502 return false;
503 }
504 const char *next, *ptr = sdb_const_get (s, key, cas);
505 if (ptr && *ptr) {
506 size_t vlen = strlen (val);
507 while (1) {
508 next = strchr (ptr, SDB_RS);
509 size_t len = next ? (size_t)(next - ptr) : strlen (ptr);
510 if (len == vlen && !memcmp (ptr, val, len)) {
511 return true;
512 }
513 if (!next) {
514 break;
515 }
516 ptr = next + 1;
517 }
518 }
519 return false;
520 }
521
sdb_array_size(Sdb * s,const char * key)522 SDB_API int sdb_array_size(Sdb *s, const char *key) {
523 return sdb_alen (sdb_const_get (s, key, 0));
524 }
525
526 // NOTE: ignore empty buckets
sdb_array_length(Sdb * s,const char * key)527 SDB_API int sdb_array_length(Sdb *s, const char *key) {
528 return sdb_alen_ignore_empty (sdb_const_get (s, key, 0));
529 }
530
sdb_array_push_num(Sdb * s,const char * key,ut64 num,ut32 cas)531 SDB_API int sdb_array_push_num(Sdb *s, const char *key, ut64 num, ut32 cas) {
532 char buf[SDB_NUM_BUFSZ], *n = sdb_itoa (num, buf, SDB_NUM_BASE);
533 return sdb_array_push (s, key, n, cas);
534 }
535
sdb_array_push(Sdb * s,const char * key,const char * val,ut32 cas)536 SDB_API bool sdb_array_push(Sdb *s, const char *key, const char *val, ut32 cas) {
537 #if PUSH_PREPENDS
538 return sdb_array_prepend (s, key, val, cas);
539 #else
540 return sdb_array_append (s, key, val, cas);
541 #endif
542 }
543
sdb_array_prepend_num(Sdb * s,const char * key,ut64 num,ut32 cas)544 SDB_API bool sdb_array_prepend_num(Sdb *s, const char *key, ut64 num, ut32 cas) {
545 char buf[SDB_NUM_BUFSZ];
546 char *n = sdb_itoa (num, buf, SDB_NUM_BASE);
547 return sdb_array_push (s, key, n, cas);
548 }
549
sdb_array_prepend(Sdb * s,const char * key,const char * val,ut32 cas)550 SDB_API bool sdb_array_prepend (Sdb *s, const char *key, const char *val, ut32 cas) {
551 if (!s || !key || !val) {
552 return false;
553 }
554 int str_len = 0;
555 ut32 kas = cas;
556 const char *str = sdb_const_get_len (s, key, &str_len, &kas);
557 if (!val || (cas && cas != kas)) {
558 return false;
559 }
560 cas = kas;
561 if (str && *str) {
562 int val_len = strlen (val);
563 char *newval = malloc (str_len + val_len + 2);
564 if (!newval) {
565 return false;
566 }
567 memcpy (newval, val, val_len);
568 newval[val_len] = SDB_RS;
569 memcpy (newval + val_len + 1, str, str_len);
570 newval[str_len + val_len + 1] = 0;
571 // TODO: optimize this because we already have allocated and strlened everything
572 sdb_set_owned (s, key, newval, cas);
573 } else {
574 sdb_set (s, key, val, cas);
575 }
576 return true;
577 }
578
sdb_array_pop_num(Sdb * s,const char * key,ut32 * cas)579 SDB_API ut64 sdb_array_pop_num(Sdb *s, const char *key, ut32 *cas) {
580 ut64 ret;
581 char *a = sdb_array_pop (s, key, cas);
582 if (!a) {
583 if (cas) {
584 *cas = UT32_MAX; // invalid
585 }
586 return UT64_MAX;
587 }
588 if (cas) {
589 *cas = 0;
590 }
591 ret = sdb_atoi (a);
592 free (a);
593 return ret;
594 }
595
sdb_array_pop(Sdb * s,const char * key,ut32 * cas)596 SDB_API char *sdb_array_pop(Sdb *s, const char *key, ut32 *cas) {
597 #if PUSH_PREPENDS
598 return sdb_array_pop_head (s, key, cas);
599 #else
600 return sdb_array_pop_tail (s, key, cas);
601 #endif
602 }
603
sdb_array_pop_head(Sdb * s,const char * key,ut32 * cas)604 SDB_API char *sdb_array_pop_head(Sdb *s, const char *key, ut32 *cas) {
605 // remove last element in
606 ut32 kas;
607 char *end, *str = sdb_get (s, key, &kas);
608 if (!str || !*str) {
609 free (str);
610 return NULL;
611 }
612 if (cas && *cas != kas) {
613 *cas = kas;
614 }
615 end = strchr (str, SDB_RS);
616 if (end) {
617 *end = 0;
618 sdb_set (s, key, end + 1, 0);
619 } else {
620 sdb_unset (s, key, 0);
621 }
622 return str;
623 }
624
sdb_array_pop_tail(Sdb * s,const char * key,ut32 * cas)625 SDB_API char *sdb_array_pop_tail(Sdb *s, const char *key, ut32 *cas) {
626 ut32 kas;
627 char *end, *str = sdb_get (s, key, &kas);
628 if (!str || !*str) {
629 free (str);
630 return NULL;
631 }
632 if (cas && *cas != kas) {
633 *cas = kas;
634 }
635 for (end = str + strlen (str) - 1; end > str && *end != SDB_RS; end--) {
636 //nothing to see here
637 }
638 if (*end == SDB_RS) {
639 *end++ = 0;
640 }
641 sdb_set_owned (s, key, str, 0);
642 // XXX: probably wrong
643 return strdup (end);
644 }
645
sdb_array_sort(Sdb * s,const char * key,ut32 cas)646 SDB_API void sdb_array_sort(Sdb *s, const char *key, ut32 cas) {
647 char *nstr, *str, **strs;
648 int lstr, j, i;
649 str = sdb_get_len (s, key, &lstr, 0);
650 if (!str) {
651 return;
652 }
653 if (!*str) {
654 free (str);
655 return;
656 }
657 strs = sdb_fmt_array (str);
658 for (i = 0; strs[i]; i++) {
659 //nothing to see here
660 }
661 qsort (strs, i, sizeof (char*), cstring_cmp);
662 nstr = str;
663 for (i = 0; strs[i]; i++) {
664 j = strlen (strs[i]);
665 memcpy (nstr, strs[i], j);
666 nstr += j;
667 *(nstr++) = SDB_RS;
668 }
669 if (nstr > str) {
670 *(--nstr) = '\0';
671 } else {
672 *nstr = '\0';
673 }
674 sdb_set_owned (s, key, str, cas);
675 free (strs);
676 }
677
sdb_array_sort_num(Sdb * s,const char * key,ut32 cas)678 SDB_API void sdb_array_sort_num(Sdb *s, const char *key, ut32 cas) {
679 char *ret, *nstr;
680
681 char *str = sdb_get (s, key, 0);
682 if (!str) {
683 return;
684 }
685 if (!*str) {
686 free (str);
687 return;
688 }
689 ut64 *nums = sdb_fmt_array_num (str);
690 free (str);
691 if (!nums) {
692 return;
693 }
694
695 qsort (nums + 1, (int)*nums, sizeof (ut64), int_cmp);
696
697 nstr = malloc (*nums + 1);
698 if (!nstr) {
699 free (nums);
700 return;
701 }
702 memset (nstr, 'q', *nums);
703 nstr[*nums] = '\0';
704
705 ret = sdb_fmt_tostr (nums + 1, nstr);
706 sdb_set_owned (s, key, ret, cas);
707
708 free (nstr);
709 free (nums);
710 return;
711 }
712