Move SDB into RzUtil

* Make SDB code SPDX compliant
* Use some RzUtil functions inside SDB

util: compile natively without any extra dependency
This commit is contained in:
Riccardo Schirone 2022-07-01 22:40:31 +02:00 committed by Riccardo Schirone
parent 35cf52eb14
commit faac2436b1
63 changed files with 11513 additions and 152 deletions

View file

@ -255,3 +255,11 @@ License: LGPL-3.0-only
Files: librz/reg/p/x86-linux.regs
Copyright: 2009 pancake <pancake@nopcode.org>
License: LGPL-3.0-only
Files: librz/util/sdb/src/json/README
Copyright: pancake <pancake@nopcode.org>
License: MIT
Files: librz/util/sdb/src/json/test.json
Copyright: pancake <pancake@nopcode.org>
License: MIT

8
LICENSES/CC-PDDC.txt Normal file
View file

@ -0,0 +1,8 @@
The person or persons who have associated work with this document (the "Dedicator" or "Certifier") hereby either (a) certifies that, to the best of his knowledge, the work of authorship identified is in the public domain of the country from which the work is published, or (b) hereby dedicates whatever copyright the dedicators holds in the work of authorship identified below (the "Work") to the public domain. A certifier, moreover, dedicates any copyright interest he may have in the associated work, and for these purposes, is described as a "dedicator" below.
A certifier has taken reasonable steps to verify the copyright status of this work. Certifier recognizes that his good faith efforts may not shield him from liability if in fact the work certified is not in the public domain.
Dedicator makes this dedication for the benefit of the public at large and to the detriment of the Dedicator's heirs and successors. Dedicator intends this dedication to be an overt act of relinquishment in perpetuity of all present and future rights under copyright law, whether vested or contingent, in the Work. Dedicator understands that such relinquishment of all rights includes the relinquishment of all rights to enforce (by lawsuit or otherwise) those copyrights in the Work.
Dedicator recognizes that, once placed in the public domain, the Work may be freely reproduced, distributed, transmitted, used, modified, built upon, or otherwise exploited by anyone for any purpose, commercial or non-commercial, and in any way, including by methods that have not yet been invented or conceived.

View file

@ -1,3 +1,4 @@
subdir('sdb/src')
rz_util_sources = [
'alloc.c',
@ -81,7 +82,7 @@ rz_util_sources = [
'x509.c',
]
rz_util_deps = [ldl, lrt, mth, th, utl, sdb_dep] + platform_deps
rz_util_deps = [ldl, lrt, mth, th, utl] + platform_deps
if zlib_dep.found()
rz_util_deps += [zlib_dep]
endif
@ -92,6 +93,13 @@ if ['freebsd', 'netbsd', 'haiku', 'dragonfly'].contains(host_machine.system())
# backtrace_symbols_fd requires -lexecinfo
rz_util_deps += [cc.find_library('execinfo', static: is_static_build)]
endif
if meson.is_cross_build()
execinfo_native = disabler()
if ['freebsd', 'netbsd', 'haiku', 'dragonfly'].contains(build_machine.system())
# backtrace_symbols_fd requires -lexecinfo
execinfo_native = cc_native.find_library('execinfo', static: is_static_build)
endif
endif
if sys_openssl.found()
rz_util_deps += [sys_openssl]
@ -100,8 +108,10 @@ else
rz_util_sources += ['big.c']
endif
rz_util = library('rz_util', rz_util_sources,
include_directories: platform_inc,
rz_util_includes = [platform_inc, include_directories('sdb/src', 'sdb')]
rz_util = library('rz_util', rz_util_sources, libsdb_sources,
include_directories: rz_util_includes,
dependencies: rz_util_deps,
install: true,
implicit_include_directories: false,
@ -114,13 +124,61 @@ rz_util = library('rz_util', rz_util_sources,
rz_util_dep = declare_dependency(
link_with: rz_util,
include_directories: platform_inc,
dependencies: [
sdb_dep.partial_dependency(includes: true),
]
include_directories: rz_util_includes,
)
meson.override_dependency('rz_util', rz_util_dep)
if meson.is_cross_build()
# this is a bit messy, it duplicates most of the code required to get the
# regular dependencies but for the build_system. This is required to build
# sdb_native, used at build time to compile .sdb.txt files into .sdb ones.
platform_native_deps = []
if build_machine.system() == 'windows'
platform_native_deps = [
cc_native.find_library('ws2_32'),
cc_native.find_library('wininet'),
cc_native.find_library('psapi'),
]
endif
rz_util_native_deps = [ldl_native, lrt_native, mth_native, th_native, utl_native] + platform_native_deps
if execinfo_native.found()
rz_util_native_deps += [execinfo_native]
endif
# do not use external libs, we do not need them
userconf_native.set10('HAVE_LIB_MAGIC', false)
userconf_native.set10('USE_LIB_MAGIC', false)
userconf_native.set10('HAVE_LIB_XXHASH', false)
userconf_native.set10('USE_LIB_XXHASH', false)
userconf_native.set10('HAVE_OPENSSL', false)
userconf_native.set10('HAVE_LIBUV', false)
userconf_native.set10('HAVE_LZMA', false)
userconf_native.set10('HAVE_ZLIB', false)
rz_userconf_native_h = configure_file(
input: rz_userconf_h_in,
output: 'rz_userconf.h',
configuration: userconf_native,
install_dir: rizin_incdir
)
rz_util_native_includes = [include_directories('.'), rz_util_includes]
rz_util_native = static_library('rz_util_native', rz_util_sources, libsdb_sources,
include_directories: rz_util_native_includes,
dependencies: rz_util_native_deps,
implicit_include_directories: false,
install_rpath: rpath_lib,
install: false,
native: true,
)
rz_util_native_dep = declare_dependency(
link_with: rz_util_native,
include_directories: rz_util_native_includes,
)
else
rz_util_native_dep = rz_util_dep
endif
pkgconfig_mod.generate(rz_util,
subdirs: ['librz', 'librz/sdb'],
version: rizin_version,
@ -149,3 +207,18 @@ if not is_static_libs_only
configuration: conf,
)
endif
sdb_exe = executable('sdb_native', 'sdb/src/main.c',
dependencies: rz_util_native_dep,
install: false,
native: true,
implicit_include_directories: false,
install_rpath: rpath_exe,
)
sdb_gen_cmd = [
sdb_exe,
'@OUTPUT@',
'==',
'@INPUT@'
]

88
librz/util/sdb/README.md Normal file
View file

@ -0,0 +1,88 @@
SDB (string database)
=====================
sdb is a simple string key/value database based on djb's cdb
disk storage and supports JSON and arrays introspection.
Author
------
pancake <pancake@nopcode.org>
Contains
--------
* namespaces (multiple sdb paths)
* atomic database sync (never corrupted)
* commandline frontend for sdb databases
* arrays support (syntax sugar)
* json parser/getter (js0n.c)
Rips
----
* disk storage based on cdb code
* linked lists from rizin api
Compilation
-----------
SDB requires [Meson](https://mesonbuild.com/) and [Ninja](https://ninja-build.org/) buildsystems to be built:
```
meson build
ninja -C build
```
Changes
-------
I have modified cdb code a little to create smaller databases and
be memory leak free in order to use it from a library.
The sdb's cdb database format is 10% smaller than the original
one. This is because keylen and valuelen are encoded in 4 bytes:
1 for the key length and 3 for the value length.
In a test case, a 4.3MB cdb database takes only 3.9MB after this
file format change.
Usage example
-------------
Let's create a database!
```
$ sdb d hello=world
$ sdb d hello
world
```
Using arrays (>=0.6):
```
$ sdb - '[]list=1,2' '[0]list' '[0]list=foo' '[]list' '[+1]list=bar'
1
foo
2
```
Let's play with json:
```
$ sdb d g='{"foo":1,"bar":{"cow":3}}'
$ sdb d g:bar.cow
3
$ sdb - user='{"id":123}' user:id=99 user:id
99
```
Using the commandline without any disk database:
```
$ sdb - foo=bar foo a=3 +a -a
bar
4
3
```
```
$ sdb -
foo=bar
foo
bar
a=3
+a
4
-a
3
```
Remove the database
```
$ rm -f d
```

715
librz/util/sdb/src/array.c Normal file
View file

@ -0,0 +1,715 @@
// SPDX-FileCopyrightText: 2011-2018 pancake <pancake@nopcode.org>
// SPDX-License-Identifier: MIT
#include "sdb.h"
#include <limits.h>
// TODO: Push should always prepend. do not make this configurable
#define PUSH_PREPENDS 1
// TODO: missing num_{inc/dec} functions
static const char *Aindexof(const char *str, int idx) {
int len = 0;
const char *n, *p = str;
for (len = 0;; len++) {
if (len == idx) {
return p;
}
if (!(n = strchr(p, SDB_RS))) {
break;
}
p = n + 1;
}
return NULL;
}
static int astrcmp(const char *a, const char *b) {
register char va = *a;
register char vb = *b;
for (;;) {
if (va == '\0' || va == SDB_RS) {
if (vb == '\0' || vb == SDB_RS) {
return 0;
}
return -1;
}
if (vb == '\0' || vb == SDB_RS) {
return 1;
}
if (va != vb) {
return (va > vb) ? 1 : -1;
}
va = *(++a);
vb = *(++b);
}
}
static inline int cstring_cmp(const void *a, const void *b) {
const char **va = (const char **)a;
const char **vb = (const char **)b;
return astrcmp(*va, *vb);
}
static inline int int_cmp(const void *a, const void *b) {
const ut64 va = *(const ut64 *)a;
const ut64 vb = *(const ut64 *)b;
if (va > vb) {
return 1;
}
if (va < vb) {
return -1;
}
return 0;
}
RZ_API ut64 sdb_array_get_num(Sdb *s, const char *key, int idx, ut32 *cas) {
int i;
const char *n, *str = sdb_const_get(s, key, cas);
if (!str || !*str) {
return 0LL;
}
if (idx) {
for (i = 0; i < idx; i++) {
n = strchr(str, SDB_RS);
if (!n) {
return 0LL;
}
str = n + 1;
}
}
return sdb_atoi(str);
}
RZ_API char *sdb_array_get(Sdb *s, const char *key, int idx, ut32 *cas) {
const char *str = sdb_const_get(s, key, cas);
const char *p = str;
char *o, *n;
int i, len;
if (!str || !*str) {
return NULL;
}
if (idx < 0) {
int len = sdb_alen(str);
idx = -idx;
if (idx > len) {
return NULL;
}
idx = len - idx;
}
if (!idx) {
n = strchr(str, SDB_RS);
if (!n) {
return strdup(str);
}
len = n - str;
o = malloc(len + 1);
if (!o) {
return NULL;
}
memcpy(o, str, len);
o[len] = 0;
return o;
}
for (i = 0; i < idx; i++) {
n = strchr(p, SDB_RS);
if (!n)
return NULL;
p = n + 1;
}
n = strchr(p, SDB_RS);
if (!n) {
return strdup(p);
}
len = n - p;
o = malloc(len + 1);
if (o) {
memcpy(o, p, len);
o[len] = 0;
return o;
}
return NULL;
}
RZ_API int sdb_array_insert_num(Sdb *s, const char *key, int idx, ut64 val,
ut32 cas) {
char valstr[64];
return sdb_array_insert(s, key, idx,
sdb_itoa(val, valstr, SDB_NUM_BASE), cas);
}
// TODO: done, but there's room for improvement
RZ_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);
}
RZ_API int sdb_array_set_num(Sdb *s, const char *key, int idx, ut64 val,
ut32 cas) {
char valstr[SDB_NUM_BUFSZ];
return sdb_array_set(s, key, idx, sdb_itoa(val, valstr, SDB_NUM_BASE),
cas);
}
RZ_API int sdb_array_add_num(Sdb *s, const char *key, ut64 val, ut32 cas) {
char buf[SDB_NUM_BUFSZ];
char *v = sdb_itoa(val, buf, SDB_NUM_BASE);
if (!sdb_array_contains(s, key, v, NULL)) {
if (val < 256) {
char *v = sdb_itoa(val, buf, 10);
return sdb_array_add(s, key, v, cas);
}
}
return sdb_array_add(s, key, v, cas);
}
// XXX: index should be supressed here? if its a set we shouldnt change the index
RZ_API int sdb_array_add(Sdb *s, const char *key, const char *val, ut32 cas) {
if (sdb_array_contains(s, key, val, NULL)) {
return 0;
}
return sdb_array_insert(s, key, -1, val, cas);
}
RZ_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;
}
str_p = sdb_const_anext(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;
}
RZ_API int sdb_array_add_sorted_num(Sdb *s, const char *key, ut64 val,
ut32 cas) {
int i;
char valstr[SDB_NUM_BUFSZ];
const char *str = sdb_const_get(s, key, 0);
const char *n = str;
if (!str || !*str) {
return sdb_set(s, key, sdb_itoa(val, valstr, SDB_NUM_BASE), cas);
}
for (i = 0; n; i++) {
if (val <= sdb_atoi(n)) {
break;
}
n = sdb_const_anext(n);
}
return sdb_array_insert_num(s, key, n ? i : -1, val, cas);
}
RZ_API int sdb_array_unset(Sdb *s, const char *key, int idx, ut32 cas) {
return sdb_array_set(s, key, idx, "", cas);
}
RZ_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
}
RZ_API bool sdb_array_append_num(Sdb *s, const char *key, ut64 val, ut32 cas) {
return sdb_array_set_num(s, key, -1, val, cas);
}
RZ_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;
}
RZ_API int sdb_array_remove_num(Sdb *s, const char *key, ut64 val, ut32 cas) {
const char *n, *p, *str = sdb_const_get(s, key, 0);
int idx = 0;
ut64 num;
if (str) {
for (p = str;; idx++) {
num = sdb_atoi(p);
if (num == val) {
return sdb_array_delete(s, key, idx, cas);
}
n = strchr(p, SDB_RS);
if (!n) {
break;
}
p = n + 1;
}
}
return 0;
}
/* get array index of given value */
RZ_API int sdb_array_indexof(Sdb *s, const char *key, const char *val,
ut32 cas) {
const char *str = sdb_const_get(s, key, 0);
const char *n, *p = str;
int i;
for (i = 0;; i++) {
if (!p) {
break;
}
if (!astrcmp(p, val)) {
return i;
}
n = strchr(p, SDB_RS);
if (!n)
break;
p = n + 1;
}
return -1;
}
// previously named del_str... pair with _add
RZ_API int sdb_array_remove(Sdb *s, const char *key, const char *val,
ut32 cas) {
const char *str = sdb_const_get(s, key, 0);
const char *n, *p = str;
int idx;
if (p) {
for (idx = 0;; idx++) {
if (!astrcmp(p, val)) {
return sdb_array_delete(s, key, idx, cas);
}
n = strchr(p, SDB_RS);
if (!n) {
break;
}
p = n + 1;
}
}
return 0;
}
RZ_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;
}
// XXX Doesnt work if numbers are stored in different base
RZ_API bool sdb_array_contains_num(Sdb *s, const char *key, ut64 num, ut32 *cas) {
char val[SDB_NUM_BUFSZ];
char *nval = sdb_itoa(num, val, SDB_NUM_BASE);
return sdb_array_contains(s, key, nval, cas);
}
RZ_API bool sdb_array_contains(Sdb *s, const char *key, const char *val, ut32 *cas) {
if (!s || !key || !val) {
return false;
}
const char *next, *ptr = sdb_const_get(s, key, cas);
if (ptr && *ptr) {
size_t vlen = strlen(val);
while (1) {
next = strchr(ptr, SDB_RS);
size_t len = next ? (size_t)(next - ptr) : strlen(ptr);
if (len == vlen && !memcmp(ptr, val, len)) {
return true;
}
if (!next) {
break;
}
ptr = next + 1;
}
}
return false;
}
RZ_API int sdb_array_size(Sdb *s, const char *key) {
return sdb_alen(sdb_const_get(s, key, 0));
}
// NOTE: ignore empty buckets
RZ_API int sdb_array_length(Sdb *s, const char *key) {
return sdb_alen_ignore_empty(sdb_const_get(s, key, 0));
}
RZ_API int sdb_array_push_num(Sdb *s, const char *key, ut64 num, ut32 cas) {
char buf[SDB_NUM_BUFSZ], *n = sdb_itoa(num, buf, SDB_NUM_BASE);
return sdb_array_push(s, key, n, cas);
}
RZ_API bool sdb_array_push(Sdb *s, const char *key, const char *val, ut32 cas) {
#if PUSH_PREPENDS
return sdb_array_prepend(s, key, val, cas);
#else
return sdb_array_append(s, key, val, cas);
#endif
}
RZ_API bool sdb_array_prepend_num(Sdb *s, const char *key, ut64 num, ut32 cas) {
char buf[SDB_NUM_BUFSZ];
char *n = sdb_itoa(num, buf, SDB_NUM_BASE);
return sdb_array_push(s, key, n, cas);
}
RZ_API bool sdb_array_prepend(Sdb *s, const char *key, const char *val, ut32 cas) {
if (!s || !key || !val) {
return false;
}
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) {
int val_len = strlen(val);
char *newval = malloc(str_len + val_len + 2);
if (!newval) {
return false;
}
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 true;
}
RZ_API ut64 sdb_array_pop_num(Sdb *s, const char *key, ut32 *cas) {
ut64 ret;
char *a = sdb_array_pop(s, key, cas);
if (!a) {
if (cas) {
*cas = UT32_MAX; // invalid
}
return UT64_MAX;
}
if (cas) {
*cas = 0;
}
ret = sdb_atoi(a);
free(a);
return ret;
}
RZ_API char *sdb_array_pop(Sdb *s, const char *key, ut32 *cas) {
#if PUSH_PREPENDS
return sdb_array_pop_head(s, key, cas);
#else
return sdb_array_pop_tail(s, key, cas);
#endif
}
RZ_API char *sdb_array_pop_head(Sdb *s, const char *key, ut32 *cas) {
// remove last element in
ut32 kas;
char *end, *str = sdb_get(s, key, &kas);
if (!str || !*str) {
free(str);
return NULL;
}
if (cas && *cas != kas) {
*cas = kas;
}
end = strchr(str, SDB_RS);
if (end) {
*end = 0;
sdb_set(s, key, end + 1, 0);
} else {
sdb_unset(s, key, 0);
}
return str;
}
RZ_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--) {
// nothing to see here
}
if (*end == SDB_RS) {
*end++ = 0;
}
sdb_set_owned(s, key, str, 0);
// XXX: probably wrong
return strdup(end);
}
RZ_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);
}
RZ_API void sdb_array_sort_num(Sdb *s, const char *key, ut32 cas) {
char *ret, *nstr;
char *str = sdb_get(s, key, 0);
if (!str) {
return;
}
if (!*str) {
free(str);
return;
}
ut64 *nums = sdb_fmt_array_num(str);
free(str);
if (!nums) {
return;
}
qsort(nums + 1, (int)*nums, sizeof(ut64), int_cmp);
nstr = malloc(*nums + 1);
if (!nstr) {
free(nums);
return;
}
memset(nstr, 'q', *nums);
nstr[*nums] = '\0';
ret = sdb_fmt_tostr(nums + 1, nstr);
sdb_set_owned(s, key, ret, cas);
free(nstr);
free(nums);
return;
}

View file

@ -0,0 +1,69 @@
// SPDX-FileCopyrightText: 2011-2016 pancake <pancake@nopcode.org>
// SPDX-License-Identifier: MIT
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <rz_util/rz_base64.h>
#include "sdb.h"
RZ_API void sdb_encode_raw(char *bout, const ut8 *bin, int len) {
rz_base64_encode(bout, bin, len);
}
RZ_API int sdb_decode_raw(ut8 *bout, const char *bin, int len) {
return rz_base64_decode(bout, bin, len);
}
RZ_API char *sdb_encode(const ut8 *bin, int len) {
char *out;
if (!bin) {
return NULL;
}
if (len < 0) {
len = strlen((const char *)bin);
}
if (!len) {
return strdup("");
}
out = calloc(8 + (len * 2), sizeof(char));
if (!out) {
return NULL;
}
sdb_encode_raw(out, bin, len);
return out;
}
RZ_API ut8 *sdb_decode(const char *in, int *len) {
ut8 *out;
ut32 size;
int olen, ilen;
if (len) {
*len = 0;
}
if (!in) {
return NULL;
}
ilen = strlen(in);
if (!ilen) {
return NULL;
}
size = (ilen * 3) + 16;
if (size < (ut32)ilen) {
return NULL;
}
out = calloc(1, size);
if (!out) {
return NULL;
}
olen = sdb_decode_raw(out, in, ilen);
if (!olen) {
free(out);
return NULL;
}
out[olen] = 0;
if (len) {
*len = olen;
}
return out;
}

View file

@ -0,0 +1,61 @@
// SPDX-FileCopyrightText: unknown
// SPDX-License-Identifier: CC-PDDC
#include "buffer.h"
void buffer_init(buffer *s, BufferOp op, int fd, char *buf, ut32 len) {
s->x = buf;
s->fd = fd;
s->op = op;
s->p = 0;
s->n = len;
}
static int allwrite(BufferOp op, int fd, const char *buf, ut32 len) {
ut32 w;
while (len > 0) {
w = op(fd, buf, len);
if (w != len) {
return 0;
}
buf += w;
len -= w;
}
return 1;
}
int buffer_flush(buffer *s) {
int p = s->p;
if (!p) {
return 1;
}
s->p = 0;
return allwrite(s->op, s->fd, s->x, p);
}
int buffer_putalign(buffer *s, const char *buf, ut32 len) {
ut32 n;
if (!s || !s->x || !buf) {
return 0;
}
while (len > (n = s->n - s->p)) {
memcpy(s->x + s->p, buf, n);
s->p += n;
buf += n;
len -= n;
if (!buffer_flush(s)) {
return 0;
}
}
/* now len <= s->n - s->p */
memcpy(s->x + s->p, buf, len);
s->p += len;
return 1;
}
int buffer_putflush(buffer *s, const char *buf, ut32 len) {
if (!buffer_flush(s)) {
return 0;
}
return allwrite(s->op, s->fd, buf, len);
}

View file

@ -0,0 +1,67 @@
// SPDX-FileCopyrightText: unknown
// SPDX-License-Identifier: CC-PDDC
#ifndef BUFFER_H
#define BUFFER_H
#include <rz_types.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef int (*BufferOp)(int, const char *, int);
typedef struct buffer {
char *x;
unsigned int p;
unsigned int n;
int fd;
BufferOp op;
} buffer;
#define BUFFER_INIT(op, fd, buf, len) \
{ (buf), 0, (len), (fd), (op) }
#define BUFFER_INSIZE 8192
#define BUFFER_OUTSIZE 8192
extern void buffer_init(buffer *, BufferOp, int, char *, unsigned int);
extern int buffer_flush(buffer *);
extern int buffer_put(buffer *, const char *, unsigned int);
extern int buffer_putalign(buffer *, const char *, unsigned int);
extern int buffer_putflush(buffer *, const char *, unsigned int);
#define buffer_PUTC(s, c) \
(((s)->n != (s)->p) \
? ((s)->x[(s)->p++] = (c), 0) \
: buffer_put((s), &(c), 1))
extern int buffer_get(buffer *, char *, unsigned int);
extern int buffer_bget(buffer *, char *, unsigned int);
extern int buffer_feed(buffer *);
extern char *buffer_peek(buffer *);
extern void buffer_seek(buffer *, unsigned int);
#define buffer_PEEK(s) ((s)->x + (s)->n)
#define buffer_SEEK(s, len) (((s)->p -= (len)), ((s)->n += (len)))
#define buffer_GETC(s, c) \
(((s)->p > 0) \
? (*(c) = (s)->x[(s)->n], buffer_SEEK((s), 1), 1) \
: buffer_get((s), (c), 1))
extern int buffer_copy(buffer *, buffer *);
extern buffer *buffer_0;
extern buffer *buffer_0small;
extern buffer *buffer_1;
extern buffer *buffer_1small;
extern buffer *buffer_2;
#ifdef __cplusplus
}
#endif
#endif

194
librz/util/sdb/src/cdb.c Normal file
View file

@ -0,0 +1,194 @@
// SPDX-FileCopyrightText: D. J. Bernstein
// SPDX-FileCopyrightText: 2014-2016 pancake <pancake@nopcode.org>
// SPDX-License-Identifier: CC-PDDC
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "cdb.h"
#if HAVE_HEADER_SYS_MMAN_H
#include <sys/mman.h>
#endif
#include "sdb_private.h"
/* XXX: this code must be rewritten . too slow */
bool cdb_getkvlen(struct cdb *c, ut32 *klen, ut32 *vlen, ut32 pos) {
ut8 buf[4] = { 0 };
*klen = *vlen = 0;
if (!cdb_read(c, (char *)buf, sizeof(buf), pos)) {
return false;
}
*klen = (ut32)buf[0];
*vlen = (ut32)(buf[1] | ((ut32)buf[2] << 8) | ((ut32)buf[3] << 16));
if (*vlen > CDB_MAX_VALUE) {
*vlen = CDB_MAX_VALUE; // untaint value for coverity
return false;
}
return true;
}
void cdb_free(struct cdb *c) {
if (!c->map) {
return;
}
#if HAVE_HEADER_SYS_MMAN_H
(void)munmap(c->map, c->size);
#else
free(c->map);
#endif
c->map = NULL;
}
void cdb_findstart(struct cdb *c) {
c->loop = 0;
#if !HAVE_HEADER_SYS_MMAN_H
if (c->fd != -1) {
lseek(c->fd, 0, SEEK_SET);
}
#endif
}
bool cdb_init(struct cdb *c, int fd) {
struct stat st;
if (fd != c->fd && c->fd != -1) {
close(c->fd);
}
c->fd = fd;
cdb_findstart(c);
if (fd != -1 && !fstat(fd, &st) && st.st_size > 4 && st.st_size != (off_t)UT64_MAX) {
#if HAVE_HEADER_SYS_MMAN_H
char *x = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
if (x == MAP_FAILED) {
eprintf("Cannot mmap %d\n", (int)st.st_size);
return false;
}
if (c->map) {
munmap(c->map, c->size);
}
#else
char *x = calloc(1, st.st_size);
if (!x) {
eprintf("Cannot malloc %d\n", (int)st.st_size);
return false;
}
/* TODO: read by chunks instead of a big huge syscall */
if (read(fd, x, st.st_size) != st.st_size) {
/* handle read error */
}
free(c->map);
#endif
c->map = x;
c->size = st.st_size;
return true;
}
c->map = NULL;
c->size = 0;
return false;
}
bool cdb_read(struct cdb *c, char *buf, ut32 len, ut32 pos) {
if (c->map) {
if ((pos > c->size) || (c->size - pos < len)) {
return false;
}
if (!buf) {
return false;
}
memcpy(buf, c->map + pos, len);
return true;
}
if (c->fd == -1 || !seek_set(c->fd, pos)) {
return false;
}
while (len > 0) {
int r = (int)read(c->fd, buf, len);
if (r < 1 || (ut32)r != len) {
return false;
}
buf += r;
len -= r;
}
return true;
}
static int match(struct cdb *c, const char *key, ut32 len, ut32 pos) {
char buf[32];
const size_t szb = sizeof buf;
while (len > 0) {
int n = (szb > len) ? len : szb;
if (!cdb_read(c, buf, n, pos)) {
return -1;
}
if (memcmp(buf, key, n)) {
return 0;
}
pos += n;
key += n;
len -= n;
}
return 1;
}
int cdb_findnext(struct cdb *c, ut32 u, const char *key, ut32 len) {
char buf[8];
ut32 pos;
int m;
len++;
if (c->fd == -1) {
return -1;
}
c->hslots = 0;
if (!c->loop) {
const int bufsz = ((u + 1) & 0xFF) ? sizeof(buf) : sizeof(buf) / 2;
if (!cdb_read(c, buf, bufsz, (u << 2) & 1023)) {
return -1;
}
/* hslots = (hpos_next - hpos) / 8 */
ut32_unpack(buf, &c->hpos);
if (bufsz == sizeof(buf)) {
ut32_unpack(buf + 4, &pos);
} else {
pos = c->size;
}
if (pos < c->hpos) {
return -1;
}
c->hslots = (pos - c->hpos) / (2 * sizeof(ut32));
if (!c->hslots) {
return 0;
}
c->khash = u;
u = ((u >> 8) % c->hslots) << 3;
c->kpos = c->hpos + u;
}
while (c->loop < c->hslots) {
if (!cdb_read(c, buf, sizeof(buf), c->kpos)) {
return 0;
}
ut32_unpack(buf + 4, &pos);
if (!pos) {
return 0;
}
c->loop++;
c->kpos += sizeof(buf);
if (c->kpos == c->hpos + (c->hslots << 3)) {
c->kpos = c->hpos;
}
ut32_unpack(buf, &u);
if (u == c->khash) {
if (!cdb_getkvlen(c, &u, &c->dlen, pos) || !u) {
return -1;
}
if (u == len) {
if ((m = match(c, key, len, pos + KVLSZ)) == -1) {
return 0;
}
if (m == 1) {
c->dpos = pos + KVLSZ + len;
return 1;
}
}
}
}
return 0;
}

48
librz/util/sdb/src/cdb.h Normal file
View file

@ -0,0 +1,48 @@
// SPDX-FileCopyrightText: D. J. Bernstein <djb@cr.yp.to>
// SPDX-License-Identifier: CC-PDDC
#ifndef CDB_H
#define CDB_H
#include <string.h>
#include <rz_types.h>
#ifdef __cplusplus
extern "C" {
#endif
#define KVLSZ 4
#define CDB_MAX_KEY 0xff
#define CDB_MAX_VALUE 0xffffff
#define CDB_HASHSTART 5381
struct cdb {
char *map; /* 0 if no map is available */
int fd; /* filedescriptor */
ut32 size; /* initialized if map is nonzero */
ut32 loop; /* number of hash slots searched under this key */
ut32 khash; /* initialized if loop is nonzero */
ut32 kpos; /* initialized if loop is nonzero */
ut32 hpos; /* initialized if loop is nonzero */
ut32 hslots; /* initialized if loop is nonzero */
ut32 dpos; /* initialized if cdb_findnext() returns 1 */
ut32 dlen; /* initialized if cdb_findnext() returns 1 */
};
/* TODO THIS MUST GTFO! */
bool cdb_getkvlen(struct cdb *db, ut32 *klen, ut32 *vlen, ut32 pos);
void cdb_free(struct cdb *);
bool cdb_init(struct cdb *, int fd);
void cdb_findstart(struct cdb *);
bool cdb_read(struct cdb *, char *, unsigned int, ut32);
int cdb_findnext(struct cdb *, ut32 u, const char *, ut32);
#define cdb_datapos(c) ((c)->dpos)
#define cdb_datalen(c) ((c)->dlen)
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,196 @@
// SPDX-FileCopyrightText: D. J. Bernstein <djb@cr.yp.to>
// SPDX-License-Identifier: CC-PDDC
#include <stdio.h>
#include <stdlib.h>
#include "sdb.h"
#include "cdb.h"
#include "cdb_make.h"
#include "sdb_private.h"
#if __APPLE__
// for MAC_OS_X_VERSION_10_6
#include <AvailabilityMacros.h>
#endif
#define ALIGNMENT sizeof(void *)
char *cdb_alloc(ut32 n) {
#if __APPLE__ && defined(MAC_OS_X_VERSION_10_6)
void *ret = NULL;
return posix_memalign(&ret, ALIGNMENT, n) ? NULL : ret;
#elif __WINDOWS__ && !__CYGWIN__
return _aligned_malloc(n, ALIGNMENT);
#else
return malloc(n);
#endif
}
void cdb_alloc_free(void *x) {
#if __WINDOWS__ && !__CYGWIN__
_aligned_free(x);
#else
free(x);
#endif
}
int cdb_make_start(struct cdb_make *c, int fd) {
int i;
c->head = 0;
c->split = 0;
c->hash = 0;
c->numentries = 0;
c->fd = fd;
c->pos = sizeof(c->final);
buffer_init(&c->b, (BufferOp)write, fd, c->bspace, sizeof(c->bspace));
c->memsize = 1;
for (i = 0; i < 256; i++) {
c->count[i] = 0;
}
return seek_set(fd, c->pos);
}
static inline int incpos(struct cdb_make *c, ut32 len) {
ut32 newpos = c->pos + len;
if (newpos < len) {
return 0;
}
c->pos = newpos;
return 1;
}
#define R_ANEW(x) (x *)cdb_alloc(sizeof(x))
int cdb_make_addend(struct cdb_make *c, ut32 keylen, ut32 datalen, ut32 h) {
ut32 u;
struct cdb_hplist *head = c->head;
if (!head || (head->num >= CDB_HPLIST)) {
if (!(head = R_ANEW(struct cdb_hplist))) {
return 0;
}
head->num = 0;
head->next = c->head;
c->head = head;
}
head->hp[head->num].h = h;
head->hp[head->num].p = c->pos;
head->num++;
c->numentries++;
c->count[255 & h]++;
u = c->count[255 & h] * 2;
if (u > c->memsize) {
c->memsize = u;
}
return incpos(c, KVLSZ + keylen + datalen);
}
static int pack_kvlen(ut8 *buf, ut32 klen, ut32 vlen) {
if (klen > SDB_CDB_MAX_KEY) {
return 0; // 0xff = 254 chars+trailing zero
}
if (vlen >= SDB_CDB_MAX_VALUE) {
return 0;
}
buf[0] = (ut8)klen;
buf[1] = (ut8)((vlen)&0xff);
buf[2] = (ut8)((vlen >> 8) & 0xff);
buf[3] = (ut8)((vlen >> 16) & 0xff);
return 1;
}
int cdb_make_addbegin(struct cdb_make *c, ut32 keylen, ut32 datalen) {
ut8 buf[KVLSZ];
if (!pack_kvlen(buf, keylen, datalen)) {
return 0;
}
return buffer_putalign(&c->b, (const char *)buf, KVLSZ);
}
int cdb_make_add(struct cdb_make *c, const char *key, ut32 keylen, const char *data, ut32 datalen) {
/* add tailing \0 to allow mmap to work later */
keylen++;
datalen++;
if (!cdb_make_addbegin(c, keylen, datalen)) {
return 0;
}
if (!buffer_putalign(&c->b, key, keylen)) {
return 0;
}
if (!buffer_putalign(&c->b, data, datalen)) {
return 0;
}
return cdb_make_addend(c, keylen, datalen, sdb_hash(key));
}
int cdb_make_finish(struct cdb_make *c) {
int i;
char buf[8];
struct cdb_hp *hp;
struct cdb_hplist *x, *n;
ut32 len, u, memsize, count, where;
memsize = c->memsize + c->numentries;
if (memsize > (UT32_MAX / sizeof(struct cdb_hp))) {
return 0;
}
c->split = (struct cdb_hp *)cdb_alloc(memsize * sizeof(struct cdb_hp));
if (!c->split) {
return 0;
}
c->hash = c->split + c->numentries;
for (u = i = 0; i < 256; i++) {
u += c->count[i]; /* bounded by numentries, so no overflow */
c->start[i] = u;
}
for (x = c->head; x; x = x->next) {
i = x->num;
while (i--) {
c->split[--c->start[255 & x->hp[i].h]] = x->hp[i];
}
}
for (i = 0; i < 256; i++) {
count = c->count[i];
len = count << 1;
ut32_pack(c->final + 4 * i, c->pos);
for (u = 0; u < len; u++) {
c->hash[u].h = c->hash[u].p = 0;
}
hp = c->split + c->start[i];
for (u = 0; u < count; u++) {
where = (hp->h >> 8) % len;
while (c->hash[where].p) {
if (++where == len) {
where = 0;
}
}
c->hash[where] = *hp++;
}
for (u = 0; u < len; u++) {
ut32_pack(buf, c->hash[u].h);
ut32_pack(buf + 4, c->hash[u].p);
if (!buffer_putalign(&c->b, buf, 8)) {
return 0;
}
if (!incpos(c, 8)) {
return 0;
}
}
}
if (!buffer_flush(&c->b)) {
return 0;
}
if (!seek_set(c->fd, 0)) {
return 0;
}
// free childs
for (x = c->head; x;) {
n = x->next;
cdb_alloc_free(x);
x = n;
}
cdb_alloc_free(c->split);
return buffer_putflush(&c->b, c->final, sizeof c->final);
}

View file

@ -0,0 +1,52 @@
// SPDX-FileCopyrightText: D. J. Bernstein <djb@cr.yp.to>
// SPDX-License-Identifier: CC-PDDC
#ifndef CDB_MAKE_H
#define CDB_MAKE_H
#include "buffer.h"
#include <rz_types.h>
#ifdef __cplusplus
extern "C" {
#endif
#define CDB_HPLIST 1000
struct cdb_hp {
ut32 h;
ut32 p;
};
struct cdb_hplist {
struct cdb_hp hp[CDB_HPLIST];
struct cdb_hplist *next;
int num;
};
struct cdb_make {
char bspace[8192];
char final[1024];
ut32 count[256];
ut32 start[256];
struct cdb_hplist *head;
struct cdb_hp *split; /* includes space for hash */
struct cdb_hp *hash;
ut32 numentries;
ut32 memsize;
buffer b;
ut32 pos;
int fd;
};
extern int cdb_make_start(struct cdb_make *, int);
extern int cdb_make_addbegin(struct cdb_make *, unsigned int, unsigned int);
extern int cdb_make_addend(struct cdb_make *, unsigned int, unsigned int, ut32);
extern int cdb_make_add(struct cdb_make *, const char *, unsigned int, const char *, unsigned int);
extern int cdb_make_finish(struct cdb_make *);
#ifdef __cplusplus
}
#endif
#endif

173
librz/util/sdb/src/diff.c Normal file
View file

@ -0,0 +1,173 @@
// SPDX-FileCopyrightText: 2019 thestr4ng3r <info@florianmaerkl.de>
// SPDX-License-Identifier: MIT
#include "sdb.h"
RZ_API int sdb_diff_format(char *str, int size, const SdbDiff *diff) {
int r = 0;
#define APPENDF(...) \
do { \
int sr = snprintf(str, size, __VA_ARGS__); \
if (sr < 0) { \
return sr; \
} \
r += sr; \
if (sr >= size) { \
/* no space left, only measure from now on */ \
str = NULL; \
size = 0; \
} else { \
str += sr; \
size -= sr; \
} \
} while (0)
APPENDF("%c%s ", diff->add ? '+' : '-', diff->v ? " " : "NS");
SdbListIter *it;
const char *component;
ls_foreach (diff->path, it, component) {
APPENDF("%s/", component);
}
if (diff->v) {
APPENDF("%s=%s", diff->k, diff->v);
} else {
APPENDF("%s", diff->k);
}
#undef APPENDF
return r;
}
typedef struct sdb_diff_ctx_t {
Sdb *a;
Sdb *b;
bool equal;
SdbList *path;
SdbDiffCallback cb;
void *cb_user;
} SdbDiffCtx;
#define DIFF(ctx, c, ret) \
do { \
(ctx)->equal = false; \
if ((ctx)->cb) { \
c \
} else { \
/* we already know it's not equal and don't care about the rest of the diff */ \
return ret; \
} \
} while (0)
static void sdb_diff_report_ns(SdbDiffCtx *ctx, SdbNs *ns, bool add) {
SdbDiff diff = { ctx->path, ns->name, NULL, add };
ctx->cb(&diff, ctx->cb_user);
}
static void sdb_diff_report_kv(SdbDiffCtx *ctx, const char *k, const char *v, bool add) {
SdbDiff diff = { ctx->path, k, v, add };
ctx->cb(&diff, ctx->cb_user);
}
typedef struct sdb_diff_kv_cb_ctx {
SdbDiffCtx *ctx;
bool add;
} SdbDiffKVCbCtx;
static bool sdb_diff_report_kv_cb(void *user, const char *k, const char *v) {
const SdbDiffKVCbCtx *ctx = user;
sdb_diff_report_kv(ctx->ctx, k, v, ctx->add);
return true;
}
/**
* just report everything from sdb to buf with prefix
*/
static void sdb_diff_report(SdbDiffCtx *ctx, Sdb *sdb, bool add) {
SdbListIter *it;
SdbNs *ns;
ls_foreach (sdb->ns, it, ns) {
sdb_diff_report_ns(ctx, ns, add);
ls_push(ctx->path, ns->name);
sdb_diff_report(ctx, ns->sdb, add);
ls_pop(ctx->path);
}
SdbDiffKVCbCtx cb_ctx = { ctx, add };
sdb_foreach(sdb, sdb_diff_report_kv_cb, &cb_ctx);
}
static bool sdb_diff_kv_cb(void *user, const char *k, const char *v) {
const SdbDiffKVCbCtx *ctx = user;
Sdb *other = ctx->add ? ctx->ctx->a : ctx->ctx->b;
const char *other_val = sdb_const_get(other, k, NULL);
if (!other_val || !*other_val) {
DIFF(ctx->ctx,
sdb_diff_report_kv(ctx->ctx, k, v, ctx->add);
, false);
} else if (!ctx->add && strcmp(v, other_val) != 0) {
DIFF(ctx->ctx,
sdb_diff_report_kv(ctx->ctx, k, v, false);
sdb_diff_report_kv(ctx->ctx, k, other_val, true);
, false);
}
return true;
}
static void sdb_diff_ctx(SdbDiffCtx *ctx) {
SdbListIter *it;
SdbNs *ns;
ls_foreach (ctx->a->ns, it, ns) {
Sdb *b_ns = sdb_ns(ctx->b, ns->name, false);
if (!b_ns) {
DIFF(ctx,
sdb_diff_report_ns(ctx, ns, false);
ls_push(ctx->path, ns->name);
sdb_diff_report(ctx, ns->sdb, false);
ls_pop(ctx->path);
, );
continue;
}
Sdb *a = ctx->a;
Sdb *b = ctx->b;
ctx->a = ns->sdb;
ctx->b = b_ns;
ls_push(ctx->path, ns->name);
sdb_diff_ctx(ctx);
ls_pop(ctx->path);
ctx->a = a;
ctx->b = b;
}
ls_foreach (ctx->b->ns, it, ns) {
if (!sdb_ns(ctx->a, ns->name, false)) {
DIFF(ctx,
sdb_diff_report_ns(ctx, ns, true);
ls_push(ctx->path, ns->name);
sdb_diff_report(ctx, ns->sdb, true);
ls_pop(ctx->path);
, );
}
}
SdbDiffKVCbCtx kv_ctx = { ctx, false };
if (!sdb_foreach(ctx->a, sdb_diff_kv_cb, &kv_ctx)) {
return;
}
kv_ctx.add = true;
sdb_foreach(ctx->b, sdb_diff_kv_cb, &kv_ctx);
}
RZ_API bool sdb_diff(Sdb *a, Sdb *b, SdbDiffCallback cb, void *cb_user) {
SdbDiffCtx ctx;
ctx.a = a;
ctx.b = b;
ctx.equal = true;
ctx.cb = cb;
ctx.cb_user = cb_user;
ctx.path = ls_new();
if (!ctx.path) {
return false;
}
sdb_diff_ctx(&ctx);
ls_free(ctx.path);
return ctx.equal;
}

123
librz/util/sdb/src/disk.c Normal file
View file

@ -0,0 +1,123 @@
// SPDX-FileCopyrightText: 2013-2018 pancake <pancake@nopcode.org>
// SPDX-License-Identifier: MIT
#include <rz_types.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <rz_util/rz_file.h>
#include <rz_util/rz_sys.h>
#include <rz_util/rz_utf8.h>
#include <rz_windows.h>
#include "sdb.h"
RZ_API bool sdb_disk_create(Sdb *s) {
int nlen;
char *str;
const char *dir;
if (!s || s->fdump >= 0) {
return false; // cannot re-create
}
if (!s->dir && s->name) {
s->dir = strdup(s->name);
}
dir = s->dir ? s->dir : "./";
RZ_FREE(s->ndump);
nlen = strlen(dir);
str = malloc(nlen + 5);
if (!str) {
return false;
}
memcpy(str, dir, nlen + 1);
char *dirname = rz_file_dirname(str);
if (!dirname) {
free(str);
return false;
}
rz_sys_mkdirp(dirname);
free(dirname);
memcpy(str + nlen, ".tmp", 5);
if (s->fdump != -1) {
close(s->fdump);
}
#if __WINDOWS__
wchar_t *wstr = rz_utf8_to_utf16(str);
if (wstr) {
s->fdump = _wopen(wstr, O_BINARY | O_RDWR | O_CREAT | O_TRUNC, SDB_MODE);
free(wstr);
} else {
s->fdump = -1;
}
#else
s->fdump = open(str, O_BINARY | O_RDWR | O_CREAT | O_TRUNC, SDB_MODE);
#endif
if (s->fdump == -1) {
eprintf("sdb: Cannot open '%s' for writing.\n", str);
free(str);
return false;
}
cdb_make_start(&s->m, s->fdump);
s->ndump = str;
return true;
}
RZ_API bool sdb_disk_insert(Sdb *s, const char *key, const char *val) {
struct cdb_make *c = &s->m;
if (!key || !val) {
return false;
}
// if (!*val) return 0; //undefine variable if no value
return cdb_make_add(c, key, strlen(key), val, strlen(val));
}
#define IFRET(x) \
if (x) \
ret = 0
RZ_API bool sdb_disk_finish(Sdb *s) {
bool reopen = false, ret = true;
IFRET(!cdb_make_finish(&s->m));
#if HAVE_HEADER_SYS_MMAN_H
IFRET(fsync(s->fdump));
#endif
IFRET(close(s->fdump));
s->fdump = -1;
// close current fd to avoid sharing violations
if (s->fd != -1) {
close(s->fd);
s->fd = -1;
reopen = true;
}
#if __WINDOWS__
wchar_t *ndump_ = rz_utf8_to_utf16(s->ndump);
wchar_t *dir_ = rz_utf8_to_utf16(s->dir);
if (!MoveFileExW(ndump_, dir_, MOVEFILE_REPLACE_EXISTING)) {
rz_sys_perror("MoveFileExW SDB file to finale location");
}
free(ndump_);
free(dir_);
#else
if (s->ndump && s->dir) {
IFRET(rename(s->ndump, s->dir));
}
#endif
free(s->ndump);
s->ndump = NULL;
// reopen if was open before
reopen = true; // always reopen if possible
if (reopen) {
int rr = sdb_open(s, s->dir);
if (ret && rr < 0) {
ret = false;
}
cdb_init(&s->db, s->fd);
}
return ret;
}
RZ_API bool sdb_disk_unlink(Sdb *s) {
return (s->dir && *(s->dir) && unlink(s->dir) != -1);
}

232
librz/util/sdb/src/fmt.c Normal file
View file

@ -0,0 +1,232 @@
// SPDX-FileCopyrightText: 2014-2018 pancake <pancake@nopcode.org>
// SPDX-License-Identifier: MIT
#include "sdb.h"
#include <stdarg.h>
#include <string.h>
// TODO: convert into a function
// TODO: Add 'a' format for array of pointers null terminated??
// XXX SLOW CONCAT
#define concat(x) \
if (x) { \
int size = 2 + strlen(x ? x : "") + (out ? strlen(out) + 4 : 0); \
if (out) { \
char *o = realloc(out, size); \
if (o) { \
strcat(o, ","); \
strcat(o, x); \
out = o; \
} \
} else { \
out = strdup(x); \
} \
}
RZ_API char *sdb_fmt(const char *fmt, ...) {
#define KL 256
#define KN 16
static char Key[KN][KL];
static int n = 0;
va_list ap;
va_start(ap, fmt);
n = (n + 1) % KN;
if (fmt) {
*Key[n] = 0;
vsnprintf(Key[n], KL - 1, fmt, ap);
Key[n][KL - 1] = 0;
}
va_end(ap);
return Key[n];
}
RZ_API char *sdb_fmt_tostr(void *p, const char *fmt) {
char buf[128], *e_str, *out = NULL;
int n, len = 0;
if (!p || !fmt) {
return NULL;
}
for (; *fmt; fmt++) {
n = 4;
switch (*fmt) {
case 'b':
concat(sdb_itoa((ut64) * ((ut8 *)p + len), buf, 10));
break;
case 'h':
concat(sdb_itoa((ut64) * ((short *)((ut8 *)p + len)), buf, 10));
break;
case 'd':
concat(sdb_itoa((ut64) * ((int *)((ut8 *)p + len)), buf, 10));
break;
case 'q':
concat(sdb_itoa(*((ut64 *)((ut8 *)p + len)), buf, 10));
n = 8;
break;
case 'z':
concat((char *)p + len);
break;
case 's':
e_str = sdb_encode((const ut8 *)*((char **)((ut8 *)p + len)), -1);
concat(e_str);
free(e_str);
break;
case 'p':
concat(sdb_itoa((ut64) * ((size_t *)((ut8 *)p + len)), buf, 16));
n = sizeof(size_t);
break;
}
len += RZ_MAX((long)sizeof(void *), n); // align
}
return out;
}
// TODO: return false if array length != fmt length
RZ_API int sdb_fmt_tobin(const char *_str, const char *fmt, void *stru) {
int n, idx = 0, items = 0;
char *stru8 = (char *)stru;
char *next, *str, *ptr, *word, *e_str;
if (!_str || !*_str || !fmt) {
return 0;
}
str = ptr = strdup(_str);
for (; *fmt; fmt++) {
word = sdb_anext(ptr, &next);
if (!word || !*word) {
break;
}
items++;
n = 4; // ALIGN
switch (*fmt) {
case 'b': *((ut8 *)(stru8 + idx)) = (ut8)sdb_atoi(word); break;
case 'd': *((int *)(stru8 + idx)) = (int)sdb_atoi(word); break;
case 'q':
*((ut64 *)(stru8 + idx)) = sdb_atoi(word);
n = 8;
break;
case 'h': *((short *)(stru8 + idx)) = (short)sdb_atoi(word); break;
case 's':
e_str = (char *)sdb_decode(word, 0);
*((char **)(stru8 + idx)) = e_str ? e_str : strdup(word);
break;
case 'z':
*((char **)(stru8 + idx)) = (char *)strdup(word);
break;
case 'p':
*((void **)(stru8 + idx)) = (void *)(size_t)sdb_atoi(word);
break;
}
idx += RZ_MAX((long)sizeof(void *), n); // align
if (!next) {
break;
}
ptr = next;
}
free(str);
return items;
}
RZ_API void sdb_fmt_free(void *stru, const char *fmt) {
int n, len = 0;
for (; *fmt; fmt++) {
n = 4;
switch (*fmt) {
case 'p': // TODO: leak or wat
case 'b':
case 'h':
case 'd':
/* do nothing */
break;
case 'q':
n = 8;
break;
case 'z':
case 's':
free((void *)*((char **)((ut8 *)stru + len)));
break;
}
len += RZ_MAX((long)sizeof(void *), n); // align
}
}
RZ_API int sdb_fmt_init(void *p, const char *fmt) {
int len = 0;
for (; *fmt; fmt++) {
switch (*fmt) {
case 'b': len += sizeof(ut8); break; // 1
case 'h': len += sizeof(short); break; // 2
case 'd': len += sizeof(ut32); break; // 4
case 'q': len += sizeof(ut64); break; // 8
case 'z': len += sizeof(char *); break; // void*
case 's': len += sizeof(char *); break; // void*
case 'p': len += sizeof(char *); break; // void *
}
}
if (p) {
memset(p, 0, len);
}
return len;
}
static const char *sdb_anext2(const char *str, const char **next) {
char *nxt, *p = strchr(str, SDB_RS);
if (p) {
nxt = p + 1;
} else {
nxt = NULL;
}
if (next) {
*next = nxt;
}
return str;
}
// TODO: move this into fmt?
RZ_API ut64 *sdb_fmt_array_num(const char *list) {
ut64 *retp, *ret = NULL;
ut32 size;
const char *next, *ptr = list;
if (list && *list) {
ut32 len = (ut32)sdb_alen(list);
size = sizeof(ut64) * (len + 1);
if (size < len) {
return NULL;
}
retp = ret = (ut64 *)malloc(size);
if (!ret) {
return NULL;
}
*retp++ = len;
do {
const char *str = sdb_anext2(ptr, &next);
ut64 n = sdb_atoi(str);
*retp++ = n;
ptr = next;
} while (next);
}
return ret;
}
RZ_API char **sdb_fmt_array(const char *list) {
char *_s, **retp, **ret = NULL;
const char *next, *ptr = list;
if (list && *list) {
int len = sdb_alen(list);
retp = ret = (char **)malloc(2 * strlen(list) +
((len + 1) * sizeof(char *)) + 1);
_s = (char *)ret + ((len + 1) * sizeof(char *));
if (!ret) {
return NULL;
}
do {
const char *str = sdb_anext2(ptr, &next);
int slen = next ? (next - str) - 1 : (int)strlen(str) + 1;
memcpy(_s, str, slen);
_s[slen] = 0;
*retp++ = _s;
_s += slen + 1;
ptr = next;
} while (next);
*retp = NULL;
}
return ret;
}

369
librz/util/sdb/src/ht_inc.c Normal file
View file

@ -0,0 +1,369 @@
// SPDX-FileCopyrightText: 2016-2018 crowell
// SPDX-FileCopyrightText: 2016-2018 pancake <pancake@nopcode.org>
// SPDX-FileCopyrightText: 2016-2018 ret2libc <sirmy15@gmail.com>
// SPDX-License-Identifier: BSD-3-Clause
#define LOAD_FACTOR 1
#define S_ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
// Sizes of the ht.
static const ut32 ht_primes_sizes[] = {
3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131,
163, 197, 239, 293, 353, 431, 521, 631, 761, 919,
1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861,
5839, 7013, 8419, 10103, 12143, 14591, 17519, 21023,
25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523,
108631, 130363, 156437, 187751, 225307, 270371, 324449,
389357, 467237, 560689, 672827, 807403, 968897, 1162687,
1395263, 1674319, 2009191, 2411033, 2893249, 3471899,
4166287, 4999559, 5999471, 7199369
};
static inline ut32 hashfn(HtName_(Ht) * ht, const KEY_TYPE k) {
return ht->opt.hashfn ? ht->opt.hashfn(k) : KEY_TO_HASH(k);
}
static inline ut32 bucketfn(HtName_(Ht) * ht, const KEY_TYPE k) {
return hashfn(ht, k) % ht->size;
}
static inline KEY_TYPE dupkey(HtName_(Ht) * ht, const KEY_TYPE k) {
return ht->opt.dupkey ? ht->opt.dupkey(k) : (KEY_TYPE)k;
}
static inline VALUE_TYPE dupval(HtName_(Ht) * ht, const VALUE_TYPE v) {
return ht->opt.dupvalue ? ht->opt.dupvalue(v) : (VALUE_TYPE)v;
}
static inline ut32 calcsize_key(HtName_(Ht) * ht, const KEY_TYPE k) {
return ht->opt.calcsizeK ? ht->opt.calcsizeK(k) : 0;
}
static inline ut32 calcsize_val(HtName_(Ht) * ht, const VALUE_TYPE v) {
return ht->opt.calcsizeV ? ht->opt.calcsizeV(v) : 0;
}
static inline void freefn(HtName_(Ht) * ht, HT_(Kv) * kv) {
if (ht->opt.freefn) {
ht->opt.freefn(kv);
}
}
static inline ut32 next_idx(ut32 idx) {
if (idx != UT32_MAX && idx < S_ARRAY_SIZE(ht_primes_sizes) - 1) {
return idx + 1;
}
return UT32_MAX;
}
static inline ut32 compute_size(ut32 idx, ut32 sz) {
// when possible, use the precomputed prime numbers which help with
// collisions, otherwise, at least make the number odd with |1
return idx != UT32_MAX && idx < S_ARRAY_SIZE(ht_primes_sizes) ? ht_primes_sizes[idx] : (sz | 1);
}
static inline bool is_kv_equal(HtName_(Ht) * ht, const KEY_TYPE key, const ut32 key_len, const HT_(Kv) * kv) {
if (key_len != kv->key_len) {
return false;
}
bool res = key == kv->key;
if (!res && ht->opt.cmp) {
res = !ht->opt.cmp(key, kv->key);
}
return res;
}
static inline HT_(Kv) * kv_at(HtName_(Ht) * ht, HT_(Bucket) * bt, ut32 i) {
return (HT_(Kv) *)((char *)bt->arr + i * ht->opt.elem_size);
}
static inline HT_(Kv) * next_kv(HtName_(Ht) * ht, HT_(Kv) * kv) {
return (HT_(Kv) *)((char *)kv + ht->opt.elem_size);
}
#define BUCKET_FOREACH(ht, bt, j, kv) \
if ((bt)->arr) \
for ((j) = 0, (kv) = (bt)->arr; (j) < (bt)->count; (j)++, (kv) = next_kv(ht, kv))
#define BUCKET_FOREACH_SAFE(ht, bt, j, count, kv) \
if ((bt)->arr) \
for ((j) = 0, (kv) = (bt)->arr, (count) = (ht)->count; \
(j) < (bt)->count; \
(j) = (count) == (ht)->count ? j + 1 : j, (kv) = (count) == (ht)->count ? next_kv(ht, kv) : kv, (count) = (ht)->count)
// Create a new hashtable and return a pointer to it.
// size - number of buckets in the hashtable
// hashfunction - the function that does the hashing, must not be null.
// comparator - the function to check if values are equal, if NULL, just checks
// == (for storing ints).
// keydup - function to duplicate to key (eg strdup), if NULL just does strup.
// valdup - same as keydup, but for values but if NULL just assign
// pair_free - function for freeing a keyvaluepair - if NULL just does free.
// calcsize - function to calculate the size of a value. if NULL, just stores 0.
static HtName_(Ht) * internal_ht_new(ut32 size, ut32 prime_idx, HT_(Options) * opt) {
HtName_(Ht) *ht = calloc(1, sizeof(*ht));
if (!ht) {
return NULL;
}
ht->size = size;
ht->count = 0;
ht->prime_idx = prime_idx;
ht->table = calloc(ht->size, sizeof(*ht->table));
if (!ht->table) {
free(ht);
return NULL;
}
ht->opt = *opt;
// if not provided, assume we are dealing with a regular HtName_(Ht), with
// HT_(Kv) as elements
if (ht->opt.elem_size == 0) {
ht->opt.elem_size = sizeof(HT_(Kv));
}
return ht;
}
RZ_API HtName_(Ht) * Ht_(new_opt)(HT_(Options) * opt) {
return internal_ht_new(ht_primes_sizes[0], 0, opt);
}
RZ_API void Ht_(free)(HtName_(Ht) * ht) {
if (!ht) {
return;
}
ut32 i;
for (i = 0; i < ht->size; i++) {
HT_(Bucket) *bt = &ht->table[i];
HT_(Kv) * kv;
ut32 j;
if (ht->opt.freefn) {
BUCKET_FOREACH(ht, bt, j, kv) {
ht->opt.freefn(kv);
}
}
free(bt->arr);
}
free(ht->table);
free(ht);
}
// Increases the size of the hashtable by 2.
static void internal_ht_grow(HtName_(Ht) * ht) {
HtName_(Ht) * ht2;
HtName_(Ht) swap;
ut32 idx = next_idx(ht->prime_idx);
ut32 sz = compute_size(idx, ht->size * 2);
ut32 i;
ht2 = internal_ht_new(sz, idx, &ht->opt);
if (!ht2) {
// we can't grow the ht anymore. Never mind, we'll be slower,
// but everything can continue to work
return;
}
for (i = 0; i < ht->size; i++) {
HT_(Bucket) *bt = &ht->table[i];
HT_(Kv) * kv;
ut32 j;
BUCKET_FOREACH(ht, bt, j, kv) {
Ht_(insert_kv)(ht2, kv, false);
}
}
// And now swap the internals.
swap = *ht;
*ht = *ht2;
*ht2 = swap;
ht2->opt.freefn = NULL;
Ht_(free)(ht2);
}
static void check_growing(HtName_(Ht) * ht) {
if (ht->count >= LOAD_FACTOR * ht->size) {
internal_ht_grow(ht);
}
}
static HT_(Kv) * reserve_kv(HtName_(Ht) * ht, const KEY_TYPE key, const int key_len, bool update) {
HT_(Bucket) *bt = &ht->table[bucketfn(ht, key)];
HT_(Kv) * kvtmp;
ut32 j;
BUCKET_FOREACH(ht, bt, j, kvtmp) {
if (is_kv_equal(ht, key, key_len, kvtmp)) {
if (update) {
freefn(ht, kvtmp);
return kvtmp;
}
return NULL;
}
}
HT_(Kv) *newkvarr = realloc(bt->arr, (bt->count + 1) * ht->opt.elem_size);
if (!newkvarr) {
return NULL;
}
bt->arr = newkvarr;
bt->count++;
ht->count++;
return kv_at(ht, bt, bt->count - 1);
}
RZ_API bool Ht_(insert_kv)(HtName_(Ht) * ht, HT_(Kv) * kv, bool update) {
HT_(Kv) *kv_dst = reserve_kv(ht, kv->key, kv->key_len, update);
if (!kv_dst) {
return false;
}
memcpy(kv_dst, kv, ht->opt.elem_size);
check_growing(ht);
return true;
}
static bool insert_update(HtName_(Ht) * ht, const KEY_TYPE key, VALUE_TYPE value, bool update) {
ut32 key_len = calcsize_key(ht, key);
HT_(Kv) *kv_dst = reserve_kv(ht, key, key_len, update);
if (!kv_dst) {
return false;
}
kv_dst->key = dupkey(ht, key);
kv_dst->key_len = key_len;
kv_dst->value = dupval(ht, value);
kv_dst->value_len = calcsize_val(ht, value);
check_growing(ht);
return true;
}
// Inserts the key value pair key, value into the hashtable.
// Doesn't allow for "update" of the value.
RZ_API bool Ht_(insert)(HtName_(Ht) * ht, const KEY_TYPE key, VALUE_TYPE value) {
return insert_update(ht, key, value, false);
}
// Inserts the key value pair key, value into the hashtable.
// Does allow for "update" of the value.
RZ_API bool Ht_(update)(HtName_(Ht) * ht, const KEY_TYPE key, VALUE_TYPE value) {
return insert_update(ht, key, value, true);
}
// Update the key of an element that has old_key as key and replace it with new_key
RZ_API bool Ht_(update_key)(HtName_(Ht) * ht, const KEY_TYPE old_key, const KEY_TYPE new_key) {
// First look for the value associated with old_key
bool found;
VALUE_TYPE value = Ht_(find)(ht, old_key, &found);
if (!found) {
return false;
}
// Associate the existing value with new_key
bool inserted = insert_update(ht, new_key, value, false);
if (!inserted) {
return false;
}
// Remove the old_key kv, paying attention to not double free the value
HT_(Bucket) *bt = &ht->table[bucketfn(ht, old_key)];
const int old_key_len = calcsize_key(ht, old_key);
HT_(Kv) * kv;
ut32 j;
BUCKET_FOREACH(ht, bt, j, kv) {
if (is_kv_equal(ht, old_key, old_key_len, kv)) {
if (!ht->opt.dupvalue) {
// do not free the value part if dupvalue is not
// set, because the old value has been
// associated with the new key and it should not
// be freed
kv->value = HT_NULL_VALUE;
kv->value_len = 0;
}
freefn(ht, kv);
void *src = next_kv(ht, kv);
memmove(kv, src, (bt->count - j - 1) * ht->opt.elem_size);
bt->count--;
ht->count--;
return true;
}
}
return false;
}
// Returns the corresponding SdbKv entry from the key.
// If `found` is not NULL, it will be set to true if the entry was found, false
// otherwise.
RZ_API HT_(Kv) * Ht_(find_kv)(HtName_(Ht) * ht, const KEY_TYPE key, bool *found) {
if (found) {
*found = false;
}
if (!ht) {
return NULL;
}
HT_(Bucket) *bt = &ht->table[bucketfn(ht, key)];
ut32 key_len = calcsize_key(ht, key);
HT_(Kv) * kv;
ut32 j;
BUCKET_FOREACH(ht, bt, j, kv) {
if (is_kv_equal(ht, key, key_len, kv)) {
if (found) {
*found = true;
}
return kv;
}
}
return NULL;
}
// Looks up the corresponding value from the key.
// If `found` is not NULL, it will be set to true if the entry was found, false
// otherwise.
RZ_API VALUE_TYPE Ht_(find)(HtName_(Ht) * ht, const KEY_TYPE key, bool *found) {
HT_(Kv) *res = Ht_(find_kv)(ht, key, found);
return res ? res->value : HT_NULL_VALUE;
}
// Deletes a entry from the hash table from the key, if the pair exists.
RZ_API bool Ht_(delete)(HtName_(Ht) * ht, const KEY_TYPE key) {
HT_(Bucket) *bt = &ht->table[bucketfn(ht, key)];
ut32 key_len = calcsize_key(ht, key);
HT_(Kv) * kv;
ut32 j;
BUCKET_FOREACH(ht, bt, j, kv) {
if (is_kv_equal(ht, key, key_len, kv)) {
freefn(ht, kv);
void *src = next_kv(ht, kv);
memmove(kv, src, (bt->count - j - 1) * ht->opt.elem_size);
bt->count--;
ht->count--;
return true;
}
}
return false;
}
RZ_API void Ht_(foreach)(HtName_(Ht) * ht, HT_(ForeachCallback) cb, void *user) {
ut32 i;
for (i = 0; i < ht->size; ++i) {
HT_(Bucket) *bt = &ht->table[i];
HT_(Kv) * kv;
ut32 j, count;
BUCKET_FOREACH_SAFE(ht, bt, j, count, kv) {
if (!cb(user, kv->key, kv->value)) {
return;
}
}
}
}

131
librz/util/sdb/src/ht_inc.h Normal file
View file

@ -0,0 +1,131 @@
// SPDX-FileCopyrightText: 2016-2018 crowell
// SPDX-FileCopyrightText: 2016-2018 pancake <pancake@nopcode.org>
// SPDX-FileCopyrightText: 2016-2018 ret2libc <sirmy15@gmail.com>
// SPDX-License-Identifier: BSD-3-Clause
#ifndef HT_TYPE
#error HT_TYPE should be defined before including this header
#endif
#undef HtName_
#undef Ht_
#undef HT_
#undef KEY_TYPE
#undef VALUE_TYPE
#undef KEY_TO_HASH
#undef HT_NULL_VALUE
#if HT_TYPE == 1
#define HtName_(name) name##PP
#define Ht_(name) ht_pp_##name
#define HT_(name) HtPP##name
#define KEY_TYPE void *
#define VALUE_TYPE void *
#define KEY_TO_HASH(x) ((ut32)(uintptr_t)(x))
#define HT_NULL_VALUE NULL
#elif HT_TYPE == 2
#define HtName_(name) name##UP
#define Ht_(name) ht_up_##name
#define HT_(name) HtUP##name
#define KEY_TYPE ut64
#define VALUE_TYPE void *
#define KEY_TO_HASH(x) ((ut32)(x))
#define HT_NULL_VALUE 0
#elif HT_TYPE == 3
#define HtName_(name) name##UU
#define Ht_(name) ht_uu_##name
#define HT_(name) HtUU##name
#define KEY_TYPE ut64
#define VALUE_TYPE ut64
#define KEY_TO_HASH(x) ((ut32)(x))
#define HT_NULL_VALUE 0
#else
#define HtName_(name) name##PU
#define Ht_(name) ht_pu_##name
#define HT_(name) HtPU##name
#define KEY_TYPE void *
#define VALUE_TYPE ut64
#define KEY_TO_HASH(x) ((ut32)(uintptr_t)(x))
#define HT_NULL_VALUE 0
#endif
#include "ls.h"
#include <rz_types.h>
/* Kv represents a single key/value element in the hashtable */
typedef struct Ht_(kv) {
KEY_TYPE key;
VALUE_TYPE value;
ut32 key_len;
ut32 value_len;
}
HT_(Kv);
typedef void (*HT_(KvFreeFunc))(HT_(Kv) *);
typedef KEY_TYPE (*HT_(DupKey))(const KEY_TYPE);
typedef VALUE_TYPE (*HT_(DupValue))(const VALUE_TYPE);
typedef ut32 (*HT_(CalcSizeK))(const KEY_TYPE);
typedef ut32 (*HT_(CalcSizeV))(const VALUE_TYPE);
typedef ut32 (*HT_(HashFunction))(const KEY_TYPE);
typedef int (*HT_(ListComparator))(const KEY_TYPE, const KEY_TYPE);
typedef bool (*HT_(ForeachCallback))(void *user, const KEY_TYPE, const VALUE_TYPE);
typedef struct Ht_(bucket_t) {
HT_(Kv) * arr;
ut32 count;
}
HT_(Bucket);
/* Options contain all the settings of the hashtable */
typedef struct Ht_(options_t) {
HT_(ListComparator)
cmp; // Function for comparing values. Returns 0 if eq.
HT_(HashFunction)
hashfn; // Function for hashing items in the hash table.
HT_(DupKey)
dupkey; // Function for making a copy of key
HT_(DupValue)
dupvalue; // Function for making a copy of value
HT_(CalcSizeK)
calcsizeK; // Function to determine the key's size
HT_(CalcSizeV)
calcsizeV; // Function to determine the value's size
HT_(KvFreeFunc)
freefn; // Function to free the keyvalue store
size_t elem_size; // Size of each HtKv element (useful for subclassing like SdbKv)
}
HT_(Options);
/* Ht is the hashtable structure */
typedef struct Ht_(t) {
ut32 size; // size of the hash table in buckets.
ut32 count; // number of stored elements.
HT_(Bucket) * table; // Actual table.
ut32 prime_idx;
HT_(Options)
opt;
}
HtName_(Ht);
// Create a new Ht with the provided Options
RZ_API HtName_(Ht) * Ht_(new_opt)(HT_(Options) * opt);
// Destroy a hashtable and all of its entries.
RZ_API void Ht_(free)(HtName_(Ht) * ht);
// Insert a new Key-Value pair into the hashtable. If the key already exists, returns false.
RZ_API bool Ht_(insert)(HtName_(Ht) * ht, const KEY_TYPE key, VALUE_TYPE value);
// Insert a new Key-Value pair into the hashtable, or updates the value if the key already exists.
RZ_API bool Ht_(update)(HtName_(Ht) * ht, const KEY_TYPE key, VALUE_TYPE value);
// Update the key of an element in the hashtable
RZ_API bool Ht_(update_key)(HtName_(Ht) * ht, const KEY_TYPE old_key, const KEY_TYPE new_key);
// Delete a key from the hashtable.
RZ_API bool Ht_(delete)(HtName_(Ht) * ht, const KEY_TYPE key);
// Find the value corresponding to the matching key.
RZ_API VALUE_TYPE Ht_(find)(HtName_(Ht) * ht, const KEY_TYPE key, bool *found);
// Iterates over all elements in the hashtable, calling the cb function on each Kv.
// If the cb returns false, the iteration is stopped.
// cb should not modify the hashtable.
// NOTE: cb can delete the current element, but it should be avoided
RZ_API void Ht_(foreach)(HtName_(Ht) * ht, HT_(ForeachCallback) cb, void *user);
RZ_API HT_(Kv) * Ht_(find_kv)(HtName_(Ht) * ht, const KEY_TYPE key, bool *found);
RZ_API bool Ht_(insert_kv)(HtName_(Ht) * ht, HT_(Kv) * kv, bool update);

View file

@ -0,0 +1,52 @@
// SPDX-FileCopyrightText: 2016-2018 crowell
// SPDX-FileCopyrightText: 2016-2018 pancake <pancake@nopcode.org>
// SPDX-FileCopyrightText: 2016-2018 ret2libc <sirmy15@gmail.com>
// SPDX-License-Identifier: BSD-3-Clause
#include "sdb.h"
#include "ht_pp.h"
#include "ht_inc.c"
static HtName_(Ht) * internal_ht_default_new(ut32 size, ut32 prime_idx, HT_(DupValue) valdup, HT_(KvFreeFunc) pair_free, HT_(CalcSizeV) calcsizeV) {
HT_(Options)
opt = {
.cmp = (HT_(ListComparator))strcmp,
.hashfn = (HT_(HashFunction))sdb_hash,
.dupkey = (HT_(DupKey))strdup,
.dupvalue = valdup,
.calcsizeK = (HT_(CalcSizeK))strlen,
.calcsizeV = calcsizeV,
.freefn = pair_free,
.elem_size = sizeof(HT_(Kv)),
};
return internal_ht_new(size, prime_idx, &opt);
}
// creates a default HtPP that has strings as keys
RZ_API HtName_(Ht) * Ht_(new)(HT_(DupValue) valdup, HT_(KvFreeFunc) pair_free, HT_(CalcSizeV) calcsizeV) {
return internal_ht_default_new(ht_primes_sizes[0], 0, valdup, pair_free, calcsizeV);
}
static void free_kv_key(HT_(Kv) * kv) {
free(kv->key);
}
// creates a default HtPP that has strings as keys but does not dup, nor free the values
RZ_API HtName_(Ht) * Ht_(new0)(void) {
return Ht_(new)(NULL, free_kv_key, NULL);
}
RZ_API HtName_(Ht) * Ht_(new_size)(ut32 initial_size, HT_(DupValue) valdup, HT_(KvFreeFunc) pair_free, HT_(CalcSizeV) calcsizeV) {
ut32 i = 0;
while (i < S_ARRAY_SIZE(ht_primes_sizes) &&
ht_primes_sizes[i] * LOAD_FACTOR < initial_size) {
i++;
}
if (i == S_ARRAY_SIZE(ht_primes_sizes)) {
i = UT32_MAX;
}
ut32 sz = compute_size(i, (ut32)(initial_size * (2 - LOAD_FACTOR)));
return internal_ht_default_new(sz, i, valdup, pair_free, calcsizeV);
}

View file

@ -0,0 +1,29 @@
// SPDX-FileCopyrightText: 2016-2018 crowell
// SPDX-FileCopyrightText: 2016-2018 pancake <pancake@nopcode.org>
// SPDX-FileCopyrightText: 2016-2018 ret2libc <sirmy15@gmail.com>
// SPDX-License-Identifier: BSD-3-Clause
#ifndef SDB_HT_PP_H
#define SDB_HT_PP_H
#ifdef __cplusplus
extern "C" {
#endif
/*
* This header provides an hashtable HtPP that has void* as key and void* as
* value. The API functions starts with "ht_pp_" and the types starts with "HtPP".
*/
#define HT_TYPE 1
#include "ht_inc.h"
RZ_API HtName_(Ht) * Ht_(new0)(void);
RZ_API HtName_(Ht) * Ht_(new)(HT_(DupValue) valdup, HT_(KvFreeFunc) pair_free, HT_(CalcSizeV) valueSize);
RZ_API HtName_(Ht) * Ht_(new_size)(ut32 initial_size, HT_(DupValue) valdup, HT_(KvFreeFunc) pair_free, HT_(CalcSizeV) valueSize);
#undef HT_TYPE
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,28 @@
// SPDX-FileCopyrightText: 2016-2018 crowell
// SPDX-FileCopyrightText: 2016-2018 pancake <pancake@nopcode.org>
// SPDX-FileCopyrightText: 2016-2018 ret2libc <sirmy15@gmail.com>
// SPDX-License-Identifier: BSD-3-Clause
#include "sdb.h"
#include "ht_pu.h"
#include "ht_inc.c"
static void free_kv_key(HT_(Kv) * kv) {
free(kv->key);
}
// creates a default HtPU that has strings as keys
RZ_API HtName_(Ht) * Ht_(new0)(void) {
HT_(Options)
opt = {
.cmp = (HT_(ListComparator))strcmp,
.hashfn = (HT_(HashFunction))sdb_hash,
.dupkey = (HT_(DupKey))strdup,
.dupvalue = NULL,
.calcsizeK = (HT_(CalcSizeK))strlen,
.calcsizeV = NULL,
.freefn = free_kv_key,
.elem_size = sizeof(HT_(Kv)),
};
return Ht_(new_opt)(&opt);
}

View file

@ -0,0 +1,27 @@
// SPDX-FileCopyrightText: 2016-2018 crowell
// SPDX-FileCopyrightText: 2016-2018 pancake <pancake@nopcode.org>
// SPDX-FileCopyrightText: 2016-2018 ret2libc <sirmy15@gmail.com>
// SPDX-License-Identifier: BSD-3-Clause
#ifndef SDB_HT_PU_H
#define SDB_HT_PU_H
#ifdef __cplusplus
extern "C" {
#endif
/*
* This header provides an hashtable HtPU that has void* as key and ut64 as
* value. The API functions starts with "ht_pu_" and the types starts with "HtPU".
*/
#define HT_TYPE 4
#include "ht_inc.h"
RZ_API HtName_(Ht) * Ht_(new0)(void);
#undef HT_TYPE
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,46 @@
// SPDX-FileCopyrightText: 2016-2018 crowell
// SPDX-FileCopyrightText: 2016-2018 pancake <pancake@nopcode.org>
// SPDX-FileCopyrightText: 2016-2018 ret2libc <sirmy15@gmail.com>
// SPDX-License-Identifier: BSD-3-Clause
#include "ht_up.h"
#include "ht_inc.c"
static HtName_(Ht) * internal_ht_default_new(ut32 size, ut32 prime_idx, HT_(DupValue) valdup, HT_(KvFreeFunc) pair_free, HT_(CalcSizeV) calcsizeV) {
HT_(Options)
opt = {
.cmp = NULL,
.hashfn = NULL, // TODO: use a better hash function for numbers
.dupkey = NULL,
.dupvalue = valdup,
.calcsizeK = NULL,
.calcsizeV = calcsizeV,
.freefn = pair_free,
.elem_size = sizeof(HT_(Kv)),
};
return internal_ht_new(size, prime_idx, &opt);
}
RZ_API HtName_(Ht) * Ht_(new)(HT_(DupValue) valdup, HT_(KvFreeFunc) pair_free, HT_(CalcSizeV) calcsizeV) {
return internal_ht_default_new(ht_primes_sizes[0], 0, valdup, pair_free, calcsizeV);
}
// creates a default HtUP that does not dup, nor free the values
RZ_API HtName_(Ht) * Ht_(new0)(void) {
return Ht_(new)(NULL, NULL, NULL);
}
RZ_API HtName_(Ht) * Ht_(new_size)(ut32 initial_size, HT_(DupValue) valdup, HT_(KvFreeFunc) pair_free, HT_(CalcSizeV) calcsizeV) {
ut32 i = 0;
while (i < S_ARRAY_SIZE(ht_primes_sizes) &&
ht_primes_sizes[i] * LOAD_FACTOR < initial_size) {
i++;
}
if (i == S_ARRAY_SIZE(ht_primes_sizes)) {
i = UT32_MAX;
}
ut32 sz = compute_size(i, (ut32)(initial_size * (2 - LOAD_FACTOR)));
return internal_ht_default_new(sz, i, valdup, pair_free, calcsizeV);
}

View file

@ -0,0 +1,29 @@
// SPDX-FileCopyrightText: 2016-2018 crowell
// SPDX-FileCopyrightText: 2016-2018 pancake <pancake@nopcode.org>
// SPDX-FileCopyrightText: 2016-2018 ret2libc <sirmy15@gmail.com>
// SPDX-License-Identifier: BSD-3-Clause
#ifndef SDB_HT_UP_H
#define SDB_HT_UP_H
#ifdef __cplusplus
extern "C" {
#endif
/*
* This header provides an hashtable HtUP that has ut64 as key and void* as
* value. The API functions starts with "ht_up_" and the types starts with "HtUP".
*/
#define HT_TYPE 2
#include "ht_inc.h"
RZ_API HtName_(Ht) * Ht_(new0)(void);
RZ_API HtName_(Ht) * Ht_(new)(HT_(DupValue) valdup, HT_(KvFreeFunc) pair_free, HT_(CalcSizeV) valueSize);
RZ_API HtName_(Ht) * Ht_(new_size)(ut32 initial_size, HT_(DupValue) valdup, HT_(KvFreeFunc) pair_free, HT_(CalcSizeV) valueSize);
#undef HT_TYPE
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,22 @@
// SPDX-FileCopyrightText: 2016-2018 crowell
// SPDX-FileCopyrightText: 2016-2018 pancake <pancake@nopcode.org>
// SPDX-FileCopyrightText: 2016-2018 ret2libc <sirmy15@gmail.com>
// SPDX-License-Identifier: BSD-3-Clause
#include "ht_uu.h"
#include "ht_inc.c"
RZ_API HtName_(Ht) * Ht_(new0)(void) {
HT_(Options)
opt = {
.cmp = NULL,
.hashfn = NULL,
.dupkey = NULL,
.dupvalue = NULL,
.calcsizeK = NULL,
.calcsizeV = NULL,
.freefn = NULL
};
return Ht_(new_opt)(&opt);
}

View file

@ -0,0 +1,27 @@
// SPDX-FileCopyrightText: 2016-2018 crowell
// SPDX-FileCopyrightText: 2016-2018 pancake <pancake@nopcode.org>
// SPDX-FileCopyrightText: 2016-2018 ret2libc <sirmy15@gmail.com>
// SPDX-License-Identifier: BSD-3-Clause
#ifndef SDB_HT_H_
#define SDB_HT_H_
#ifdef __cplusplus
extern "C" {
#endif
/*
* This header provides an hashtable Ht that has ut64 as key and ut64 as
* value. The API functions starts with "ht_" and the types starts with "Ht".
*/
#define HT_TYPE 3
#include "ht_inc.h"
RZ_API HtName_(Ht) * Ht_(new0)(void);
#undef HT_TYPE
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,110 @@
// SPDX-FileCopyrightText: 2011-2016 pancake <pancake@nopcode.org>
// SPDX-License-Identifier: MIT
#include "sdb.h"
#include <fcntl.h>
static const char *sdb_journal_filename(Sdb *s) {
return (s && s->name)
? sdb_fmt("%s.journal", s->name)
: NULL;
}
RZ_API bool sdb_journal_close(Sdb *s) {
if (s->journal == -1) {
return false;
}
close(s->journal);
s->journal = -1;
unlink(sdb_journal_filename(s));
return true;
}
RZ_API bool sdb_journal_open(Sdb *s) {
const char *filename;
if (!s || !s->name) {
return false;
}
filename = sdb_journal_filename(s);
if (!filename) {
return false;
}
close(s->journal);
s->journal = open(filename, O_CREAT | O_RDWR | O_APPEND, 0600);
return s->journal != -1;
}
// TODO boolify and save changes somewhere else? or just dont count that?
RZ_API int sdb_journal_load(Sdb *s) {
int rr, sz, fd, changes = 0;
char *eq, *str, *cur, *ptr = NULL;
if (!s) {
return 0;
}
fd = s->journal;
if (fd == -1) {
return 0;
}
sz = lseek(fd, 0, SEEK_END);
if (sz < 1) {
return 0;
}
lseek(fd, 0, SEEK_SET);
str = malloc(sz + 1);
if (!str) {
return 0;
}
rr = read(fd, str, sz);
if (rr < 0) {
free(str);
return 0;
}
str[sz] = 0;
for (cur = str;;) {
ptr = strchr(cur, '\n');
if (!ptr) {
break;
}
*ptr = 0;
eq = strchr(cur, '=');
if (eq) {
*eq++ = 0;
sdb_set(s, cur, eq, 0);
changes++;
}
cur = ptr + 1;
}
free(str);
return changes;
}
RZ_API bool sdb_journal_log(Sdb *s, const char *key, const char *val) {
if (s->journal == -1) {
return false;
}
const char *str = sdb_fmt("%s=%s\n", key, val);
int len = strlen(str);
if (write(s->journal, str, len) != len) {
return false;
}
#if HAVE_HEADER_SYS_MMAN_H
(void)fsync(s->journal);
#endif
return true;
}
RZ_API bool sdb_journal_clear(Sdb *s) {
if (s->journal != -1) {
return !ftruncate(s->journal, 0);
}
return false;
}
RZ_API bool sdb_journal_unlink(Sdb *s) {
const char *filename = sdb_journal_filename(s);
sdb_journal_close(s);
if (filename) {
return !unlink(filename);
}
return false;
}

356
librz/util/sdb/src/json.c Normal file
View file

@ -0,0 +1,356 @@
// SPDX-FileCopyrightText: 2012-2016 pancake <pancake@nopcode.org>
// SPDX-License-Identifier: MIT
#include <stdarg.h>
#include "sdb.h"
#include "json/rangstr.c"
#include "json/js0n.c"
#include "json/path.c"
#include "json/api.c"
#include "json/indent.c"
RZ_API char *sdb_json_get_str(const char *json, const char *path) {
Rangstr rs = json_get(json, path);
return rangstr_dup(&rs);
}
RZ_API bool sdb_json_get_bool(const char *json, const char *path) {
Rangstr rs = json_get(json, path);
const char *p = rs.p + rs.f;
return (rangstr_length(&rs) == 4 && !strncmp(p, "true", 4));
}
RZ_API char *sdb_json_get(Sdb *s, const char *k, const char *p, ut32 *cas) {
Rangstr rs;
char *u, *v = sdb_get(s, k, cas);
if (!v) {
return NULL;
}
rs = json_get(v, p);
u = rangstr_dup(&rs);
free(v);
return u;
}
RZ_API int sdb_json_num_inc(Sdb *s, const char *k, const char *p, int n, ut32 cas) {
ut32 c;
int cur = sdb_json_num_get(s, k, p, &c);
if (cas && c != cas) {
return 0;
}
sdb_json_num_set(s, k, p, cur + n, cas);
return cur + n;
}
RZ_API int sdb_json_num_dec(Sdb *s, const char *k, const char *p, int n, ut32 cas) {
ut32 c;
int cur = sdb_json_num_get(s, k, p, &c);
if (cas && c != cas) {
return 0;
}
sdb_json_num_set(s, k, p, cur - n, cas);
return cur - n;
}
RZ_API int sdb_json_num_get(Sdb *s, const char *k, const char *p, ut32 *cas) {
char *v = sdb_get(s, k, cas);
if (v) {
Rangstr rs = json_get(v, p);
int ret = rangstr_int(&rs);
free(v);
return ret;
}
return 0;
}
static int findkey(Rangstr *rs) {
int i;
for (i = rs->f; i > 0; i--) {
// Find the quote after the key
if (rs->p[i] == '"') {
for (--i; i > 0; i--) {
// Find the quote before the key
if (rs->p[i] == '"') {
return i;
}
}
}
}
return -1;
}
static bool isstring(const char *s) {
if (!strcmp(s, "true")) {
return false;
}
if (!strcmp(s, "false")) {
return false;
}
for (; *s; s++) {
if (*s < '0' || *s > '9') {
return true;
}
}
return false;
}
// JSON only supports base16 numbers
RZ_API int sdb_json_num_set(Sdb *s, const char *k, const char *p, int v, ut32 cas) {
char *_str, str[64];
_str = sdb_itoa(v, str, 10);
return sdb_json_set(s, k, p, _str, cas);
}
RZ_API int sdb_json_unset(Sdb *s, const char *k, const char *p, ut32 cas) {
return sdb_json_set(s, k, p, NULL, cas);
}
RZ_API bool sdb_json_set(Sdb *s, const char *k, const char *p, const char *v, ut32 cas) {
int l, idx, len[3], jslen = 0;
char *b, *str = NULL;
const char *beg[3];
const char *end[3];
const char *js;
Rangstr rs;
ut32 c;
if (!s || !k || !v) {
return false;
}
js = sdb_const_get_len(s, k, &jslen, &c);
if (!js) {
const int v_len = strlen(v);
const int p_len = strlen(p);
b = malloc(p_len + v_len + 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 true;
}
return false;
}
jslen++;
if (cas && c != cas) {
return false;
}
rs = json_get(js, p);
if (!rs.p) {
// jslen already comprehends the NULL-terminator and is
// ensured to be positive by sdb_const_get_len
// 7 corresponds to the length of '{"":"",'
size_t buf_len = jslen + strlen(p) + strlen(v) + 7;
char *buf = malloc(buf_len);
if (buf) {
int curlen, is_str = isstring(v);
const char *quote = is_str ? "\"" : "";
const char *end = ""; // XX: or comma
if (js[0] && js[1] != '}') {
end = ",";
}
curlen = sprintf(buf, "{\"%s\":%s%s%s%s",
p, quote, v, quote, end);
strcpy(buf + curlen, js + 1);
// transfer ownership
sdb_set_owned(s, k, buf, cas);
return true;
}
// invalid json?
return false;
}
// rs.p and js point to the same memory location
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);
// 2 is the maximum amount of quotes that can be inserted
int msz = len[0] + len[1] + len[2] + strlen(v) + 2;
if (msz < 1) {
return false;
}
str = malloc(msz);
if (!str) {
return false;
}
idx = len[0];
memcpy(str, beg[0], idx);
if (is_str) {
if (beg[2][0] != '"') {
str[idx] = '"';
idx++;
}
} else {
if (beg[2][0] == '"') {
beg[2]++;
len[2]--;
}
}
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]++;
len[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] = RZ_MAX(1, kidx - 1);
// Delete quote if deleted value was a string
if (beg[2][0] == '"') {
beg[2]++;
len[2]--;
}
// If not the last key, delete comma
if (len[2] != 2) {
beg[2]++;
len[2]--;
}
str = malloc(len[0] + len[2] + 1);
if (!str) {
return false;
}
memcpy(str, beg[0], len[0]);
memcpy(str + len[0], beg[2], len[2]);
str[len[0] + len[2]] = 0;
}
sdb_set_owned(s, k, str, cas);
return true;
}
RZ_API const char *sdb_json_format(SdbJsonString *s, const char *fmt, ...) {
char *arg_s, *x, tmp[128];
ut64 arg_l;
int i, arg_i;
double arg_f;
va_list ap;
#define JSONSTR_ALLOCATE(y) \
if (s->len + y > s->blen) { \
s->blen *= 2; \
x = realloc(s->buf, s->blen); \
if (!x) { \
va_end(ap); \
return NULL; \
} \
s->buf = x; \
}
if (!s) {
return NULL;
}
if (!s->buf) {
s->blen = 1024;
s->buf = malloc(s->blen);
if (!s->buf) {
return NULL;
}
*s->buf = 0;
}
if (!fmt || !*fmt) {
return s->buf;
}
va_start(ap, fmt);
for (; *fmt; fmt++) {
if (*fmt == '%') {
fmt++;
switch (*fmt) {
case 'b':
JSONSTR_ALLOCATE(32);
arg_i = va_arg(ap, int);
arg_i = arg_i ? 4 : 5;
memcpy(s->buf + s->len, (arg_i == 4) ? "true" : "false", 5);
s->len += arg_i;
break;
case 'f':
JSONSTR_ALLOCATE(32);
arg_f = va_arg(ap, double);
snprintf(tmp, sizeof(tmp), "%f", arg_f);
memcpy(s->buf + s->len, tmp, strlen(tmp));
s->len += strlen(tmp);
break;
case 'l':
JSONSTR_ALLOCATE(32);
arg_l = va_arg(ap, ut64);
snprintf(tmp, sizeof(tmp), "0x%" PFMT64x "x", arg_l);
memcpy(s->buf + s->len, tmp, strlen(tmp));
s->len += strlen(tmp);
break;
case 'd':
case 'i':
JSONSTR_ALLOCATE(32);
arg_i = va_arg(ap, int);
snprintf(tmp, sizeof(tmp), "%d", arg_i);
memcpy(s->buf + s->len, tmp, strlen(tmp));
s->len += strlen(tmp);
break;
case 's':
arg_s = va_arg(ap, char *);
JSONSTR_ALLOCATE(strlen(arg_s) + 3);
s->buf[s->len++] = '"';
for (i = 0; arg_s[i]; i++) {
if (arg_s[i] == '"') {
s->buf[s->len++] = '\\';
}
s->buf[s->len++] = arg_s[i];
}
s->buf[s->len++] = '"';
break;
}
} else {
JSONSTR_ALLOCATE(10);
s->buf[s->len++] = *fmt;
}
s->buf[s->len] = 0;
}
va_end(ap);
return s->buf;
}
#if 0
int main () {
SdbJsonString s = {
0
};
sdb_json_format (&s, "[{%s:%d},%b]", "Hello \"world\"", 1024, 3);
printf ("%s\n", sdb_json_format (&s, 0));
sdb_json_format_free (&s);
return 0;
}
#endif

View file

@ -0,0 +1,20 @@
JSON support for sdb
====================
Use js0n: https://github.com/quartzjer/js0n
git clone git://github.com/quartzjer/js0n.git
----------
user.pancake={"name":"Pancake","pass":"4e2e631e3671727c3f7d8d11fc4dcb0d"}
user.blah={"name":"Bin Daboo","pass":"bd20ae2b25e8d4ff64e0720a5b4162d2"}
user.boun={"name":"Bob Union","pass":"2f03be08fcddd9a889e8c9d8de933cea"}
user.pancake?name
user.pancake?pass
user.pancake?name=My name
user?name=Bob Union => user.boun
user?name~B => user.boun

View file

@ -0,0 +1,55 @@
// SPDX-FileCopyrightText: 2012 pancake <pancake@nopcode.org>
// SPDX-License-Identifier: MIT
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "rangstr.h"
/* public sdb api */
char *api_json_get(const char *s, const char *p) {
Rangstr rs = json_get(s, p);
return rangstr_dup(&rs);
}
char *api_json_set(const char *s, const char *k, const char *v) {
const char *beg[3];
const char *end[3];
int idx, len[3];
char *str = NULL;
Rangstr rs = json_get(s, k);
if (!rs.p) {
return NULL;
}
#define WLEN(x) (int)(size_t)(end[x] - beg[x])
beg[0] = s;
end[0] = rs.p + rs.f;
len[0] = WLEN(0);
beg[1] = v;
end[1] = v + strlen(v);
len[1] = WLEN(1);
beg[2] = rs.p + rs.t;
end[2] = s + strlen(s);
len[2] = WLEN(2);
str = malloc(len[0] + len[1] + len[2] + 1);
if (!str) {
return NULL;
}
idx = len[0];
memcpy(str, beg[0], idx);
memcpy(str + idx, beg[1], len[1]);
idx += len[1];
memcpy(str + idx, beg[2], len[2]);
str[idx + len[2]] = 0;
return str;
}
char *api_json_seti(const char *s, const char *k, int a) {
char str[64];
sprintf(str, "%d", a);
return api_json_set(s, k, str);
}

View file

@ -0,0 +1,140 @@
// SPDX-FileCopyrightText: 2012-2018 pancake <pancake@nopcode.org>
// SPDX-License-Identifier: MIT
#include <limits.h>
static void doIndent(int idt, char **o, const char *tab) {
int i;
char *x;
for (i = 0; i < idt; i++) {
for (x = (char *)tab; *x; x++) {
*(*o)++ = *x;
}
}
}
RZ_API char *sdb_json_indent(const char *s, const char *tab) {
int idx, indent = 0;
int instr = 0;
size_t o_size = 0;
char *o, *O;
if (!s) {
return NULL;
}
size_t tab_len = strlen(tab);
for (idx = 0; s[idx]; idx++) {
if (o_size > INT_MAX - (indent * tab_len + 2)) {
return NULL;
}
if (s[idx] == '{' || s[idx] == '[') {
indent++;
// 2 corresponds to the \n and the parenthesis
o_size += indent * tab_len + 2;
} else if (s[idx] == '}' || s[idx] == ']') {
if (indent > 0) {
indent--;
}
// 2 corresponds to the \n and the parenthesis
o_size += indent * tab_len + 2;
} else if (s[idx] == ',') {
// 2 corresponds to the \n and the ,
o_size += indent * tab_len + 2;
} else if (s[idx] == ':') {
o_size += 2;
} else {
o_size++;
}
}
// 2 corresponds to the last \n and \0
o_size += 2;
indent = 0;
O = malloc(o_size + 1);
if (!O) {
return NULL;
}
for (o = O; *s; s++) {
if (instr) {
if (s[0] == '"') {
instr = 0;
} else if (s[0] == '\\' && s[1] == '"') {
*o++ = *s;
}
*o++ = *s;
continue;
} else {
if (s[0] == '"') {
instr = 1;
}
}
if (*s == '\n' || *s == '\r' || *s == '\t' || *s == ' ') {
continue;
}
switch (*s) {
case ':':
*o++ = *s;
*o++ = ' ';
break;
case ',':
*o++ = *s;
*o++ = '\n';
doIndent(indent, &o, tab);
break;
case '{':
case '[':
*o++ = *s;
*o++ = (indent != -1) ? '\n' : ' ';
indent++;
doIndent(indent, &o, tab);
break;
case '}':
case ']':
*o++ = '\n';
indent--;
doIndent(indent, &o, tab);
*o++ = *s;
break;
default:
*o++ = *s;
}
}
*o++ = '\n';
*o = 0;
return O;
}
// TODO: move to utils?
RZ_API char *sdb_json_unindent(const char *s) {
int instr = 0;
int len = strlen(s);
char *o, *O = malloc(len + 1);
if (!O) {
return NULL;
}
memset(O, 0, len);
for (o = O; *s; s++) {
if (instr) {
if (s[0] != '"') {
if (s[0] == '\\' && s[1] == '"') {
*o++ = *s;
}
} else {
instr = 0;
}
*o++ = *s;
continue;
} else if (s[0] == '"') {
instr = 1;
}
if (*s == '\n' || *s == '\r' || *s == '\t' || *s == ' ') {
continue;
}
*o++ = *s;
}
*o = 0;
return O;
}

View file

@ -0,0 +1,306 @@
// SPDX-FileCopyrightText: 2010-2018 Jeremie Miller
// SPDX-License-Identifier: MIT
// opportunity to further optimize would be having different jump tables for higher depths
#include "rangstr.h"
#define PUSH(i) \
if (depth == 1) \
prev = *out++ = ((cur + i) - js)
#define CAP(i) \
if (depth == 1) \
prev = *out++ = ((cur + i) - (js + prev) + 1)
#ifdef _MSC_VER
#define HAVE_COMPUTED_GOTOS 0
#elif __EMSCRIPTEN__
#define HAVE_COMPUTED_GOTOS 0
#else
#define HAVE_COMPUTED_GOTOS 1
#endif
#if HAVE_COMPUTED_GOTOS
#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
#pragma GCC diagnostic push
#endif
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#pragma GCC diagnostic ignored "-Wpragmas"
#pragma GCC diagnostic ignored "-Winitializer-overrides"
#define HAVE_RAWSTR 0
int sdb_js0n(const ut8 *js, RangstrType len, RangstrType *out) {
ut32 prev = 0;
const ut8 *cur, *end;
int depth = 0, utf8_remain = 0;
static void *gostruct[] = {
[0 ... 255] = &&l_bad,
['\t'] = &&l_loop,
[' '] = &&l_loop,
['\r'] = &&l_loop,
['\n'] = &&l_loop,
['"'] = &&l_qup,
[':'] = &&l_loop,
[','] = &&l_loop,
['['] = &&l_up,
[']'] = &&l_down, // tracking [] and {} individually would allow fuller validation but is really messy
['{'] = &&l_up,
['}'] = &&l_down,
// TODO: add support for rawstrings
#if HAVE_RAWSTR
['a' ... 'z'] = &&l_rawstr,
#else
['-'] = &&l_bare,
[48 ... 57] = &&l_bare, // 0-9
['t'] = &&l_bare,
['f'] = &&l_bare,
['n'] = &&l_bare // true, false, null
#endif
};
static void *gobare[] = {
[0 ... 31] = &&l_bad,
[32 ... 126] = &&l_loop, // could be more pedantic/validation-checking
['\t'] = &&l_unbare,
[' '] = &&l_unbare,
['\r'] = &&l_unbare,
['\n'] = &&l_unbare,
[','] = &&l_unbare,
[']'] = &&l_unbare,
['}'] = &&l_unbare,
[127 ... 255] = &&l_bad
};
#if HAVE_RAWSTR
static void *gorawstr[] = {
[0 ... 31] = &&l_bad, [127] = &&l_bad, [32 ... 126] = &&l_loop, ['\\'] = &&l_esc, [':'] = &&l_qdown, [128 ... 191] = &&l_bad, [192 ... 223] = &&l_utf8_2, [224 ... 239] = &&l_utf8_3, [240 ... 247] = &&l_utf8_4, [248 ... 255] = &&l_bad
};
#endif
static void *gostring[] = {
[0 ... 31] = &&l_bad, [127] = &&l_bad, [32 ... 126] = &&l_loop, ['\\'] = &&l_esc, ['"'] = &&l_qdown, [128 ... 191] = &&l_bad, [192 ... 223] = &&l_utf8_2, [224 ... 239] = &&l_utf8_3, [240 ... 247] = &&l_utf8_4, [248 ... 255] = &&l_bad
};
static void *goutf8_continue[] = {
[0 ... 127] = &&l_bad,
[128 ... 191] = &&l_utf_continue,
[192 ... 255] = &&l_bad
};
static void *goesc[] = {
[0 ... 255] = &&l_bad,
['"'] = &&l_unesc,
['\\'] = &&l_unesc,
['/'] = &&l_unesc,
['b'] = &&l_unesc,
['f'] = &&l_unesc,
['n'] = &&l_unesc,
['r'] = &&l_unesc,
['t'] = &&l_unesc,
['u'] = &&l_unesc
};
static void **go = gostruct;
#if 0
printf (" gostrct= %p\n", gostruct);
printf (" gobare = %p\n", gobare);
printf (" gostr = %p\n", gostring);
printf (" goesc = %p\n", goesc);
printf (" goutf8= %p\n", goutf8_continue);
#endif
for (cur = js, end = js + len; cur < end; cur++) {
// printf (" --> %s %p\n", cur, go[*cur]);
goto *go[*cur];
l_loop:;
}
return depth; // 0 if successful full parse, >0 for incomplete data
l_bad:
return 1;
l_up:
PUSH(0);
++depth;
goto l_loop;
l_down:
--depth;
CAP(0);
goto l_loop;
l_qup:
PUSH(1);
go = gostring;
goto l_loop;
l_qdown:
CAP(-1);
go = gostruct;
goto l_loop;
l_esc:
go = goesc;
goto l_loop;
l_unesc:
go = gostring;
goto l_loop;
#if HAVE_RAWSTR
l_rawstr:
PUSH(0);
go = gorawstr;
goto l_loop;
#endif
l_bare:
PUSH(0);
go = gobare;
goto l_loop;
l_unbare:
CAP(-1);
go = gostruct;
goto *go[*cur];
l_utf8_2:
go = goutf8_continue;
utf8_remain = 1;
goto l_loop;
l_utf8_3:
go = goutf8_continue;
utf8_remain = 2;
goto l_loop;
l_utf8_4:
go = goutf8_continue;
utf8_remain = 3;
goto l_loop;
l_utf_continue:
if (!--utf8_remain)
go = gostring;
goto l_loop;
}
#else // HAVE_COMPUTED_GOTOS
#define GO_DOWN (1)
#define GO_UP (1 << 1)
#define GO_Q_DOWN (1 << 2)
#define GO_Q_UP (1 << 3)
#define GO_BARE (1 << 4)
#define GO_UNBARE (1 << 5)
#define GO_ESCAPE (1 << 6)
#define GO_UNESCAPE (1 << 7)
#define GO_UTF8 (1 << 8)
#define GO_UTF8_CONTINUE (1 << 9)
int sdb_js0n(const ut8 *js, RangstrType len, RangstrType *out) {
ut32 prev = 0;
const ut8 *cur, *end;
int depth = 0, utf8_remain = 0, what_did = 1;
for (cur = js, end = js + len; cur < end; cur++) {
if (what_did & GO_BARE) {
switch (*cur) {
case ' ':
case '\t':
case '\r':
case '\n':
case ',':
case ']':
case '}':
what_did = GO_UNBARE;
CAP(-1);
break;
default:
if (*cur >= 32 && *cur <= 126) {
continue;
}
return 1;
}
// Same *cur
}
if (what_did & GO_UTF8) {
if (*cur < 128 || (*cur >= 192 && *cur <= 255)) {
return 1;
}
if (!--utf8_remain) {
what_did = GO_UTF8_CONTINUE;
}
continue;
}
if (what_did & GO_ESCAPE) {
switch (*cur) {
case '"':
case '\\':
case '/':
case 'b':
case 'f':
case 'n':
case 'r':
case 't':
case 'u':
what_did = GO_UNESCAPE;
break;
default:
return 1;
}
continue;
}
if (what_did & GO_Q_UP || what_did & GO_UTF8_CONTINUE || what_did & GO_UNESCAPE) {
switch (*cur) {
case '\\':
what_did = GO_ESCAPE;
break;
case '"':
what_did = GO_Q_DOWN;
CAP(-1);
break;
default:
if (*cur <= 31 || (*cur >= 127 && *cur <= 191) || (*cur >= 248 && *cur <= 255)) {
return 1;
}
if (*cur < 127) {
continue;
}
what_did = GO_UTF8;
if (*cur < 224) {
utf8_remain = 1;
continue;
}
if (*cur < 239) {
utf8_remain = 2;
continue;
}
utf8_remain = 3;
break;
}
continue;
}
switch (*cur) {
case '\t':
case ' ':
case '\r':
case '\n':
case ',':
case ':':
break;
case '"':
PUSH(1);
what_did = GO_Q_UP;
break;
case '[':
case '{':
PUSH(0);
++depth;
what_did = GO_UP;
break;
case ']':
case '}':
--depth;
CAP(0);
what_did = GO_DOWN;
break;
case '-':
case 't':
case 'f':
case 'n':
what_did = GO_BARE;
PUSH(0);
break;
default:
if (*cur >= 48 && *cur <= 57) { // 0-9
what_did = GO_BARE;
PUSH(0);
break;
}
return 1;
}
}
return depth;
}
#endif // HAVE_COMPUTED_GOTOS

View file

@ -0,0 +1,97 @@
// SPDX-FileCopyrightText: 2012 pancake <pancake@nopcode.org>
// SPDX-License-Identifier: MIT
#include <stdio.h>
#include <stdlib.h>
#include "rangstr.h"
#include "json.h"
#include "../sdb.h"
Rangstr json_find(const char *s, Rangstr *rs);
int test_main() {
Rangstr rs;
rs = json_get("{\"hello\":\"world\"}", "hello");
printf("OUT: ");
rangstr_print(&rs);
printf("\n");
printf("--\n");
rs = json_get("{\"hello\":{\"world\":123}}", "hello.world");
printf("OUT: ");
rangstr_print(&rs);
printf("\n");
return 0;
}
int test_glossary(char *buf) {
char *path;
{
path = "glossary.title";
char *s = api_json_set(buf, path, "patata");
if (s) {
printf("%s\n", s);
free(s);
} else
printf("set failed\n");
}
{
path = "glossary.title";
char *s = api_json_get(buf, path);
if (s) {
printf("%s\n", s);
free(s);
} else
printf("set failed\n");
}
{
path = "glossary.title";
char *s = api_json_get(buf, path);
if (s) {
printf("%s\n", s);
free(s);
} else
printf("set failed\n");
}
return 0;
}
int main(int argc, char **argv) {
Rangstr rs;
char buf[4096];
int n = fread(buf, 1, sizeof(buf), stdin);
buf[n] = 0;
// return test_glossary (buf);
// return test_main ();
char *path = argv[1];
#if 1
printf(">>>> %s <<<<\n", sdb_json_unindent(buf));
printf(">>>> %s <<<<\n", sdb_json_indent(buf, " "));
// set value //
path = "glossary.title";
char *s = api_json_set(buf, path, "patata");
if (s) {
printf("%s\n", s);
free(s);
} else
printf("set failed\n");
#endif
// printf ("%s\n", str); return 0;
// s = "[1,3,4]";
#if 0
char *a = "{}";
a = json_seti (a, "id", 123);
a = json_seti (a, "user.name", "blah");
printf ("id = %d\n", json_geti ("{'id':123}", "id"));
#endif
// json_walk (buf);
path = argv[1];
rs = json_get(buf, path);
printf("-- output --\n");
rangstr_print(&rs);
printf("\n");
return 0;
}

View file

@ -0,0 +1,176 @@
// SPDX-FileCopyrightText: 2012-2017 pancake <pancake@nopcode.org>
// SPDX-License-Identifier: MIT
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "rangstr.c"
#include <rz_types.h>
RZ_IPI void json_path_first(Rangstr *s) {
char *p;
if (!s->p) {
return;
}
p = strchr(s->p, '.');
s->f = 0;
s->t = p ? (size_t)(p - s->p) : strlen(s->p);
}
RZ_IPI int json_path_next(Rangstr *s) {
int stop = '.';
if (!s || !s->p || !s->p[s->t]) {
return 0;
}
if (!s->next) {
return 0;
}
if (s->p[s->t] == '"') {
s->t++;
}
rep:
if (s->p[s->t] == '[') {
s->type = '[';
stop = ']';
} else {
s->type = 0;
}
s->f = ++s->t;
if (s->p[s->t] == stop) {
s->f = ++s->t;
}
if (!s->p[s->t]) {
return 0;
}
while (s->p[s->t] != stop) {
if (!s->p[s->t]) {
s->next = 0;
return 1;
}
if (s->p[s->t] == '[') {
break;
}
s->t++;
}
if (s->f == s->t) {
goto rep;
}
if (s->p[s->f] == '"') {
s->f++;
s->t--;
}
return 1;
}
RZ_IPI Rangstr json_find(const char *s, Rangstr *rs) {
#define RESFIXSZ 1024
RangstrType resfix[RESFIXSZ] = { 0 };
RangstrType *res = resfix;
int i, j, n, len, ret;
Rangstr rsn;
if (!s) {
return rangstr_null();
}
len = strlen(s);
if (len > RESFIXSZ) {
res = calloc(len + 1, sizeof(RangstrType));
if (!res) {
eprintf("Cannot allocate %d byte%s\n",
len + 1, (len > 1) ? "s" : "");
return rangstr_null();
}
}
ret = sdb_js0n((const unsigned char *)s, len, res);
#define PFREE(x) \
if (x && x != resfix) \
free(x)
if (ret > 0) {
PFREE(res);
return rangstr_null();
}
if (*s == '[') {
n = rangstr_int(rs);
if (n < 0) {
goto beach;
}
for (i = j = 0; res[i] && j < n; i += 2, j++)
;
if (!res[i]) {
goto beach;
}
rsn = rangstr_news(s, res, i);
PFREE(res);
return rsn;
} else {
for (i = 0; res[i]; i += 4) {
Rangstr rsn = rangstr_news(s, res, i);
if (!rangstr_cmp(rs, &rsn)) {
rsn = rangstr_news(s, res, i + 2);
PFREE(res);
return rsn;
}
}
}
beach:
PFREE(res);
return rangstr_null();
}
RZ_IPI Rangstr json_get(const char *js, const char *p) {
int x, n = 0;
size_t rst;
Rangstr rj2, rj = rangstr_new(js);
Rangstr rs = rangstr_new(p);
json_path_first(&rs);
do {
rst = rs.t;
rs.f++;
x = rangstr_find(&rs, '[');
rs.f--;
if (x != -1)
rs.t = x;
#if 0
printf ("x = %d f = %d t = %d\n", x, rs.f, rs.t);
fprintf (stderr, "source (%s)\n", rangstr_dup (&rs));
fprintf (stderr, "onjson (%s)\n", rangstr_dup (&rj));
#endif
if (rst == rs.t && n && rj.p) // last key
break;
if (!rj.p)
break;
do {
rj2 = json_find(rangstr_str(&rj), &rs);
// fprintf (stderr, "++ (%s)(%d vs %d)\n", rangstr_dup (&rs), x, rs.t);
// if (rj.p[rj.f]=='[') { break; }
// fprintf (stderr, "ee %c\n", rj.p[rj.f]);
if (!rj2.p) {
if (!rj.p[rj.t])
return rj2;
break;
}
rj = rj2;
#if 0
fprintf (stderr, "-- (%s)\n", rangstr_dup (&rj));
#endif
} while (json_path_next(&rs));
// if (!rj.p) return rj;
#if 0
printf ("x = %d\n", x); printf ("rsf = %d\n", rs.f);
fprintf (stderr, "xxx (%s)\n", rangstr_dup (&rj));
return rj;
#endif
if ((rst == rs.t && n && rj.p)) // last key
break;
rs.t = rst;
rs.f = x;
n++;
} while (x != -1);
return rj;
}

View file

@ -0,0 +1,121 @@
// SPDX-FileCopyrightText: 2012-2017 pancake <pancake@nopcode.org>
// SPDX-License-Identifier: MIT
#ifndef RANGSTR_C
#define RANGSTR_C
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "rangstr.h"
#if 0
RZ_IPI void rangstr_print (Rangstr *s) {
if (s && s->p) {
(void) fwrite (s->p+s->f,
s->t-s->f, 1, stdout);
}
}
#endif
RZ_IPI Rangstr rangstr_null(void) {
Rangstr rs = { 0, 0, 0, 0, 0 };
return rs;
}
RZ_IPI Rangstr rangstr_new(const char *s) {
Rangstr rs;
if (!s) {
return rangstr_null();
}
rs.f = 0;
rs.next = 1;
rs.t = strlen(s);
rs.p = s;
rs.type = 0;
return rs;
}
RZ_IPI int rangstr_length(Rangstr *rs) {
if (rs->t > rs->f) {
return rs->t - rs->f;
}
return 0;
}
RZ_IPI int rangstr_int(Rangstr *s) {
if (!s || !s->p) {
return 0;
}
const int base = 10;
int mul = 1;
int ch, n = 0;
size_t i = 0;
if (s->p[s->f] == '[') {
i++;
}
if (s->p[s->f] == '-') {
mul = -1;
i += s->f + 1;
} else {
i += s->f;
}
for (; i < s->t; i++) {
ch = s->p[i];
if (ch < '0' || ch > '9') {
break;
}
n = n * base + (ch - '0');
}
return n * mul;
}
RZ_IPI char *rangstr_dup(Rangstr *rs) {
if (!rs->p) {
return NULL;
}
int len = rangstr_length(rs);
char *p = malloc(len + 1);
if (p) {
memcpy(p, rs->p + rs->f, len);
p[len] = 0;
}
return p;
}
RZ_IPI Rangstr rangstr_news(const char *s, RangstrType *res, int i) {
Rangstr rs;
rs.next = 1;
rs.f = res[i];
rs.t = res[i] + res[i + 1];
rs.p = s;
rs.type = 0;
return rs;
}
RZ_IPI int rangstr_cmp(Rangstr *a, Rangstr *b) {
int la = a->t - a->f;
int lb = b->t - b->f;
int lbz = strlen(b->p + b->f);
if (lbz < lb) {
lb = lbz;
}
if (la != lb) {
return 1;
}
return memcmp(a->p + a->f, b->p + b->f, la);
}
RZ_IPI int rangstr_find(Rangstr *a, char ch) {
size_t i = a->f;
while (i < a->t && a->p[i] && a->p[i] != ch)
i++;
return (i < a->t && a->p[i]) ? (int)i : -1;
}
RZ_IPI const char *rangstr_str(Rangstr *rs) {
return rs->p + rs->f;
}
#endif

View file

@ -0,0 +1,32 @@
// SPDX-FileCopyrightText: pancake <pancake@nopcode.org>
// SPDX-License-Identifier: MIT
#ifndef _INCLUDE_RANGSTR_H_
#define _INCLUDE_RANGSTR_H_
#include <sys/types.h>
#include <rz_types.h>
#define RangstrType unsigned int
typedef struct {
int type;
int next;
size_t f, t;
const char *p;
} Rangstr;
#if 0
RZ_IPI void rangstr_print (Rangstr *s);
RZ_IPI Rangstr rangstr_new (const char *s);
RZ_IPI Rangstr rangstr_null(void);
RZ_IPI int rangstr_int (Rangstr *s);
RZ_IPI char *rangstr_dup (Rangstr *rs);
RZ_IPI Rangstr rangstr_news (const char *s, RangstrType *res, int i);
RZ_IPI int rangstr_cmp (Rangstr *a, Rangstr *b);
RZ_IPI const char *rangstr_str (Rangstr* rs);
RZ_IPI int rangstr_length (Rangstr* rs);
RZ_IPI int rangstr_find (Rangstr* rs, char ch);
#endif
#endif

View file

@ -0,0 +1,14 @@
// SPDX-FileCopyrightText: 2012 pancake <pancake@nopcode.org>
// SPDX-License-Identifier: MIT
int test_parse_json_path() {
Rangstr rs = rangstr_new("ping.board[\"pop\"][1][2][\"caca\"].panda");
json_path_first(&rs);
do {
printf(" - ");
rangstr_print(&rs);
printf(" Int (%d)", rangstr_int(&rs));
printf("\n");
} while (json_path_next(&rs));
printf("--\n");
}

View file

@ -0,0 +1,4 @@
{ "glossary": { "title": "example glossary", "page": 1, "GlossDiv": { "title": "First gloss title",
"GlossList": { "GlossEntry": { "ID": "SGML", "SortAs": "SGML", "GlossTerm": "Standard Generalized Markup Language",
"Acronym": "SGML", "Abbrev": "ISO 8879:1986", "GlossDef": { "para": "A meta-markup language, used to create markup languages such as DocBook.",
"GlossSeeAlso": [ "OK", "GML", "XML" ] }, "GlossSee": "markup" } } } } }

81
librz/util/sdb/src/lock.c Normal file
View file

@ -0,0 +1,81 @@
// SPDX-FileCopyrightText: 2012-2016 pancake <pancake@nopcode.org>
// SPDX-License-Identifier: MIT
#include <rz_userconf.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <rz_util/rz_sys.h>
#include "sdb.h"
#if __WINDOWS__
#include <windows.h>
#endif
RZ_API const char *sdb_lock_file(const char *f) {
static char buf[128];
size_t len;
if (!f || !*f) {
return NULL;
}
len = strlen(f);
if (len + 10 > sizeof buf) {
return NULL;
}
memcpy(buf, f, len);
strcpy(buf + len, ".lock");
return buf;
}
RZ_API bool sdb_lock(const char *s) {
int fd;
char *pid, pidstr[64];
if (!s) {
return false;
}
fd = open(s, O_CREAT | O_TRUNC | O_WRONLY | O_EXCL, SDB_MODE);
if (fd == -1) {
return false;
}
pid = sdb_itoa(rz_sys_getpid(), pidstr, 10);
if (pid) {
if ((write(fd, pid, strlen(pid)) < 0) || (write(fd, "\n", 1) < 0)) {
close(fd);
return false;
}
}
close(fd);
return true;
}
RZ_API int sdb_lock_wait(const char *s) {
// TODO use flock() here
// wait forever here?
while (!sdb_lock(s)) {
// TODO: if waiting too much return 0
#if __WINDOWS__
Sleep(500); // hack
#else
// TODO use lockf() here .. flock is not much useful (fd, LOCK_EX);
sleep(1); // hack
#endif
}
return 1;
}
RZ_API void sdb_unlock(const char *s) {
// flock (fd, LOCK_UN);
unlink(s);
}
#if TEST
main() {
int r;
r = sdb_lock(".lock");
printf("%d\n", r);
r = sdb_lock(".lock");
printf("%d\n", r);
sdb_unlock(".lock");
r = sdb_lock(".lock");
printf("%d\n", r);
}
#endif

378
librz/util/sdb/src/ls.c Normal file
View file

@ -0,0 +1,378 @@
// SPDX-FileCopyrightText: 2007-2017 pancake <pancake@nopcode.org>
// SPDX-FileCopyrightText: 2007-2017 alvaro
// SPDX-License-Identifier: MIT
#include <string.h>
#include "ls.h"
RZ_API SdbList *ls_newf(SdbListFree freefn) {
SdbList *list = ls_new();
if (list) {
list->free = freefn;
}
return list;
}
RZ_API SdbList *ls_new(void) {
SdbList *list = RZ_NEW0(SdbList);
if (!list) {
return NULL;
}
return list;
}
static void ls_insertion_sort_iter(SdbListIter *iter, SdbListComparator cmp) {
SdbListIter *it, *it2;
for (it = iter; it && it->data; it = it->n) {
for (it2 = it->n; it2 && it2->data; it2 = it2->n) {
if (cmp(it->data, it2->data) > 0) {
void *t = it->data;
it->data = it2->data;
it2->data = t;
}
}
}
}
static void ls_insertion_sort(SdbList *list, SdbListComparator cmp) {
ls_insertion_sort_iter(list->head, cmp);
}
static SdbListIter *_merge(SdbListIter *first, SdbListIter *second, SdbListComparator cmp) {
SdbListIter *next = NULL, *result = NULL, *head = NULL;
while (first || second) {
if (!second) {
next = first;
first = first->n;
} else if (!first) {
next = second;
second = second->n;
} else if (cmp(first->data, second->data) <= 0) {
next = first;
first = first->n;
} else {
next = second;
second = second->n;
}
if (!head) {
result = next;
head = result;
head->p = NULL;
} else {
result->n = next;
next->p = result;
result = result->n;
}
}
head->p = NULL;
next->n = NULL;
return head;
}
static SdbListIter *_sdb_list_split(SdbListIter *head) {
SdbListIter *tmp;
SdbListIter *fast;
SdbListIter *slow;
if (!head || !head->n) {
return head;
}
slow = head;
fast = head;
while (fast && fast->n && fast->n->n) {
fast = fast->n->n;
slow = slow->n;
}
tmp = slow->n;
slow->n = NULL;
return tmp;
}
static SdbListIter *_merge_sort(SdbListIter *head, SdbListComparator cmp) {
SdbListIter *second;
if (!head || !head->n) {
return head;
}
second = _sdb_list_split(head);
head = _merge_sort(head, cmp);
second = _merge_sort(second, cmp);
return _merge(head, second, cmp);
}
RZ_API bool ls_merge_sort(SdbList *list, SdbListComparator cmp) {
if (!cmp) {
return false;
}
if (list && list->head && cmp) {
SdbListIter *iter;
list->head = _merge_sort(list->head, cmp);
// update tail reference
iter = list->head;
while (iter && iter->n) {
iter = iter->n;
}
list->tail = iter;
list->sorted = true;
}
return true;
}
RZ_API bool ls_sort(SdbList *list, SdbListComparator cmp) {
if (!cmp || list->cmp == cmp) {
return false;
}
if (list->length > 43) {
ls_merge_sort(list, cmp);
} else {
ls_insertion_sort(list, cmp);
}
list->cmp = cmp;
list->sorted = true;
return true;
}
RZ_API void ls_delete(SdbList *list, SdbListIter *iter) {
if (!list || !iter) {
return;
}
ls_split_iter(list, iter);
if (list->free && iter->data) {
list->free(iter->data);
iter->data = NULL;
}
free(iter);
}
RZ_API bool ls_delete_data(SdbList *list, void *ptr) {
void *kvp;
SdbListIter *iter;
ls_foreach (list, iter, kvp) {
if (ptr == kvp) {
ls_delete(list, iter);
return true;
}
}
return false;
}
RZ_API void ls_split_iter(SdbList *list, SdbListIter *iter) {
if (!list || !iter) {
return;
}
if (list->head == iter) {
list->head = iter->n;
}
if (list->tail == iter) {
list->tail = iter->p;
}
if (iter->p) {
iter->p->n = iter->n;
}
if (iter->n) {
iter->n->p = iter->p;
}
list->length--;
}
RZ_API void ls_destroy(SdbList *list) {
SdbListIter *it;
if (!list) {
return;
}
it = list->head;
while (it) {
SdbListIter *next = it->n;
ls_delete(list, it);
it = next;
}
list->head = list->tail = NULL;
list->length = 0;
}
RZ_API void ls_free(SdbList *list) {
if (!list) {
return;
}
ls_destroy(list);
list->free = NULL;
free(list);
}
RZ_API SdbListIter *ls_append(SdbList *list, void *data) {
SdbListIter *it;
if (!list) {
return NULL;
}
it = RZ_NEW(SdbListIter);
if (!it) {
return NULL;
}
if (list->tail) {
list->tail->n = it;
}
it->data = data;
it->p = list->tail;
it->n = NULL;
list->tail = it;
if (!list->head) {
list->head = it;
}
list->length++;
list->sorted = false;
return it;
}
RZ_API SdbListIter *ls_prepend(SdbList *list, void *data) {
SdbListIter *it = RZ_NEW(SdbListIter);
if (!it) {
return NULL;
}
if (list->head) {
list->head->p = it;
}
it->data = data;
it->n = list->head;
it->p = NULL;
list->head = it;
if (!list->tail) {
list->tail = it;
}
list->length++;
list->sorted = false;
return it;
}
RZ_API void *ls_pop(SdbList *list) {
void *data = NULL;
SdbListIter *iter;
if (list) {
if (list->tail) {
iter = list->tail;
if (list->head == list->tail) {
list->head = list->tail = NULL;
} else {
list->tail = iter->p;
list->tail->n = NULL;
}
data = iter->data;
free(iter);
list->length--;
}
return data;
}
return NULL;
}
RZ_API SdbList *ls_clone(SdbList *list) {
if (!list) {
return NULL;
}
SdbList *r = ls_new(); // ownership of elements stays in original list
if (!r) {
return NULL;
}
void *v;
SdbListIter *iter;
ls_foreach (list, iter, v) {
ls_append(r, v);
}
return r;
}
RZ_API int ls_join(SdbList *list1, SdbList *list2) {
if (!list1 || !list2) {
return 0;
}
if (!(list2->length)) {
return 0;
}
if (!(list1->length)) {
list1->head = list2->head;
list1->tail = list2->tail;
} else {
list1->tail->n = list2->head;
list2->head->p = list1->tail;
list1->tail = list2->tail;
list1->tail->n = NULL;
}
list1->length += list2->length;
list2->head = list2->tail = NULL;
list1->sorted = false;
return 1;
}
RZ_API SdbListIter *ls_insert(SdbList *list, int n, void *data) {
SdbListIter *it, *item;
int i;
if (list) {
if (!list->head || !n) {
return ls_prepend(list, data);
}
for (it = list->head, i = 0; it && it->data; it = it->n, i++) {
if (i == n) {
item = RZ_NEW0(SdbListIter);
if (!item) {
return NULL;
}
item->data = data;
item->n = it;
item->p = it->p;
if (it->p) {
it->p->n = item;
}
it->p = item;
list->length++;
list->sorted = false;
return item;
}
}
}
return ls_append(list, data);
}
RZ_API void *ls_pop_head(SdbList *list) {
void *data = NULL;
SdbListIter *iter;
if (list) {
if (list->head) {
iter = list->head;
if (list->head == list->tail) {
list->head = list->tail = NULL;
} else {
list->head = iter->n;
list->head->p = NULL;
}
data = iter->data;
free(iter);
}
list->length--;
return data;
}
return NULL;
}
RZ_API int ls_del_n(SdbList *list, int n) {
SdbListIter *it;
int i;
if (!list) {
return false;
}
for (it = list->head, i = 0; it && it->data; it = it->n, i++)
if (i == n) {
if (!it->p && !it->n) {
list->head = list->tail = NULL;
} else if (!it->p) {
it->n->p = NULL;
list->head = it->n;
} else if (!it->n) {
it->p->n = NULL;
list->tail = it->p;
} else {
it->p->n = it->n;
it->n->p = it->p;
}
free(it);
list->length--;
return true;
}
return false;
}

88
librz/util/sdb/src/ls.h Normal file
View file

@ -0,0 +1,88 @@
// SPDX-FileCopyrightText: pancake <pancake@nopcode.org>
// SPDX-License-Identifier: MIT
#ifndef LS_H
#define LS_H
#include <stdio.h>
#include <rz_types.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*SdbListFree)(void *ptr);
typedef int (*SdbListComparator)(const void *a, const void *b);
typedef struct ls_iter_t {
void *data;
struct ls_iter_t *n, *p;
} SdbListIter;
typedef struct ls_t {
size_t length;
SdbListIter *head;
SdbListIter *tail;
SdbListFree free;
SdbListComparator cmp;
bool sorted;
} SdbList;
#define ls_foreach(list, it, pos) \
if ((list)) \
for (it = (list)->head; it && (pos = it->data); it = it->n)
#define ls_foreach_safe(list, it, tmp, pos) \
if ((list)) \
for (it = list->head; \
it && (pos = it->data) && ((tmp = it->n) || 1); it = tmp)
#define ls_foreach_prev(list, it, pos) \
if ((list)) \
for (it = list->tail; it && (pos = it->data); it = it->p)
#define ls_iterator(x) (x) ? (x)->head : NULL
// #define ls_empty(x) (!x || (!x->head && !x->tail))
#define ls_empty(x) (!x || !x->length)
#define ls_head(x) x->head
#define ls_tail(x) x->tail
#define ls_unref(x) x
#define ls_iter_get(x) \
x->data; \
x = x->n
#define ls_iter_next(x) (x ? 1 : 0)
#define ls_iter_cur(x) x->p
#define ls_iter_unref(x) x
#define ls_length(x) x->length
RZ_API SdbList *ls_new(void);
RZ_API SdbList *ls_newf(SdbListFree freefn);
RZ_API SdbListIter *ls_append(SdbList *list, void *data);
RZ_API SdbListIter *ls_prepend(SdbList *list, void *data);
// RZ_API void ls_add_sorted(SdbList *list, void *data, SdbListComparator cmp);
RZ_API bool ls_sort(SdbList *list, SdbListComparator cmp);
RZ_API bool ls_merge_sort(SdbList *list, SdbListComparator cmp);
RZ_API void ls_delete(SdbList *list, SdbListIter *iter);
RZ_API bool ls_delete_data(SdbList *list, void *ptr);
RZ_API void ls_iter_init(SdbListIter *iter, SdbList *list);
RZ_API void ls_destroy(SdbList *list);
RZ_API void ls_free(SdbList *list);
RZ_API SdbListIter *ls_item_new(void *data);
RZ_API void ls_unlink(SdbList *list, void *ptr);
RZ_API void ls_split(SdbList *list, void *ptr);
// Removes element `iter` from `list`.
RZ_API void ls_split_iter(SdbList *list, SdbListIter *iter);
RZ_API void *ls_get_n(SdbList *list, int n);
RZ_API void *ls_get_top(SdbList *list);
#define ls_push(x, y) ls_append(x, y)
RZ_API void *ls_pop(SdbList *list);
RZ_API void ls_reverse(SdbList *list);
RZ_API SdbList *ls_clone(SdbList *list);
RZ_API int ls_join(SdbList *first, SdbList *second);
RZ_API int ls_del_n(SdbList *list, int n);
RZ_API SdbListIter *ls_insert(SdbList *list, int n, void *data);
RZ_API void *ls_pop_head(SdbList *list);
#ifdef __cplusplus
}
#endif
#endif

532
librz/util/sdb/src/main.c Normal file
View file

@ -0,0 +1,532 @@
// SPDX-FileCopyrightText: 2011-2020 pancake <pancake@nopcode.org>
// SPDX-License-Identifier: MIT
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include "sdb.h"
#include "sdb_private.h"
#define MODE_ZERO '0'
#define MODE_JSON 'j'
#define MODE_DFLT 0
static int save = 0;
static Sdb *s = NULL;
static ut32 options = SDB_OPTION_FS | SDB_OPTION_NOSTAMP;
static void terminate(RZ_UNUSED int sig) {
if (!s) {
return;
}
if (save && !sdb_sync(s)) {
sdb_free(s);
s = NULL;
exit(1);
}
sdb_free(s);
exit(sig < 2 ? sig : 0);
}
static void write_null(void) {
write_(1, "", 1);
}
#define BS 128
#define USE_SLURPIN 1
static char *slurp(FILE *f, size_t *sz) {
int blocksize = BS;
static int bufsize = BS;
static char *next = NULL;
static size_t nextlen = 0;
size_t len, rr, rr2;
char *tmp, *buf = NULL;
if (sz) {
*sz = 0;
}
#if USE_SLURPIN
if (!sz) {
/* this is faster but have limits */
/* run test/add10k.sh script to benchmark */
const int buf_size = 96096;
buf = calloc(1, buf_size);
if (!buf) {
return NULL;
}
if (!fgets(buf, buf_size, f)) {
free(buf);
return NULL;
}
if (feof(f)) {
free(buf);
return NULL;
}
size_t buf_len = strlen(buf);
if (buf_len > 0) {
buf[buf_len - 1] = '\0';
}
char *newbuf = realloc(buf, buf_len + 1);
// realloc behaves like free if buf_len is 0
if (!newbuf) {
return buf;
}
return newbuf;
}
#endif
buf = calloc(BS + 1, 1);
if (!buf) {
return NULL;
}
len = 0;
for (;;) {
if (next) {
free(buf);
buf = next;
bufsize = nextlen + blocksize;
// len = nextlen;
rr = nextlen;
rr2 = fread(buf + nextlen, 1, blocksize, f);
if (rr2 > 0) {
rr += rr2;
bufsize += rr2;
}
next = NULL;
nextlen = 0;
} else {
rr = fread(buf + len, 1, blocksize, f);
}
if (rr < 1) { // EOF
buf[len] = 0;
next = NULL;
break;
}
len += rr;
// buf[len] = 0;
#if !USE_SLURPIN
if (!sz) {
char *nl = strchr(buf, '\n');
if (nl) {
*nl++ = 0;
int nlen = nl - buf;
nextlen = len - nlen;
if (nextlen > 0) {
next = malloc(nextlen + blocksize + 1);
if (!next) {
eprintf("Cannot malloc %d\n", nextlen);
break;
}
memcpy(next, nl, nextlen);
if (!*next) {
next = NULL;
} else {
// continue;
}
} else {
next = NULL;
nextlen = 0; // strlen (next);;
}
break;
}
}
#endif
bufsize += blocksize;
tmp = realloc(buf, bufsize + 1);
if (!tmp) {
bufsize -= blocksize;
break;
}
memset(tmp + bufsize - blocksize, 0, blocksize);
buf = tmp;
}
if (sz) {
*sz = len;
}
if (len < 1) {
free(buf);
return buf = NULL;
}
buf[len] = 0;
return buf;
}
#if HAVE_HEADER_SYS_MMAN_H
static void synchronize(RZ_UNUSED int sig) {
// TODO: must be in sdb_sync() or wat?
sdb_sync(s);
Sdb *n = sdb_new(s->path, s->name, s->lock);
if (n) {
sdb_config(n, options);
sdb_free(s);
s = n;
}
}
#endif
static int sdb_grep_dump(const char *dbname, int fmt, bool grep,
const char *expgrep) {
char *v, k[SDB_CDB_MAX_KEY] = { 0 };
const char *comma = "";
// local db beacuse is readonly and we dont need to finalize in case of ^C
Sdb *db = sdb_new(NULL, dbname, 0);
if (!db) {
return 1;
}
sdb_config(db, options);
sdb_dump_begin(db);
if (fmt == MODE_JSON) {
printf("{");
}
while (sdb_dump_dupnext(db, k, &v, NULL)) {
if (grep && !strstr(k, expgrep) && !strstr(v, expgrep)) {
free(v);
continue;
}
switch (fmt) {
case MODE_JSON:
if (!strcmp(v, "true") || !strcmp(v, "false")) {
printf("%s\"%s\":%s", comma, k, v);
} else if (sdb_isnum(v)) {
printf("%s\"%s\":%" PFMT64x "u", comma, k, sdb_atoi(v));
} else if (*v == '{' || *v == '[') {
printf("%s\"%s\":%s", comma, k, v);
} else {
printf("%s\"%s\":\"%s\"", comma, k, v);
}
comma = ",";
break;
case MODE_ZERO:
printf("%s=%s", k, v);
break;
default:
printf("%s=%s\n", k, v);
break;
}
free(v);
}
switch (fmt) {
case MODE_ZERO:
fflush(stdout);
write_null();
break;
case MODE_JSON:
printf("}\n");
break;
}
sdb_free(db);
return 0;
}
static int sdb_grep(const char *db, int fmt, const char *grep) {
return sdb_grep_dump(db, fmt, true, grep);
}
static int sdb_dump(const char *db, int fmt) {
return sdb_grep_dump(db, fmt, false, NULL);
}
static int insertkeys(Sdb *s, const char **args, int nargs, int mode) {
int must_save = 0;
if (args && nargs > 0) {
int i;
for (i = 0; i < nargs; i++) {
switch (mode) {
case '-':
must_save |= sdb_query(s, args[i]);
break;
case '=':
if (strchr(args[i], '=')) {
char *v, *kv = (char *)strdup(args[i]);
v = strchr(kv, '=');
if (v) {
*v++ = 0;
sdb_disk_insert(s, kv, v);
}
free(kv);
}
break;
}
}
}
return must_save;
}
static int createdb(const char *f, const char **args, int nargs) {
s = sdb_new(NULL, f, 0);
if (!s) {
eprintf("Cannot create database\n");
return 1;
}
sdb_config(s, options);
int ret = 0;
if (args) {
int i;
for (i = 0; i < nargs; i++) {
if (!sdb_text_load(s, args[i])) {
eprintf("Failed to load text sdb from %s\n", args[i]);
}
}
} else {
size_t len;
char *in = slurp(stdin, &len);
if (!in) {
return 0;
}
if (!sdb_text_load_buf(s, in, len)) {
eprintf("Failed to read text sdb from stdin\n");
}
free(in);
}
sdb_sync(s);
return ret;
}
static int showusage(int o) {
printf("usage: sdb [-0cdehjJv|-D A B] [-|db] "
"[.file]|[-=]|==||[-+][(idx)key[:json|=value] ..]\n");
if (o == 2) {
printf(" -0 terminate results with \\x00\n"
" -c count the number of keys database\n"
" -d decode base64 from stdin\n"
" -D diff two databases\n"
" -e encode stdin as base64\n"
" -h show this help\n"
" -j output in json\n"
" -J enable journaling\n"
" -v show version information\n");
return 0;
}
return o;
}
static int showversion(void) {
fflush(stdout);
return 0;
}
static int jsonIndent(void) {
size_t len;
char *out;
char *in = slurp(stdin, &len);
if (!in) {
return 0;
}
out = sdb_json_indent(in, " ");
if (!out) {
free(in);
return 1;
}
puts(out);
free(out);
free(in);
return 0;
}
static int base64encode(void) {
char *out;
size_t len = 0;
ut8 *in = (ut8 *)slurp(stdin, &len);
if (!in) {
return 0;
}
out = sdb_encode(in, (int)len);
if (!out) {
free(in);
return 1;
}
puts(out);
free(out);
free(in);
return 0;
}
static int base64decode(void) {
ut8 *out;
size_t len, ret = 1;
char *in = slurp(stdin, &len);
if (in) {
int declen;
out = sdb_decode(in, &declen);
if (out && declen >= 0) {
write_(1, out, declen);
ret = 0;
}
free(out);
free(in);
}
return ret;
}
static void dbdiff_cb(const SdbDiff *diff, void *user) {
char sbuf[512];
int r = sdb_diff_format(sbuf, sizeof(sbuf), diff);
if (r < 0) {
return;
}
char *buf = sbuf;
char *hbuf = NULL;
if ((size_t)r >= sizeof(sbuf)) {
hbuf = malloc(r + 1);
if (!hbuf) {
return;
}
r = sdb_diff_format(hbuf, r + 1, diff);
if (r < 0) {
goto beach;
}
}
printf("\x1b[%sm%s\x1b[0m\n", diff->add ? "32" : "31", buf);
beach:
free(hbuf);
}
static bool dbdiff(const char *a, const char *b) {
Sdb *A = sdb_new(NULL, a, 0);
Sdb *B = sdb_new(NULL, b, 0);
bool equal = sdb_diff(A, B, dbdiff_cb, NULL);
sdb_free(A);
sdb_free(B);
return equal;
}
int showcount(const char *db) {
ut32 d;
s = sdb_new(NULL, db, 0);
if (sdb_stats(s, &d, NULL)) {
printf("%d\n", d);
}
// TODO: show version, timestamp information
sdb_free(s);
return 0;
}
int main(int argc, const char **argv) {
char *line;
const char *arg, *grep = NULL;
int i, fmt = MODE_DFLT;
int db0 = 1, argi = 1;
bool interactive = false;
/* terminate flags */
if (argc < 2) {
return showusage(1);
}
arg = argv[1];
if (arg[0] == '-') { // && arg[1] && arg[2]==0) {
switch (arg[1]) {
case 0:
/* no-op */
break;
case '0':
fmt = MODE_ZERO;
db0++;
argi++;
if (db0 >= argc) {
return showusage(1);
}
break;
case 'g':
db0 += 2;
if (db0 >= argc) {
return showusage(1);
}
grep = argv[2];
argi += 2;
break;
case 'J':
options |= SDB_OPTION_JOURNAL;
db0++;
argi++;
if (db0 >= argc) {
return showusage(1);
}
break;
case 'c': return (argc < 3) ? showusage(1) : showcount(argv[2]);
case 'v': return showversion();
case 'h': return showusage(2);
case 'e': return base64encode();
case 'd': return base64decode();
case 'D':
if (argc == 4) {
return dbdiff(argv[2], argv[3]) ? 0 : 1;
}
return showusage(0);
case 'j':
if (argc > 2) {
return sdb_dump(argv[db0 + 1], MODE_JSON);
}
return jsonIndent();
default:
eprintf("Invalid flag %s\n", arg);
break;
}
}
/* sdb - */
if (argi == 1 && !strcmp(argv[argi], "-")) {
/* no database */
argv[argi] = "";
if (argc == db0 + 1) {
interactive = true;
/* if no argument passed */
argv[argi] = "-";
argc++;
argi++;
}
}
/* sdb dbname */
if (argc - 1 == db0) {
if (grep) {
return sdb_grep(argv[db0], fmt, grep);
}
return sdb_dump(argv[db0], fmt);
}
#if HAVE_HEADER_SYS_MMAN_H
signal(SIGINT, terminate);
signal(SIGHUP, synchronize);
#endif
int ret = 0;
if (interactive || !strcmp(argv[db0 + 1], "-")) {
if ((s = sdb_new(NULL, argv[db0], 0))) {
sdb_config(s, options);
int kvs = db0 + 2;
if (kvs < argc) {
save |= insertkeys(s, argv + argi + 2, argc - kvs, '-');
}
for (; (line = slurp(stdin, NULL));) {
save |= sdb_query(s, line);
if (fmt) {
fflush(stdout);
write_null();
}
free(line);
}
}
} else if (!strcmp(argv[db0 + 1], "=")) {
ret = createdb(argv[db0], NULL, 0);
} else if (!strcmp(argv[db0 + 1], "==")) {
ret = createdb(argv[db0], argv + db0 + 2, argc - (db0 + 2));
} else {
s = sdb_new(NULL, argv[db0], 0);
if (!s) {
return 1;
}
sdb_config(s, options);
for (i = db0 + 1; i < argc; i++) {
save |= sdb_query(s, argv[i]);
if (fmt) {
fflush(stdout);
write_null();
}
}
}
terminate(ret);
return ret;
}

120
librz/util/sdb/src/match.c Normal file
View file

@ -0,0 +1,120 @@
// SPDX-FileCopyrightText: 2015-2016 pancake <pancake@nopcode.org>
// SPDX-License-Identifier: MIT
#include "sdb.h"
#include <ctype.h>
static inline int haveSuffix(const char *glob, int glob_len, const char *sfx) {
const int sfx_len = strlen(sfx);
return (glob_len > sfx_len && !strcmp(glob + glob_len - sfx_len, sfx));
}
static inline int havePrefix(const char *glob, int glob_len, const char *pfx) {
const int pfx_len = strlen(pfx);
return (pfx_len < glob_len && !strncmp(glob, pfx, pfx_len));
}
enum MatchFlag {
SDB_LIKE_NONE = 0,
SDB_LIKE_ICASE = 1, // ?i
SDB_LIKE_START = 2, // ^
SDB_LIKE_END = 4, // $
SDB_LIKE_BASE64 = 8 // %
};
static inline int mycmp(const char *a, const char *b, int n, int any) {
int i, j;
for (i = j = 0; a[i] && b[j] && j < n; i++) {
if (tolower((const ut8)a[i]) == tolower((const ut8)b[j])) {
j++;
} else {
if (!any) {
return 0;
}
j = 0;
}
}
return any ? j != n : 1;
}
static inline int strstr2(const char *a, const char *b, int n) {
int i, j;
for (i = j = 0; a[i] && b[j] && j < n; i++) {
if (a[i] == b[j]) {
j++;
} else {
j = 0;
}
}
return j == n;
}
static inline bool compareString(const char *a, const char *b, int blen, int flags) {
const int start = flags & SDB_LIKE_START;
const int end = flags & SDB_LIKE_END;
char *aa = NULL;
int alen;
bool ret = false;
if (!a || !b || blen < 0) {
return 0;
}
if (flags & SDB_LIKE_BASE64) {
aa = (char *)sdb_decode(a, &alen);
if (!aa) {
return 0;
}
a = (const char *)aa;
} else {
alen = strlen(a);
}
if (blen <= alen) {
if (flags & SDB_LIKE_ICASE) {
if (start && end)
ret = (alen == blen && !mycmp(a, b, blen, 0));
else if (start)
ret = !mycmp(a, b, blen, 0);
else if (end)
ret = !mycmp(a + (alen - blen), b, blen, 0);
else
ret = !mycmp(a, b, blen, 1);
} else {
if (start && end)
ret = (alen == blen && !strncmp(a, b, blen));
else if (start)
ret = !strncmp(a, b, blen);
else if (end)
ret = !strncmp(a + (alen - blen), b, blen);
else
ret = strstr2(a, b, blen);
}
}
free(aa);
return ret;
}
RZ_API bool sdb_match(const char *str, const char *glob) {
int glob_len, flags = SDB_LIKE_NONE;
if (!str || !glob) {
return false;
}
glob_len = strlen(glob);
if (haveSuffix(glob, glob_len, "?i")) {
glob_len -= 2;
flags |= SDB_LIKE_ICASE;
}
if (havePrefix(glob, glob_len, "%")) {
glob++;
glob_len--;
flags |= SDB_LIKE_BASE64;
}
if (havePrefix(glob, glob_len, "^")) {
glob++;
glob_len--;
flags |= SDB_LIKE_START;
}
if (haveSuffix(glob, glob_len, "$")) {
glob_len--;
flags |= SDB_LIKE_END;
}
return compareString(str, glob, glob_len, flags);
}

View file

@ -0,0 +1,51 @@
libsdb_sources = files(
'array.c',
'set.c',
'base64.c',
'buffer.c',
'cdb.c',
'cdb_make.c',
'diff.c',
'disk.c',
'fmt.c',
'ht_uu.c',
'ht_pp.c',
'ht_up.c',
'ht_pu.c',
'journal.c',
'json.c',
#'json/api.c',
#'json/indent.c',
#'json/js0n.c',
#'json/path.c',
#'json/rangstr.c',
'lock.c',
'ls.c',
'match.c',
'ns.c',
'num.c',
'query.c',
'sdb.c',
'sdbht.c',
'util.c',
'text.c'
)
libsdb_inc = [platform_inc, include_directories(['..', '.'])]
include_files = files(
'buffer.h',
'cdb.h',
'cdb_make.h',
'ht_inc.h',
'ht_pp.h',
'ht_up.h',
'ht_uu.h',
'ht_pu.h',
'ls.h',
'sdb.h',
'sdbht.h',
'set.h',
)
install_headers(include_files, install_dir: join_paths(rizin_incdir, 'sdb'))

253
librz/util/sdb/src/ns.c Normal file
View file

@ -0,0 +1,253 @@
// SPDX-FileCopyrightText: 2011-2016 pancake <pancake@nopcode.org>
// SPDX-License-Identifier: MIT
#include "sdb.h"
RZ_API void sdb_ns_lock(Sdb *s, int lock, int depth) {
SdbListIter *it;
SdbNs *ns;
s->ns_lock = lock;
if (depth) { // handles -1 as infinite
ls_foreach (s->ns, it, ns) {
sdb_ns_lock(ns->sdb, lock, depth - 1);
}
}
}
static int in_list(SdbList *list, void *item) {
SdbNs *ns;
SdbListIter *it;
if (list && item)
ls_foreach (list, it, ns) {
if (item == ns) {
return 1;
}
}
return 0;
}
static void ns_free_exc_list(Sdb *s, SdbList *list) {
SdbListIter next;
SdbListIter *it;
int deleted;
SdbNs *ns;
if (!list || !s) {
return;
}
// TODO: Implement and use ls_foreach_safe
if (in_list(list, s)) {
return;
}
ls_append(list, s);
ls_foreach (s->ns, it, ns) {
deleted = 0;
next.n = it->n;
if (!in_list(list, ns)) {
ls_delete(s->ns, it); // free (it)
free(ns->name);
ns->name = NULL;
deleted = 1;
if (ns->sdb) {
if (sdb_free(ns->sdb)) {
ns->sdb = NULL;
free(ns->name);
ns->name = NULL;
}
}
ls_append(list, ns);
ls_append(list, ns->sdb);
ns_free_exc_list(ns->sdb, list);
sdb_free(ns->sdb);
}
if (!deleted) {
sdb_free(ns->sdb);
s->ns->free = NULL;
ls_delete(s->ns, it); // free (it)
}
free(ns);
it = &next;
}
ls_free(s->ns);
s->ns = NULL;
}
RZ_API void sdb_ns_free_all(Sdb *s) {
SdbList *list;
if (!s) {
return;
}
list = ls_new();
list->free = NULL;
ns_free_exc_list(s, list);
ls_free(list);
ls_free(s->ns);
s->ns = NULL;
}
static SdbNs *sdb_ns_new(Sdb *s, const char *name, ut32 hash) {
char dir[SDB_MAX_PATH];
SdbNs *ns;
if (s->dir && *s->dir && name && *name) {
int dir_len = strlen(s->dir);
int name_len = strlen(name);
if ((dir_len + name_len + 3) > SDB_MAX_PATH) {
return NULL;
}
memcpy(dir, s->dir, dir_len);
memcpy(dir + dir_len, ".", 1);
memcpy(dir + dir_len + 1, name, name_len + 1);
} else {
dir[0] = 0;
}
ns = malloc(sizeof(SdbNs));
if (!ns) {
return NULL;
}
ns->hash = hash;
ns->name = name ? strdup(name) : NULL;
// ns->sdb = sdb_new (dir, ns->name, 0);
ns->sdb = sdb_new0();
// TODO: generate path
if (ns->sdb) {
free(ns->sdb->path);
ns->sdb->path = NULL;
if (*dir) {
ns->sdb->path = strdup(dir);
}
free(ns->sdb->name);
if (name && *name) {
ns->sdb->name = strdup(name);
}
} else {
free(ns->name);
free(ns);
ns = NULL;
}
return ns;
}
static void sdb_ns_free(SdbNs *ns) {
sdb_free(ns->sdb);
free(ns->name);
free(ns);
}
RZ_API bool sdb_ns_unset(Sdb *s, const char *name, Sdb *r) {
SdbNs *ns;
SdbListIter *it;
if (s && (name || r)) {
ls_foreach (s->ns, it, ns) {
if (name && (!strcmp(name, ns->name))) {
sdb_ns_free(ns);
ls_delete(s->ns, it);
return true;
}
if (r && ns->sdb == r) {
sdb_ns_free(ns);
ls_delete(s->ns, it);
return true;
}
}
}
return false;
}
RZ_API int sdb_ns_set(Sdb *s, const char *name, Sdb *r) {
SdbNs *ns;
SdbListIter *it;
ut32 hash = sdb_hash(name);
if (!s || !r || !name) {
return 0;
}
ls_foreach (s->ns, it, ns) {
if (ns->hash == hash) {
if (ns->sdb == r) {
return 0;
}
sdb_free(ns->sdb);
r->refs++; // sdb_ref / sdb_unref //
ns->sdb = r;
return 1;
}
}
if (s->ns_lock) {
return 0;
}
ns = RZ_NEW(SdbNs);
ns->name = strdup(name);
ns->hash = hash;
ns->sdb = r;
r->refs++;
ls_append(s->ns, ns);
return 1;
}
RZ_API Sdb *sdb_ns(Sdb *s, const char *name, int create) {
SdbListIter *it;
SdbNs *ns;
ut32 hash;
if (!s || !name || !*name) {
return NULL;
}
hash = sdb_hash(name);
ls_foreach (s->ns, it, ns) {
if (ns->hash == hash) {
return ns->sdb;
}
}
if (!create) {
return NULL;
}
if (s->ns_lock) {
return NULL;
}
ns = sdb_ns_new(s, name, hash);
if (!ns) {
return NULL;
}
ls_append(s->ns, ns);
return ns->sdb;
}
RZ_API Sdb *sdb_ns_path(Sdb *s, const char *path, int create) {
char *ptr, *str;
char *slash;
if (!s || !path || !*path)
return s;
ptr = str = strdup(path);
do {
slash = strchr(ptr, '/');
if (slash)
*slash = 0;
s = sdb_ns(s, ptr, create);
if (!s)
break;
if (slash)
ptr = slash + 1;
} while (slash);
free(str);
return s;
}
static void ns_sync(Sdb *s, SdbList *list) {
SdbNs *ns;
SdbListIter *it;
ls_foreach (s->ns, it, ns) {
if (in_list(list, ns)) {
continue;
}
ls_append(list, ns);
ns_sync(ns->sdb, list);
sdb_sync(ns->sdb);
}
sdb_sync(s);
}
RZ_API void sdb_ns_sync(Sdb *s) {
SdbList *list = ls_new();
ns_sync(s, list);
list->free = NULL;
ls_free(list);
}

89
librz/util/sdb/src/num.c Normal file
View file

@ -0,0 +1,89 @@
// SPDX-FileCopyrightText: 2011-2016 pancake <pancake@nopcode.org>
// SPDX-License-Identifier: MIT
#include "sdb.h"
#include <rz_types.h>
// check if key exists and if it's a number.. rename?
RZ_API bool sdb_num_exists(Sdb *s, const char *key) {
const char *o = sdb_const_get(s, key, NULL);
return o ? (*o >= '0' && *o <= '9') : false;
}
RZ_API ut64 sdb_num_get(Sdb *s, const char *key, ut32 *cas) {
const char *v = sdb_const_get(s, key, cas);
return (!v || *v == '-') ? 0LL : sdb_atoi(v);
}
RZ_API int sdb_num_add(Sdb *s, const char *key, ut64 v, ut32 cas) {
char *val, b[SDB_NUM_BUFSZ];
int numbase = sdb_num_base(sdb_const_get(s, key, NULL));
val = sdb_itoa(v, b, numbase);
return sdb_add(s, key, val, cas);
}
RZ_API int sdb_num_set(Sdb *s, const char *key, ut64 v, ut32 cas) {
char *val, b[SDB_NUM_BUFSZ];
int numbase = sdb_num_base(sdb_const_get(s, key, NULL));
val = sdb_itoa(v, b, numbase);
return sdb_set(s, key, val, cas);
}
RZ_API ut64 sdb_num_inc(Sdb *s, const char *key, ut64 n2, ut32 cas) {
ut32 c;
ut64 n = sdb_num_get(s, key, &c);
ut64 res = n + n2;
if ((cas && c != cas) || res < n) {
return 0LL;
}
sdb_num_set(s, key, res, cas);
return res;
}
RZ_API ut64 sdb_num_dec(Sdb *s, const char *key, ut64 n2, ut32 cas) {
ut32 c;
ut64 n = sdb_num_get(s, key, &c);
if (cas && c != cas) {
return 0LL;
}
if (n2 > n) {
sdb_set(s, key, "0", cas);
return 0LL; // XXX must be -1LL?
}
n -= n2;
sdb_num_set(s, key, n, cas);
return n;
}
RZ_API int sdb_num_min(Sdb *db, const char *k, ut64 n, ut32 cas) {
const char *a = sdb_const_get(db, k, NULL);
return (!a || n < sdb_atoi(a))
? sdb_num_set(db, k, n, cas)
: 0;
}
RZ_API int sdb_num_max(Sdb *db, const char *k, ut64 n, ut32 cas) {
const char *a = sdb_const_get(db, k, NULL);
return (!a || n > sdb_atoi(a))
? sdb_num_set(db, k, n, cas)
: 0;
}
RZ_API int sdb_bool_set(Sdb *db, const char *str, bool v, ut32 cas) {
return sdb_set(db, str, v ? "true" : "false", cas);
}
RZ_API bool sdb_bool_get(Sdb *db, const char *str, ut32 *cas) {
const char *b = sdb_const_get(db, str, cas);
return b && (!strcmp(b, "1") || !strcmp(b, "true"));
}
/* pointers */
RZ_API int sdb_ptr_set(Sdb *db, const char *key, void *p, ut32 cas) {
return sdb_num_set(db, key, (ut64)(size_t)p, cas);
}
RZ_API void *sdb_ptr_get(Sdb *db, const char *key, ut32 *cas) {
return (void *)(size_t)sdb_num_get(db, key, cas);
}

921
librz/util/sdb/src/query.c Normal file
View file

@ -0,0 +1,921 @@
// SPDX-FileCopyrightText: 2011-2020 pancake <pancake@nopcode.org>
// SPDX-License-Identifier: MIT
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <fcntl.h>
#include <ctype.h>
#include "sdb.h"
typedef struct {
char *buf;
int len;
int size;
} StrBuf;
static StrBuf *strbuf_new(void) {
return calloc(sizeof(StrBuf), 1);
}
#define NEWLINE_AFTER_QUERY 1
static StrBuf *strbuf_append(StrBuf *sb, const char *str, const int nl) {
if (!sb || !str || nl < 0) {
return sb;
}
int len = strlen(str);
if ((sb->len + len + 2) >= sb->size) {
int newsize = sb->size + len + 256;
char *b = realloc(sb->buf, newsize);
/// TODO perform free and force all callers to update the ref?
if (!b) {
return NULL;
}
sb->buf = b;
sb->size = newsize;
}
if (sb->buf && str) {
memcpy(sb->buf + sb->len, str, len);
sb->len += len;
}
#if NEWLINE_AFTER_QUERY
if (sb->buf && nl) {
sb->buf[sb->len++] = '\n';
len++;
}
#endif
if (sb->buf) {
sb->buf[sb->len] = 0;
}
return sb;
}
static StrBuf *strbuf_free(StrBuf *sb) {
free(sb->buf);
free(sb);
return NULL;
}
RZ_API int sdb_queryf(Sdb *s, const char *fmt, ...) {
char string[4096];
int ret;
va_list ap;
va_start(ap, fmt);
vsnprintf(string, sizeof(string), fmt, ap);
ret = sdb_query(s, string);
va_end(ap);
return ret;
}
RZ_API char *sdb_querysf(Sdb *s, char *buf, size_t buflen, const char *fmt, ...) {
char string[4096];
char *ret;
va_list ap;
va_start(ap, fmt);
vsnprintf(string, sizeof(string), fmt, ap);
ret = sdb_querys(s, buf, buflen, string);
va_end(ap);
return ret;
}
// TODO: Reimplement as a function with optimized concat
#define out_concat(x) \
if (x && *x) { \
strbuf_append(out, x, 1); \
}
typedef struct {
StrBuf *out;
int encode;
char *root;
} ForeachListUser;
static bool foreach_list_cb(void *user, const char *k, const char *v) {
ForeachListUser *rlu = user;
char *line, *root;
int rlen, klen, vlen;
ut8 *v2 = NULL;
if (!rlu) {
return false;
}
root = rlu->root;
klen = strlen(k);
if (rlu->encode) {
v2 = sdb_decode(v, NULL);
if (v2) {
v = (const char *)v2;
}
}
vlen = strlen(v);
if (root) {
rlen = strlen(root);
line = malloc(klen + vlen + rlen + 3);
if (!line) {
free(v2);
return false;
}
memcpy(line, root, rlen);
line[rlen] = '/'; /*append the '/' at the end of the namespace */
memcpy(line + rlen + 1, k, klen);
line[rlen + klen + 1] = '=';
memcpy(line + rlen + klen + 2, v, vlen + 1);
} else {
line = malloc(klen + vlen + 2);
if (!line) {
free(v2);
return false;
}
memcpy(line, k, klen);
line[klen] = '=';
memcpy(line + klen + 1, v, vlen + 1);
}
strbuf_append(rlu->out, line, 1);
free(v2);
free(line);
return true;
}
static void walk_namespace(StrBuf *sb, char *root, int left, char *p, SdbNs *ns, int encode) {
int len;
SdbListIter *it;
char *_out, *out = sb->buf;
SdbNs *n;
ForeachListUser user = { sb, encode, root };
char *roote = root + strlen(root);
if (!ns->sdb) {
return;
}
/*Pick all key=value in the local ns*/
sdb_foreach(ns->sdb, foreach_list_cb, &user);
/*Pick "sub"-ns*/
ls_foreach (ns->sdb->ns, it, n) {
len = strlen(n->name);
p[0] = '/';
if (len + 2 < left) {
memcpy(p + 1, n->name, len + 1);
left -= len + 2;
}
_out = out;
walk_namespace(sb, root, left,
roote + len + 1, n, encode);
out = _out;
}
}
RZ_API char *sdb_querys(Sdb *r, char *buf, size_t len, const char *_cmd) {
int i, d, ok, w, alength, bufset = 0, is_ref = 0, encode = 0;
const char *p, *q, *val = NULL;
char *eq, *tmp, *json, *next, *quot, *slash, *res,
*cmd, *newcmd = NULL, *original_cmd = NULL;
StrBuf *out;
Sdb *s = r;
ut64 n;
if (!s || (!_cmd && !buf)) {
return NULL;
}
out = strbuf_new();
if ((int)len < 1 || !buf) {
bufset = 1;
buf = malloc((len = 64));
if (!buf) {
strbuf_free(out);
return NULL;
}
}
if (_cmd) {
cmd = original_cmd = strdup(_cmd);
if (!cmd) {
free(out);
if (bufset) {
free(buf);
}
return NULL;
}
} else {
cmd = buf;
}
// if cmd is null, we take buf as cmd
next = NULL;
repeat:
/* skip spaces */
while (*cmd && (*cmd == ' ' || *cmd == '\t')) {
cmd++;
}
s = r;
p = cmd;
eq = NULL;
encode = 0;
is_ref = 0;
quot = NULL;
json = NULL;
if (*p == '#') {
p++;
next = strchr(p, ';');
if (next) {
*next = 0;
}
out_concat(sdb_fmt("0x%08x\n", sdb_hash(p)));
if (next) {
*next = ';';
}
goto runNext;
} else if (*p == '%') {
encode = 1;
cmd++;
p++;
}
if (next)
*next = ';';
eq = strchr(p, '=');
if (eq) {
d = 1;
*eq++ = 0;
if (*eq == '$') {
next = strchr(eq + 1, ';');
if (next)
*next = 0;
val = sdb_const_get(s, eq + 1, 0);
if (!val) {
eprintf("No value for '%s'\n", eq + 1);
goto fail;
}
if (next)
*next = ';';
is_ref = 1; // protect readonly buffer from being processed
} else {
val = eq;
}
} else {
val = NULL;
d = 0;
}
if (!is_ref) {
next = strchr(val ? val : cmd, ';');
}
// if (!val) val = eq;
if (!is_ref && val && *val == '"') {
val++;
// TODO: escape \" too
quot = (char *)val;
next_quote:
quot = strchr(quot, '"');
if (quot) {
if (*(quot - 1) == '\\') {
memmove(quot - 1, quot, strlen(quot) + 1);
goto next_quote;
}
*quot++ = 0; // crash on read only mem!!
} else {
eprintf("Missing quote\n");
*eq++ = 0;
out = strbuf_free(out);
goto fail;
}
next = strchr(quot, ';');
} else {
quot = NULL;
}
if (next) {
*next = 0;
}
slash = strchr(cmd, '/');
while (slash) {
*slash = 0;
s = sdb_ns(s, cmd, eq ? 1 : 0);
if (!s) {
eprintf("Cant find namespace %s\n", cmd);
out = strbuf_free(out);
goto fail;
}
cmd = slash + 1;
slash = strchr(cmd, '/');
}
if (*cmd == '?') {
const char *val = sdb_const_get(s, cmd + 1, 0);
const char *type = sdb_type(val);
out_concat(type);
} else if (*cmd == '*') {
if (!strcmp(cmd, "***")) {
char root[1024]; // limit namespace length?
SdbListIter *it;
SdbNs *ns;
ls_foreach (s->ns, it, ns) {
int name_len = strlen(ns->name);
if (name_len < (long)sizeof(root)) {
memcpy(root, ns->name, name_len + 1);
walk_namespace(out, root,
sizeof(root) - name_len,
root + name_len, ns, encode);
} else {
eprintf("TODO: Namespace too long\n");
}
}
goto fail;
} else if (!strcmp(cmd, "**")) {
SdbListIter *it;
SdbNs *ns;
ls_foreach (s->ns, it, ns) {
out_concat(ns->name);
}
goto fail;
} else if (!strcmp(cmd, "*")) {
ForeachListUser user = { out, encode, NULL };
SdbList *list = sdb_foreach_list(s, true);
SdbListIter *iter;
SdbKv *kv;
ls_foreach (list, iter, kv) {
foreach_list_cb(&user, sdbkv_key(kv), sdbkv_value(kv));
}
ls_free(list);
goto fail;
}
}
json = strchr(cmd, ':');
if (*cmd == '[') {
char *tp = strchr(cmd, ']');
if (!tp) {
eprintf("Missing ']'.\n");
goto fail;
}
*tp++ = 0;
p = (const char *)tp;
} else {
p = cmd;
}
if (*cmd == '$') {
free(newcmd);
char *nc = sdb_get(s, cmd + 1, 0);
cmd = newcmd = (nc) ? nc : strdup("");
}
// cmd = val
// cmd is key and val is value
if (*cmd == '.') {
if (s->options & SDB_OPTION_FS) {
if (!sdb_query_file(s, cmd + 1)) {
eprintf("sdb: cannot open '%s'\n", cmd + 1);
goto fail;
}
} else {
eprintf("sdb: filesystem access disabled in config\n");
}
} else if (*cmd == '~') { // delete
if (cmd[1] == '~') { // grep
SdbKv *kv;
SdbListIter *li;
SdbList *l = sdb_foreach_match(s, cmd + 2, false);
ls_foreach (l, li, kv) {
strbuf_append(out, sdbkv_key(kv), 0);
strbuf_append(out, "=", 0);
strbuf_append(out, sdbkv_value(kv), 1);
}
fflush(stdout);
ls_free(l);
} else {
d = 1;
sdb_unset_like(s, cmd + 1);
}
} else if (*cmd == '+' || *cmd == '-') {
d = 1;
if (!buf) {
buf = calloc(1, len);
if (!buf) {
goto fail;
}
bufset = 1;
}
*buf = 0;
if (cmd[1] == '[') {
const char *eb = strchr(cmd, ']');
if (!eb) {
eprintf("Missing ']'.\n");
goto fail;
}
int idx = sdb_atoi(cmd + 2);
/* +[idx]key=n */
/* -[idx]key=n */
ut64 curnum = sdb_array_get_num(s,
eb + 1, idx, 0);
if (eq) {
/* +[idx]key=n --> key[idx] += n */
/* -[idx]key=n --> key[idx] -= n */
st64 n = sdb_atoi(eq);
if (*cmd == '+') {
curnum += n;
} else if (*cmd == '-') {
curnum -= n;
} else {
// never happens
}
sdb_array_set_num(s, eb + 1, idx, curnum, 0);
} else {
/* +[idx]key --> key[idx] + 1 */
/* -[idx]key --> key[idx] - 1 */
char *nstr, numstr[128];
if (*cmd == '+') {
curnum++;
} else if (*cmd == '-') {
curnum--;
} else {
// never happens
}
nstr = sdb_itoa(curnum, numstr, 10);
strbuf_append(out, nstr, 1);
}
} else if (val) {
if (sdb_isnum(val)) {
int op = *cmd;
if (*val == '-') {
if (*cmd == '-') {
op = '+';
} else {
op = '-';
}
d = sdb_atoi(val + 1);
} else {
d = sdb_atoi(val);
}
if (op == '+') {
sdb_num_inc(s, cmd + 1, d, 0);
} else {
sdb_num_dec(s, cmd + 1, d, 0);
}
} else {
if (*cmd == '+') {
sdb_concat(s, cmd + 1, val, 0);
} else {
sdb_uncat(s, cmd + 1, val, 0);
}
}
} else {
int base = sdb_num_base(sdb_const_get(s, cmd + 1, 0));
if (json) {
base = 10; // NOTE: json is base10 only
*json = 0;
if (*cmd == '+') {
n = sdb_json_num_inc(s, cmd + 1, json + 1, d, 0);
} else {
n = sdb_json_num_dec(s, cmd + 1, json + 1, d, 0);
}
*json = ':';
} else {
if (*cmd == '+') {
n = sdb_num_inc(s, cmd + 1, d, 0);
} else {
n = sdb_num_dec(s, cmd + 1, d, 0);
}
}
// keep base
if (base == 16) {
w = snprintf(buf, len - 1, "0x%" PFMT64x "x", n);
if (w < 0 || (size_t)w > len) {
if (bufset && len < 0xff) {
free(buf);
buf = malloc(len = 0xff);
if (!buf) {
goto fail;
}
}
bufset = 1;
snprintf(buf, 0xff, "0x%" PFMT64x "x", n);
}
} else {
w = snprintf(buf, len - 1, "%" PFMT64x "d", n);
if (w < 0 || (size_t)w > len) {
if (bufset && len < 0xff) {
free(buf);
buf = malloc(len = 0xff);
if (!buf) {
goto fail;
}
}
bufset = 1;
snprintf(buf, 0xff, "%" PFMT64x "d", n);
}
}
}
out_concat(buf);
} else if (*cmd == '[') {
// [?] - count elements of array
if (cmd[1] == '?') {
// if (!eq) ...
alength = sdb_array_length(s, p);
if (!buf) {
buf = malloc(++len);
if (!buf) {
goto fail;
}
bufset = 1;
}
w = snprintf(buf, len, "%d", alength);
if (w < 0 || (size_t)w > len) {
if (bufset) {
free(buf);
}
buf = malloc(len = 32);
bufset = 1;
snprintf(buf, 31, "%d", alength);
}
out_concat(buf);
} else if (cmd[1] == '!') {
if (cmd[2] == '+') {
// [!+]key=aa # add_sorted
sdb_array_add_sorted(s, p, val, 0);
} else {
// [!]key # sort
sdb_array_sort(s, p, 0);
}
} else if (cmd[1] == '#') {
// [#+]key=num # add_sorted_num
if (cmd[2] == '+') {
// [#]key # sort_num
sdb_array_add_sorted_num(s, p, sdb_atoi(val), 0);
} else {
sdb_array_sort_num(s, p, 0);
}
} else if (cmd[1] == '+' || cmd[1] == '-') {
if (cmd[1] == cmd[2]) {
// stack
#if 0
[++]foo=33 # push
[++]foo # <invalid>
[--]foo # pop
[--]foo=b # <invalid>
#endif
if (cmd[1] == '-' && eq) {
/* invalid syntax */
} else if (cmd[1] == '+' && !eq) {
/* invalid syntax */
} else {
if (eq) {
sdb_array_push(s, p, val, 0);
} else {
char *ret = sdb_array_pop(s, p, 0);
out_concat(ret);
free(ret);
}
}
} else
// [+]foo remove first element */
// [+]foo=bar ADD */
// [-]foo POP */
// [-]foo=xx REMOVE (=xx ignored) */
if (!cmd[2] || cmd[2] == ']') {
// insert
if (eq) {
if (cmd[1] == '+') {
// [+]K=1
sdb_array_add(s, p, val, 0);
} else {
// [-]K= = remove first element
sdb_array_remove(s, p, val, 0);
}
// return NULL;
} else {
char *ret;
if (cmd[1] == '+') {
// [+]K = remove first element
// XXX: this is a little strange syntax to remove an item
ret = sdb_array_get(s, p, 0, 0);
if (ret && *ret) {
out_concat(ret);
}
// (+)foo :: remove first element
sdb_array_delete(s, p, 0, 0);
} else {
// [-]K = remove last element
ret = sdb_array_get(s, p, -1, 0);
if (ret && *ret) {
out_concat(ret);
}
// (-)foo :: remove last element
sdb_array_delete(s, p, -1, 0);
}
free(ret);
}
} else {
// get/set specific element in array
i = atoi(cmd + 1);
if (eq) {
/* [+3]foo=bla */
if (i < 0) {
char *tmp = sdb_array_get(s, p, -i, NULL);
if (tmp) {
if (encode) {
char *newtmp = (void *)sdb_decode(tmp, NULL);
if (!newtmp) {
goto fail;
}
free(tmp);
tmp = newtmp;
}
ok = 0;
out_concat(tmp);
sdb_array_delete(s, p, -i, 0);
free(tmp);
} else
goto fail;
} else {
if (encode) {
val = sdb_encode((const ut8 *)val, -1);
}
ok = cmd[1] ? ((cmd[1] == '+') ? sdb_array_insert(s, p, i, val, 0) : sdb_array_set(s, p, i, val, 0)) : sdb_array_delete(s, p, i, 0);
if (encode) {
free((void *)val);
val = NULL;
}
}
if (ok && buf)
*buf = 0;
else
buf = NULL;
} else {
if (i == 0) {
/* [-b]foo */
if (cmd[1] == '-') {
sdb_array_remove(s, p, cmd + 2, 0);
} else {
eprintf("TODO: [b]foo -> get index of b key inside foo array\n");
// sdb_array_dels (s, p, cmd+1, 0);
}
} else if (i < 0) {
/* [-3]foo */
char *tmp = sdb_array_get(s, p, -i, NULL);
if (tmp && *tmp) {
out_concat(tmp);
sdb_array_delete(s, p, -i, 0);
}
free(tmp);
} else {
/* [+3]foo */
char *tmp = sdb_array_get(s, p, i, NULL);
if (tmp && *tmp) {
out_concat(tmp);
}
free(tmp);
}
}
}
} else {
if (eq) {
/* [3]foo=bla */
char *sval = (char *)val;
if (encode) {
sval = sdb_encode((const ut8 *)val, -1);
}
if (cmd[1]) {
int idx = atoi(cmd + 1);
ok = sdb_array_set(s, p, idx, sval, 0);
// TODO: handle when idx > sdb_alen
if (encode)
free(sval);
} else {
if (encode) {
ok = sdb_set_owned(s, p, sval, 0);
} else {
ok = sdb_set(s, p, sval, 0);
}
}
if (ok && buf) {
*buf = 0;
}
} else {
/* [3]foo */
const char *sval = sdb_const_get(s, p, 0);
size_t wl;
if (cmd[1]) {
i = atoi(cmd + 1);
buf = sdb_array_get(s, p, i, NULL);
if (buf) {
bufset = 1;
len = strlen(buf) + 1;
}
if (encode) {
char *newbuf = (void *)sdb_decode(buf, NULL);
if (newbuf) {
free(buf);
buf = newbuf;
len = strlen(buf) + 1;
}
}
out_concat(buf);
} else {
if (!sval) {
goto fail;
}
wl = strlen(sval);
if (!buf || wl >= len) {
buf = malloc(wl + 2);
if (!buf) {
free(out->buf);
out->buf = NULL;
goto fail;
}
bufset = 1;
len = wl + 2;
}
for (i = 0; sval[i]; i++) {
if (sval[i + 1]) {
buf[i] = (sval[i] == SDB_RS)
? '\n'
: sval[i];
} else {
buf[i] = sval[i];
}
}
buf[i] = 0;
if (encode) {
char *newbuf = (void *)sdb_decode(buf, NULL);
if (newbuf) {
if (bufset) {
free(buf);
}
buf = newbuf;
len = strlen(buf) + 1;
}
}
out_concat(buf);
}
}
}
} else {
if (eq) {
// 1 0 kvpath=value
// 1 1 kvpath:jspath=value
if (encode) {
val = sdb_encode((const ut8 *)val, -1);
}
if (json > eq) {
json = NULL;
}
if (json) {
*json++ = 0;
ok = sdb_json_set(s, cmd, json, val, 0);
} else {
while (*val && isspace(*val)) {
val++;
}
int i = strlen(cmd) - 1;
while (i >= 0 && isspace(cmd[i])) {
cmd[i] = '\0';
i--;
}
ok = sdb_set(s, cmd, val, 0);
}
if (encode) {
free((void *)val);
val = NULL;
}
if (ok && buf) {
*buf = 0;
}
} else {
// 0 1 kvpath:jspath
// 0 0 kvpath
if (json) {
*json++ = 0;
if (*json) {
// TODO: not optimized to reuse 'buf'
if ((tmp = sdb_json_get(s, cmd, json, 0))) {
if (encode) {
char *newtmp = (void *)sdb_decode(tmp, NULL);
if (!newtmp)
goto fail;
free(tmp);
tmp = newtmp;
}
out_concat(tmp);
free(tmp);
}
} else {
// kvpath: -> show indented json
char *o = sdb_json_indent(sdb_const_get(s, cmd, 0), " ");
out_concat(o);
free(o);
}
} else {
// sdbget
if ((q = sdb_const_get(s, cmd, 0))) {
if (encode) {
q = (void *)sdb_decode(q, NULL);
}
out_concat(q);
if (encode) {
free((void *)q);
}
}
}
}
}
runNext:
if (next) {
if (bufset) {
free(buf);
buf = NULL;
bufset = 0;
}
cmd = next + 1;
encode = 0;
goto repeat;
}
if (eq) {
*--eq = '=';
}
fail:
if (bufset) {
free(buf);
}
if (out) {
res = out->buf;
free(out);
} else {
res = NULL;
}
free(original_cmd);
free(newcmd);
return res;
}
RZ_API int sdb_query(Sdb *s, const char *cmd) {
char buf[1024], *out;
int must_save = ((*cmd == '~') || strchr(cmd, '='));
out = sdb_querys(s, buf, sizeof(buf) - 1, cmd);
if (out) {
if (*out) {
puts(out);
}
if (out != buf) {
free(out);
}
}
return must_save;
}
RZ_API int sdb_query_lines(Sdb *s, const char *cmd) {
char *o, *p, *op;
if (!s || !cmd) {
return 0;
}
op = strdup(cmd);
if (!op) {
return 0;
}
p = op;
do {
o = strchr(p, '\n');
if (o) {
*o = 0;
}
(void)sdb_query(s, p);
if (o) {
p = o + 1;
}
} while (o);
free(op);
return 1;
}
static char *slurp(const char *file) {
int ret, fd;
char *text;
long sz;
if (!file || !*file)
return NULL;
fd = open(file, O_RDONLY);
if (fd == -1) {
return NULL;
}
sz = lseek(fd, 0, SEEK_END);
if (sz < 0) {
close(fd);
return NULL;
}
lseek(fd, 0, SEEK_SET);
text = malloc(sz + 1);
if (!text) {
close(fd);
return NULL;
}
ret = read(fd, text, sz);
if (ret != sz) {
free(text);
text = NULL;
} else {
text[sz] = 0;
}
close(fd);
return text;
}
RZ_API int sdb_query_file(Sdb *s, const char *file) {
int ret = 0;
char *txt = slurp(file);
if (txt) {
ret = sdb_query_lines(s, txt);
free(txt);
}
return ret;
}

1179
librz/util/sdb/src/sdb.c Normal file

File diff suppressed because it is too large Load diff

401
librz/util/sdb/src/sdb.h Normal file
View file

@ -0,0 +1,401 @@
// SPDX-FileCopyrightText: pancake <pancake@nopcode.org>
// SPDX-License-Identifier: MIT
#ifndef SDB_H
#define SDB_H
#if !defined(O_BINARY) && !defined(_MSC_VER)
#undef O_BINARY
#define O_BINARY 0
#endif
#ifdef __cplusplus
extern "C" {
#endif
#include <rz_types.h>
#include "sdbht.h"
#include "ls.h"
#include "cdb.h"
#include "cdb_make.h"
/* Key value sizes */
#define SDB_CDB_MIN_VALUE 1
#define SDB_CDB_MAX_VALUE CDB_MAX_VALUE
#define SDB_CDB_MIN_KEY 1
#define SDB_CDB_MAX_KEY CDB_MAX_KEY
#if !defined(SZT_ADD_OVFCHK)
#define SZT_ADD_OVFCHK(x, y) ((SIZE_MAX - (x)) <= (y))
#endif
/* printf format check attributes */
#if defined(__clang__) || defined(__GNUC__)
#define SDB_PRINTF_CHECK(fmt, dots) __attribute__((format(printf, fmt, dots)))
#else
#define SDB_PRINTF_CHECK(fmt, dots)
#endif
#if __WINDOWS__ && !__CYGWIN__
#include <fcntl.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <io.h>
#define ftruncate _chsize
#ifndef _MSC_VER
extern __attribute__((dllimport)) void *__cdecl _aligned_malloc(size_t, size_t);
extern __attribute__((dllimport)) void __cdecl _aligned_free(void *memblock);
extern char *strdup(const char *);
#else
#include <process.h>
#include <malloc.h> // for _aligned_malloc
#endif
//#define SDB_MODE 0
#define SDB_MODE _S_IWRITE | _S_IREAD
#else
#define SDB_MODE 0644
//#define SDB_MODE 0600
#endif
//#define SDB_RS '\x1e'
#define SDB_RS ','
#define SDB_SS ","
#define SDB_MAX_PATH 256
#define SDB_NUM_BASE 16
#define SDB_NUM_BUFSZ 64
#define SDB_OPTION_NONE 0
#define SDB_OPTION_ALL 0xff
#define SDB_OPTION_SYNC (1 << 0)
#define SDB_OPTION_NOSTAMP (1 << 1)
#define SDB_OPTION_FS (1 << 2)
#define SDB_OPTION_JOURNAL (1 << 3)
#define SDB_LIST_UNSORTED 0
#define SDB_LIST_SORTED 1
typedef struct sdb_t {
char *dir; // path+name
char *path;
char *name;
int fd;
int refs; // reference counter
int lock;
int journal;
struct cdb db;
struct cdb_make m;
HtPP *ht;
ut32 eod;
ut32 pos;
int fdump;
char *ndump;
ut64 expire;
ut64 last; // timestamp of last change
int options;
int ns_lock; // TODO: merge into options?
SdbList *ns;
SdbList *hooks;
ut32 depth;
bool timestamped;
} Sdb;
typedef struct sdb_ns_t {
char *name;
ut32 hash;
Sdb *sdb;
} SdbNs;
RZ_API Sdb *sdb_new0(void);
RZ_API Sdb *sdb_new(const char *path, const char *file, int lock);
RZ_API int sdb_open(Sdb *s, const char *file);
RZ_API void sdb_close(Sdb *s);
RZ_API void sdb_config(Sdb *s, int options);
RZ_API bool sdb_free(Sdb *s);
RZ_API void sdb_file(Sdb *s, const char *dir);
RZ_API bool sdb_merge(Sdb *d, Sdb *s);
RZ_API int sdb_count(Sdb *s);
RZ_API void sdb_reset(Sdb *s);
RZ_API void sdb_setup(Sdb *s, int options);
RZ_API void sdb_drain(Sdb *, Sdb *);
// Copy everything, including namespaces, from src to dst
RZ_API void sdb_copy(Sdb *src, Sdb *dst);
RZ_API bool sdb_stats(Sdb *s, ut32 *disk, ut32 *mem);
RZ_API bool sdb_dump_hasnext(Sdb *s);
typedef bool (*SdbForeachCallback)(void *user, const char *k, const char *v);
RZ_API bool sdb_foreach(Sdb *s, SdbForeachCallback cb, void *user);
RZ_API SdbList *sdb_foreach_list(Sdb *s, bool sorted);
RZ_API SdbList *sdb_foreach_list_filter_user(Sdb *s, SdbForeachCallback filter, bool sorted, void *user);
RZ_API SdbList *sdb_foreach_list_filter(Sdb *s, SdbForeachCallback filter, bool sorted);
RZ_API SdbList *sdb_foreach_match(Sdb *s, const char *expr, bool sorted);
RZ_API int sdb_query(Sdb *s, const char *cmd);
RZ_API int sdb_queryf(Sdb *s, const char *fmt, ...);
RZ_API int sdb_query_lines(Sdb *s, const char *cmd);
RZ_API char *sdb_querys(Sdb *s, char *buf, size_t len, const char *cmd);
RZ_API char *sdb_querysf(Sdb *s, char *buf, size_t buflen, const char *fmt, ...);
RZ_API int sdb_query_file(Sdb *s, const char *file);
RZ_API bool sdb_exists(Sdb *, const char *key);
RZ_API bool sdb_remove(Sdb *, const char *key, ut32 cas);
RZ_API int sdb_unset(Sdb *, const char *key, ut32 cas);
RZ_API int sdb_unset_like(Sdb *s, const char *k);
RZ_API char **sdb_like(Sdb *s, const char *k, const char *v, SdbForeachCallback cb);
// diffing
typedef struct sdb_diff_t {
const SdbList *path;
const char *k;
const char *v; // if null, k is a namespace
bool add;
} SdbDiff;
// Format diff in a readable form into str. str, size and return are like in snprintf.
RZ_API int sdb_diff_format(char *str, int size, const SdbDiff *diff);
typedef void (*SdbDiffCallback)(const SdbDiff *diff, void *user);
// Returns true iff the contents of a and b are equal including contained namespaces
// If cb is non-null, it will be called subsequently with differences.
RZ_API bool sdb_diff(Sdb *a, Sdb *b, SdbDiffCallback cb, void *cb_user);
// Gets a pointer to the value associated with `key`.
RZ_API char *sdb_get(Sdb *, const char *key, ut32 *cas);
// Gets a pointer to the value associated with `key` and returns in `vlen` the
// length of the value string.
RZ_API char *sdb_get_len(Sdb *, const char *key, int *vlen, ut32 *cas);
// Gets a const pointer to the value associated with `key`
RZ_API const char *sdb_const_get(Sdb *, const char *key, ut32 *cas);
// Gets a const pointer to the value associated with `key` and returns in
// `vlen` the length of the value string.
RZ_API const char *sdb_const_get_len(Sdb *s, const char *key, int *vlen, ut32 *cas);
RZ_API int sdb_set(Sdb *, const char *key, const char *data, ut32 cas);
RZ_API int sdb_set_owned(Sdb *s, const char *key, char *val, ut32 cas);
RZ_API int sdb_concat(Sdb *s, const char *key, const char *value, ut32 cas);
RZ_API int sdb_uncat(Sdb *s, const char *key, const char *value, ut32 cas);
RZ_API int sdb_add(Sdb *s, const char *key, const char *val, ut32 cas);
RZ_API bool sdb_sync(Sdb *);
RZ_API void sdbkv_free(SdbKv *kv);
/* num.c */
RZ_API bool sdb_num_exists(Sdb *, const char *key);
RZ_API int sdb_num_base(const char *s);
RZ_API ut64 sdb_num_get(Sdb *s, const char *key, ut32 *cas);
RZ_API int sdb_num_set(Sdb *s, const char *key, ut64 v, ut32 cas);
RZ_API int sdb_num_add(Sdb *s, const char *key, ut64 v, ut32 cas);
RZ_API ut64 sdb_num_inc(Sdb *s, const char *key, ut64 n, ut32 cas);
RZ_API ut64 sdb_num_dec(Sdb *s, const char *key, ut64 n, ut32 cas);
RZ_API int sdb_num_min(Sdb *s, const char *key, ut64 v, ut32 cas);
RZ_API int sdb_num_max(Sdb *s, const char *key, ut64 v, ut32 cas);
/* ptr */
RZ_API int sdb_ptr_set(Sdb *db, const char *key, void *p, ut32 cas);
RZ_API void *sdb_ptr_get(Sdb *db, const char *key, ut32 *cas);
/* create db */
RZ_API bool sdb_disk_create(Sdb *s);
RZ_API bool sdb_disk_insert(Sdb *s, const char *key, const char *val);
RZ_API bool sdb_disk_finish(Sdb *s);
RZ_API bool sdb_disk_unlink(Sdb *s);
/* plaintext sdb files */
RZ_API bool sdb_text_save_fd(Sdb *s, int fd, bool sort);
RZ_API bool sdb_text_save(Sdb *s, const char *file, bool sort);
RZ_API bool sdb_text_load_buf(Sdb *s, char *buf, size_t sz);
RZ_API bool sdb_text_load(Sdb *s, const char *file);
/* iterate */
RZ_API void sdb_dump_begin(Sdb *s);
RZ_API SdbKv *sdb_dump_next(Sdb *s);
RZ_API bool sdb_dump_dupnext(Sdb *s, char *key, char **value, int *_vlen);
/* journaling */
RZ_API bool sdb_journal_close(Sdb *s);
RZ_API bool sdb_journal_open(Sdb *s);
RZ_API int sdb_journal_load(Sdb *s);
RZ_API bool sdb_journal_log(Sdb *s, const char *key, const char *val);
RZ_API bool sdb_journal_clear(Sdb *s);
RZ_API bool sdb_journal_unlink(Sdb *s);
/* numeric */
RZ_API char *sdb_itoa(ut64 n, char *s, int base);
RZ_API ut64 sdb_atoi(const char *s);
RZ_API const char *sdb_itoca(ut64 n);
/* locking */
RZ_API bool sdb_lock(const char *s);
RZ_API const char *sdb_lock_file(const char *f);
RZ_API void sdb_unlock(const char *s);
RZ_API bool sdb_unlink(Sdb *s);
RZ_API int sdb_lock_wait(RZ_UNUSED const char *s);
/* expiration */
RZ_API bool sdb_expire_set(Sdb *s, const char *key, ut64 expire, ut32 cas);
RZ_API ut64 sdb_expire_get(Sdb *s, const char *key, ut32 *cas);
RZ_API ut64 sdb_now(void);
RZ_API ut64 sdb_unow(void);
RZ_API ut32 sdb_hash(const char *key);
RZ_API ut32 sdb_hash_len(const char *key, ut32 *len);
RZ_API ut8 sdb_hash_byte(const char *s);
/* json api */
// RZ_API int sdb_js0n(const unsigned char *js, RangstrType len, RangstrType *out);
RZ_API bool sdb_isjson(const char *k);
RZ_API char *sdb_json_get_str(const char *json, const char *path);
RZ_API bool sdb_json_get_bool(const char *json, const char *path);
RZ_API char *sdb_json_get(Sdb *s, const char *key, const char *p, ut32 *cas);
RZ_API bool sdb_json_set(Sdb *s, const char *k, const char *p, const char *v, ut32 cas);
RZ_API int sdb_json_num_get(Sdb *s, const char *k, const char *p, ut32 *cas);
RZ_API int sdb_json_num_set(Sdb *s, const char *k, const char *p, int v, ut32 cas);
RZ_API int sdb_json_num_dec(Sdb *s, const char *k, const char *p, int n, ut32 cas);
RZ_API int sdb_json_num_inc(Sdb *s, const char *k, const char *p, int n, ut32 cas);
RZ_API char *sdb_json_indent(const char *s, const char *tab);
RZ_API char *sdb_json_unindent(const char *s);
typedef struct {
char *buf;
size_t blen;
size_t len;
} SdbJsonString;
RZ_API const char *sdb_json_format(SdbJsonString *s, const char *fmt, ...);
#define sdb_json_format_free(x) free((x)->buf)
// namespace
RZ_API Sdb *sdb_ns(Sdb *s, const char *name, int create);
RZ_API Sdb *sdb_ns_path(Sdb *s, const char *path, int create);
RZ_API void sdb_ns_init(Sdb *s);
RZ_API void sdb_ns_free_all(Sdb *s);
RZ_API void sdb_ns_lock(Sdb *s, int lock, int depth);
RZ_API void sdb_ns_sync(Sdb *s);
RZ_API int sdb_ns_set(Sdb *s, const char *name, Sdb *r);
RZ_API bool sdb_ns_unset(Sdb *s, const char *name, Sdb *r);
// array
RZ_API bool sdb_array_contains(Sdb *s, const char *key, const char *val, ut32 *cas);
RZ_API bool sdb_array_contains_num(Sdb *s, const char *key, ut64 val, ut32 *cas);
RZ_API int sdb_array_indexof(Sdb *s, const char *key, const char *val, ut32 cas);
RZ_API int sdb_array_set(Sdb *s, const char *key, int idx, const char *val, ut32 cas);
RZ_API int sdb_array_set_num(Sdb *s, const char *key, int idx, ut64 val, ut32 cas);
RZ_API bool sdb_array_append(Sdb *s, const char *key, const char *val, ut32 cas);
RZ_API bool sdb_array_append_num(Sdb *s, const char *key, ut64 val, ut32 cas);
RZ_API bool sdb_array_prepend(Sdb *s, const char *key, const char *val, ut32 cas);
RZ_API bool sdb_array_prepend_num(Sdb *s, const char *key, ut64 val, ut32 cas);
RZ_API char *sdb_array_get(Sdb *s, const char *key, int idx, ut32 *cas);
RZ_API ut64 sdb_array_get_num(Sdb *s, const char *key, int idx, ut32 *cas);
RZ_API int sdb_array_get_idx(Sdb *s, const char *key, const char *val, ut32 cas); // agetv
RZ_API int sdb_array_insert(Sdb *s, const char *key, int idx, const char *val, ut32 cas);
RZ_API int sdb_array_insert_num(Sdb *s, const char *key, int idx, ut64 val, ut32 cas);
RZ_API int sdb_array_unset(Sdb *s, const char *key, int n, ut32 cas); // leaves empty bucket
RZ_API int sdb_array_delete(Sdb *s, const char *key, int n, ut32 cas);
RZ_API void sdb_array_sort(Sdb *s, const char *key, ut32 cas);
RZ_API void sdb_array_sort_num(Sdb *s, const char *key, ut32 cas);
// set
// Adds string `val` at the end of array `key`.
RZ_API int sdb_array_add(Sdb *s, const char *key, const char *val, ut32 cas);
// Adds number `val` at the end of array `key`.
RZ_API int sdb_array_add_num(Sdb *s, const char *key, ut64 val, ut32 cas);
// Adds string `val` in the sorted array `key`.
RZ_API int sdb_array_add_sorted(Sdb *s, const char *key, const char *val, ut32 cas);
// Adds number `val` in the sorted array `key`.
RZ_API int sdb_array_add_sorted_num(Sdb *s, const char *key, ut64 val, ut32 cas);
// Removes the string `val` from the array `key`.
RZ_API int sdb_array_remove(Sdb *s, const char *key, const char *val, ut32 cas);
// Removes the number `val` from the array `key`.
RZ_API int sdb_array_remove_num(Sdb *s, const char *key, ut64 val, ut32 cas);
// helpers
RZ_API char *sdb_anext(char *str, char **next);
RZ_API const char *sdb_const_anext(const char *str);
RZ_API int sdb_alen(const char *str);
RZ_API int sdb_alen_ignore_empty(const char *str);
RZ_API int sdb_array_size(Sdb *s, const char *key);
RZ_API int sdb_array_length(Sdb *s, const char *key);
int sdb_array_list(Sdb *s, const char *key);
// Adds the string `val` to the start of array `key`.
RZ_API bool sdb_array_push(Sdb *s, const char *key, const char *val, ut32 cas);
// Returns the string at the start of array `key` or
// NULL if there are no elements.
RZ_API char *sdb_array_pop(Sdb *s, const char *key, ut32 *cas);
// Adds the number `val` to the start of array `key`.
RZ_API int sdb_array_push_num(Sdb *s, const char *key, ut64 num, ut32 cas);
// Returns the number at the start of array `key`.
RZ_API ut64 sdb_array_pop_num(Sdb *s, const char *key, ut32 *cas);
RZ_API char *sdb_array_pop_head(Sdb *s, const char *key, ut32 *cas);
RZ_API char *sdb_array_pop_tail(Sdb *s, const char *key, ut32 *cas);
typedef void (*SdbHook)(Sdb *s, void *user, const char *k, const char *v);
RZ_API void sdb_global_hook(SdbHook hook, void *user);
RZ_API bool sdb_hook(Sdb *s, SdbHook cb, void *user);
RZ_API bool sdb_unhook(Sdb *s, SdbHook h);
RZ_API int sdb_hook_call(Sdb *s, const char *k, const char *v);
RZ_API void sdb_hook_free(Sdb *s);
/* Util.c */
RZ_API int sdb_isnum(const char *s);
RZ_API bool sdb_isempty(Sdb *s);
RZ_API const char *sdb_type(const char *k);
RZ_API bool sdb_match(const char *str, const char *glob);
RZ_API int sdb_bool_set(Sdb *db, const char *str, bool v, ut32 cas);
RZ_API bool sdb_bool_get(Sdb *db, const char *str, ut32 *cas);
// base64
RZ_API ut8 *sdb_decode(const char *in, int *len);
RZ_API char *sdb_encode(const ut8 *bin, int len);
RZ_API void sdb_encode_raw(char *bout, const ut8 *bin, int len);
RZ_API int sdb_decode_raw(ut8 *bout, const char *bin, int len);
// binfmt
RZ_API char *sdb_fmt(const char *fmt, ...) SDB_PRINTF_CHECK(1, 2);
RZ_API int sdb_fmt_init(void *p, const char *fmt);
RZ_API void sdb_fmt_free(void *p, const char *fmt);
RZ_API int sdb_fmt_tobin(const char *_str, const char *fmt, void *stru);
RZ_API char *sdb_fmt_tostr(void *stru, const char *fmt);
RZ_API char **sdb_fmt_array(const char *list);
RZ_API ut64 *sdb_fmt_array_num(const char *list);
// raw array helpers
RZ_API char *sdb_array_compact(char *p);
RZ_API char *sdb_aslice(char *out, int from, int to);
#define sdb_aforeach(x, y) \
{ \
char *next; \
if (y) \
for (x = y;;) { \
x = sdb_anext(x, &next);
#define sdb_aforeach_next(x) \
if (!next) \
break; \
*(next - 1) = ','; \
x = next; \
} \
}
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,59 @@
// SPDX-FileCopyrightText: pancake <pancake@nopcode.org>
// SPDX-License-Identifier: MIT
#ifndef SDB_PRIVATE_H_
#define SDB_PRIVATE_H_
#ifdef __cplusplus
extern "C" {
#endif
#ifndef HAVE_HEADER_SYS_MMAN_H
#define HAVE_HEADER_SYS_MMAN_H HAVE_MMAN
#endif
#define SDB_V_NOT(op, fail_ret) \
if ((op) == (fail_ret)) \
eprintf(#op " at %s:%d failed: %s\n", __FILE__, __LINE__, strerror(errno))
#define write_(fd, buf, count) SDB_V_NOT(write(fd, buf, count), -1)
#define read_(fd, buf, count) SDB_V_NOT(read(fd, buf, count), -1)
static inline int seek_set(int fd, off_t pos) {
return ((fd == -1) || (lseek(fd, (off_t)pos, SEEK_SET) == -1)) ? 0 : 1;
}
static inline void ut32_pack(char s[4], ut32 u) {
s[0] = u & 255;
u >>= 8;
s[1] = u & 255;
u >>= 8;
s[2] = u & 255;
s[3] = u >> 8;
}
static inline void ut32_pack_big(char s[4], ut32 u) {
s[3] = u & 255;
u >>= 8;
s[2] = u & 255;
u >>= 8;
s[1] = u & 255;
s[0] = u >> 8;
}
static inline void ut32_unpack(char s[4], ut32 *u) {
ut32 result = 0;
result = (ut8)s[3];
result <<= 8;
result += (ut8)s[2];
result <<= 8;
result += (ut8)s[1];
result <<= 8;
result += (ut8)s[0];
*u = result;
}
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,69 @@
// SPDX-FileCopyrightText: 2011-2020 pancake <pancake@nopcode.org>
// SPDX-License-Identifier: MIT
#include "sdbht.h"
void sdbkv_fini(SdbKv *kv) {
free(kv->base.key);
free(kv->base.value);
}
RZ_API HtPP *sdb_ht_new(void) {
HtPP *ht = ht_pp_new((HtPPDupValue)strdup, (HtPPKvFreeFunc)sdbkv_fini, (HtPPCalcSizeV)strlen);
if (ht) {
ht->opt.elem_size = sizeof(SdbKv);
}
return ht;
}
static bool sdb_ht_internal_insert(HtPP *ht, const char *key, const char *value, bool update) {
if (!ht || !key || !value) {
return false;
}
SdbKv kvp = { { 0 } };
kvp.base.key = strdup((void *)key);
if (!kvp.base.key) {
goto err;
}
kvp.base.value = strdup((void *)value);
if (!kvp.base.value) {
goto err;
}
kvp.base.key_len = strlen(kvp.base.key);
kvp.base.value_len = strlen(kvp.base.value);
kvp.expire = 0;
return ht_pp_insert_kv(ht, (HtPPKv *)&kvp, update);
err:
free(kvp.base.key);
free(kvp.base.value);
return false;
}
RZ_API bool sdb_ht_insert(HtPP *ht, const char *key, const char *value) {
return sdb_ht_internal_insert(ht, key, value, false);
}
RZ_API bool sdb_ht_insert_kvp(HtPP *ht, SdbKv *kvp, bool update) {
return ht_pp_insert_kv(ht, (HtPPKv *)kvp, update);
}
RZ_API bool sdb_ht_update(HtPP *ht, const char *key, const char *value) {
return sdb_ht_internal_insert(ht, key, value, true);
}
RZ_API SdbKv *sdb_ht_find_kvp(HtPP *ht, const char *key, bool *found) {
return (SdbKv *)ht_pp_find_kv(ht, key, found);
}
RZ_API char *sdb_ht_find(HtPP *ht, const char *key, bool *found) {
return (char *)ht_pp_find(ht, key, found);
}
RZ_API void sdb_ht_free(HtPP *ht) {
ht_pp_free(ht);
}
RZ_API bool sdb_ht_delete(HtPP *ht, const char *key) {
return ht_pp_delete(ht, key);
}

View file

@ -0,0 +1,63 @@
// SPDX-FileCopyrightText: pancake <pancake@nopcode.org>
// SPDX-License-Identifier: MIT
#ifndef __SDB_HT_H
#define __SDB_HT_H
#include "ht_pp.h"
#ifdef __cplusplus
extern "C" {
#endif
/** keyvalue pair **/
typedef struct sdb_kv {
// sub of HtPPKv so we can cast safely
HtPPKv base;
ut32 cas;
ut64 expire;
} SdbKv;
static inline char *sdbkv_key(const SdbKv *kv) {
return (char *)kv->base.key;
}
static inline char *sdbkv_value(const SdbKv *kv) {
return (char *)kv->base.value;
}
static inline ut32 sdbkv_key_len(const SdbKv *kv) {
return kv->base.key_len;
}
static inline ut32 sdbkv_value_len(const SdbKv *kv) {
return kv->base.value_len;
}
RZ_API SdbKv *sdbkv_new2(const char *k, int kl, const char *v, int vl);
RZ_API SdbKv *sdbkv_new(const char *k, const char *v);
extern RZ_API void sdbkv_free(SdbKv *kv);
extern RZ_API ut32 sdb_hash(const char *key);
RZ_API HtPP *sdb_ht_new(void);
// Destroy a hashtable and all of its entries.
RZ_API void sdb_ht_free(HtPP *ht);
// Insert a new Key-Value pair into the hashtable. If the key already exists, returns false.
RZ_API bool sdb_ht_insert(HtPP *ht, const char *key, const char *value);
// Insert a new Key-Value pair into the hashtable, or updates the value if the key already exists.
RZ_API bool sdb_ht_insert_kvp(HtPP *ht, SdbKv *kvp, bool update);
// Insert a new Key-Value pair into the hashtable, or updates the value if the key already exists.
RZ_API bool sdb_ht_update(HtPP *ht, const char *key, const char *value);
// Delete a key from the hashtable.
RZ_API bool sdb_ht_delete(HtPP *ht, const char *key);
// Find the value corresponding to the matching key.
RZ_API char *sdb_ht_find(HtPP *ht, const char *key, bool *found);
// Find the KeyValuePair corresponding to the matching key.
RZ_API SdbKv *sdb_ht_find_kvp(HtPP *ht, const char *key, bool *found);
#ifdef __cplusplus
}
#endif
#endif // __SDB_HT_H

48
librz/util/sdb/src/set.c Normal file
View file

@ -0,0 +1,48 @@
// SPDX-FileCopyrightText: 2019 pancake <pancake@nopcode.org>
// SPDX-License-Identifier: MIT
#include "set.h"
// p
RZ_API SetP *set_p_new(void) {
return ht_pp_new0();
}
RZ_API void set_p_add(SetP *s, const void *u) {
ht_pp_insert(s, u, (void *)1);
}
RZ_API bool set_p_contains(SetP *s, const void *u) {
return ht_pp_find(s, u, NULL) != NULL;
}
RZ_API void set_p_delete(SetP *s, const void *u) {
ht_pp_delete(s, u);
}
RZ_API void set_p_free(SetP *p) {
ht_pp_free((HtPP *)p);
}
// u
RZ_API SetU *set_u_new(void) {
return (SetU *)ht_up_new0();
}
RZ_API void set_u_add(SetU *s, ut64 u) {
ht_up_insert(s, u, (void *)1);
}
RZ_API bool set_u_contains(SetU *s, ut64 u) {
return ht_up_find(s, u, NULL) != NULL;
}
RZ_API void set_u_delete(SetU *s, ut64 u) {
ht_up_delete(s, u);
}
RZ_API void set_u_free(SetU *s) {
ht_up_free(s);
}

34
librz/util/sdb/src/set.h Normal file
View file

@ -0,0 +1,34 @@
// SPDX-FileCopyrightText: pancake <pancake@nopcode.org>
// SPDX-License-Identifier: MIT
#ifndef SDB_SET_H
#define SDB_SET_H
#include "ht_pp.h"
#include "ht_up.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef HtPP SetP;
RZ_API SetP *set_p_new(void);
RZ_API void set_p_add(SetP *p, const void *u);
RZ_API bool set_p_contains(SetP *s, const void *u);
RZ_API void set_p_delete(SetP *s, const void *u);
RZ_API void set_p_free(SetP *p);
typedef HtUP SetU;
RZ_API SetU *set_u_new(void);
RZ_API void set_u_add(SetU *p, ut64 u);
RZ_API bool set_u_contains(SetU *s, ut64 u);
RZ_API void set_u_delete(SetU *s, ut64 u);
RZ_API void set_u_free(SetU *p);
#ifdef __cplusplus
}
#endif
#endif

439
librz/util/sdb/src/text.c Normal file
View file

@ -0,0 +1,439 @@
// SPDX-FileCopyrightText: 2020 thestr4ng3r <info@florianmaerkl.de>
// SPDX-License-Identifier: MIT
#include "sdb.h"
#include <fcntl.h>
#include <limits.h>
#include <sys/stat.h>
#if HAVE_HEADER_SYS_MMAN_H
#include <sys/mman.h>
#endif
#include "sdb_private.h"
/**
* ********************
* Plaintext SDB Format
* ********************
*
* Files are UTF-8 and use '\n' line endings. Always.
*
* Lines starting with '/' denote the path of the namespace for the following data:
*
* /some/namespace
*
* The default path is root, just a slash also means root.
* These paths are always absolute from the root. Characters that must be escaped in a path are: '/', '\\', '\n', '\r':
*
* /s\/ome/name\nspa\\ce
*
* SDB entries are written each as a single k=v line:
*
* somekey=somevalue
*
* To distinguish these from path lines, if there is a leading '/' in the key, it must be escaped
* (slashes later in the line don't have to be escaped):
*
* \/slashedkey=somevalue
*
* Other than that, at any postion, '\\', '\n' and '\r' must be escaped:
*
* some\\key=some\nvalue
*
* In the key, '=' must also be escaped (not necessary in the value):
*
* some\=key=some=value
*
* --------
* Example:
*
* /
* key=intheroot
* \/slashedkey=somevalue
* some\\key=some\nvalue
* some\=key=some=value
*
* /subns
* some=stuff in the sub-namespace
*
* /subns/deeper
* this=is in /subns/deeper
*
*/
static int cmp_ns(const void *a, const void *b) {
const SdbNs *nsa = a;
const SdbNs *cia = b;
return strcmp(nsa->name, cia->name);
}
// n = position we are currently looking at
// p = position until we have already written everything
// flush a block of text that doesn't have to be escaped
#define FLUSH \
do { \
if (p != n) { \
write_(fd, p, n - p); \
p = n; \
} \
} while (0)
// write and escape a string from str to fd
#define ESCAPE_LOOP(fd, str, escapes) \
do { \
const char *p = str; \
const char *n = p; \
while (*n) { \
switch (*n) { escapes } \
n++; \
} \
FLUSH; \
} while (0)
#define ESCAPE(c, repl, replsz) \
case c: \
FLUSH; \
p++; \
write_(fd, "\\" repl, replsz + 1); \
break;
static void write_path(int fd, SdbList *path) {
write_(fd, "/", 1); // always print a /, even if path is empty
SdbListIter *it;
const char *path_token;
bool first = true;
ls_foreach (path, it, path_token) {
if (first) {
first = false;
} else {
write_(fd, "/", 1);
}
ESCAPE_LOOP(fd, path_token,
ESCAPE('\\', "\\", 1);
ESCAPE('/', "/", 1);
ESCAPE('\n', "n", 1);
ESCAPE('\r', "r", 1););
}
}
static void write_key(int fd, const char *k) {
// escape leading '/'
if (*k == '/') {
write_(fd, "\\", 1);
}
ESCAPE_LOOP(fd, k,
ESCAPE('\\', "\\", 1);
ESCAPE('=', "=", 1);
ESCAPE('\n', "n", 1);
ESCAPE('\r', "r", 1););
}
static void write_value(int fd, const char *v) {
ESCAPE_LOOP(fd, v,
ESCAPE('\\', "\\", 1);
ESCAPE('\n', "n", 1);
ESCAPE('\r', "r", 1););
}
#undef FLUSH
#undef ESCAPE_LOOP
#undef ESCAPE
static bool save_kv_cb(void *user, const char *k, const char *v) {
int fd = *(int *)user;
write_key(fd, k);
write_(fd, "=", 1);
write_value(fd, v);
write_(fd, "\n", 1);
return true;
}
static bool text_save(Sdb *s, int fd, bool sort, SdbList *path) {
// path
write_path(fd, path);
write_(fd, "\n", 1);
// k=v entries
if (sort) {
SdbList *l = sdb_foreach_list(s, true);
SdbKv *kv;
SdbListIter *it;
ls_foreach (l, it, kv) {
save_kv_cb(&fd, sdbkv_key(kv), sdbkv_value(kv));
}
ls_free(l);
} else {
// This is faster when sorting is not needed.
sdb_foreach(s, save_kv_cb, &fd);
}
// sub-namespaces
SdbList *l = s->ns;
if (sort) {
l = ls_clone(l);
ls_sort(l, cmp_ns);
}
SdbNs *ns;
SdbListIter *it;
ls_foreach (l, it, ns) {
write_(fd, "\n", 1);
ls_push(path, ns->name);
text_save(ns->sdb, fd, sort, path);
ls_pop(path);
}
if (l != s->ns) {
ls_free(l);
}
return true;
}
#undef write_
RZ_API bool sdb_text_save_fd(Sdb *s, int fd, bool sort) {
SdbList *path = ls_new();
if (!path) {
return false;
}
bool r = text_save(s, fd, sort, path);
ls_free(path);
return r;
}
RZ_API bool sdb_text_save(Sdb *s, const char *file, bool sort) {
int fd = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
if (fd < 0) {
return false;
}
bool r = sdb_text_save_fd(s, fd, sort);
close(fd);
return r;
}
typedef enum {
STATE_NEWLINE,
STATE_PATH,
STATE_KEY,
STATE_VALUE
} LoadState;
typedef struct {
bool eof;
char *buf;
size_t bufsz;
Sdb *root_db;
Sdb *cur_db; // current namespace, changes when encountering a path line
size_t pos; // current processing position in the buffer
size_t line_begin;
size_t token_begin; // beginning of the currently processed token in the buffer
size_t shift; // amount to shift chars to the left (from unescaping)
SdbList /*<size_t>*/ *path;
LoadState state;
bool unescape; // whether the prev char was a backslash, i.e. the current one is escaped
} LoadCtx;
// to be called at the end of a line.
// save all the data processed from the line into the database.
// assumes that the ctx->buf is allocated until ctx->buf[ctx->pos] inclusive!
static void load_process_line(LoadCtx *ctx) {
ctx->unescape = false;
// finish up the line
ctx->buf[ctx->pos - ctx->shift] = '\0';
switch (ctx->state) {
case STATE_PATH: {
ls_push(ctx->path, (void *)ctx->token_begin);
SdbListIter *it;
void *token_off_tmp;
ctx->cur_db = ctx->root_db;
ls_foreach (ctx->path, it, token_off_tmp) {
size_t token_off = (size_t)token_off_tmp;
if (!ctx->buf[token_off]) {
continue;
}
ctx->cur_db = sdb_ns(ctx->cur_db, ctx->buf + token_off, 1);
if (!ctx->cur_db) {
ctx->cur_db = ctx->root_db;
break;
}
}
ls_destroy(ctx->path);
break;
}
case STATE_VALUE: {
const char *k = ctx->buf + ctx->line_begin;
const char *v = ctx->buf + ctx->token_begin;
if (!*k || !*v) {
break;
}
sdb_set(ctx->cur_db, k, v, 0);
break;
}
default:
break;
}
// prepare for next line
ctx->shift = 0;
ctx->state = STATE_NEWLINE;
}
static inline char unescape_raw_char(char c) {
switch (c) {
case 'n':
return '\n';
case 'r':
return '\r';
case 't':
return '\t';
default:
return c;
}
}
static void load_process_single_char(LoadCtx *ctx) {
char c = ctx->buf[ctx->pos];
if (c == '\n' || c == '\r') {
load_process_line(ctx);
ctx->pos++;
return;
}
if (ctx->state == STATE_NEWLINE) {
ctx->line_begin = ctx->pos;
// at the start of a line, decide whether it's a path or a k=v
// by whether there is a leading slash.
if (c == '/') {
ctx->state = STATE_PATH;
ctx->token_begin = ctx->pos + 1;
ctx->pos++;
c = ctx->buf[ctx->pos];
return;
}
ctx->state = STATE_KEY;
}
if (ctx->unescape) {
ctx->buf[ctx->pos - ctx->shift] = unescape_raw_char(c);
ctx->unescape = false;
} else if (c == '\\') {
// got a backslash, the next char, unescape in the next iteration or die!
ctx->shift++;
ctx->unescape = true;
} else if (ctx->state == STATE_PATH && c == '/') {
// new path token
ctx->buf[ctx->pos - ctx->shift] = '\0';
ls_push(ctx->path, (void *)ctx->token_begin);
ctx->token_begin = ctx->pos + 1;
ctx->shift = 0;
} else if (ctx->state == STATE_KEY && c == '=') {
// switch from key to value mode
ctx->buf[ctx->pos - ctx->shift] = '\0';
ctx->token_begin = ctx->pos + 1;
ctx->shift = 0;
ctx->state = STATE_VALUE;
} else if (ctx->shift) {
// just some char, shift it back if necessary
ctx->buf[ctx->pos - ctx->shift] = c;
}
ctx->pos++;
}
static bool load_process_final_line(LoadCtx *ctx) {
// load_process_line needs ctx.buf[ctx.pos] to be allocated!
// so we need room for one additional byte after the buffer.
size_t linesz = ctx->bufsz - ctx->line_begin;
char *linebuf = malloc(linesz + 1);
if (!linebuf) {
return false;
}
memcpy(linebuf, ctx->buf + ctx->line_begin, linesz);
ctx->buf = linebuf;
// shift everything by the size we skipped
ctx->bufsz -= ctx->line_begin;
ctx->pos = linesz;
ctx->token_begin -= ctx->line_begin;
SdbListIter *it;
void *token_off_tmp;
ls_foreach (ctx->path, it, token_off_tmp) {
it->data = (void *)((size_t)token_off_tmp - ctx->line_begin);
}
ctx->line_begin = 0;
load_process_line(ctx);
free(linebuf);
ctx->buf = NULL;
return true;
}
static void load_ctx_fini(LoadCtx *ctx) {
ls_free(ctx->path);
}
static bool load_ctx_init(LoadCtx *ctx, Sdb *s, char *buf, size_t sz) {
ctx->eof = false;
ctx->buf = buf;
ctx->bufsz = sz;
ctx->root_db = s;
ctx->cur_db = s;
ctx->pos = 0;
ctx->line_begin = 0;
ctx->token_begin = 0;
ctx->shift = 0;
ctx->path = ls_new();
ctx->state = STATE_NEWLINE;
ctx->unescape = false;
if (!ctx->buf || !ctx->path) {
load_ctx_fini(ctx);
return false;
}
return true;
}
RZ_API bool sdb_text_load_buf(Sdb *s, char *buf, size_t sz) {
if (!sz) {
return true;
}
LoadCtx ctx;
if (!load_ctx_init(&ctx, s, buf, sz)) {
return false;
}
bool ret = true;
while (ctx.pos < ctx.bufsz) {
load_process_single_char(&ctx);
}
if (ctx.line_begin < ctx.bufsz && ctx.state != STATE_NEWLINE) {
load_process_final_line(&ctx);
}
load_ctx_fini(&ctx);
return ret;
}
RZ_API bool sdb_text_load(Sdb *s, const char *file) {
int fd = open(file, O_RDONLY | O_BINARY);
if (fd < 0) {
return false;
}
bool r = false;
struct stat st;
if (fstat(fd, &st) || !st.st_size) {
goto beach;
}
#if HAVE_HEADER_SYS_MMAN_H
char *x = mmap(0, st.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
if (x == MAP_FAILED) {
goto beach;
}
#else
char *x = calloc(1, st.st_size);
if (!x) {
goto beach;
}
if (read(fd, x, st.st_size) != st.st_size) {
free(x);
goto beach;
}
#endif
r = sdb_text_load_buf(s, x, st.st_size);
#if HAVE_HEADER_SYS_MMAN_H
munmap(x, st.st_size);
#else
free(x);
#endif
beach:
close(fd);
return r;
}

283
librz/util/sdb/src/util.c Normal file
View file

@ -0,0 +1,283 @@
// SPDX-FileCopyrightText: 2011-2018 pancake <pancake@nopcode.org>
// SPDX-License-Identifier: MIT
#include "sdb.h"
#include <rz_util/rz_time.h>
RZ_API ut32 sdb_hash_len(const char *s, ut32 *len) {
ut32 h = CDB_HASHSTART;
ut32 count = 0;
if (s) {
while (*s) {
h = (h + (h << 5)) ^ *s++;
count++;
}
}
if (len) {
*len = count;
}
return h;
}
RZ_API ut32 sdb_hash(const char *s) {
return sdb_hash_len(s, NULL);
}
RZ_API ut8 sdb_hash_byte(const char *s) {
const ut32 hash = sdb_hash_len(s, NULL);
const ut8 *h = (const ut8 *)&hash;
return h[0] ^ h[1] ^ h[2] ^ h[3];
}
RZ_API const char *sdb_itoca(ut64 n) {
return sdb_itoa(n, sdb_fmt(NULL), 16);
}
// assert (sizeof (s)>64)
// if s is null, the returned pointer must be freed!!
RZ_API char *sdb_itoa(ut64 n, char *s, int base) {
static const char *lookup = "0123456789abcdef";
const int imax = 62;
int i = imax, copy_string = 1, alloc = 0;
if (s) {
*s = 0;
} else {
alloc = 1;
s = malloc(64);
if (!s) {
return NULL;
}
}
if (base < 0) {
copy_string = 0;
base = -base;
}
if ((base > 16) || (base < 1)) {
if (alloc) {
free(s);
}
return NULL;
}
if (!n) {
strcpy(s, "0");
return s;
}
s[imax + 1] = '\0';
if (base <= 10) {
for (; n && i > 0; n /= base) {
s[i--] = (n % base) + '0';
}
} else {
for (; n && i > 0; n /= base) {
s[i--] = lookup[(n % base)];
}
if (i != imax) {
s[i--] = 'x';
}
s[i--] = '0';
}
if (copy_string || alloc) {
// unnecessary memmove in case we use the return value
// return s + i + 1;
memmove(s, s + i + 1, strlen(s + i + 1) + 1);
return s;
}
return s + i + 1;
}
RZ_API ut64 sdb_atoi(const char *s) {
char *p;
ut64 ret;
if (!s || *s == '-') {
return 0LL;
}
ret = strtoull(s, &p, 0);
return p ? ret : 0LL;
}
// NOTE: Reuses memory. probably not bindings friendly..
RZ_API char *sdb_array_compact(char *p) {
char *e;
// remove empty elements
while (*p) {
if (!strncmp(p, ",,", 2)) {
p++;
for (e = p + 1; *e == ','; e++) {
};
memmove(p, e, strlen(e) + 1);
} else {
p++;
}
}
return p;
}
// NOTE: Reuses memory. probably not bindings friendly..
RZ_API char *sdb_aslice(char *out, int from, int to) {
int len, idx = 0;
char *str = NULL;
char *end = NULL;
char *p = out;
if (from >= to) {
return NULL;
}
while (*p) {
if (!str && idx == from) {
str = p;
}
if (idx == to) {
end = p;
break;
}
if (*p == ',') {
idx++;
}
p++;
}
if (str) {
if (!end) {
end = str + strlen(str);
}
len = (size_t)(end - str);
memmove(out, str, len);
out[len] = 0;
return out;
}
return NULL;
}
// TODO: find better name for it
// TODO: optimize, because this is the main bottleneck for sdb_array_set()
RZ_API int sdb_alen(const char *str) {
int len = 1;
const char *n, *p = str;
if (!p || !*p) {
return 0;
}
for (len = 0;; len++) {
n = strchr(p, SDB_RS);
if (!n) {
break;
}
p = n + 1;
}
return ++len;
}
RZ_API int sdb_alen_ignore_empty(const char *str) {
int len = 1;
const char *n, *p = str;
if (!p || !*p) {
return 0;
}
while (*p == SDB_RS) {
p++;
}
for (len = 0;;) {
n = strchr(p, SDB_RS);
if (!n) {
break;
}
p = n + 1;
if (*(p) == SDB_RS) {
continue;
}
len++;
}
if (*p)
len++;
return len;
}
RZ_API char *sdb_anext(char *str, char **next) {
char *nxt, *p = strchr(str, SDB_RS);
if (p) {
*p = 0;
nxt = p + 1;
} else {
nxt = NULL;
}
if (next) {
*next = nxt;
}
return str;
}
RZ_API const char *sdb_const_anext(const char *str) {
const char *p = strchr(str, SDB_RS);
return p ? p + 1 : NULL;
}
RZ_API ut64 sdb_now(void) {
ut64 usec = rz_time_now();
return usec / 1000000;
}
RZ_API int sdb_isnum(const char *s) {
const char vs = *s;
return ((vs == '-' || vs == '+') || (vs >= '0' && vs <= '9'));
}
RZ_API int sdb_num_base(const char *s) {
if (!s) {
return SDB_NUM_BASE;
}
if (!strncmp(s, "0x", 2)) {
return 16;
}
return (*s == '0' && s[1]) ? 8 : 10;
}
RZ_API const char *sdb_type(const char *k) {
if (!k || !*k) {
return "undefined";
}
if (sdb_isnum(k)) {
return "number";
}
if (sdb_isjson(k)) {
return "json";
}
if (strchr(k, ',')) {
return "array";
}
if (!strcmp(k, "true") || !strcmp(k, "false")) {
return "boolean";
}
return "string";
}
// TODO: check all the values
RZ_API bool sdb_isjson(const char *k) {
int level = 0;
bool quotes = false;
if (!k || (*k != '{' && *k != '[')) {
return false;
}
for (; *k; k++) {
if (quotes) {
if (*k == '"') {
quotes = false;
}
continue;
}
switch (*k) {
case '"':
quotes = true;
break;
case '[':
case '{':
level++;
break;
case ']':
case '}':
level--;
if (level < 0) {
/* invalid json */
return false;
}
break;
}
}
return (!quotes && !level);
}

View file

@ -69,17 +69,7 @@ message('rizin lib version: ' + rizin_libversion)
# system dependencies
cc = meson.get_compiler('c')
# required for linux
ldl = cc.find_library('dl', required: false, static: is_static_build)
th = dependency('threads', required: false, static: is_static_build)
have_pthread = th.found() and host_machine.system() != 'windows'
utl = cc.find_library('util', required: false, static: is_static_build)
if host_machine.system() == 'sunos'
# workaround for Solaris until https://github.com/mesonbuild/meson/issues/4328 is fixed
mth = declare_dependency(link_args: '-lm')
else
mth = cc.find_library('m', required: false, static: is_static_build)
endif
cc_native = meson.get_compiler('c', native: true)
platform_deps = []
platform_inc = ['.', 'librz', 'librz/include']
@ -368,133 +358,173 @@ endif
message('RZ_CHECKS_LEVEL: @0@'.format(checks_level))
userconf = configuration_data()
userconf.set('RZ_CHECKS_LEVEL', checks_level)
userconf.set10('IS_PORTABLE', get_option('portable'))
userconf.set10('HAVE_LIB_MAGIC', sys_magic.found())
userconf.set10('USE_LIB_MAGIC', sys_magic.found())
userconf.set10('HAVE_LIB_XXHASH', xxhash_dep.found())
userconf.set10('USE_LIB_XXHASH', xxhash_dep.found())
userconf.set10('DEBUGGER', has_debugger)
userconf.set('PREFIX', rizin_prefix)
userconf.set('BINDIR', rizin_bindir)
userconf.set('BINDIR_DEPTH', bindir_depth)
userconf.set('LIBDIR', rizin_libdir)
userconf.set('INCLUDEDIR', rizin_incdir)
userconf.set('DATADIR_RZ', rizin_datdir_rz)
is_ppc = host_machine.cpu_family() == 'ppc' or host_machine.cpu_family() == 'ppc64'
userconf.set10('HAVE_JEMALLOC', host_machine.system() != 'windows' and (host_machine.system() != 'darwin' or not is_ppc))
userconf.set('DATADIR', rizin_datdir)
userconf.set('WWWROOT', rizin_wwwroot)
userconf.set('SDB', rizin_sdb)
userconf.set('SIGDB', rizin_sigdb)
userconf.set('THEMES', rizin_themes)
userconf.set('FORTUNES', rizin_fortunes)
userconf.set('FLAGS', rizin_flags)
userconf.set('HUD', rizin_hud)
userconf.set('PLUGINS', rizin_plugins)
userconf.set('BINDINGS', rizin_bindings)
userconf.set10('HAVE_OPENSSL', sys_openssl.found())
userconf.set10('HAVE_LIBUV', libuv_dep.found())
userconf.set10('WANT_DYLINK', true)
userconf.set10('HAVE_PTRACE', have_ptrace)
userconf.set10('USE_PTRACE_WRAP', use_ptrace_wrap)
userconf.set10('WITH_GPL', get_option('use_gpl'))
userconf.set10('WITH_SWIFT_DEMANGLER', get_option('use_swift_demangler'))
userconf.set10('RZ_BUILD_DEBUG', get_option('buildtype').startswith('debug'))
ok = cc.has_header_symbol('sys/personality.h', 'ADDR_NO_RANDOMIZE')
userconf.set10('HAVE_DECL_ADDR_NO_RANDOMIZE', ok)
ok = cc.has_header_symbol('sys/procctl.h', 'PROC_ASLR_CTL')
userconf.set10('HAVE_DECL_PROCCTL_ASLR_CTL', ok)
userconf.set10('HAVE_THREADS', th.found())
userconf.set10('HAVE_PTHREAD', have_pthread)
userconf.set10('HAVE_LZMA', get_option('use_lzma'))
userconf.set10('HAVE_ZLIB', get_option('use_zlib'))
if host_machine.system() == 'freebsd' or host_machine.system() == 'dragonfly'
add_project_link_arguments('-Wl,--unresolved-symbols,ignore-in-object-files', language: 'c')
add_project_link_arguments('-Wl,--allow-shlib-undefined', language: 'c')
ccs = [[cc, userconf, host_machine, false]]
if meson.is_cross_build()
userconf_native = configuration_data()
ccs += [[cc_native, userconf_native, build_machine, true]]
endif
userconfs = []
lrt = dependency('', required: false)
if not cc.has_function('clock_gettime', prefix: '#include <time.h>') and cc.has_header_symbol('features.h', '__GLIBC__')
lrt = cc.find_library('rt', required: true, static: is_static_build)
endif
foreach it : ccs
it_cc = it[0]
it_userconf = it[1]
it_machine = it[2]
it_native = it[3]
have_lrt = not ['windows', 'darwin', 'openbsd', 'android', 'haiku'].contains(host_machine.system())
if have_lrt and not lrt.found()
lrt = cc.find_library('rt', required: true, static: is_static_build)
endif
# required for linux
it_utl = it_cc.find_library('util', required: false, static: is_static_build)
it_ldl = it_cc.find_library('dl', required: false, static: is_static_build)
it_th = dependency('threads', required: false, static: is_static_build, native: it_native)
have_pthread = it_th.found() and it_machine.system() != 'windows'
if it_machine.system() == 'sunos'
# workaround for Solaris until https://github.com/mesonbuild/meson/issues/4328 is fixed
it_mth = declare_dependency(link_args: '-lm', native: it_native)
else
it_mth = it_cc.find_library('m', required: false, static: is_static_build)
endif
it_lrt = dependency('', required: false, native: it_native)
if not it_cc.has_function('clock_gettime', prefix: '#include <time.h>') and it_cc.has_header_symbol('features.h', '__GLIBC__')
it_lrt = it_cc.find_library('rt', required: true, static: is_static_build)
endif
have_lrt = not ['windows', 'darwin', 'openbsd', 'android', 'haiku'].contains(it_machine.system())
if have_lrt and not it_lrt.found()
it_lrt = it_cc.find_library('rt', required: true, static: is_static_build)
endif
if host_machine.system() == 'darwin'
# On older Mac OS X versions (at least Lion and older), environ is only available when compiling an executable,
# but we will use is primarily in shared libs. Unfortunately, cc.links() only checks for executable compiling,
# so this does not help us.
# But in fact, even the environ(7) man page on macOS 11.6 still claims that environ is not avaliable for
# shared libs (even though it seems to work) and suggests using _NSGetEnviron(), so let's just do this always.
have_environ = false
else
code = '#include <unistd.h>\nextern char **environ;\nint main() { char **env = environ; }'
have_environ = cc.links(code, name: 'have extern char **environ')
endif
userconf.set10('HAVE_ENVIRON', have_environ)
message('HAVE_ENVIRON: @0@'.format(have_environ))
it_userconf.set('RZ_CHECKS_LEVEL', checks_level)
it_userconf.set10('IS_PORTABLE', get_option('portable'))
it_userconf.set10('HAVE_LIB_MAGIC', sys_magic.found())
it_userconf.set10('USE_LIB_MAGIC', sys_magic.found())
it_userconf.set10('HAVE_LIB_XXHASH', xxhash_dep.found())
it_userconf.set10('USE_LIB_XXHASH', xxhash_dep.found())
it_userconf.set10('DEBUGGER', has_debugger)
it_userconf.set('PREFIX', rizin_prefix)
it_userconf.set('BINDIR', rizin_bindir)
it_userconf.set('BINDIR_DEPTH', bindir_depth)
it_userconf.set('LIBDIR', rizin_libdir)
it_userconf.set('INCLUDEDIR', rizin_incdir)
it_userconf.set('DATADIR_RZ', rizin_datdir_rz)
is_ppc = it_machine.cpu_family() == 'ppc' or it_machine.cpu_family() == 'ppc64'
it_userconf.set10('HAVE_JEMALLOC', it_machine.system() != 'windows' and (it_machine.system() != 'darwin' or not is_ppc))
it_userconf.set('DATADIR', rizin_datdir)
it_userconf.set('WWWROOT', rizin_wwwroot)
it_userconf.set('SDB', rizin_sdb)
it_userconf.set('SIGDB', rizin_sigdb)
it_userconf.set('THEMES', rizin_themes)
it_userconf.set('FORTUNES', rizin_fortunes)
it_userconf.set('FLAGS', rizin_flags)
it_userconf.set('HUD', rizin_hud)
it_userconf.set('PLUGINS', rizin_plugins)
it_userconf.set('BINDINGS', rizin_bindings)
it_userconf.set10('HAVE_OPENSSL', sys_openssl.found())
it_userconf.set10('HAVE_LIBUV', libuv_dep.found())
it_userconf.set10('WANT_DYLINK', true)
it_userconf.set10('HAVE_PTRACE', have_ptrace)
it_userconf.set10('USE_PTRACE_WRAP', use_ptrace_wrap)
it_userconf.set10('WITH_GPL', get_option('use_gpl'))
it_userconf.set10('WITH_SWIFT_DEMANGLER', get_option('use_swift_demangler'))
it_userconf.set10('RZ_BUILD_DEBUG', get_option('buildtype').startswith('debug'))
ok = it_cc.has_header_symbol('sys/personality.h', 'ADDR_NO_RANDOMIZE')
it_userconf.set10('HAVE_DECL_ADDR_NO_RANDOMIZE', ok)
ok = it_cc.has_header_symbol('sys/procctl.h', 'PROC_ASLR_CTL')
it_userconf.set10('HAVE_DECL_PROCCTL_ASLR_CTL', ok)
it_userconf.set10('HAVE_THREADS', it_th.found())
it_userconf.set10('HAVE_PTHREAD', have_pthread)
it_userconf.set10('HAVE_LZMA', get_option('use_lzma'))
it_userconf.set10('HAVE_ZLIB', get_option('use_zlib'))
code = '#if __is_target_os(ios)\nint x = 0;\n#endif\nint main() { x++; }'
is_ios = cc.links(code, name: 'target is ios')
userconf.set10('IS_IOS', is_ios)
message('IS_IOS: @0@'.format(is_ios))
if it_machine.system() == 'freebsd' or it_machine.system() == 'dragonfly'
add_project_link_arguments('-Wl,--unresolved-symbols,ignore-in-object-files', language: 'c', native: it_native)
add_project_link_arguments('-Wl,--allow-shlib-undefined', language: 'c', native: it_native)
endif
foreach item : [
['arc4random_uniform', '#include <stdlib.h>', []],
['explicit_bzero', '#include <string.h>', []],
['explicit_memset', '#include <string.h>', []],
['clock_nanosleep', '#include <time.h>', []],
['clock_gettime', '#include <time.h>', [lrt]],
['sigaction', '#include <signal.h>', []],
['pipe', '#include <unistd.h>', []],
['execv', '#include <unistd.h>', []],
['execve', '#include <unistd.h>', []],
['execvp', '#include <unistd.h>', []],
['execl', '#include <unistd.h>', []],
['system', '#include <stdlib.h>', []],
['realpath', '#include <stdlib.h>', []],
['fork', '#include <unistd.h>', []],
['nice', '#include <unistd.h>', []],
['copyfile', '#include <copyfile.h>', []],
['strlcpy', '#include <string.h>', []],
['strnlen', '#include <string.h>', []],
['shm_open', '#include <sys/mman.h>', [lrt]],
['openpty', '', [utl]],
['forkpty', '', [utl]],
['login_tty', '', [utl]],
['pipe2', '#define _GNU_SOURCE\n#include <fcntl.h>\n#include <unistd.h>', []],
# copy_file_range for now disable on freebsd as it s not reliable even for small chunks
['copy_file_range', '#ifdef __linux__\n#define _GNU_SOURCE\n#include <unistd.h>\n#endif', []],
['backtrace', '', []],
]
func = item[0]
ok = cc.has_function(func, prefix: item[1], dependencies: item[2])
userconf.set10('HAVE_@0@'.format(func.to_upper()), ok)
if it_machine.system() == 'darwin'
# On older Mac OS X versions (at least Lion and older), environ is only available when compiling an executable,
# but we will use is primarily in shared libs. Unfortunately, it_cc.links() only checks for executable compiling,
# so this does not help us.
# But in fact, even the environ(7) man page on macOS 11.6 still claims that environ is not avaliable for
# shared libs (even though it seems to work) and suggests using _NSGetEnviron(), so let's just do this always.
have_environ = false
else
code = '#include <unistd.h>\nextern char **environ;\nint main() { char **env = environ; }'
have_environ = it_cc.links(code, name: 'have extern char **environ')
endif
it_userconf.set10('HAVE_ENVIRON', have_environ)
message('HAVE_ENVIRON: @0@'.format(have_environ))
code = '#if __is_target_os(ios)\nint x = 0;\n#endif\nint main() { x++; }'
is_ios = it_cc.links(code, name: 'target is ios')
it_userconf.set10('IS_IOS', is_ios)
message('IS_IOS: @0@'.format(is_ios))
foreach item : [
['arc4random_uniform', '#include <stdlib.h>', []],
['explicit_bzero', '#include <string.h>', []],
['explicit_memset', '#include <string.h>', []],
['clock_nanosleep', '#include <time.h>', []],
['clock_gettime', '#include <time.h>', [it_lrt]],
['sigaction', '#include <signal.h>', []],
['pipe', '#include <unistd.h>', []],
['execv', '#include <unistd.h>', []],
['execve', '#include <unistd.h>', []],
['execvp', '#include <unistd.h>', []],
['execl', '#include <unistd.h>', []],
['system', '#include <stdlib.h>', []],
['realpath', '#include <stdlib.h>', []],
['fork', '#include <unistd.h>', []],
['nice', '#include <unistd.h>', []],
['copyfile', '#include <copyfile.h>', []],
['strlcpy', '#include <string.h>', []],
['strnlen', '#include <string.h>', []],
['shm_open', '#include <sys/mman.h>', [it_lrt]],
['openpty', '', [it_utl]],
['forkpty', '', [it_utl]],
['login_tty', '', [it_utl]],
['pipe2', '#define _GNU_SOURCE\n#include <fcntl.h>\n#include <unistd.h>', []],
# copy_file_range for now disable on freebsd as it s not reliable even for small chunks
['copy_file_range', '#ifdef __linux__\n#define _GNU_SOURCE\n#include <unistd.h>\n#endif', []],
['backtrace', '', []],
]
func = item[0]
ok = it_cc.has_function(func, prefix: item[1], dependencies: item[2])
it_userconf.set10('HAVE_@0@'.format(func.to_upper()), ok)
endforeach
foreach item : [
['linux/ashmem.h', '', []],
['sys/shm.h', '', []],
['sys/ipc.h', '', []],
['sys/mman.h', '', []],
['inttypes.h', '', []],
]
hdr = item[0]
ok = it_cc.has_header(hdr, prefix: item[1], dependencies: item[2])
it_userconf.set10('HAVE_HEADER_@0@'.format(hdr.underscorify().to_upper()), ok)
endforeach
if it_userconf.get('HAVE_PIPE2') == 1
add_project_arguments('-D_GNU_SOURCE', language: ['c', 'cpp'], native: it_native)
endif
userconfs += [[it_userconf, it_lrt, it_utl, it_ldl, it_mth, it_th]]
endforeach
foreach item : [
['linux/ashmem.h', '', []],
['sys/shm.h', '', []],
['sys/ipc.h', '', []],
['sys/mman.h', '', []],
['inttypes.h', '', []],
]
hdr = item[0]
ok = cc.has_header(hdr, prefix: item[1], dependencies: item[2])
userconf.set10('HAVE_HEADER_@0@'.format(hdr.underscorify().to_upper()), ok)
endforeach
if userconf.get('HAVE_PIPE2') == 1
add_project_arguments('-D_GNU_SOURCE', language: ['c', 'cpp'])
userconf = userconfs[0][0]
lrt = userconfs[0][1]
utl = userconfs[0][2]
ldl = userconfs[0][3]
mth = userconfs[0][4]
th = userconfs[0][5]
if meson.is_cross_build()
userconf_native = userconfs[1][0]
lrt_native = userconfs[1][1]
utl_native = userconfs[1][2]
ldl_native = userconfs[1][3]
mth_native = userconfs[1][4]
th_native = userconfs[1][5]
endif
rz_userconf_h_in = files('librz/include/rz_userconf.h.in')
rz_userconf_h = configure_file(
input: 'librz/include/rz_userconf.h.in',
input: rz_userconf_h_in,
output: 'rz_userconf.h',
configuration: userconf,
install_dir: rizin_incdir
@ -606,23 +636,6 @@ endif
yxml_proj = subproject('yxml', default_options: ['default_library=static'])
yxml_dep = yxml_proj.get_variable('yxml_dep')
# handle sdb dependency
r = run_command(py3_exe, check_meson_subproject_py, 'sdb', check: false)
if r.returncode() == 1 and get_option('subprojects_check')
error('Subprojects are not updated. Please run `git clean -dxff subprojects/` to delete all local subprojects directories. If you want to compile against current subprojects then set option `subprojects_check=false`.')
endif
sdb_proj = subproject('sdb', default_options: ['default_library=static', 'sdb_includedir=' + rizin_incdir])
sdb_dep = sdb_proj.get_variable('sdb_whole_dep')
sdb_exe = sdb_proj.get_variable('sdb_native_exe')
sdb_gen_cmd = [
sdb_exe,
'@OUTPUT@',
'==',
'@INPUT@'
]
use_rpath = false
use_rpath_absolute = false
if host_machine.system() != 'windows' and get_option('default_library') == 'shared'

View file

@ -73,6 +73,12 @@ if get_option('enable_tests')
'reg',
'run',
'rz_test',
'sdb_array',
'sdb_diff',
'sdb_hash',
'sdb_ls',
'sdb_sdb',
'sdb_util',
'serialize_analysis',
'serialize_config',
'serialize_debug',

View file

@ -0,0 +1,65 @@
// SPDX-FileCopyrightText: pancake <pancake@nopcode.org>
// SPDX-License-Identifier: MIT
#include <sdb.h>
#include "minunit.h"
bool test_sdb_array_push_pop(void) {
Sdb *db = sdb_new(NULL, NULL, false);
char *p;
sdb_array_push(db, "foo", "foo", 0);
sdb_array_push(db, "foo", "bar", 0);
sdb_array_push(db, "foo", "cow", 0);
mu_assert_streq(sdb_const_get(db, "foo", 0), "cow,bar,foo", "Not all items found");
p = sdb_array_pop(db, "foo", NULL);
mu_assert_streq(p, "cow", "cow was not at the top");
free(p);
p = sdb_array_pop(db, "foo", NULL);
mu_assert_streq(p, "bar", "bar was not at the top");
free(p);
p = sdb_array_pop(db, "foo", NULL);
mu_assert_streq(p, "foo", "foo was not at the top");
free(p);
p = sdb_array_pop(db, "foo", NULL);
mu_assert_eq((int)(size_t)p, (int)(size_t)NULL, "there shouldn't be any element in the array");
free(p);
sdb_free(db);
mu_end;
}
bool test_sdb_array_add_remove(void) {
Sdb *db = sdb_new(NULL, NULL, false);
sdb_array_add(db, "foo", "foo", 0);
sdb_array_add(db, "foo", "bar", 0);
sdb_array_add(db, "foo", "cow", 0);
mu_assert_streq(sdb_const_get(db, "foo", 0), "foo,bar,cow", "Not all items found");
sdb_array_remove(db, "foo", "bar", 0);
mu_assert_streq(sdb_const_get(db, "foo", 0), "foo,cow", "bar was not deleted");
sdb_array_remove(db, "foo", "nothing", 0);
mu_assert_streq(sdb_const_get(db, "foo", 0), "foo,cow", "nothing should be deleted");
sdb_array_remove(db, "foo", "cow", 0);
sdb_array_remove(db, "foo", "foo", 0);
mu_assert_eq((int)(size_t)sdb_const_get(db, "foo", 0), (int)(size_t)NULL, "all elements should be deleted");
sdb_free(db);
mu_end;
}
int all_tests() {
mu_run_test(test_sdb_array_push_pop);
mu_run_test(test_sdb_array_add_remove);
return tests_passed != tests_run;
}
int main(int argc, char **argv) {
return all_tests();
}

258
test/unit/test_sdb_diff.c Normal file
View file

@ -0,0 +1,258 @@
// SPDX-FileCopyrightText: pancake <pancake@nopcode.org>
// SPDX-License-Identifier: MIT
#include <sdb.h>
#include "minunit.h"
// #define SAVE_FILES
static Sdb *test_sdb_new(const char *file) {
#ifdef SAVE_FILES
Sdb *r = sdb_new(NULL, file, 0);
// sdb_disk_create (r);
#else
Sdb *r = sdb_new0();
#endif
sdb_set(r, "some", "stuff", 0);
sdb_set(r, "and", "even", 0);
sdb_set(r, "more", "stuff", 0);
sdb_ns(r, "emptyns", true);
Sdb *test_ns = sdb_ns(r, "test", true);
sdb_set(test_ns, "a", "123", 0);
sdb_set(test_ns, "b", "test", 0);
sdb_set(test_ns, "c", "hello", 0);
Sdb *subspace_ns = sdb_ns(test_ns, "subspace", true);
sdb_set(subspace_ns, "some", "values", 0);
sdb_set(subspace_ns, "are", "saved", 0);
sdb_set(subspace_ns, "here", "lol", 0);
return r;
}
static void test_sdb_free(Sdb *sdb) {
if (!sdb) {
return;
}
#ifdef SAVE_FILES
sdb_sync(sdb); // sdb_disk_finish (sdb);
#endif
sdb_free(sdb);
}
typedef struct {
char buf[2048];
size_t buf_len;
} Ctx;
void diff_cb(const SdbDiff *diff, void *user) {
Ctx *ctx = user;
int r = sdb_diff_format(ctx->buf + ctx->buf_len, sizeof(ctx->buf) - ctx->buf_len, diff);
if (r >= 0) {
ctx->buf_len += r;
if (ctx->buf_len >= sizeof(ctx->buf)) {
ctx->buf_len = sizeof(ctx->buf) - 1;
}
if (ctx->buf_len < sizeof(ctx->buf) + 1) {
ctx->buf[ctx->buf_len++] = '\n';
ctx->buf[ctx->buf_len] = '\0';
}
}
}
static bool diff_str(Sdb *a, Sdb *b, char **diff) {
Ctx ctx = { 0 };
bool eq = sdb_diff(a, b, diff ? diff_cb : NULL, diff ? &ctx : NULL);
if (diff) {
*diff = strdup(ctx.buf);
}
return eq;
}
bool test_sdb_diff_equal_empty() {
Sdb *a = sdb_new0();
Sdb *b = sdb_new0();
mu_assert("equal db (no diff)", diff_str(a, b, NULL));
char *diff;
mu_assert("equal db (diff)", diff_str(a, b, &diff));
mu_assert_streq(diff, "", "equal db diff");
free(diff);
test_sdb_free(a);
test_sdb_free(b);
mu_end;
}
bool test_sdb_diff_equal() {
Sdb *a = test_sdb_new("equal_a.sdb");
Sdb *b = test_sdb_new("equal_b.sdb");
mu_assert("equal db (no diff)", diff_str(a, b, NULL));
char *diff;
mu_assert("equal db (diff)", diff_str(a, b, &diff));
mu_assert_streq(diff, "", "equal db diff");
free(diff);
test_sdb_free(a);
test_sdb_free(b);
mu_end;
}
bool test_sdb_diff_ns_empty() {
Sdb *a = test_sdb_new("ns_empty_a.sdb");
Sdb *b = test_sdb_new("ns_empty_b.sdb");
sdb_ns_unset(b, "emptyns", NULL);
mu_assert("empty ns removed (no diff)", !diff_str(a, b, NULL));
char *diff;
mu_assert("empty ns removed (diff)", !diff_str(a, b, &diff));
mu_assert_streq(diff, "-NS emptyns\n", "empty ns removed diff");
free(diff);
mu_assert("empty ns added (no diff)", !diff_str(b, a, NULL));
mu_assert("empty ns added (diff)", !diff_str(b, a, &diff));
mu_assert_streq(diff, "+NS emptyns\n", "empty ns added diff");
free(diff);
test_sdb_free(a);
test_sdb_free(b);
mu_end;
}
bool test_sdb_diff_ns() {
Sdb *a = test_sdb_new("ns_a.sdb");
Sdb *b = test_sdb_new("ns_b.sdb");
sdb_ns_unset(b, "test", NULL);
mu_assert("ns removed (no diff)", !diff_str(a, b, NULL));
char *diff;
mu_assert("ns removed (diff)", !diff_str(a, b, &diff));
mu_assert_streq(diff,
"-NS test\n"
"-NS test/subspace\n"
"- test/subspace/here=lol\n"
"- test/subspace/some=values\n"
"- test/subspace/are=saved\n"
"- test/a=123\n"
"- test/c=hello\n"
"- test/b=test\n",
"ns removed diff");
free(diff);
mu_assert("ns added (no diff)", !diff_str(b, a, NULL));
mu_assert("ns added (diff)", !diff_str(b, a, &diff));
mu_assert_streq(diff,
"+NS test\n"
"+NS test/subspace\n"
"+ test/subspace/here=lol\n"
"+ test/subspace/some=values\n"
"+ test/subspace/are=saved\n"
"+ test/a=123\n"
"+ test/c=hello\n"
"+ test/b=test\n",
"ns added diff");
free(diff);
test_sdb_free(a);
test_sdb_free(b);
mu_end;
}
bool test_sdb_diff_ns_sub() {
Sdb *a = test_sdb_new("ns_sub_a.sdb");
Sdb *b = test_sdb_new("ns_sub_b.sdb");
sdb_ns_unset(sdb_ns(b, "test", 0), "subspace", NULL);
mu_assert("sub ns removed (no diff)", !diff_str(a, b, NULL));
char *diff;
mu_assert("sub ns removed (diff)", !diff_str(a, b, &diff));
mu_assert_streq(diff,
"-NS test/subspace\n"
"- test/subspace/here=lol\n"
"- test/subspace/some=values\n"
"- test/subspace/are=saved\n",
"sub ns removed diff");
free(diff);
mu_assert("sub ns added (no diff)", !diff_str(b, a, NULL));
mu_assert("sub ns added (diff)", !diff_str(b, a, &diff));
mu_assert_streq(diff,
"+NS test/subspace\n"
"+ test/subspace/here=lol\n"
"+ test/subspace/some=values\n"
"+ test/subspace/are=saved\n",
"sub ns added diff");
free(diff);
test_sdb_free(a);
test_sdb_free(b);
mu_end;
}
bool test_sdb_diff_kv() {
Sdb *a = test_sdb_new("kv_a.sdb");
Sdb *b = test_sdb_new("kv_b.sdb");
sdb_unset(b, "more", 0);
sdb_unset(sdb_ns(b, "test", false), "a", 0);
mu_assert("kv removed (no diff)", !diff_str(a, b, NULL));
char *diff;
mu_assert("kv removed (diff)", !diff_str(a, b, &diff));
mu_assert_streq(diff,
"- test/a=123\n"
"- more=stuff\n",
"ns removed diff");
free(diff);
mu_assert("kv added (no diff)", !diff_str(b, a, NULL));
mu_assert("kv added (diff)", !diff_str(b, a, &diff));
mu_assert_streq(diff,
"+ test/a=123\n"
"+ more=stuff\n",
"ns added diff");
free(diff);
test_sdb_free(a);
test_sdb_free(b);
mu_end;
}
bool test_sdb_diff_kv_value() {
Sdb *a = test_sdb_new("kv_value_a.sdb");
Sdb *b = test_sdb_new("kv_value_b.sdb");
sdb_set(b, "more", "cowbell", 0);
sdb_set(sdb_ns(b, "test", false), "a", "reaper", 0);
mu_assert("kv value changed (no diff)", !diff_str(a, b, NULL));
char *diff;
mu_assert("kv value changed (diff)", !diff_str(a, b, &diff));
mu_assert_streq(diff,
"- test/a=123\n"
"+ test/a=reaper\n"
"- more=stuff\n"
"+ more=cowbell\n",
"ns value changed diff");
free(diff);
test_sdb_free(a);
test_sdb_free(b);
mu_end;
}
int all_tests() {
mu_run_test(test_sdb_diff_equal_empty);
mu_run_test(test_sdb_diff_equal);
mu_run_test(test_sdb_diff_ns_empty);
mu_run_test(test_sdb_diff_ns);
mu_run_test(test_sdb_diff_ns_sub);
mu_run_test(test_sdb_diff_kv);
mu_run_test(test_sdb_diff_kv_value);
return tests_passed != tests_run;
}
int main(int argc, char **argv) {
// Sdb *sdb = test_sdb_new();
// sdb_query (sdb, "*");
// sdb_query (sdb, "***");
// test_sdb_free (sdb);
// return 0;
return all_tests();
}

564
test/unit/test_sdb_hash.c Normal file
View file

@ -0,0 +1,564 @@
// SPDX-FileCopyrightText: pancake <pancake@nopcode.org>
// SPDX-License-Identifier: MIT
#include "minunit.h"
#include <sdb.h>
#include <ht_uu.h>
#include <ht_up.h>
#include <ht_pp.h>
#include <ht_pu.h>
typedef struct _test_struct {
char *name;
int age;
} Person;
bool test_ht_insert_lookup(void) {
HtPP *ht = sdb_ht_new();
sdb_ht_insert(ht, "AAAA", "vAAAA");
sdb_ht_insert(ht, "BBBB", "vBBBB");
sdb_ht_insert(ht, "CCCC", "vCCCC");
mu_assert_streq(sdb_ht_find(ht, "BBBB", NULL), "vBBBB", "BBBB value wrong");
mu_assert_streq(sdb_ht_find(ht, "AAAA", NULL), "vAAAA", "AAAA value wrong");
mu_assert_streq(sdb_ht_find(ht, "CCCC", NULL), "vCCCC", "CCCC value wrong");
sdb_ht_free(ht);
mu_end;
}
bool test_ht_update_lookup(void) {
HtPP *ht = sdb_ht_new();
sdb_ht_insert(ht, "AAAA", "vAAAA");
sdb_ht_insert(ht, "BBBB", "vBBBB");
// test update to add a new element
sdb_ht_update(ht, "CCCC", "vCCCC");
mu_assert_streq(sdb_ht_find(ht, "CCCC", NULL), "vCCCC", "CCCC value wrong");
// test update to replace an existing element
sdb_ht_update(ht, "AAAA", "vDDDD");
mu_assert_streq(sdb_ht_find(ht, "AAAA", NULL), "vDDDD", "DDDD value wrong");
sdb_ht_free(ht);
mu_end;
}
bool test_ht_delete(void) {
HtPP *ht = sdb_ht_new();
mu_assert("nothing should be deleted", !sdb_ht_delete(ht, "non existing"));
sdb_ht_insert(ht, "AAAA", "vAAAA");
mu_assert("AAAA should be deleted", sdb_ht_delete(ht, "AAAA"));
mu_assert("AAAA still there", !sdb_ht_find(ht, "AAAA", NULL));
sdb_ht_free(ht);
mu_end;
}
bool test_ht_insert_kvp(void) {
HtPP *ht = sdb_ht_new();
SdbKv *kv = sdbkv_new("AAAA", "vAAAA");
mu_assert("AAAA shouldn't exist", !sdb_ht_find_kvp(ht, "AAAA", NULL));
sdb_ht_insert_kvp(ht, kv, false);
free(kv);
mu_assert("AAAA should exist", sdb_ht_find_kvp(ht, "AAAA", NULL));
SdbKv *kv2 = sdbkv_new("AAAA", "vNEWAAAA");
mu_assert("AAAA shouldn't be replaced", !sdb_ht_insert_kvp(ht, kv2, false));
mu_assert("AAAA should be replaced", sdb_ht_insert_kvp(ht, kv2, true));
free(kv2);
SdbKv *foundkv = sdb_ht_find_kvp(ht, "AAAA", NULL);
mu_assert_streq(foundkv->base.value, "vNEWAAAA", "vNEWAAAA should be there");
sdb_ht_free(ht);
mu_end;
}
ut32 create_collision(const void *key) {
return 10;
}
bool test_ht_insert_collision(void) {
HtPP *ht = sdb_ht_new();
ht->opt.hashfn = create_collision;
ht_pp_insert(ht, "AAAA", "vAAAA");
mu_assert_streq(sdb_ht_find(ht, "AAAA", NULL), "vAAAA", "AAAA should be there");
ht_pp_insert(ht, "BBBB", "vBBBB");
mu_assert_streq(sdb_ht_find(ht, "AAAA", NULL), "vAAAA", "AAAA should still be there");
mu_assert_streq(sdb_ht_find(ht, "BBBB", NULL), "vBBBB", "BBBB should be there");
ht_pp_insert(ht, "CCCC", "vBBBB");
mu_assert_streq(sdb_ht_find(ht, "CCCC", NULL), "vBBBB", "CCCC should be there");
sdb_ht_free(ht);
mu_end;
}
ut32 key2hash(const void *key) {
return atoi(key);
}
bool test_ht_grow(void) {
HtPP *ht = sdb_ht_new();
char str[15], vstr[15];
char buf[100];
int i;
ht->opt.hashfn = key2hash;
for (i = 0; i < 20000; ++i) {
snprintf(str, 15, "%d", i);
snprintf(vstr, 15, "v%d", i);
sdb_ht_insert(ht, str, vstr);
}
for (i = 0; i < 20000; ++i) {
snprintf(str, 15, "%d", i);
snprintf(vstr, 15, "v%d", i);
char *v = sdb_ht_find(ht, str, NULL);
snprintf(buf, 100, "%s/%s should be there", str, vstr);
mu_assert(buf, v);
snprintf(buf, 100, "%s/%s should be right", str, vstr);
mu_assert_streq(v, vstr, buf);
}
sdb_ht_free(ht);
mu_end;
}
bool test_ht_kvp(void) {
HtPP *ht = sdb_ht_new();
SdbKv *kvp = sdbkv_new("AAAA", "vAAAA");
mu_assert_eq(kvp->base.key_len, 4, "key_len should be 4");
mu_assert_eq(kvp->base.value_len, 5, "value_len should be 5");
mu_assert("kvp should be inserted", sdb_ht_insert_kvp(ht, kvp, false));
free(kvp);
kvp = sdb_ht_find_kvp(ht, "AAAA", NULL);
mu_assert_eq(kvp->base.key_len, 4, "key_len should be 4 after kvp_insert");
mu_assert_eq(kvp->base.value_len, 5, "value_len should be 5 after kvp_insert");
sdb_ht_insert(ht, "BBBB", "vBBBB");
kvp = sdb_ht_find_kvp(ht, "BBBB", NULL);
mu_assert_eq(kvp->base.key_len, 4, "key_len should be 4 after insert");
mu_assert_eq(kvp->base.value_len, 5, "value_len should be 5 after insert");
sdb_ht_free(ht);
mu_end;
}
Person *duplicate_person(Person *p) {
Person *c = malloc(sizeof(Person));
c->name = strdup(p->name);
c->age = p->age;
return c;
}
void free_kv(HtPPKv *kv) {
free(kv->key);
Person *p = kv->value;
free(p->name);
free(p);
}
size_t calcSizePerson(void *c) {
Person *p = c;
return sizeof(*p);
}
bool test_ht_general(void) {
int retval = MU_PASSED;
bool found = false;
Person *p, *person1 = malloc(sizeof(Person));
if (!person1) {
mu_cleanup_fail(err_malloc, "person1 malloc");
}
person1->name = strdup("radare");
person1->age = 10;
Person *person2 = malloc(sizeof(Person));
if (!person2) {
mu_cleanup_fail(err_free_person1, "person2 malloc");
}
person2->name = strdup("pancake");
person2->age = 9000;
HtPP *ht = ht_pp_new((HtPPDupValue)duplicate_person, free_kv, (HtPPCalcSizeV)calcSizePerson);
if (!ht) {
mu_cleanup_fail(err_free_persons, "ht alloc");
}
ht_pp_insert(ht, "radare", (void *)person1);
ht_pp_insert(ht, "pancake", (void *)person2);
p = ht_pp_find(ht, "radare", &found);
mu_assert("radare not found", found);
mu_assert_streq(p->name, "radare", "wrong person");
mu_assert_eq(p->age, 10, "wrong radare age");
p = ht_pp_find(ht, "pancake", &found);
mu_assert("radare not found", found);
mu_assert_streq(p->name, "pancake", "wrong person");
mu_assert_eq(p->age, 9000, "wrong pancake age");
(void)ht_pp_find(ht, "not", &found);
mu_assert("found but it should not exists", !found);
ht_pp_delete(ht, "pancake");
p = ht_pp_find(ht, "pancake", &found);
mu_assert("pancake was deleted", !found);
ht_pp_insert(ht, "pancake", (void *)person2);
ht_pp_delete(ht, "radare");
ht_pp_update(ht, "pancake", (void *)person1);
p = ht_pp_find(ht, "pancake", &found);
mu_assert("pancake was updated", found);
mu_assert_streq(p->name, "radare", "wrong person");
mu_assert_eq(p->age, 10, "wrong age");
ht_pp_free(ht);
err_free_persons:
free(person2->name);
free(person2);
err_free_person1:
free(person1->name);
free(person1);
err_malloc:
mu_cleanup_end;
}
static void free_key_value(HtPPKv *kv) {
free(kv->key);
free(kv->value);
}
bool should_not_be_caled(void *user, const char *k, void *v) {
mu_fail("this function should not be called");
return false;
}
bool test_empty_ht(void) {
HtPP *ht = ht_pp_new0();
ht_pp_foreach(ht, (HtPPForeachCallback)should_not_be_caled, NULL);
void *r = ht_pp_find(ht, "key1", NULL);
mu_assert_null(r, "key1 should not be present");
ht_pp_free(ht);
mu_end;
}
bool test_insert(void) {
HtPP *ht = ht_pp_new0();
void *r;
bool res;
bool found;
res = ht_pp_insert(ht, "key1", "value1");
mu_assert("key1 should be a new element", res);
r = ht_pp_find(ht, "key1", &found);
mu_assert("found should be true", found);
mu_assert_streq(r, "value1", "value1 should be retrieved");
res = ht_pp_insert(ht, "key1", "value2");
mu_assert("key1 should be an already existing element", !res);
r = ht_pp_find(ht, "key1", &found);
mu_assert_streq(r, "value1", "value1 should be retrieved");
mu_assert("found should be true", found);
r = ht_pp_find(ht, "key2", &found);
mu_assert_null(r, "key2 should not be present");
mu_assert("found for key2 should be false", !found);
ht_pp_free(ht);
mu_end;
}
bool test_update(void) {
HtPP *ht = ht_pp_new0();
bool found;
ht_pp_insert(ht, "key1", "value1");
ht_pp_update(ht, "key1", "value2");
void *r = ht_pp_find(ht, "key1", &found);
mu_assert_streq(r, "value2", "value2 should be retrieved");
mu_assert("found should be true", found);
ht_pp_free(ht);
mu_end;
}
bool test_delete(void) {
HtPP *ht = ht_pp_new0();
bool found;
ht_pp_insert(ht, "key1", "value1");
ht_pp_delete(ht, "key1");
void *r = ht_pp_find(ht, "key1", &found);
mu_assert_null(r, "key1 should not be found");
mu_assert("found should be false", !found);
ht_pp_free(ht);
mu_end;
}
static bool grow_1_found[3];
static bool grow_1_foreach(void *user, const char *k, int v) {
grow_1_found[v] = true;
return true;
}
bool test_grow_1(void) {
HtPP *ht = ht_pp_new0();
int i;
for (i = 0; i < 3; ++i) {
grow_1_found[i] = false;
}
ht_pp_insert(ht, "key0", (void *)0);
ht_pp_insert(ht, "key1", (void *)1);
ht_pp_insert(ht, "key2", (void *)2);
ht_pp_foreach(ht, (HtPPForeachCallback)grow_1_foreach, NULL);
for (i = 0; i < 3; ++i) {
if (!grow_1_found[i]) {
fprintf(stderr, "i = %d\n", i);
mu_fail("An element has not been traversed");
}
}
ht_pp_free(ht);
mu_end;
}
bool test_grow_2(void) {
HtPP *ht = ht_pp_new((HtPPDupValue)strdup, (HtPPKvFreeFunc)free_key_value, NULL);
char *r;
bool found;
int i;
for (i = 0; i < 3000; ++i) {
char buf[20], buf2[20];
snprintf(buf, 20, "key%d", i);
snprintf(buf2, 20, "value%d", i);
ht_pp_insert(ht, buf, buf2);
}
r = ht_pp_find(ht, "key1", &found);
mu_assert_streq(r, "value1", "value1 should be retrieved");
mu_assert("found should be true", found);
r = ht_pp_find(ht, "key2000", &found);
mu_assert_streq(r, "value2000", "value2000 should be retrieved");
mu_assert("found should be true", found);
r = ht_pp_find(ht, "key4000", &found);
mu_assert_null(r, "key4000 should not be there");
mu_assert("found should be false", !found);
ht_pp_free(ht);
mu_end;
}
bool test_grow_3(void) {
HtPP *ht = ht_pp_new((HtPPDupValue)strdup, (HtPPKvFreeFunc)free_key_value, NULL);
char *r;
bool found;
int i;
for (i = 0; i < 3000; ++i) {
char buf[20], buf2[20];
snprintf(buf, 20, "key%d", i);
snprintf(buf2, 20, "value%d", i);
ht_pp_insert(ht, buf, buf2);
}
for (i = 0; i < 3000; i += 3) {
char buf[20];
snprintf(buf, 20, "key%d", i);
ht_pp_delete(ht, buf);
}
r = ht_pp_find(ht, "key1", &found);
mu_assert_streq(r, "value1", "value1 should be retrieved");
mu_assert("found should be true", found);
r = ht_pp_find(ht, "key2000", &found);
mu_assert_streq(r, "value2000", "value2000 should be retrieved");
mu_assert("found should be true", found);
r = ht_pp_find(ht, "key4000", &found);
mu_assert_null(r, "key4000 should not be there");
mu_assert("found should be false", !found);
r = ht_pp_find(ht, "key0", &found);
mu_assert_null(r, "key0 should not be there");
mu_assert("found should be false", !found);
for (i = 1; i < 3000; i += 3) {
char buf[20];
snprintf(buf, 20, "key%d", i);
ht_pp_delete(ht, buf);
}
r = ht_pp_find(ht, "key1", &found);
mu_assert_null(r, "key1 should not be there");
mu_assert("found should be false", !found);
ht_pp_free(ht);
mu_end;
}
bool test_grow_4(void) {
HtPP *ht = ht_pp_new(NULL, (HtPPKvFreeFunc)free_key_value, NULL);
char *r;
bool found;
int i;
for (i = 0; i < 3000; ++i) {
char buf[20], *buf2;
snprintf(buf, 20, "key%d", i);
buf2 = malloc(20);
snprintf(buf2, 20, "value%d", i);
ht_pp_insert(ht, buf, buf2);
}
r = ht_pp_find(ht, "key1", &found);
mu_assert_streq(r, "value1", "value1 should be retrieved");
mu_assert("found should be true", found);
r = ht_pp_find(ht, "key2000", &found);
mu_assert_streq(r, "value2000", "value2000 should be retrieved");
mu_assert("found should be true", found);
for (i = 0; i < 3000; i += 3) {
char buf[20];
snprintf(buf, 20, "key%d", i);
ht_pp_delete(ht, buf);
}
r = ht_pp_find(ht, "key2000", &found);
mu_assert_streq(r, "value2000", "value2000 should be retrieved");
mu_assert("found should be true", found);
r = ht_pp_find(ht, "key0", &found);
mu_assert_null(r, "key0 should not be there");
mu_assert("found should be false", !found);
for (i = 1; i < 3000; i += 3) {
char buf[20];
snprintf(buf, 20, "key%d", i);
ht_pp_delete(ht, buf);
}
r = ht_pp_find(ht, "key1", &found);
mu_assert_null(r, "key1 should not be there");
mu_assert("found should be false", !found);
ht_pp_free(ht);
mu_end;
}
bool foreach_delete_cb(void *user, const ut64 key, const void *v) {
HtUP *ht = (HtUP *)user;
ht_up_delete(ht, key);
return true;
}
static void free_up_value(HtUPKv *kv) {
free(kv->value);
}
bool test_foreach_delete(void) {
HtUP *ht = ht_up_new((HtUPDupValue)strdup, free_up_value, NULL);
// create a collision
ht_up_insert(ht, 0, "value1");
ht_up_insert(ht, ht->size, "value2");
ht_up_insert(ht, ht->size * 2, "value3");
ht_up_insert(ht, ht->size * 3, "value4");
ht_up_foreach(ht, foreach_delete_cb, ht);
ht_up_foreach(ht, (HtUPForeachCallback)should_not_be_caled, NULL);
ht_up_free(ht);
mu_end;
}
bool test_update_key(void) {
bool res;
HtUP *ht = ht_up_new((HtUPDupValue)strdup, free_up_value, NULL);
// create a collision
ht_up_insert(ht, 0, "value1");
ht_up_insert(ht, 0xdeadbeef, "value2");
ht_up_insert(ht, 0xcafebabe, "value3");
res = ht_up_update_key(ht, 0xcafebabe, 0x10000);
mu_assert("cafebabe should be updated", res);
res = ht_up_update_key(ht, 0xdeadbeef, 0x10000);
mu_assert("deadbeef should NOT be updated, because there's already an element at 0x10000", !res);
const char *v = ht_up_find(ht, 0x10000, NULL);
mu_assert_streq(v, "value3", "value3 should be at 0x10000");
v = ht_up_find(ht, 0xdeadbeef, NULL);
mu_assert_streq(v, "value2", "value2 should remain at 0xdeadbeef");
ht_up_free(ht);
mu_end;
}
bool test_ht_pu_ops(void) {
bool res;
ut64 val;
HtPU *ht = ht_pu_new0();
ht_pu_insert(ht, "key1", 0xcafebabe);
val = ht_pu_find(ht, "key1", &res);
mu_assert_eq(val, 0xcafebabe, "0xcafebabe should be retrieved");
mu_assert("found should be true", res);
res = ht_pu_insert(ht, "key1", 0xdeadbeefdeadbeef);
mu_assert("key1 should be an already existing element", !res);
val = ht_pu_find(ht, "key1", &res);
mu_assert_eq(val, 0xcafebabe, "0xcafebabe should still be retrieved");
res = ht_pu_update(ht, "key1", 0xdeadbeefdeadbeef);
mu_assert("key1 should be updated", res);
val = ht_pu_find(ht, "key1", &res);
mu_assert_eq(val, 0xdeadbeefdeadbeef, "0xdeadbeefdeadbeef should be retrieved");
mu_assert("found should be true", res);
res = ht_pu_delete(ht, "key1");
mu_assert("key1 should be deleted", res);
val = ht_pu_find(ht, "key1", &res);
mu_assert_eq(val, 0, "0 should be retrieved");
mu_assert("found should be false", !res);
ht_pu_free(ht);
mu_end;
}
int all_tests() {
mu_run_test(test_ht_insert_lookup);
mu_run_test(test_ht_update_lookup);
mu_run_test(test_ht_delete);
mu_run_test(test_ht_insert_kvp);
mu_run_test(test_ht_insert_collision);
mu_run_test(test_ht_grow);
mu_run_test(test_ht_kvp);
mu_run_test(test_ht_general);
mu_run_test(test_empty_ht);
mu_run_test(test_insert);
mu_run_test(test_update);
mu_run_test(test_delete);
mu_run_test(test_grow_1);
mu_run_test(test_grow_2);
mu_run_test(test_grow_3);
mu_run_test(test_grow_4);
mu_run_test(test_foreach_delete);
mu_run_test(test_update_key);
mu_run_test(test_ht_pu_ops);
return tests_passed != tests_run;
}
int main(int argc, char **argv) {
return all_tests();
}

454
test/unit/test_sdb_ls.c Normal file
View file

@ -0,0 +1,454 @@
// SPDX-FileCopyrightText: pancake <pancake@nopcode.org>
// SPDX-License-Identifier: MIT
#include <sdb.h>
#include "minunit.h"
#define BUF_LENGTH 100
#define R_ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
bool test_r_list_size(void) {
// Test that r_list adding and deleting works correctly.
int i;
SdbList *list = ls_new();
intptr_t test = 0x101010;
// Add 100 items.
for (i = 0; i < 100; ++i) {
ls_append(list, (void *)test);
mu_assert_eq((int)ls_length(list), i + 1, "ls_length failed on append");
}
// Delete 50 of them.
for (i = 0; i < 50; ++i) {
(void)ls_pop(list);
mu_assert_eq(99 - i, (int)ls_length(list), "ls_length failed on pop");
}
// Purge the list.
ls_destroy(list);
mu_assert_eq(0, (int)ls_length(list), "ls_length failed on purged list");
ls_free(list);
mu_end;
}
bool test_r_list_values(void) {
SdbList *list = ls_new();
intptr_t test1 = 0x12345;
intptr_t test2 = 0x88888;
ls_append(list, (void *)test1);
ls_append(list, (void *)test2);
int top1 = (intptr_t)ls_pop(list);
int top2 = (intptr_t)ls_pop(list);
mu_assert_eq(top1, 0x88888, "first value not 0x88888");
mu_assert_eq(top2, 0x12345, "first value not 0x12345");
ls_free(list);
mu_end;
}
bool test_ls_join(void) {
SdbList *list1 = ls_new();
SdbList *list2 = ls_new();
intptr_t test1 = 0x12345;
intptr_t test2 = 0x88888;
ls_append(list1, (void *)test1);
ls_append(list2, (void *)test2);
int joined = ls_join(list1, list2);
mu_assert_eq(joined, 1, "ls_join of two lists");
mu_assert_eq((int)ls_length(list1), 2, "ls_join two single element lists result length is 1");
ls_free(list1);
ls_free(list2);
mu_end;
}
bool test_ls_free(void) {
SdbList *list = ls_newf((void *)0x9999);
mu_assert_eq((int)(intptr_t)list->free, 0x9999, "ls_newf function gets set properly");
ls_free(list);
mu_end;
}
bool test_ls_del_n(void) {
SdbList *list = ls_new();
intptr_t test1 = 0x12345;
intptr_t test2 = 0x88888;
ls_append(list, (void *)test1);
ls_append(list, (void *)test2);
mu_assert_eq((int)ls_length(list), 2,
"list is of length 2 when adding 2 values");
ls_del_n(list, 0);
int top1 = (intptr_t)ls_pop(list);
mu_assert_eq(top1, 0x88888,
"error, first value not 0x88888");
ls_free(list);
mu_end;
}
bool test_r_list_sort(void) {
SdbList *list = ls_new();
char *test1 = "AAAA";
char *test2 = "BBBB";
char *test3 = "CCCC";
// Put in not sorted order.
ls_append(list, (void *)test1);
ls_append(list, (void *)test3);
ls_append(list, (void *)test2);
// Sort.
ls_sort(list, (SdbListComparator)strcmp);
// Check that the list is actually sorted.
mu_assert_streq((char *)list->head->data, "AAAA", "first value in sorted list");
mu_assert_streq((char *)list->head->n->data, "BBBB", "second value in sorted list");
mu_assert_streq((char *)list->head->n->n->data, "CCCC", "third value in sorted list");
ls_free(list);
mu_end;
}
bool test_r_list_sort2(void) {
SdbList *list = ls_new();
char *test1 = "AAAA";
char *test2 = "BBBB";
char *test3 = "CCCC";
// Put in not sorted order.
ls_append(list, (void *)test3);
ls_append(list, (void *)test2);
ls_append(list, (void *)test1);
// Sort.
ls_merge_sort(list, (SdbListComparator)strcmp);
// Check that the list is actually sorted.
mu_assert_streq((char *)list->head->data, "AAAA", "first value in sorted list");
mu_assert_streq((char *)list->head->n->data, "BBBB", "second value in sorted list");
mu_assert_streq((char *)list->head->n->n->data, "CCCC", "third value in sorted list");
ls_free(list);
mu_end;
}
static int cmp_range(const void *a, const void *b) {
int ra = *(int *)a;
int rb = *(int *)b;
return ra - rb;
}
bool test_r_list_sort3(void) {
SdbList *list = ls_new();
int test1 = 33508;
int test2 = 33480;
int test3 = 33964;
// Put in not sorted order.
ls_append(list, (void *)&test1);
ls_append(list, (void *)&test3);
ls_append(list, (void *)&test2);
// Sort.
ls_merge_sort(list, (SdbListComparator)cmp_range);
// Check that the list is actually sorted.
mu_assert_eq(*(int *)list->head->data, 33480, "first value in sorted list");
mu_assert_eq(*(int *)list->head->n->data, 33508, "second value in sorted list");
mu_assert_eq(*(int *)list->head->n->n->data, 33964, "third value in sorted list");
ls_free(list);
mu_end;
}
bool test_ls_length(void) {
SdbList *list = ls_new();
SdbList *list2 = ls_new();
SdbListIter *iter;
int count = 0;
int test1 = 33508;
int test2 = 33480;
int test3 = 33964;
// Put in not sorted order.
ls_append(list, (void *)&test1);
ls_append(list, (void *)&test3);
ls_append(list, (void *)&test2);
iter = list->head;
while (iter) {
count++;
iter = iter->n;
}
mu_assert_eq((int)list->length, 3, "First length check");
ls_delete_data(list, (void *)&test1);
mu_assert_eq((int)list->length, 2, "Second length check");
ls_append(list, (void *)&test1);
mu_assert_eq((int)list->length, 3, "Third length check");
ls_pop(list);
mu_assert_eq((int)list->length, 2, "Fourth length check");
ls_pop_head(list);
mu_assert_eq((int)list->length, 1, "Fifth length check");
ls_insert(list, 2, (void *)&test2);
mu_assert_eq((int)list->length, 2, "Sixth length check");
ls_prepend(list, (void *)&test3);
mu_assert_eq((int)list->length, 3, "Seventh length check");
ls_del_n(list, 2);
mu_assert_eq((int)list->length, 2, "Eighth length check");
ls_append(list2, (void *)&test1);
ls_append(list2, (void *)&test3);
ls_append(list2, (void *)&test2);
ls_join(list, list2);
mu_assert_eq((int)list->length, 5, "Ninth length check");
iter = list->head;
count = 0;
while (iter) {
count++;
iter = iter->n;
}
mu_assert_eq((int)list->length, count, "Tenth length check");
ls_free(list);
ls_free(list2);
mu_end;
}
bool test_r_list_sort5(void) {
SdbList *list = ls_new();
int i = 0;
char *upper[] = { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" };
char *lower[] = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z" };
for (i = 0; i < 26; i++) {
ls_append(list, (void *)lower[i]);
}
for (i = 0; i < 26; i++) {
ls_append(list, (void *)upper[i]);
}
// add more than 43 elements to trigger merge sort
ls_sort(list, (SdbListComparator)strcmp);
mu_assert_streq((char *)list->head->data, upper[0], "First element");
mu_assert_streq((char *)list->tail->data, lower[25], "Last element");
ls_free(list);
mu_end;
}
static int cmp_order(const void *a, const void *b) {
int ra = *(int *)a;
int rb = *(int *)b;
return ra > rb;
}
bool test_r_list_sort6(void) {
int values[] = {
4640,
5152,
5664,
6176,
6688,
7200,
7712,
32,
544,
1056,
1568,
2080,
2592,
3104,
3616,
4128,
4640,
5152,
5664,
6176,
6688,
7200,
7712,
8224,
8273,
8337,
8356,
8452,
8577,
8625,
8641,
8657,
8673,
8689,
8736,
9248,
9760,
10272,
10784,
11296,
11808,
12320,
12832,
13344,
13856,
14368,
14880,
15392,
15904,
16416,
16928,
17440,
17952,
18464,
18976,
19488,
20000,
20512,
21024,
21536,
22048,
22560,
23072,
23584,
24096,
24608,
8768,
9792,
10816,
11840,
12864,
13888,
14912,
15936,
16960,
17984,
19008,
20032,
21056,
22080,
23104,
24128,
25152,
26176,
27200,
28224,
29248,
30272,
31296,
32320,
33344,
34368,
35392,
36416,
37440,
38464,
39488,
40512,
8832,
10880,
12928,
14976,
17024,
19072,
21120,
23168,
25216,
27264,
29312,
31360,
33408,
35456,
37504,
39552,
};
int i;
int a, b;
SdbListIter *iter;
SdbList *list = ls_new();
for (i = 0; i < R_ARRAY_SIZE(values); i++) {
ls_append(list, (void *)&values[i]);
}
ls_merge_sort(list, (SdbListComparator)cmp_order);
a = *(int *)list->head->data;
for (iter = list->head->n, i = 0; iter; iter = iter->n, i++) {
b = *(int *)iter->data;
#if 0 // Debug print
printf("Element %d : %d < %d\n", i+1, a, b);
#endif
mu_assert("nth element not inferior or equal to next", a <= b);
a = b;
}
ls_free(list);
mu_end;
}
bool test_r_list_sort4(void) {
SdbList *list = ls_new();
char *test1 = "AAAA";
char *test2 = "BBBB";
char *test3 = "CCCC";
char *test4 = "DDDD";
char *test5 = "EEEE";
char *test6_later = "FFFF";
char *test7 = "GGGG";
char *test8 = "HHHH";
char *test9 = "IIII";
char *test10 = "JJJJ";
char *ins_tests_odd[] = { test10, test1, test3, test7, test5, test9, test2,
test4, test8 };
char *exp_tests_odd[] = { test1, test2, test3, test4, test5, test7,
test8, test9, test10 };
int i;
// Put in not sorted order.
for (i = 0; i < R_ARRAY_SIZE(ins_tests_odd); ++i) {
ls_append(list, (void *)ins_tests_odd[i]);
}
// Sort.
ls_merge_sort(list, (SdbListComparator)strcmp);
// Check that the list (odd-length) is actually sorted.
SdbListIter *next = list->head;
for (i = 0; i < R_ARRAY_SIZE(exp_tests_odd); ++i) {
char buf[BUF_LENGTH];
snprintf(buf, BUF_LENGTH, "%d-th value in sorted list", i);
mu_assert_streq((char *)next->data, exp_tests_odd[i], buf);
next = next->n;
}
char *data;
printf("after sorted 1 \n");
ls_foreach (list, next, data) {
printf("l -> %s\n", data);
}
char *exp_tests_even[] = { test1, test2, test3, test4, test5,
test6_later, test7, test8, test9, test10 };
// Add test6 to make the length even
ls_append(list, (void *)test6_later);
printf("after adding FFFF \n");
ls_foreach (list, next, data) {
printf("l -> %s\n", data);
}
// Sort
ls_merge_sort(list, (SdbListComparator)strcmp);
printf("after sorting 2 \n");
ls_foreach (list, next, data) {
printf("l -> %s\n", data);
}
// Check that the list (even-length) is actually sorted.
next = list->head;
for (i = 0; i < R_ARRAY_SIZE(exp_tests_even); ++i) {
char buf[BUF_LENGTH];
snprintf(buf, BUF_LENGTH, "%d-th value in sorted list", i);
mu_assert_streq((char *)next->data, exp_tests_even[i], buf);
next = next->n;
}
ls_free(list);
mu_end;
}
int all_tests() {
mu_run_test(test_r_list_size);
mu_run_test(test_r_list_values);
mu_run_test(test_ls_join);
mu_run_test(test_ls_free);
mu_run_test(test_ls_del_n);
mu_run_test(test_r_list_sort);
mu_run_test(test_r_list_sort2);
mu_run_test(test_r_list_sort3);
mu_run_test(test_r_list_sort4);
mu_run_test(test_r_list_sort5);
mu_run_test(test_r_list_sort6);
mu_run_test(test_ls_length);
return tests_passed != tests_run;
}
int main(int argc, char **argv) {
return all_tests();
}

579
test/unit/test_sdb_sdb.c Normal file
View file

@ -0,0 +1,579 @@
// SPDX-FileCopyrightText: pancake <pancake@nopcode.org>
// SPDX-License-Identifier: MIT
#include "minunit.h"
#include <sdb.h>
#include <fcntl.h>
#include <stdio.h>
#include <rz_util/rz_file.h>
static bool foreach_delete_cb(void *user, const char *key, const char *val) {
if (strcmp(key, "bar")) {
sdb_unset(user, key, 0);
}
return 1;
}
bool test_sdb_foreach_delete(void) {
Sdb *db = sdb_new(NULL, NULL, false);
sdb_set(db, "foo", "bar", 0);
sdb_set(db, "bar", "cow", 0);
sdb_set(db, "low", "bar", 0);
sdb_foreach(db, foreach_delete_cb, db);
mu_assert("Item not deleted", !(int)(size_t)sdb_const_get(db, "foo", NULL));
mu_assert("Item not deleted", (int)(size_t)sdb_const_get(db, "bar", NULL));
sdb_free(db);
mu_end;
}
bool test_sdb_list_delete(void) {
Sdb *db = sdb_new(NULL, NULL, false);
sdb_set(db, "foo", "bar", 0);
sdb_set(db, "bar", "cow", 0);
sdb_set(db, "low", "bar", 0);
SdbList *list = sdb_foreach_list(db, true);
SdbListIter *iter;
SdbKv *kv;
ls_foreach (list, iter, kv) {
// printf ("--> %s\n", kv->key);
sdb_unset(db, kv->base.key, 0);
}
SdbList *list2 = sdb_foreach_list(db, true);
mu_assert("List is empty", !ls_length(list2));
ls_free(list);
ls_free(list2);
sdb_free(db);
mu_end;
}
bool test_sdb_list_big(void) {
Sdb *db = sdb_new0();
int i;
for (i = 0; i < 5000; i++) {
sdb_num_set(db, sdb_fmt("%d", i), i + 1, 0);
}
SdbList *list = sdb_foreach_list(db, true);
// TODO: verify if its sorted
ls_free(list);
sdb_free(db);
mu_end;
}
bool test_sdb_delete_none(void) {
Sdb *db = sdb_new(NULL, NULL, false);
sdb_set(db, "foo", "bar", 0);
sdb_set(db, "bar", "cow", 0);
sdb_set(db, "low", "bar", 0);
sdb_unset(db, "fundas", 0);
sdb_unset(db, "barnas", 0);
sdb_unset(db, "bar", 0);
sdb_unset(db, "pinuts", 0);
SdbList *list = sdb_foreach_list(db, false);
mu_assert_eq(ls_length(list), 2, "Unmatched rows");
ls_free(list);
sdb_free(db);
mu_end;
}
bool test_sdb_delete_alot(void) {
Sdb *db = sdb_new(NULL, NULL, false);
const int count = 2048;
int i;
for (i = 0; i < count; i++) {
sdb_set(db, sdb_fmt("key.%d", i), "bar", 0);
}
for (i = 0; i < count; i++) {
sdb_unset(db, sdb_fmt("key.%d", i), 0);
}
SdbList *list = sdb_foreach_list(db, false);
mu_assert_eq(ls_length(list), 0, "Unmatched rows");
ls_free(list);
sdb_free(db);
mu_end;
}
bool test_sdb_milset(void) {
int i = 0;
const int MAX = 1999;
Sdb *s = sdb_new0();
sdb_set(s, "foo", "bar", 0);
for (i = 0; i < MAX; i++) {
if (!sdb_set(s, "foo", "bar", 0)) {
mu_assert("milset: sdb_set failed", 0);
break;
}
}
sdb_free(s);
mu_end;
}
bool test_sdb_milset_random(void) {
int i = 0;
const int MAX = 1999;
bool solved = true;
Sdb *s = sdb_new0();
sdb_set(s, "foo", "bar", 0);
for (i = 0; i < MAX; i++) {
char *v = sdb_fmt("bar%d", i);
if (!sdb_set(s, "foo", v, 0)) {
solved = false;
break;
}
}
mu_assert("milset: sdb_set", solved);
sdb_free(s);
mu_end;
}
bool test_sdb_namespace(void) {
bool solved = false;
const char *dbname = ".bar";
Sdb *s = sdb_new0();
if (!s) {
return false;
}
unlink(dbname);
Sdb *n = sdb_ns(s, dbname, 1);
if (n) {
sdb_set(n, "user.pancake", "pancake foo", 0);
sdb_set(n, "user.password", "jklsdf8r3o", 0);
/* FIXED BUG1 ns_sync doesnt creates the database file */
sdb_ns_sync(s);
// sdb_sync (n);
/* FIXED BUG2 crash in free */
sdb_free(s);
int fd = open(dbname, O_RDONLY);
if (fd != -1) {
close(fd);
solved = true;
}
unlink(dbname);
}
mu_assert("namespace sync", solved);
rz_file_rm(".tmp");
mu_end;
}
static bool foreach_filter_user_cb(void *user, const char *key, const char *val) {
Sdb *db = (Sdb *)user;
const char *v = sdb_const_get(db, key, NULL);
if (!v) {
return false;
}
return key[0] == 'b' && v[0] == 'c';
}
bool test_sdb_foreach_filter_user(void) {
Sdb *db = sdb_new(NULL, NULL, false);
sdb_set(db, "crow", NULL, 0);
sdb_set(db, "foo", "bar", 0);
sdb_set(db, "bar", "cow", 0);
sdb_set(db, "bag", "horse", 0);
sdb_set(db, "boo", "cow", 0);
sdb_set(db, "bog", "horse", 0);
sdb_set(db, "low", "bar", 0);
sdb_set(db, "bip", "cow", 0);
sdb_set(db, "big", "horse", 0);
SdbList *ls = sdb_foreach_list_filter_user(db, foreach_filter_user_cb, true, db);
SdbListIter *it = ls_iterator(ls);
HtPPKv *kv = ls_iter_get(it);
mu_assert_streq((const char *)kv->key, "bar", "list should be sorted");
mu_assert_streq((const char *)kv->value, "cow", "list should be filtered");
kv = ls_iter_get(it);
mu_assert_streq((const char *)kv->key, "bip", "list should be sorted");
mu_assert_streq((const char *)kv->value, "cow", "list should be filtered");
kv = ls_iter_get(it);
mu_assert_streq((const char *)kv->key, "boo", "list should be sorted");
mu_assert_streq((const char *)kv->value, "cow", "list should be filtered");
mu_assert_null(it, "list should be terminated");
ls_free(ls);
sdb_free(db);
mu_end;
}
static bool foreach_filter_cb(void *user, const char *key, const char *val) {
return key[0] == 'b';
}
bool test_sdb_foreach_filter(void) {
Sdb *db = sdb_new(NULL, NULL, false);
sdb_set(db, "foo", "bar", 0);
sdb_set(db, "bar", "cow", 0);
sdb_set(db, "boo", "cow", 0);
sdb_set(db, "low", "bar", 0);
sdb_set(db, "bip", "cow", 0);
SdbList *ls = sdb_foreach_list_filter(db, foreach_filter_cb, true);
SdbListIter *it = ls_iterator(ls);
HtPPKv *kv = ls_iter_get(it);
mu_assert_streq((const char *)kv->key, "bar", "list should be sorted");
kv = ls_iter_get(it);
mu_assert_streq((const char *)kv->key, "bip", "list should be sorted");
kv = ls_iter_get(it);
mu_assert_streq((const char *)kv->key, "boo", "list should be sorted");
mu_assert_null(it, "list should be terminated");
ls_free(ls);
sdb_free(db);
mu_end;
}
bool test_sdb_copy() {
Sdb *src = sdb_new0();
sdb_set(src, "i am", "thou", 0);
sdb_set(src, "thou art", "i", 0);
Sdb *sub = sdb_ns(src, "subns", true);
sdb_set(sub, "rizin", "cool", 0);
sdb_set(sub, "cutter", "cooler", 0);
Sdb *dst = sdb_new0();
sdb_copy(src, dst);
sdb_free(src);
mu_assert_eq(sdb_count(dst), 2, "root count");
mu_assert_streq(sdb_const_get(dst, "i am", 0), "thou", "root entries");
mu_assert_streq(sdb_const_get(dst, "thou art", 0), "i", "root entries");
mu_assert_eq(ls_length(dst->ns), 1, "sub ns count");
Sdb *dst_sub = sdb_ns(dst, "subns", false);
mu_assert_notnull(dst_sub, "subns");
mu_assert_eq(sdb_count(dst_sub), 2, "sub ns entries count");
mu_assert_streq(sdb_const_get(dst_sub, "rizin", 0), "cool", "sub ns entries");
mu_assert_streq(sdb_const_get(dst_sub, "cutter", 0), "cooler", "sub ns entries");
sdb_free(dst);
mu_end;
}
#define PERTURBATOR "\\,\";]\n [\r}{'=/"
static const char *text_ref_simple =
"/\n"
"aaa=stuff\n"
"bbb=other stuff\n"
"somekey=somevalue\n"
"\n"
"/subnamespace\n"
"\\/more stuff=in sub\n"
"key in sub=value in sub\n"
"\n"
"/subnamespace/subsub\n"
"some stuff=also down here\n";
// the order in here is implementation-defined
static const char *text_ref_simple_unsorted =
"/\n"
"aaa=stuff\n"
"somekey=somevalue\n"
"bbb=other stuff\n"
"\n"
"/subnamespace\n"
"key in sub=value in sub\n"
"\\/more stuff=in sub\n"
"\n"
"/subnamespace/subsub\n"
"some stuff=also down here\n";
static const char *text_ref =
"/\n"
"\\\\,\";]\\n [\\r}{'\\=/key\\\\,\";]\\n [\\r}{'\\=/=\\\\,\";]\\n [\\r}{'=/value\\\\,\";]\\n [\\r}{'=/\n"
"aaa=stuff\n"
"bbb=other stuff\n"
"\n"
"/sub\\\\,\";]\\n [\\r}{'=\\/namespace\n"
"\\/more stuff\\n=\\nin\\nsub\\n\n"
"key\\\\,\";]\\n [\\r}{'\\=/in sub=value\\\\,\";]\\n [\\r}{'=/in sub\n"
"\n"
"/sub\\\\,\";]\\n [\\r}{'=\\/namespace/subsub\n"
"some stuff=also down here\n";
static const char *text_ref_bad_nl =
"/\r\n"
"non=unix\r"
"newlines=should\n"
"be=banned";
static const char *text_ref_broken =
"/////\n"
"just garbage\n"
"no\\=equal\\=here\n"
"=nokey\n"
"novalue=\n"
"more/garbage/////\n"
"\\/\\/\\/unnecessary=\\/escapes\\u\\x\\a\n"
"////some/////subns/\n"
"more=equal=signs=than=one=\n"
"////some//subns//more\n"
"////some//subns//more/further//////\n"
"escape=newlines\\\n"
"also escape=nothingness\\";
static const char *text_ref_path_last_line =
"/\r\n"
"some=stuff\r"
"/a/useless/namespace";
static Sdb *text_ref_simple_db() {
Sdb *db = sdb_new0();
sdb_set(db, "somekey", "somevalue", 0);
sdb_set(db, "aaa", "stuff", 0);
sdb_set(db, "bbb", "other stuff", 0);
Sdb *sub = sdb_ns(db, "subnamespace", true);
sdb_set(sub, "key in sub", "value in sub", 0);
sdb_set(sub, "/more stuff", "in sub", 0);
Sdb *subsub = sdb_ns(sub, "subsub", true);
sdb_set(subsub, "some stuff", "also down here", 0);
return db;
}
static Sdb *text_ref_db() {
Sdb *db = sdb_new0();
sdb_set(db, PERTURBATOR "key" PERTURBATOR, PERTURBATOR "value" PERTURBATOR, 0);
sdb_set(db, "aaa", "stuff", 0);
sdb_set(db, "bbb", "other stuff", 0);
Sdb *sub = sdb_ns(db, "sub" PERTURBATOR "namespace", true);
sdb_set(sub, "key" PERTURBATOR "in sub", "value" PERTURBATOR "in sub", 0);
sdb_set(sub, "/more stuff\n", "\nin\nsub\n", 0);
Sdb *subsub = sdb_ns(sub, "subsub", true);
sdb_set(subsub, "some stuff", "also down here", 0);
return db;
}
static Sdb *text_ref_bad_nl_db() {
Sdb *db = sdb_new0();
sdb_set(db, "non", "unix", 0);
sdb_set(db, "newlines", "should", 0);
sdb_set(db, "be", "banned", 0);
return db;
}
static Sdb *text_ref_broken_db() {
Sdb *db = sdb_new0();
sdb_set(db, "///unnecessary", "/escapesuxa", 0);
Sdb *a = sdb_ns(db, "some", true);
Sdb *b = sdb_ns(a, "subns", true);
sdb_set(b, "more", "equal=signs=than=one=", 0);
Sdb *c = sdb_ns(b, "more", true);
Sdb *d = sdb_ns(c, "further", true);
sdb_set(d, "escape", "newlines", 0);
sdb_set(d, "also escape", "nothingness", 0);
return db;
}
static Sdb *text_ref_path_last_line_db() {
Sdb *db = sdb_new0();
sdb_set(db, "some", "stuff", 0);
Sdb *a = sdb_ns(db, "a", true);
Sdb *b = sdb_ns(a, "useless", true);
sdb_ns(b, "namespace", true);
return db;
}
static int tmpfile_new(const char *filename, const char *buf, size_t sz) {
int fd = open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0644);
if (fd < 0) {
return -1;
}
if (!buf) {
return fd;
}
int w = write(fd, buf, sz);
if (w < (int)sz) {
close(fd);
return -1;
}
lseek(fd, 0, SEEK_SET);
return fd;
}
#define TEST_BUF_SZ 0x1000
bool test_sdb_text_save_simple() {
Sdb *db = text_ref_simple_db();
int fd = tmpfile_new(".text_save_simple", NULL, 0);
bool succ = sdb_text_save_fd(db, fd, true);
lseek(fd, 0, SEEK_SET);
char buf[TEST_BUF_SZ] = { 0 };
mu_assert_neq(-1, read(fd, buf, sizeof(buf) - 1), "read succeed");
close(fd);
unlink(".text_save_simple");
sdb_free(db);
mu_assert_true(succ, "save success");
mu_assert_streq(buf, text_ref_simple, "text save");
mu_end;
}
bool test_sdb_text_save_simple_unsorted() {
Sdb *db = text_ref_simple_db();
int fd = tmpfile_new(".text_save_simple_unsorted", NULL, 0);
bool succ = sdb_text_save_fd(db, fd, false);
lseek(fd, 0, SEEK_SET);
char buf[TEST_BUF_SZ] = { 0 };
mu_assert_neq(-1, read(fd, buf, sizeof(buf) - 1), "read succeed");
close(fd);
unlink(".text_save_simple_unsorted");
sdb_free(db);
mu_assert_true(succ, "save success");
mu_assert_streq(buf, text_ref_simple_unsorted, "text save");
mu_end;
}
bool test_sdb_text_save() {
Sdb *db = text_ref_db();
int fd = tmpfile_new(".text_save", NULL, 0);
bool succ = sdb_text_save_fd(db, fd, true);
lseek(fd, 0, SEEK_SET);
char buf[TEST_BUF_SZ] = { 0 };
mu_assert_neq(-1, read(fd, buf, sizeof(buf) - 1), "read succeed");
close(fd);
unlink(".text_save");
sdb_free(db);
mu_assert_true(succ, "save success");
mu_assert_streq(buf, text_ref, "text save");
mu_end;
}
static void diff_cb(const SdbDiff *diff, void *user) {
char buf[2048];
if (sdb_diff_format(buf, sizeof(buf), diff) < 0) {
return;
}
printf("%s\n", buf);
}
bool test_sdb_text_load_simple() {
char *buf = strdup(text_ref_simple);
Sdb *db = sdb_new0();
bool succ = sdb_text_load_buf(db, buf, strlen(buf));
free(buf);
mu_assert_true(succ, "load success");
Sdb *ref_db = text_ref_simple_db();
bool eq = sdb_diff(ref_db, db, diff_cb, NULL);
sdb_free(ref_db);
sdb_free(db);
mu_assert_true(eq, "load correct");
mu_end;
}
bool test_sdb_text_load() {
char *buf = strdup(text_ref);
Sdb *db = sdb_new0();
bool succ = sdb_text_load_buf(db, buf, strlen(buf));
free(buf);
mu_assert_true(succ, "load success");
Sdb *ref_db = text_ref_db();
bool eq = sdb_diff(ref_db, db, diff_cb, NULL);
sdb_free(ref_db);
sdb_free(db);
mu_assert_true(eq, "load correct");
mu_end;
}
bool test_sdb_text_load_bad_nl() {
char *buf = strdup(text_ref_bad_nl);
Sdb *db = sdb_new0();
bool succ = sdb_text_load_buf(db, buf, strlen(buf));
free(buf);
mu_assert_true(succ, "load success");
Sdb *ref_db = text_ref_bad_nl_db();
bool eq = sdb_diff(ref_db, db, diff_cb, NULL);
sdb_free(ref_db);
sdb_free(db);
mu_assert_true(eq, "load correct");
mu_end;
}
bool test_sdb_text_load_broken() {
char *buf = strdup(text_ref_broken);
Sdb *db = sdb_new0();
bool succ = sdb_text_load_buf(db, buf, strlen(buf));
free(buf);
mu_assert_true(succ, "load success");
Sdb *ref_db = text_ref_broken_db();
bool eq = sdb_diff(ref_db, db, diff_cb, NULL);
sdb_free(ref_db);
sdb_free(db);
mu_assert_true(eq, "load correct");
mu_end;
}
bool test_sdb_text_load_path_last_line() {
char *buf = strdup(text_ref_path_last_line);
Sdb *db = sdb_new0();
bool succ = sdb_text_load_buf(db, buf, strlen(buf));
free(buf);
mu_assert_true(succ, "load success");
Sdb *ref_db = text_ref_path_last_line_db();
bool eq = sdb_diff(ref_db, db, diff_cb, NULL);
sdb_free(ref_db);
sdb_free(db);
mu_assert_true(eq, "load correct");
mu_end;
}
bool test_sdb_text_load_file() {
close(tmpfile_new(".text_load_simple", text_ref_simple, strlen(text_ref_simple)));
Sdb *db = sdb_new0();
bool succ = sdb_text_load(db, ".text_load_simple");
unlink(".text_load_simple");
mu_assert_true(succ, "load success");
Sdb *ref_db = text_ref_simple_db();
bool eq = sdb_diff(ref_db, db, diff_cb, NULL);
sdb_free(ref_db);
sdb_free(db);
mu_assert_true(eq, "load correct");
mu_end;
}
int all_tests() {
// XXX two bugs found with crash
mu_run_test(test_sdb_namespace);
mu_run_test(test_sdb_foreach_delete);
mu_run_test(test_sdb_list_delete);
mu_run_test(test_sdb_delete_none);
mu_run_test(test_sdb_delete_alot);
mu_run_test(test_sdb_milset);
mu_run_test(test_sdb_milset_random);
mu_run_test(test_sdb_list_big);
mu_run_test(test_sdb_foreach_filter_user);
mu_run_test(test_sdb_foreach_filter);
mu_run_test(test_sdb_copy);
mu_run_test(test_sdb_text_save_simple);
mu_run_test(test_sdb_text_save_simple_unsorted);
mu_run_test(test_sdb_text_load_simple);
mu_run_test(test_sdb_text_save);
mu_run_test(test_sdb_text_load);
mu_run_test(test_sdb_text_load_bad_nl);
mu_run_test(test_sdb_text_load_broken);
mu_run_test(test_sdb_text_load_path_last_line);
mu_run_test(test_sdb_text_load_file);
return tests_passed != tests_run;
}
int main(int argc, char **argv) {
return all_tests();
}

36
test/unit/test_sdb_util.c Normal file
View file

@ -0,0 +1,36 @@
// SPDX-FileCopyrightText: pancake <pancake@nopcode.org>
// SPDX-License-Identifier: MIT
#include "minunit.h"
#include <sdb.h>
#include <fcntl.h>
#include <stdio.h>
bool test_sdb_itoa_null_arg() {
mu_assert_streq_free(sdb_itoa(0, NULL, 10), "0", "0 is converted");
mu_assert_streq_free(sdb_itoa(10, NULL, 10), "10", "10 is converted");
mu_assert_streq_free(sdb_itoa(3, NULL, 10), "3", "3 is converted");
mu_assert_streq_free(sdb_itoa(100, NULL, 16), "0x64", "100 is converted");
mu_assert_streq_free(sdb_itoa(100, NULL, 10), "100", "100 is converted");
mu_end;
}
bool test_sdb_itoa() {
char s[64];
mu_assert_streq(sdb_itoa(0, s, 10), "0", "0 is converted");
mu_assert_streq(sdb_itoa(10, s, 10), "10", "10 is converted");
mu_assert_streq(sdb_itoa(3, s, 10), "3", "3 is converted");
mu_assert_streq(sdb_itoa(100, s, 16), "0x64", "100 is converted");
mu_assert_streq(sdb_itoa(100, s, 10), "100", "100 is converted");
mu_end;
}
int all_tests() {
mu_run_test(test_sdb_itoa_null_arg);
mu_run_test(test_sdb_itoa);
return tests_passed != tests_run;
}
int main(int argc, char **argv) {
return all_tests();
}