SDB_API void sdb_array_sort(Sdb *s, const char *key, ut32 cas) { char *nstr, *str, **strs; int lstr, j, i; str = sdb_get_len (s, key, &lstr, 0); if (!str) { return; } if (!*str) { free (str); return; } strs = sdb_fmt_array (str); for (i = 0; strs[i]; i++) { //nothing to see here } qsort (strs, i, sizeof (char*), cstring_cmp); nstr = str; for (i = 0; strs[i]; i++) { j = strlen (strs[i]); memcpy (nstr, strs[i], j); nstr += j; *(nstr++) = SDB_RS; } if (nstr > str) { *(--nstr) = '\0'; } else { *nstr = '\0'; } sdb_set_owned (s, key, str, cas); free (strs); }
SDB_API int sdb_array_delete(Sdb *s, const char *key, int idx, ut32 cas) { int i; char *p, *n, *str = sdb_get (s, key, 0); p = str; if (!str || !*str) { free (str); return 0; } if (idx < 0) { idx = sdb_alen (str); if (idx) idx--; } for (i = 0; i < idx; i++) { if ( (n = strchr (p, SDB_RS)) ) { p = n + 1; } else { free (str); return 0; } } n = strchr (p, SDB_RS); if (n) { memmove (p, n + 1, strlen (n)); } else { if (p != str) p--; // remove tailing SDB_RS *p = 0; p[1] = 0; } sdb_set_owned (s, key, str, cas); return 1; }
SDB_API int sdb_array_prepend (Sdb *s, const char *key, const char *val, ut32 cas) { int str_len = 0; ut32 kas = cas; const char *str = sdb_const_get_len (s, key, &str_len, &kas); if (!val || (cas && cas != kas)) { return 0; } cas = kas; if (str && *str) { int val_len = strlen (val); char *newval = malloc (str_len + val_len + 2); if (!newval) { return 0; } memcpy (newval, val, val_len); newval[val_len] = SDB_RS; memcpy (newval + val_len + 1, str, str_len); newval[str_len + val_len + 1] = 0; // TODO: optimize this because we already have allocated and strlened everything sdb_set_owned (s, key, newval, cas); } else { sdb_set (s, key, val, cas); } return 1; }
SDB_API bool sdb_array_append(Sdb *s, const char *key, const char *val, ut32 cas) { #if SLOW return sdb_array_set (s, key, -1, val, cas); #else int str_len = 0; ut32 kas = cas; const char *str = sdb_const_get_len (s, key, &str_len, &kas); if (!val || (cas && cas != kas)) { return false; } cas = kas; if (str && *str && str_len > 0) { int val_len = strlen (val); char *newval = malloc (str_len + val_len + 2); if (!newval) { return false; } memcpy (newval, str, str_len); newval[str_len] = SDB_RS; memcpy (newval+str_len+1, val, val_len); newval[str_len+val_len+1] = 0; sdb_set_owned (s, key, newval, cas); } else { sdb_set (s, key, val, cas); } return true; #endif }
SDB_API char *sdb_array_pop(Sdb *s, const char *key, ut32 *cas) { ut32 kas; char *end, *str = sdb_get (s, key, &kas); if (!str || !*str) { free (str); return NULL; } if (cas && *cas != kas) *cas = kas; #if PUSH_PREPENDS end = strchr (str, SDB_RS); if (end) { *end = 0; sdb_set (s, key, end+1, 0); } else { sdb_unset (s, key, 0); } return str; #else for (end = str+strlen (str)-1; end>str && *end!=SDB_RS; end--); if (*end==SDB_RS) *end++ = 0; sdb_set_owned (s, key, str, 0); // XXX: probably wrong return strdup (end); #endif }
SDB_API int sdb_array_add_sorted(Sdb *s, const char *key, const char *val, ut32 cas) { int lstr, lval, i, j; const char *str_e, *str_lp, *str_p, *str = sdb_const_get_len (s, key, &lstr, 0); char *nstr, *nstr_p, **vals; const char null = '\0'; if (!str || !*str) { str = &null; lstr = 0; } str_e = str + lstr; str_lp = str_p = str; if (!val || !*val) { return 1; } lval = strlen (val); vals = sdb_fmt_array (val); for (i = 0; vals[i]; i++) { /* empty */ } if (i > 1) { qsort (vals, i, sizeof (ut64*), cstring_cmp); } nstr_p = nstr = malloc (lstr + lval + 3); if (!nstr) { return 1; } for (i = 0; vals[i]; i++) { while (str_p < str_e) { if (astrcmp (vals[i], str_p) < 0) { break; } sdb_const_anext (str_p, &str_p); if (!str_p) { str_p = str_e; } } memcpy (nstr_p, str_lp, str_p - str_lp); nstr_p += str_p - str_lp; if (str_p == str_e && str_lp != str_e) { *(nstr_p++) = SDB_RS; } str_lp = str_p; j = strlen (vals[i]); memcpy (nstr_p, vals[i], j); nstr_p += j; *(nstr_p++) = SDB_RS; } if (str_lp < str_e) { memcpy (nstr_p, str_lp, str_e - str_lp); nstr_p += str_e - str_lp; *(nstr_p) = '\0'; } else { *(--nstr_p) = '\0'; } sdb_set_owned (s, key, nstr, cas); free (vals); return 0; }
SDB_API int sdb_array_set(Sdb *s, const char *key, int idx, const char *val, ut32 cas) { int lstr, lval, len; const char *usr, *str = sdb_const_get_len (s, key, &lstr, 0); char *ptr; if (!str || !*str) { return sdb_set (s, key, val, cas); } // XXX: should we cache sdb_alen value inside kv? len = sdb_alen (str); lstr--; if (idx < 0 || idx == len) { // append return sdb_array_insert (s, key, -1, val, cas); } lval = strlen (val); if (idx > len) { int ret, i, ilen = idx-len; char *newkey = malloc (ilen + lval + 1); if (!newkey) { return 0; } for (i = 0; i < ilen; i++) { newkey [i] = SDB_RS; } memcpy (newkey + i, val, lval + 1); ret = sdb_array_insert (s, key, -1, newkey, cas); free (newkey); return ret; } //lstr = strlen (str); ptr = (char*)Aindexof (str, idx); if (ptr) { int diff = ptr - str; char *nstr = malloc (lstr + lval + 2); if (!nstr) { return false; } ptr = nstr + diff; //memcpy (nstr, str, lstr+1); memcpy (nstr, str, diff); memcpy (ptr, val, lval + 1); usr = Aindexof (str, idx + 1); if (usr) { ptr[lval] = SDB_RS; strcpy (ptr + lval + 1, usr); } return sdb_set_owned (s, key, nstr, 0); } return 0; }
// TODO: done, but there's room for improvement SDB_API int sdb_array_insert(Sdb *s, const char *key, int idx, const char *val, ut32 cas) { int lnstr, lstr, lval; char *x, *ptr; const char *str = sdb_const_get_len (s, key, &lstr, 0); if (!str || !*str) { return sdb_set (s, key, val, cas); } lval = strlen (val); lstr--; //lstr = strlen (str); // we can optimize this by caching value len in memory . add sdb_const_get_size() x = malloc (lval + lstr + 2); if (idx == -1) { memcpy (x, str, lstr); x[lstr] = SDB_RS; memcpy (x+lstr+1, val, lval + 1); } else if (idx == 0) { memcpy (x, val, lval); x[lval] = SDB_RS; memcpy (x + lval + 1, str, lstr + 1); } else { char *nstr = malloc (lstr + 1); if (!nstr) { free (x); return false; } memcpy (nstr, str, lstr + 1); ptr = (char *)Aindexof (nstr, idx); if (ptr) { int lptr = (nstr+lstr+1)-ptr; *(ptr-1) = 0; lnstr = ptr-nstr-1; memcpy (x, nstr, lnstr); x[lnstr] = SDB_RS; memcpy (x + lnstr + 1, val, lval); x[lnstr + lval + 1] = SDB_RS; // TODO: this strlen hurts performance memcpy (x + lval + 2 + lnstr, ptr, lptr); //strlen (ptr)+1); free (nstr); } else { // this is not efficient free (nstr); free (x); // fallback for empty buckets return sdb_array_set (s, key, idx, val, cas); } } return sdb_set_owned (s, key, x, cas); }
SDB_API char *sdb_array_pop_tail(Sdb *s, const char *key, ut32 *cas) { ut32 kas; char *end, *str = sdb_get (s, key, &kas); if (!str || !*str) { free (str); return NULL; } if (cas && *cas != kas) *cas = kas; for (end = str + strlen (str) - 1; end > str && *end != SDB_RS; end--); if (*end == SDB_RS) *end++ = 0; sdb_set_owned (s, key, str, 0); // XXX: probably wrong return strdup (end); }
SDB_API void sdb_array_sort_num(Sdb *s, const char *key, ut32 cas) { char *ret, *nstr, *str; int lstr; ut64 *nums; str = sdb_get_len (s, key, &lstr, 0); if (!str) return; if (!*str) { free (str); return; } nums = sdb_fmt_array_num (str); qsort (nums + 1, (int)*nums, sizeof (ut64), int_cmp); nstr = str; memset (nstr, 'q', *nums); nstr += *nums; *nstr = '\0'; ret = sdb_fmt_tostr (nums + 1, str); sdb_set_owned (s, key, ret, cas); free (str); free (nums); return; }
// TODO: done, but there's room for improvement SDB_API int sdb_array_insert(Sdb *s, const char *key, int idx, const char *val, ut32 cas) { int lnstr, lstr; size_t lval; char *x, *ptr; const char *str = sdb_const_get_len (s, key, &lstr, 0); if (!str || !*str) { return sdb_set (s, key, val, cas); } lval = strlen (val); lstr--; // XXX: lstr is wrongly computed in sdb_const_get_with an off-by-one // we can optimize this by caching value len in memory . add // sdb_const_get_size() lstr = strlen (str); // When removing strlen this conversion should be checked size_t lstr_tmp = lstr; if (SZT_ADD_OVFCHK (lval, lstr_tmp) || SZT_ADD_OVFCHK (lval + lstr_tmp, 2)) { return false; } x = malloc (lval + lstr_tmp + 2); if (!x) { return false; } if (idx == -1) { memcpy (x, str, lstr); x[lstr] = SDB_RS; memcpy (x + lstr + 1, val, lval + 1); } else if (!idx) { memcpy (x, val, lval); x[lval] = SDB_RS; memcpy (x + lval + 1, str, lstr + 1); } else { char *nstr = malloc (lstr + 1); if (!nstr) { free (x); return false; } memcpy (nstr, str, lstr + 1); ptr = (char *)Aindexof (nstr, idx); if (ptr) { int lptr = (nstr + lstr + 1) - ptr; char *p_1 = ptr > nstr? ptr - 1: ptr; *p_1 = 0; lnstr = ptr - nstr - 1; memcpy (x, nstr, lnstr); x[lnstr] = SDB_RS; memcpy (x + lnstr + 1, val, lval); x[lnstr + lval + 1] = SDB_RS; // TODO: this strlen hurts performance memcpy (x + lval + 2 + lnstr, ptr, lptr); //strlen (ptr)+1); free (nstr); } else { // this is not efficient free (nstr); free (x); // fallback for empty buckets return sdb_array_set (s, key, idx, val, cas); } } return sdb_set_owned (s, key, x, cas); }
SDB_API int sdb_json_set (Sdb *s, const char *k, const char *p, const char *v, ut32 cas) { const char *beg[3]; const char *end[3]; int jslen, l, idx, len[3]; char *b, *str = NULL; const char *js; Rangstr rs; ut32 c; if (!s || !k) return 0; js = sdb_const_get_len (s, k, &jslen, &c); if (!js) { b = malloc (strlen (p)+strlen (v)+8); if (b) { int is_str = isstring (v); const char *q = is_str?"\"":""; sprintf (b, "{\"%s\":%s%s%s}", p, q, v, q); #if 0 /* disabled because it memleaks */ sdb_set_owned (s, k, b, cas); #else sdb_set (s, k, b, cas); free (b); #endif return 1; } return 0; } if (cas && c != cas) { return 0; } rs = json_get (js, p); if (!rs.p) { char *b = malloc (jslen+strlen(k)+strlen (v)+32); if (b) { int curlen, is_str = isstring (v); const char *q = is_str?"\"":""; const char *e = ""; // XX: or comma if (js[0] && js[1] != '}') e = ","; curlen = sprintf (b, "{\"%s\":%s%s%s%s", p, q, v, q, e); strcpy (b+curlen, js+1); // transfer ownership sdb_set_owned (s, k, b, cas); return 1; } // invalid json? return 0; } #define WLEN(x) (int)(size_t)(end[x]-beg[x]) beg[0] = js; end[0] = rs.p + rs.f; len[0] = WLEN (0); if (*v) { beg[1] = v; end[1] = v + strlen (v); len[1] = WLEN (1); } beg[2] = rs.p + rs.t; end[2] = js + jslen; len[2] = WLEN (2); // TODO: accelerate with small buffer in stack for small jsons if (*v) { int is_str = isstring (v); int msz = len[0]+len[1]+len[2]+strlen (v); if (msz<1) return 0; str = malloc (msz); if (!str) return 0; idx = len[0]; memcpy (str, beg[0], idx); if (is_str) { if (beg[2][0]!='"') { str[idx]='"'; idx++; } } else { if (beg[2][0]=='"') { idx--; } } l = len[1]; memcpy (str+idx, beg[1], l); idx += len[1]; if (is_str) { // TODO: add quotes if (beg[2][0]!='"') { str[idx]='"'; idx++; } } else { if (beg[2][0]=='"') { beg[2]++; } } l = len[2]; memcpy (str+idx, beg[2], l); str[idx+l] = 0; } else { int kidx; // DELETE KEY rs.f -= 2; kidx = findkey (&rs); len[0] = R_MAX (1, kidx-1); if (kidx==1) { if (beg[2][0]=='"') beg[2]++; beg[2]++; } str = malloc (len[0]+len[2]+1); if (!str) return 0; memcpy (str, beg[0], len[0]); if (!*beg[2]) beg[2]--; memcpy (str+len[0], beg[2], len[2]); str[len[0]+len[2]] = 0; } sdb_set_owned (s, k, str, cas); return 1; }