|
/* kv_trie.c - Bash loadable builtin backed by libexpanse (native C API). |
|
* |
|
* Same idea as fast_kv.c, but keys and values live in an off-heap Expanse |
|
* trie instead of a hand-rolled linked list on the shell heap. This is the |
|
* pattern you want for high-cardinality data (100k+ keys). |
|
* |
|
* Install libexpanse (see https://github.com/orieg/expanse): |
|
* Debian/Ubuntu : sudo apt-get install libexpanse1 libexpanse-dev |
|
* Fedora/RHEL : sudo dnf install libexpanse libexpanse-devel |
|
* |
|
* gcc -fPIC -shared -I/usr/include/bash -I/usr/include/bash/include \ |
|
* -I/usr/include/bash/builtins -o libkv_trie.so kv_trie.c -lexpanse |
|
*/ |
|
#include <config.h> |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <stdint.h> |
|
|
|
#include "builtins.h" |
|
#include "shell.h" |
|
#include "bashgetopt.h" |
|
#include "common.h" |
|
|
|
#include <expanse.h> |
|
|
|
/* One ordered uint64 -> uint64 map, off-heap, for the shell's lifetime. */ |
|
static expanse_map_t *kv = NULL; |
|
|
|
int kv_trie_builtin(WORD_LIST *list) { |
|
if (list == NULL) { |
|
builtin_usage(); |
|
return (EX_USAGE); |
|
} |
|
if (kv == NULL && (kv = expanse_map_new()) == NULL) { |
|
builtin_error("kv_trie: cannot allocate map"); |
|
return (EXECUTION_FAILURE); |
|
} |
|
|
|
char *action = list->word->word; |
|
list = list->next; |
|
|
|
if (strcmp(action, "set") == 0) { |
|
if (!list || !list->next) { |
|
builtin_error("usage: kv_trie set <key> <value>"); |
|
return (EXECUTION_FAILURE); |
|
} |
|
uint64_t key = strtoull(list->word->word, NULL, 10); |
|
uint64_t val = strtoull(list->next->word->word, NULL, 10); |
|
expanse_map_insert(kv, key, val, NULL); /* insert or overwrite */ |
|
return (EXECUTION_SUCCESS); |
|
} |
|
|
|
if (strcmp(action, "get") == 0) { |
|
if (!list) { |
|
builtin_error("usage: kv_trie get <key>"); |
|
return (EXECUTION_FAILURE); |
|
} |
|
uint64_t key = strtoull(list->word->word, NULL, 10); |
|
uint64_t val; |
|
if (!expanse_map_get(kv, key, &val)) |
|
return (EXECUTION_FAILURE); /* key not found */ |
|
printf("%llu\n", (unsigned long long) val); |
|
return (EXECUTION_SUCCESS); |
|
} |
|
|
|
builtin_error("unknown action '%s': use set or get", action); |
|
return (EX_USAGE); |
|
} |
|
|
|
char *kv_trie_doc[] = { |
|
"Store and retrieve integer key/value pairs in an off-heap trie.", |
|
"", |
|
"Usage: kv_trie set <key> <value>", |
|
" kv_trie get <key>", |
|
"", |
|
"Backed by libexpanse's native C API, not a shell heap allocation.", |
|
(char *)NULL |
|
}; |
|
|
|
struct builtin kv_trie_struct = { |
|
"kv_trie", /* Builtin command name */ |
|
kv_trie_builtin, /* Function pointer */ |
|
BUILTIN_ENABLED, /* Initial status */ |
|
kv_trie_doc, /* Documentation strings */ |
|
"kv_trie set|get <key> [val]", /* Short usage synopsis */ |
|
0 /* Reserved */ |
|
}; |