#include <assert.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <stdbool.h>
#include <fcntl.h>
#include <emmintrin.h>
#include <sys/types.h>
#include "libpmemobj.h"
#include "art.h"
TOID(var_string) null_var_string;
TOID(art_leaf) null_art_leaf;
TOID(art_node_u) null_art_node_u;
int art_tree_init(PMEMobjpool *pop, int *newpool);
TOID(art_node_u) make_leaf(PMEMobjpool *pop, const unsigned char *key,
int key_len, void *value, int val_len);
int fill_leaf(PMEMobjpool *pop, TOID(art_leaf) al, const unsigned char *key,
int key_len, void *value, int val_len);
TOID(art_node_u) alloc_node(PMEMobjpool *pop, art_node_type node_type);
TOID(var_string) art_insert(PMEMobjpool *pop, const unsigned char *key,
int key_len, void *value, int val_len);
TOID(var_string) art_delete(PMEMobjpool *pop, const unsigned char *key,
int key_len);
static TOID(var_string) recursive_insert(PMEMobjpool *pop,
TOID(art_node_u) n, TOID(art_node_u) *ref,
const unsigned char *key, int key_len,
void *value, int val_len, int depth, int *old_val);
static TOID(art_leaf) recursive_delete(PMEMobjpool *pop,
TOID(art_node_u) n, TOID(art_node_u) *ref,
const unsigned char *key, int key_len, int depth);
static int leaf_matches(TOID(art_leaf) n, const unsigned char *key,
int key_len, int depth);
static int longest_common_prefix(TOID(art_leaf) l1, TOID(art_leaf) l2,
int depth);
static int prefix_mismatch(TOID(art_node_u) n, unsigned char *key,
int key_len, int depth);
#ifdef LIBART_ITER_PREFIX
static int leaf_prefix_matches(TOID(art_leaf) n,
const unsigned char *prefix, int prefix_len);
#endif
static TOID(art_leaf) minimum(TOID(art_node_u) n_u);
static TOID(art_leaf) maximum(TOID(art_node_u) n_u);
static void copy_header(art_node *dest, art_node *src);
static void add_child(PMEMobjpool *pop, TOID(art_node_u) n,
TOID(art_node_u) *ref, unsigned char c,
TOID(art_node_u) child);
static void add_child4(PMEMobjpool *pop, TOID(art_node4) n,
TOID(art_node_u) *ref, unsigned char c,
TOID(art_node_u) child);
static void add_child16(PMEMobjpool *pop, TOID(art_node16) n,
TOID(art_node_u) *ref, unsigned char c,
TOID(art_node_u) child);
static void add_child48(PMEMobjpool *pop, TOID(art_node48) n,
TOID(art_node_u) *ref, unsigned char c,
TOID(art_node_u) child);
static void add_child256(PMEMobjpool *pop, TOID(art_node256) n,
TOID(art_node_u) *ref, unsigned char c,
TOID(art_node_u) child);
static void remove_child(PMEMobjpool *pop, TOID(art_node_u) n,
TOID(art_node_u) *ref, unsigned char c,
TOID(art_node_u) *l);
static void remove_child4(PMEMobjpool *pop, TOID(art_node4) n,
TOID(art_node_u) *ref, TOID(art_node_u) *l);
static void remove_child16(PMEMobjpool *pop, TOID(art_node16) n,
TOID(art_node_u) *ref, TOID(art_node_u) *l);
static void remove_child48(PMEMobjpool *pop, TOID(art_node48) n,
TOID(art_node_u) *ref, unsigned char c);
static void remove_child256(PMEMobjpool *pop, TOID(art_node256) n,
TOID(art_node_u) *ref, unsigned char c);
static TOID(art_node_u)* find_child(TOID(art_node_u) n, unsigned char c);
static int check_prefix(const art_node *n, const unsigned char *key,
int key_len, int depth);
static int leaf_matches(TOID(art_leaf) n, const unsigned char *key,
int key_len, int depth);
TOID(art_leaf) art_minimum(TOID(struct art_tree_root) t);
TOID(art_leaf) art_maximum(TOID(struct art_tree_root) t);
#if 0#endif
int art_iter(PMEMobjpool *pop, art_callback cb, void *data);
static void PMEMOIDcopy(PMEMoid *dest, const PMEMoid *src, const int n);
static void PMEMOIDmove(PMEMoid *dest, PMEMoid *src, const int n);
static void
PMEMOIDcopy(PMEMoid *dest, const PMEMoid *src, const int n)
{
int i;
for (i = 0; i < n; i++) {
dest[i] = src[i];
}
}
static void
PMEMOIDmove(PMEMoid *dest, PMEMoid *src, const int n)
{
int i;
if (dest > src) {
for (i = n - 1; i >= 0; i--) {
dest[i] = src[i];
}
} else {
for (i = 0; i < n; i++) {
dest[i] = src[i];
}
}
}
TOID(art_node_u)
alloc_node(PMEMobjpool *pop, art_node_type node_type)
{
TOID(art_node_u) node;
TOID(art_node4) an4;
TOID(art_node16) an16;
TOID(art_node48) an48;
TOID(art_node256) an256;
TOID(art_leaf) al;
node = TX_ZNEW(art_node_u);
D_RW(node)->art_node_type = (uint8_t)node_type;
switch (node_type) {
case NODE4:
an4 = TX_ZNEW(art_node4);
D_RW(node)->u.an4 = an4;
break;
case NODE16:
an16 = TX_ZNEW(art_node16);
D_RW(node)->u.an16 = an16;
break;
case NODE48:
an48 = TX_ZNEW(art_node48);
D_RW(node)->u.an48 = an48;
break;
case NODE256:
an256 = TX_ZNEW(art_node256);
D_RW(node)->u.an256 = an256;
break;
case art_leaf_t:
al = TX_ZNEW(art_leaf);
D_RW(node)->u.al = al;
break;
default:
D_RW(node)->art_node_type = (uint8_t)art_node_types;
break;
}
return node;
}
int
art_tree_init(PMEMobjpool *pop, int *newpool)
{
int errors = 0;
TOID(struct art_tree_root) root;
if (pop == NULL) {
errors++;
}
null_var_string.oid = OID_NULL;
null_art_leaf.oid = OID_NULL;
null_art_node_u.oid = OID_NULL;
if (!errors) {
TX_BEGIN(pop) {
root = POBJ_ROOT(pop, struct art_tree_root);
if (*newpool) {
TX_ADD(root);
D_RW(root)->root.oid = OID_NULL;
D_RW(root)->size = 0;
*newpool = 0;
}
} TX_END
}
return errors;
}
#if 0#endif
static TOID(art_node_u)*
find_child(TOID(art_node_u) n, unsigned char c)
{
int i;
int mask;
int bitfield;
TOID(art_node4) an4;
TOID(art_node16) an16;
TOID(art_node48) an48;
TOID(art_node256) an256;
switch (D_RO(n)->art_node_type) {
case NODE4:
an4 = D_RO(n)->u.an4;
for (i = 0; i < D_RO(an4)->n.num_children; i++) {
if (D_RO(an4)->keys[i] == c) {
return &(D_RW(an4)->children[i]);
}
}
break;
case NODE16: {
__m128i cmp;
an16 = D_RO(n)->u.an16;
cmp = _mm_cmpeq_epi8(_mm_set1_epi8(c),
_mm_loadu_si128((__m128i *)D_RO(an16)->keys));
mask = (1 << D_RO(an16)->n.num_children) - 1;
bitfield = _mm_movemask_epi8(cmp) & mask;
if (bitfield) {
return &(D_RW(an16)->children[__builtin_ctz(bitfield)]);
}
break;
}
case NODE48:
an48 = D_RO(n)->u.an48;
i = D_RO(an48)->keys[c];
if (i) {
return &(D_RW(an48)->children[i - 1]);
}
break;
case NODE256:
an256 = D_RO(n)->u.an256;
if (!TOID_IS_NULL(D_RO(an256)->children[c])) {
return &(D_RW(an256)->children[c]);
}
break;
default:
abort();
}
return &null_art_node_u;
}
static inline int
min(int a, int b)
{
return (a < b) ? a : b;
}
static int
check_prefix(const art_node *n,
const unsigned char *key, int key_len, int depth)
{
int max_cmp = min(min(n->partial_len, MAX_PREFIX_LEN), key_len - depth);
int idx;
for (idx = 0; idx < max_cmp; idx++) {
if (n->partial[idx] != key[depth + idx])
return idx;
}
return idx;
}
static int
leaf_matches(TOID(art_leaf) n, const unsigned char *key, int key_len, int depth)
{
(void) depth;
if (D_RO(D_RO(n)->key)->len != (uint32_t)key_len)
return 1;
return memcmp(D_RO(D_RO(n)->key)->s, key, key_len);
}
TOID(var_string)
art_search(PMEMobjpool *pop, const unsigned char *key, int key_len)
{
TOID(struct art_tree_root)t = POBJ_ROOT(pop, struct art_tree_root);
TOID(art_node_u) *child;
TOID(art_node_u) n = D_RO(t)->root;
const art_node *n_an;
int prefix_len;
int depth = 0;
while (!TOID_IS_NULL(n)) {
if (IS_LEAF(D_RO(n))) {
if (!leaf_matches(D_RO(n)->u.al, key, key_len, depth)) {
return (D_RO(D_RO(n)->u.al))->value;
}
return null_var_string;
}
switch (D_RO(n)->art_node_type) {
case NODE4: n_an = &(D_RO(D_RO(n)->u.an4)->n); break;
case NODE16: n_an = &(D_RO(D_RO(n)->u.an16)->n); break;
case NODE48: n_an = &(D_RO(D_RO(n)->u.an48)->n); break;
case NODE256: n_an = &(D_RO(D_RO(n)->u.an256)->n); break;
default:
return null_var_string;
}
if (n_an->partial_len) {
prefix_len = check_prefix(n_an, key, key_len, depth);
if (prefix_len !=
min(MAX_PREFIX_LEN, n_an->partial_len))
return null_var_string;
depth = depth + n_an->partial_len;
}
child = find_child(n, key[depth]);
if (TOID_IS_NULL(*child)) {
n.oid = OID_NULL;
} else {
n = *child;
}
depth++;
}
return null_var_string;
}
static TOID(art_leaf)
minimum(TOID(art_node_u) n_u)
{
TOID(art_node4) an4;
TOID(art_node16) an16;
TOID(art_node48) an48;
TOID(art_node256) an256;
if (TOID_IS_NULL(n_u))
return null_art_leaf;
if (IS_LEAF(D_RO(n_u)))
return D_RO(n_u)->u.al;
int idx;
switch (D_RO(n_u)->art_node_type) {
case NODE4:
an4 = D_RO(n_u)->u.an4;
return minimum(D_RO(an4)->children[0]);
case NODE16:
an16 = D_RO(n_u)->u.an16;
return minimum(D_RO(an16)->children[0]);
case NODE48:
an48 = D_RO(n_u)->u.an48;
idx = 0;
while (!(D_RO(an48)->keys[idx]))
idx++;
idx = D_RO(an48)->keys[idx] - 1;
assert(idx < 48);
return minimum(D_RO(an48)->children[idx]);
case NODE256:
an256 = D_RO(n_u)->u.an256;
idx = 0;
while (!(TOID_IS_NULL(D_RO(an256)->children[idx])))
idx++;
return minimum(D_RO(an256)->children[idx]);
default:
abort();
}
}
static TOID(art_leaf)
maximum(TOID(art_node_u) n_u)
{
TOID(art_node4) an4;
TOID(art_node16) an16;
TOID(art_node48) an48;
TOID(art_node256) an256;
const art_node *n_an;
if (TOID_IS_NULL(n_u))
return null_art_leaf;
if (IS_LEAF(D_RO(n_u)))
return D_RO(n_u)->u.al;
int idx;
switch (D_RO(n_u)->art_node_type) {
case NODE4:
an4 = D_RO(n_u)->u.an4;
n_an = &(D_RO(an4)->n);
return maximum(D_RO(an4)->children[n_an->num_children - 1]);
case NODE16:
an16 = D_RO(n_u)->u.an16;
n_an = &(D_RO(an16)->n);
return maximum(D_RO(an16)->children[n_an->num_children - 1]);
case NODE48:
an48 = D_RO(n_u)->u.an48;
idx = 255;
while (!(D_RO(an48)->keys[idx]))
idx--;
idx = D_RO(an48)->keys[idx] - 1;
assert((idx >= 0) && (idx < 48));
return maximum(D_RO(an48)->children[idx]);
case NODE256:
an256 = D_RO(n_u)->u.an256;
idx = 255;
while (!(TOID_IS_NULL(D_RO(an256)->children[idx])))
idx--;
return maximum(D_RO(an256)->children[idx]);
default:
abort();
}
}
TOID(art_leaf)
art_minimum(TOID(struct art_tree_root) t)
{
return minimum(D_RO(t)->root);
}
TOID(art_leaf)
art_maximum(TOID(struct art_tree_root) t)
{
return maximum(D_RO(t)->root);
}
TOID(art_node_u)
make_leaf(PMEMobjpool *pop,
const unsigned char *key, int key_len, void *value, int val_len)
{
TOID(art_node_u)newleaf;
newleaf = alloc_node(pop, art_leaf_t);
fill_leaf(pop, D_RW(newleaf)->u.al, key, key_len, value, val_len);
return newleaf;
}
static int
longest_common_prefix(TOID(art_leaf) l1, TOID(art_leaf) l2, int depth)
{
TOID(var_string) l1_key = D_RO(l1)->key;
TOID(var_string) l2_key = D_RO(l2)->key;
int max_cmp;
int idx;
max_cmp = min(D_RO(l1_key)->len, D_RO(l2_key)->len) - depth;
for (idx = 0; idx < max_cmp; idx++) {
if (D_RO(l1_key)->s[depth + idx] !=
D_RO(l2_key)->s[depth + idx])
return idx;
}
return idx;
}
static void
copy_header(art_node *dest, art_node *src)
{
dest->num_children = src->num_children;
dest->partial_len = src->partial_len;
memcpy(dest->partial, src->partial,
min(MAX_PREFIX_LEN, src->partial_len));
}
static void
add_child256(PMEMobjpool *pop, TOID(art_node256) n, TOID(art_node_u) *ref,
unsigned char c, TOID(art_node_u) child)
{
art_node *n_an;
(void) ref;
TX_ADD(n);
n_an = &(D_RW(n)->n);
n_an->num_children++;
D_RW(n)->children[c] = child;
}
static void
add_child48(PMEMobjpool *pop, TOID(art_node48) n, TOID(art_node_u) *ref,
unsigned char c, TOID(art_node_u) child)
{
art_node *n_an;
n_an = &(D_RW(n)->n);
if (n_an->num_children < 48) {
int pos = 0;
TX_ADD(n);
while (!(TOID_IS_NULL(D_RO(n)->children[pos])))
pos++;
D_RW(n)->children[pos] = child;
D_RW(n)->keys[c] = pos + 1;
n_an->num_children++;
} else {
TOID(art_node_u) newnode_u = alloc_node(pop, NODE256);
TOID(art_node256) newnode = D_RO(newnode_u)->u.an256;
pmemobj_tx_add_range_direct(ref, sizeof(TOID(art_node_u)));
for (int i = 0; i < 256; i++) {
if (D_RO(n)->keys[i]) {
D_RW(newnode)->children[i] =
D_RO(n)->children[D_RO(n)->keys[i] - 1];
}
}
copy_header(&(D_RW(newnode)->n), n_an);
*ref = newnode_u;
TX_FREE(n);
add_child256(pop, newnode, ref, c, child);
}
}
static void
add_child16(PMEMobjpool *pop, TOID(art_node16) n, TOID(art_node_u)*ref,
unsigned char c, TOID(art_node_u) child)
{
art_node *n_an;
n_an = &(D_RW(n)->n);
if (n_an->num_children < 16) {
__m128i cmp;
TX_ADD(n);
cmp = _mm_cmplt_epi8(_mm_set1_epi8(c),
_mm_loadu_si128((__m128i *)(D_RO(n)->keys)));
unsigned mask = (1 << n_an->num_children) - 1;
unsigned bitfield = _mm_movemask_epi8(cmp) & mask;
unsigned idx;
if (bitfield) {
idx = __builtin_ctz(bitfield);
memmove(&(D_RW(n)->keys[idx + 1]),
&(D_RO(n)->keys[idx]),
n_an->num_children - idx);
PMEMOIDmove(&(D_RW(n)->children[idx + 1].oid),
&(D_RW(n)->children[idx].oid),
n_an->num_children - idx);
} else {
idx = n_an->num_children;
}
D_RW(n)->keys[idx] = c;
D_RW(n)->children[idx] = child;
n_an->num_children++;
} else {
TOID(art_node_u) newnode_u = alloc_node(pop, NODE48);
TOID(art_node48) newnode = D_RO(newnode_u)->u.an48;
PMEMOIDcopy(&(D_RW(newnode)->children[0].oid),
&(D_RO(n)->children[0].oid),
n_an->num_children);
for (int i = 0; i < n_an->num_children; i++) {
D_RW(newnode)->keys[D_RO(n)->keys[i]] = i + 1;
}
copy_header(&(D_RW(newnode))->n, n_an);
*ref = newnode_u;
TX_FREE(n);
add_child48(pop, newnode, ref, c, child);
}
}
static void
add_child4(PMEMobjpool *pop, TOID(art_node4) n, TOID(art_node_u) *ref,
unsigned char c, TOID(art_node_u) child)
{
art_node *n_an;
n_an = &(D_RW(n)->n);
if (n_an->num_children < 4) {
int idx;
TX_ADD(n);
for (idx = 0; idx < n_an->num_children; idx++) {
if (c < D_RO(n)->keys[idx]) break;
}
memmove(D_RW(n)->keys + idx + 1, D_RO(n)->keys + idx,
n_an->num_children - idx);
assert((idx + 1) < 4);
PMEMOIDmove(&(D_RW(n)->children[idx + 1].oid),
&(D_RW(n)->children[idx].oid),
n_an->num_children - idx);
D_RW(n)->keys[idx] = c;
D_RW(n)->children[idx] = child;
n_an->num_children++;
} else {
TOID(art_node_u) newnode_u = alloc_node(pop, NODE16);
TOID(art_node16) newnode = D_RO(newnode_u)->u.an16;
pmemobj_tx_add_range_direct(ref, sizeof(TOID(art_node_u)));
PMEMOIDcopy(&(D_RW(newnode)->children[0].oid),
&(D_RO(n)->children[0].oid), n_an->num_children);
memcpy(D_RW(newnode)->keys, D_RO(n)->keys, n_an->num_children);
copy_header(&(D_RW(newnode)->n), n_an);
*ref = newnode_u;
TX_FREE(n);
add_child16(pop, newnode, ref, c, child);
}
}
static void
add_child(PMEMobjpool *pop, TOID(art_node_u) n, TOID(art_node_u) *ref,
unsigned char c, TOID(art_node_u) child)
{
switch (D_RO(n)->art_node_type) {
case NODE4:
add_child4(pop, D_RO(n)->u.an4, ref, c, child);
break;
case NODE16:
add_child16(pop, D_RO(n)->u.an16, ref, c, child);
break;
case NODE48:
add_child48(pop, D_RO(n)->u.an48, ref, c, child);
break;
case NODE256:
add_child256(pop, D_RO(n)->u.an256, ref, c, child);
break;
default:
abort();
}
}
static int
prefix_mismatch(TOID(art_node_u) n, unsigned char *key, int key_len, int depth)
{
const art_node *n_an;
int max_cmp;
int idx;
switch (D_RO(n)->art_node_type) {
case NODE4: n_an = &(D_RO(D_RO(n)->u.an4)->n); break;
case NODE16: n_an = &(D_RO(D_RO(n)->u.an16)->n); break;
case NODE48: n_an = &(D_RO(D_RO(n)->u.an48)->n); break;
case NODE256: n_an = &(D_RO(D_RO(n)->u.an256)->n); break;
default: return 0;
}
max_cmp = min(min(MAX_PREFIX_LEN, n_an->partial_len), key_len - depth);
for (idx = 0; idx < max_cmp; idx++) {
if (n_an->partial[idx] != key[depth + idx])
return idx;
}
if (n_an->partial_len > MAX_PREFIX_LEN) {
TOID(art_leaf) l = minimum(n);
max_cmp = min(D_RO(D_RO(l)->key)->len, key_len) - depth;
for (; idx < max_cmp; idx++) {
if (D_RO(D_RO(l)->key)->s[idx + depth] !=
key[depth + idx])
return idx;
}
}
return idx;
}
static TOID(var_string)
recursive_insert(PMEMobjpool *pop, TOID(art_node_u) n, TOID(art_node_u) *ref,
const unsigned char *key, int key_len,
void *value, int val_len, int depth, int *old)
{
art_node *n_an;
TOID(var_string) retval;
if (TOID_IS_NULL(n)) {
*ref = make_leaf(pop, key, key_len, value, val_len);
TX_ADD(*ref);
SET_LEAF(D_RW(*ref));
retval = null_var_string;
return retval;
}
if (IS_LEAF(D_RO(n))) {
TOID(art_leaf)l = D_RO(n)->u.al;
if (!leaf_matches(l, key, key_len, depth)) {
*old = 1;
retval = D_RO(l)->value;
TX_ADD(D_RW(l)->value);
COPY_BLOB(D_RW(l)->value, value, val_len);
return retval;
}
pmemobj_tx_add_range_direct(ref,
sizeof(TOID(art_node_u)));
TOID(art_node_u) newnode_u = alloc_node(pop, NODE4);
TOID(art_node4) newnode = D_RO(newnode_u)->u.an4;
art_node *newnode_n = &(D_RW(newnode)->n);
TOID(art_node_u) l2_u =
make_leaf(pop, key, key_len, value, val_len);
TOID(art_leaf) l2 = D_RO(l2_u)->u.al;
int longest_prefix =
longest_common_prefix(l, l2, depth);
newnode_n->partial_len = longest_prefix;
memcpy(newnode_n->partial, key + depth,
min(MAX_PREFIX_LEN, longest_prefix));
*ref = newnode_u;
add_child4(pop, newnode, ref,
D_RO(D_RO(l)->key)->s[depth + longest_prefix],
n);
add_child4(pop, newnode, ref,
D_RO(D_RO(l2)->key)->s[depth + longest_prefix],
l2_u);
return null_var_string;
}
switch (D_RO(n)->art_node_type) {
case NODE4: n_an = &(D_RW(D_RW(n)->u.an4)->n); break;
case NODE16: n_an = &(D_RW(D_RW(n)->u.an16)->n); break;
case NODE48: n_an = &(D_RW(D_RW(n)->u.an48)->n); break;
case NODE256: n_an = &(D_RW(D_RW(n)->u.an256)->n); break;
default: abort();
}
if (n_an->partial_len) {
int prefix_diff =
prefix_mismatch(n, (unsigned char *)key, key_len, depth);
if ((uint32_t)prefix_diff >= n_an->partial_len) {
depth += n_an->partial_len;
goto RECURSE_SEARCH;
}
pmemobj_tx_add_range_direct(ref,
sizeof(TOID(art_node_u)));
pmemobj_tx_add_range_direct(n_an, sizeof(art_node));
TOID(art_node_u) newnode_u = alloc_node(pop, NODE4);
TOID(art_node4) newnode = D_RO(newnode_u)->u.an4;
art_node *newnode_n = &(D_RW(newnode)->n);
*ref = newnode_u;
newnode_n->partial_len = prefix_diff;
memcpy(newnode_n->partial, n_an->partial,
min(MAX_PREFIX_LEN, prefix_diff));
if (n_an->partial_len <= MAX_PREFIX_LEN) {
add_child4(pop, newnode, ref,
n_an->partial[prefix_diff], n);
n_an->partial_len -= (prefix_diff + 1);
memmove(n_an->partial,
n_an->partial + prefix_diff + 1,
min(MAX_PREFIX_LEN, n_an->partial_len));
} else {
unsigned char *dst;
const unsigned char *src;
size_t len;
n_an->partial_len -= (prefix_diff + 1);
TOID(art_leaf) l = minimum(n);
add_child4(pop, newnode, ref,
D_RO(D_RO(l)->key)->s[depth + prefix_diff],
n);
dst = n_an->partial;
src =
&(D_RO(D_RO(l)->key)->s[depth + prefix_diff + 1 ]);
len = min(MAX_PREFIX_LEN, n_an->partial_len);
memcpy(dst, src, len);
}
TOID(art_node_u) l =
make_leaf(pop, key, key_len, value, val_len);
SET_LEAF(D_RW(l));
add_child4(pop, newnode, ref, key[depth + prefix_diff], l);
return null_var_string;
}
RECURSE_SEARCH:;
TOID(art_node_u) *child = find_child(n, key[depth]);
if (!TOID_IS_NULL(*child)) {
return recursive_insert(pop, *child, child,
key, key_len, value, val_len, depth + 1, old);
}
TOID(art_node_u) l =
make_leaf(pop, key, key_len, value, val_len);
SET_LEAF(D_RW(l));
add_child(pop, n, ref, key[depth], l);
retval = null_var_string;
return retval;
}
uint64_t
art_size(PMEMobjpool *pop)
{
TOID(struct art_tree_root) root;
root = POBJ_ROOT(pop, struct art_tree_root);
return D_RO(root)->size;
}
TOID(var_string)
art_insert(PMEMobjpool *pop,
const unsigned char *key, int key_len, void *value, int val_len)
{
int old_val = 0;
TOID(var_string) old;
TOID(struct art_tree_root) root;
TX_BEGIN(pop) {
root = POBJ_ROOT(pop, struct art_tree_root);
TX_ADD(root);
old = recursive_insert(pop, D_RO(root)->root,
&(D_RW(root)->root),
(const unsigned char *)key, key_len,
value, val_len, 0, &old_val);
if (!old_val)
D_RW(root)->size++;
} TX_ONABORT {
abort();
} TX_END
return old;
}
static void
remove_child256(PMEMobjpool *pop,
TOID(art_node256) n, TOID(art_node_u) *ref, unsigned char c)
{
art_node *n_an = &(D_RW(n)->n);
TX_ADD(n);
D_RW(n)->children[c].oid = OID_NULL;
n_an->num_children--;
if (n_an->num_children == 37) {
TOID(art_node_u) newnode_u = alloc_node(pop, NODE48);
TOID(art_node48) newnode_an48 = D_RO(newnode_u)->u.an48;
pmemobj_tx_add_range_direct(ref, sizeof(TOID(art_node_u)));
*ref = newnode_u;
copy_header(&(D_RW(newnode_an48)->n), n_an);
int pos = 0;
for (int i = 0; i < 256; i++) {
if (!TOID_IS_NULL(D_RO(n)->children[i])) {
assert(pos < 48);
D_RW(newnode_an48)->children[pos] =
D_RO(n)->children[i];
D_RW(newnode_an48)->keys[i] = pos + 1;
pos++;
}
}
TX_FREE(n);
}
}
static void
remove_child48(PMEMobjpool *pop,
TOID(art_node48) n, TOID(art_node_u) *ref, unsigned char c)
{
int pos = D_RO(n)->keys[c];
art_node *n_an = &(D_RW(n)->n);
TX_ADD(n);
D_RW(n)->keys[c] = 0;
D_RW(n)->children[pos - 1].oid = OID_NULL;
n_an->num_children--;
if (n_an->num_children == 12) {
TOID(art_node_u) newnode_u = alloc_node(pop, NODE16);
TOID(art_node16) newnode_an16 = D_RO(newnode_u)->u.an16;
pmemobj_tx_add_range_direct(ref, sizeof(TOID(art_node_u)));
*ref = newnode_u;
copy_header(&(D_RW(newnode_an16)->n), n_an);
int child = 0;
for (int i = 0; i < 256; i++) {
pos = D_RO(n)->keys[i];
if (pos) {
assert(child < 16);
D_RW(newnode_an16)->keys[child] = i;
D_RW(newnode_an16)->children[child] =
D_RO(n)->children[pos - 1];
child++;
}
}
TX_FREE(n);
}
}
static void
remove_child16(PMEMobjpool *pop,
TOID(art_node16) n, TOID(art_node_u) *ref, TOID(art_node_u) *l)
{
int pos = l - &(D_RO(n)->children[0]);
uint8_t num_children = ((D_RW(n)->n).num_children);
TX_ADD(n);
memmove(D_RW(n)->keys + pos, D_RO(n)->keys + pos + 1,
num_children - 1 - pos);
memmove(D_RW(n)->children + pos,
D_RO(n)->children + pos + 1,
(num_children - 1 - pos) * sizeof(void *));
((D_RW(n)->n).num_children)--;
if (--num_children == 3) {
TOID(art_node_u) newnode_u = alloc_node(pop, NODE4);
TOID(art_node4) newnode_an4 = D_RO(newnode_u)->u.an4;
pmemobj_tx_add_range_direct(ref, sizeof(TOID(art_node_u)));
*ref = newnode_u;
copy_header(&(D_RW(newnode_an4)->n), &(D_RW(n)->n));
memcpy(D_RW(newnode_an4)->keys, D_RO(n)->keys, 4);
memcpy(D_RW(newnode_an4)->children,
D_RO(n)->children, 4 * sizeof(TOID(art_node_u)));
TX_FREE(n);
}
}
static void
remove_child4(PMEMobjpool *pop,
TOID(art_node4) n, TOID(art_node_u) *ref, TOID(art_node_u) *l)
{
int pos = l - &(D_RO(n)->children[0]);
uint8_t *num_children = &((D_RW(n)->n).num_children);
TX_ADD(n);
memmove(D_RW(n)->keys + pos, D_RO(n)->keys + pos + 1,
*num_children - 1 - pos);
memmove(D_RW(n)->children + pos, D_RO(n)->children + pos + 1,
(*num_children - 1 - pos) * sizeof(void *));
(*num_children)--;
if (*num_children == 1) {
TOID(art_node_u) child_u = D_RO(n)->children[0];
art_node *child = &(D_RW(D_RW(child_u)->u.an4)->n);
pmemobj_tx_add_range_direct(ref, sizeof(TOID(art_node_u)));
if (!IS_LEAF(D_RO(child_u))) {
int prefix = (D_RW(n)->n).partial_len;
if (prefix < MAX_PREFIX_LEN) {
(D_RW(n)->n).partial[prefix] =
D_RO(n)->keys[0];
prefix++;
}
if (prefix < MAX_PREFIX_LEN) {
int sub_prefix = min(child->partial_len,
MAX_PREFIX_LEN - prefix);
memcpy((D_RW(n)->n).partial + prefix,
child->partial, sub_prefix);
prefix += sub_prefix;
}
memcpy(child->partial,
(D_RO(n)->n).partial, min(prefix, MAX_PREFIX_LEN));
child->partial_len += (D_RO(n)->n).partial_len + 1;
}
*ref = child_u;
TX_FREE(n);
}
}
static void
remove_child(PMEMobjpool *pop,
TOID(art_node_u) n, TOID(art_node_u) *ref,
unsigned char c, TOID(art_node_u) *l)
{
switch (D_RO(n)->art_node_type) {
case NODE4:
return remove_child4(pop, D_RO(n)->u.an4, ref, l);
case NODE16:
return remove_child16(pop, D_RO(n)->u.an16, ref, l);
case NODE48:
return remove_child48(pop, D_RO(n)->u.an48, ref, c);
case NODE256:
return remove_child256(pop, D_RO(n)->u.an256, ref, c);
default:
abort();
}
}
static TOID(art_leaf)
recursive_delete(PMEMobjpool *pop,
TOID(art_node_u) n, TOID(art_node_u) *ref,
const unsigned char *key, int key_len, int depth)
{
const art_node *n_an;
if (TOID_IS_NULL(n))
return null_art_leaf;
if (IS_LEAF(D_RO(n))) {
TOID(art_leaf) l = D_RO(n)->u.al;
if (!leaf_matches(l, key, key_len, depth)) {
*ref = null_art_node_u;
return l;
}
return null_art_leaf;
}
switch (D_RO(n)->art_node_type) {
case NODE4: n_an = &(D_RO(D_RO(n)->u.an4)->n); break;
case NODE16: n_an = &(D_RO(D_RO(n)->u.an16)->n); break;
case NODE48: n_an = &(D_RO(D_RO(n)->u.an48)->n); break;
case NODE256: n_an = &(D_RO(D_RO(n)->u.an256)->n); break;
default: abort();
}
if (n_an->partial_len) {
int prefix_len = check_prefix(n_an, key, key_len, depth);
if (prefix_len != min(MAX_PREFIX_LEN, n_an->partial_len)) {
return null_art_leaf;
}
depth = depth + n_an->partial_len;
}
TOID(art_node_u) *child = find_child(n, key[depth]);
if (TOID_IS_NULL(*child))
return null_art_leaf;
if (IS_LEAF(D_RO(*child))) {
TOID(art_leaf)l = D_RO(*child)->u.al;
if (!leaf_matches(l, key, key_len, depth)) {
remove_child(pop, n, ref, key[depth], child);
return l;
}
return null_art_leaf;
} else {
return recursive_delete(pop, *child, child,
(const unsigned char *)key, key_len, depth + 1);
}
}
TOID(var_string)
art_delete(PMEMobjpool *pop,
const unsigned char *key, int key_len)
{
TOID(struct art_tree_root)root = POBJ_ROOT(pop, struct art_tree_root);
TOID(art_leaf) l;
TOID(var_string) retval;
retval = null_var_string;
TX_BEGIN(pop) {
TX_ADD(root);
l = recursive_delete(pop, D_RO(root)->root,
&D_RW(root)->root, key, key_len, 0);
if (!TOID_IS_NULL(l)) {
D_RW(root)->size--;
TOID(var_string)old = D_RO(l)->value;
TX_FREE(l);
retval = old;
}
} TX_ONABORT {
abort();
} TX_END
return retval;
}
static int
recursive_iter(TOID(art_node_u)n, art_callback cb, void *data)
{
const art_node *n_an;
TOID(art_node4) an4;
TOID(art_node16) an16;
TOID(art_node48) an48;
TOID(art_node256) an256;
TOID(art_leaf) l;
TOID(var_string) key;
TOID(var_string) value;
cb_data cbd;
if (TOID_IS_NULL(n)) {
return 0;
}
cbd.node = n;
cbd.child_idx = -1;
if (IS_LEAF(D_RO(n))) {
l = D_RO(n)->u.al;
key = D_RO(l)->key;
value = D_RO(l)->value;
return cb(&cbd, D_RO(key)->s, D_RO(key)->len,
D_RO(value)->s, D_RO(value)->len);
}
int idx, res;
switch (D_RO(n)->art_node_type) {
case NODE4:
an4 = D_RO(n)->u.an4;
n_an = &(D_RO(an4)->n);
for (int i = 0; i < n_an->num_children; i++) {
cbd.child_idx = i;
cb(&cbd, NULL, 0, NULL, 0);
res = recursive_iter(D_RO(an4)->children[i], cb, data);
if (res)
return res;
}
break;
case NODE16:
an16 = D_RO(n)->u.an16;
n_an = &(D_RO(an16)->n);
for (int i = 0; i < n_an->num_children; i++) {
cbd.child_idx = i;
cb(&cbd, NULL, 0, NULL, 0);
res = recursive_iter(D_RO(an16)->children[i], cb, data);
if (res)
return res;
}
break;
case NODE48:
an48 = D_RO(n)->u.an48;
for (int i = 0; i < 256; i++) {
idx = D_RO(an48)->keys[i];
if (!idx)
continue;
cbd.child_idx = idx - 1;
cb(&cbd, NULL, 0, NULL, 0);
res = recursive_iter(D_RO(an48)->children[idx - 1],
cb, data);
if (res)
return res;
}
break;
case NODE256:
an256 = D_RO(n)->u.an256;
for (int i = 0; i < 256; i++) {
if (TOID_IS_NULL(D_RO(an256)->children[i]))
continue;
cbd.child_idx = i;
cb(&cbd, NULL, 0, NULL, 0);
res = recursive_iter(D_RO(an256)->children[i],
cb, data);
if (res)
return res;
}
break;
default:
abort();
}
return 0;
}
int
art_iter(PMEMobjpool *pop, art_callback cb, void *data)
{
TOID(struct art_tree_root) t = POBJ_ROOT(pop, struct art_tree_root);
return recursive_iter(D_RO(t)->root, cb, data);
}
#ifdef LIBART_ITER_PREFIX
static int
leaf_prefix_matches(TOID(art_leaf) n,
const unsigned char *prefix, int prefix_len)
{
if (D_RO(D_RO(n)->key)->len < (uint32_t)prefix_len)
return 1;
return memcmp(D_RO(D_RO(n)->key)->s, prefix, prefix_len);
}
int
art_iter_prefix(art_tree *t,
const unsigned char *key, int key_len, art_callback cb, void *data)
{
art_node **child;
art_node *n = t->root;
int prefix_len, depth = 0;
while (n) {
if (IS_LEAF(n)) {
n = LEAF_RAW(n);
if (!leaf_prefix_matches((art_leaf *)n, key, key_len)) {
art_leaf *l = (art_leaf *)n;
return cb(data,
(const unsigned char *)l->key,
l->key_len, l->value);
}
return 0;
}
if (depth == key_len) {
art_leaf *l = minimum(n);
if (!leaf_prefix_matches(l, key, key_len))
return recursive_iter(n, cb, data);
return 0;
}
if (n->partial_len) {
prefix_len = prefix_mismatch(n, key, key_len, depth);
if (!prefix_len)
return 0;
else if (depth + prefix_len == key_len) {
return recursive_iter(n, cb, data);
}
depth = depth + n->partial_len;
}
child = find_child(n, key[depth]);
n = (child) ? *child : NULL;
depth++;
}
return 0;
}
#endif
int
fill_leaf(PMEMobjpool *pop, TOID(art_leaf) al,
const unsigned char *key, int key_len, void *value, int val_len)
{
int retval = 0;
size_t l_key;
size_t l_val;
TOID(var_string) Tkey;
TOID(var_string) Tval;
l_key = (sizeof(var_string) + key_len);
l_val = (sizeof(var_string) + val_len);
Tkey = TX_ALLOC(var_string, l_key);
Tval = TX_ALLOC(var_string, l_val);
COPY_BLOB(Tkey, key, key_len);
COPY_BLOB(Tval, value, val_len);
D_RW(al)->key = Tkey;
D_RW(al)->value = Tval;
return retval;
}