You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2643 lines
71 KiB
2643 lines
71 KiB
From 5bc9b6e08b2555f2f1a9fe65db266dc97f4a50ba Mon Sep 17 00:00:00 2001
|
|
From: David Robillard <d@drobilla.net>
|
|
Date: Tue, 12 Jan 2021 15:18:49 +0100
|
|
Subject: [PATCH] Update zix
|
|
|
|
This fixes, once again, a potential BTree crash with GCC 10 which got lost
|
|
somewhere along the way.
|
|
---
|
|
NEWS | 6 +
|
|
src/sord_config.h | 2 +-
|
|
src/zix/btree.c | 1336 +++++++++++++++++++++++----------------------
|
|
src/zix/btree.h | 55 +-
|
|
src/zix/common.h | 109 ++--
|
|
src/zix/digest.c | 120 ++--
|
|
src/zix/digest.h | 16 +-
|
|
src/zix/hash.c | 289 +++++-----
|
|
src/zix/hash.h | 48 +-
|
|
wscript | 2 +-
|
|
10 files changed, 1004 insertions(+), 979 deletions(-)
|
|
|
|
diff --git a/NEWS b/NEWS
|
|
index b09bba5..325be60 100644
|
|
--- a/NEWS
|
|
+++ b/NEWS
|
|
@@ -1,3 +1,9 @@
|
|
+sord (0.16.9) unstable;
|
|
+
|
|
+ * Fix potential crash or incorrectness issue with GCC 10 again
|
|
+
|
|
+ -- David Robillard <d@drobilla.net> Tue, 12 Jan 2021 14:17:39 +0000
|
|
+
|
|
sord (0.16.8) stable;
|
|
|
|
* Clean up code
|
|
diff --git a/src/zix/btree.c b/src/zix/btree.c
|
|
index 3279f6c..7926a56 100644
|
|
--- a/src/zix/btree.c
|
|
+++ b/src/zix/btree.c
|
|
@@ -25,121 +25,119 @@
|
|
// #define ZIX_BTREE_SORTED_CHECK 1
|
|
|
|
#ifndef ZIX_BTREE_PAGE_SIZE
|
|
-# define ZIX_BTREE_PAGE_SIZE 4096
|
|
+# define ZIX_BTREE_PAGE_SIZE 4096
|
|
#endif
|
|
|
|
#define ZIX_BTREE_NODE_SPACE (ZIX_BTREE_PAGE_SIZE - 2 * sizeof(uint16_t))
|
|
-#define ZIX_BTREE_LEAF_VALS ((ZIX_BTREE_NODE_SPACE / sizeof(void*)) - 1)
|
|
+#define ZIX_BTREE_LEAF_VALS ((ZIX_BTREE_NODE_SPACE / sizeof(void*)) - 1)
|
|
#define ZIX_BTREE_INODE_VALS (ZIX_BTREE_LEAF_VALS / 2)
|
|
|
|
struct ZixBTreeImpl {
|
|
- ZixBTreeNode* root;
|
|
- ZixDestroyFunc destroy;
|
|
- ZixComparator cmp;
|
|
- const void* cmp_data;
|
|
- size_t size;
|
|
- unsigned height; ///< Number of levels, i.e. root only has height 1
|
|
+ ZixBTreeNode* root;
|
|
+ ZixDestroyFunc destroy;
|
|
+ ZixComparator cmp;
|
|
+ const void* cmp_data;
|
|
+ size_t size;
|
|
+ unsigned height; ///< Number of levels, i.e. root only has height 1
|
|
};
|
|
|
|
struct ZixBTreeNodeImpl {
|
|
- uint16_t is_leaf;
|
|
- uint16_t n_vals;
|
|
- // On 64-bit we rely on some padding here to get page-sized nodes
|
|
- union {
|
|
- struct {
|
|
- void* vals[ZIX_BTREE_LEAF_VALS];
|
|
- } leaf;
|
|
-
|
|
- struct {
|
|
- void* vals[ZIX_BTREE_INODE_VALS];
|
|
- ZixBTreeNode* children[ZIX_BTREE_INODE_VALS + 1];
|
|
- } inode;
|
|
- } data;
|
|
+ uint16_t is_leaf;
|
|
+ uint16_t n_vals;
|
|
+ // On 64-bit we rely on some padding here to get page-sized nodes
|
|
+ union {
|
|
+ struct {
|
|
+ void* vals[ZIX_BTREE_LEAF_VALS];
|
|
+ } leaf;
|
|
+
|
|
+ struct {
|
|
+ void* vals[ZIX_BTREE_INODE_VALS];
|
|
+ ZixBTreeNode* children[ZIX_BTREE_INODE_VALS + 1];
|
|
+ } inode;
|
|
+ } data;
|
|
};
|
|
|
|
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112l) || \
|
|
- (defined(__cplusplus) && __cplusplus >= 201103L)
|
|
+ (defined(__cplusplus) && __cplusplus >= 201103L)
|
|
static_assert(sizeof(ZixBTreeNode) == ZIX_BTREE_PAGE_SIZE, "");
|
|
#endif
|
|
|
|
typedef struct {
|
|
- ZixBTreeNode* node;
|
|
- unsigned index;
|
|
+ ZixBTreeNode* node;
|
|
+ unsigned index;
|
|
} ZixBTreeIterFrame;
|
|
|
|
struct ZixBTreeIterImpl {
|
|
- unsigned n_levels; ///< Maximum depth of stack
|
|
- unsigned level; ///< Current level in stack
|
|
- ZixBTreeIterFrame stack[]; ///< Position stack
|
|
+ unsigned n_levels; ///< Maximum depth of stack
|
|
+ unsigned level; ///< Current level in stack
|
|
+ ZixBTreeIterFrame stack[]; ///< Position stack
|
|
};
|
|
|
|
#ifdef ZIX_BTREE_DEBUG
|
|
|
|
-ZIX_PRIVATE void
|
|
+static void
|
|
print_node(const ZixBTreeNode* n, const char* prefix)
|
|
{
|
|
- printf("%s[", prefix);
|
|
- for (uint16_t v = 0; v < n->n_vals; ++v) {
|
|
- printf(" %lu", (uintptr_t)n->vals[v]);
|
|
- }
|
|
- printf(" ]\n");
|
|
+ printf("%s[", prefix);
|
|
+ for (uint16_t v = 0; v < n->n_vals; ++v) {
|
|
+ printf(" %lu", (uintptr_t)n->vals[v]);
|
|
+ }
|
|
+ printf(" ]\n");
|
|
}
|
|
|
|
-ZIX_PRIVATE void
|
|
+static void
|
|
print_tree(const ZixBTreeNode* parent, const ZixBTreeNode* node, int level)
|
|
{
|
|
- if (node) {
|
|
- if (!parent) {
|
|
- printf("TREE {\n");
|
|
- }
|
|
- for (int i = 0; i < level + 1; ++i) {
|
|
- printf(" ");
|
|
- }
|
|
- print_node(node, "");
|
|
- if (!node->is_leaf) {
|
|
- for (uint16_t i = 0; i < node->n_vals + 1; ++i) {
|
|
- print_tree(node, node->data.inode.children[i], level + 1);
|
|
- }
|
|
- }
|
|
- if (!parent) {
|
|
- printf("}\n");
|
|
- }
|
|
- }
|
|
-}
|
|
-
|
|
-#endif // ZIX_BTREE_DEBUG
|
|
-
|
|
-ZIX_PRIVATE ZixBTreeNode*
|
|
+ if (node) {
|
|
+ if (!parent) {
|
|
+ printf("TREE {\n");
|
|
+ }
|
|
+ for (int i = 0; i < level + 1; ++i) {
|
|
+ printf(" ");
|
|
+ }
|
|
+ print_node(node, "");
|
|
+ if (!node->is_leaf) {
|
|
+ for (uint16_t i = 0; i < node->n_vals + 1; ++i) {
|
|
+ print_tree(node, node->data.inode.children[i], level + 1);
|
|
+ }
|
|
+ }
|
|
+ if (!parent) {
|
|
+ printf("}\n");
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+#endif // ZIX_BTREE_DEBUG
|
|
+
|
|
+static ZixBTreeNode*
|
|
zix_btree_node_new(const bool leaf)
|
|
{
|
|
#if !((defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112l) || \
|
|
(defined(__cplusplus) && __cplusplus >= 201103L))
|
|
- assert(sizeof(ZixBTreeNode) == ZIX_BTREE_PAGE_SIZE);
|
|
+ assert(sizeof(ZixBTreeNode) == ZIX_BTREE_PAGE_SIZE);
|
|
#endif
|
|
|
|
- ZixBTreeNode* node = (ZixBTreeNode*)malloc(sizeof(ZixBTreeNode));
|
|
- if (node) {
|
|
- node->is_leaf = leaf;
|
|
- node->n_vals = 0;
|
|
- }
|
|
- return node;
|
|
+ ZixBTreeNode* node = (ZixBTreeNode*)malloc(sizeof(ZixBTreeNode));
|
|
+ if (node) {
|
|
+ node->is_leaf = leaf;
|
|
+ node->n_vals = 0;
|
|
+ }
|
|
+ return node;
|
|
}
|
|
|
|
-ZIX_PRIVATE
|
|
-void*
|
|
+static void*
|
|
zix_btree_value(const ZixBTreeNode* const node, const unsigned i)
|
|
{
|
|
- assert(i < node->n_vals);
|
|
- return node->is_leaf ? node->data.leaf.vals[i] : node->data.inode.vals[i];
|
|
+ assert(i < node->n_vals);
|
|
+ return node->is_leaf ? node->data.leaf.vals[i] : node->data.inode.vals[i];
|
|
}
|
|
|
|
-ZIX_PRIVATE
|
|
-ZixBTreeNode*
|
|
+static ZixBTreeNode*
|
|
zix_btree_child(const ZixBTreeNode* const node, const unsigned i)
|
|
{
|
|
- assert(!node->is_leaf);
|
|
- assert(i <= ZIX_BTREE_INODE_VALS);
|
|
- return node->data.inode.children[i];
|
|
+ assert(!node->is_leaf);
|
|
+ assert(i <= ZIX_BTREE_INODE_VALS);
|
|
+ return node->data.inode.children[i];
|
|
}
|
|
|
|
ZixBTree*
|
|
@@ -147,455 +145,452 @@ zix_btree_new(const ZixComparator cmp,
|
|
const void* const cmp_data,
|
|
const ZixDestroyFunc destroy)
|
|
{
|
|
- ZixBTree* t = (ZixBTree*)malloc(sizeof(ZixBTree));
|
|
- if (t) {
|
|
- t->root = zix_btree_node_new(true);
|
|
- t->destroy = destroy;
|
|
- t->cmp = cmp;
|
|
- t->cmp_data = cmp_data;
|
|
- t->size = 0;
|
|
- t->height = 1;
|
|
- if (!t->root) {
|
|
- free(t);
|
|
- return NULL;
|
|
- }
|
|
- }
|
|
- return t;
|
|
-}
|
|
-
|
|
-ZIX_PRIVATE void
|
|
+ ZixBTree* t = (ZixBTree*)malloc(sizeof(ZixBTree));
|
|
+ if (t) {
|
|
+ t->root = zix_btree_node_new(true);
|
|
+ t->destroy = destroy;
|
|
+ t->cmp = cmp;
|
|
+ t->cmp_data = cmp_data;
|
|
+ t->size = 0;
|
|
+ t->height = 1;
|
|
+ if (!t->root) {
|
|
+ free(t);
|
|
+ return NULL;
|
|
+ }
|
|
+ }
|
|
+ return t;
|
|
+}
|
|
+
|
|
+static void
|
|
zix_btree_free_rec(ZixBTree* const t, ZixBTreeNode* const n)
|
|
{
|
|
- if (n) {
|
|
- if (n->is_leaf) {
|
|
- if (t->destroy) {
|
|
- for (uint16_t i = 0; i < n->n_vals; ++i) {
|
|
- t->destroy(n->data.leaf.vals[i]);
|
|
- }
|
|
- }
|
|
- } else {
|
|
- if (t->destroy) {
|
|
- for (uint16_t i = 0; i < n->n_vals; ++i) {
|
|
- t->destroy(n->data.inode.vals[i]);
|
|
- }
|
|
- }
|
|
-
|
|
- for (uint16_t i = 0; i < n->n_vals + 1; ++i) {
|
|
- zix_btree_free_rec(t, zix_btree_child(n, i));
|
|
- }
|
|
- }
|
|
- free(n);
|
|
- }
|
|
+ if (n) {
|
|
+ if (n->is_leaf) {
|
|
+ if (t->destroy) {
|
|
+ for (uint16_t i = 0; i < n->n_vals; ++i) {
|
|
+ t->destroy(n->data.leaf.vals[i]);
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ if (t->destroy) {
|
|
+ for (uint16_t i = 0; i < n->n_vals; ++i) {
|
|
+ t->destroy(n->data.inode.vals[i]);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ for (uint16_t i = 0; i < n->n_vals + 1; ++i) {
|
|
+ zix_btree_free_rec(t, zix_btree_child(n, i));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ free(n);
|
|
+ }
|
|
}
|
|
|
|
void
|
|
zix_btree_free(ZixBTree* const t)
|
|
{
|
|
- if (t) {
|
|
- zix_btree_free_rec(t, t->root);
|
|
- free(t);
|
|
- }
|
|
+ if (t) {
|
|
+ zix_btree_free_rec(t, t->root);
|
|
+ free(t);
|
|
+ }
|
|
}
|
|
|
|
size_t
|
|
zix_btree_size(const ZixBTree* const t)
|
|
{
|
|
- return t->size;
|
|
+ return t->size;
|
|
}
|
|
|
|
-ZIX_PRIVATE uint16_t
|
|
+static uint16_t
|
|
zix_btree_max_vals(const ZixBTreeNode* const node)
|
|
{
|
|
- return node->is_leaf ? ZIX_BTREE_LEAF_VALS : ZIX_BTREE_INODE_VALS;
|
|
+ return node->is_leaf ? ZIX_BTREE_LEAF_VALS : ZIX_BTREE_INODE_VALS;
|
|
}
|
|
|
|
-ZIX_PRIVATE uint16_t
|
|
+static uint16_t
|
|
zix_btree_min_vals(const ZixBTreeNode* const node)
|
|
{
|
|
- return (uint16_t)(((zix_btree_max_vals(node) + 1U) / 2U) - 1U);
|
|
+ return (uint16_t)(((zix_btree_max_vals(node) + 1U) / 2U) - 1U);
|
|
}
|
|
|
|
/** Shift pointers in `array` of length `n` right starting at `i`. */
|
|
-ZIX_PRIVATE void
|
|
+static void
|
|
zix_btree_ainsert(void** const array,
|
|
const unsigned n,
|
|
const unsigned i,
|
|
void* const e)
|
|
{
|
|
- memmove(array + i + 1, array + i, (n - i) * sizeof(e));
|
|
- array[i] = e;
|
|
+ memmove(array + i + 1, array + i, (n - i) * sizeof(e));
|
|
+ array[i] = e;
|
|
}
|
|
|
|
/** Erase element `i` in `array` of length `n` and return erased element. */
|
|
-ZIX_PRIVATE void*
|
|
+static void*
|
|
zix_btree_aerase(void** const array, const unsigned n, const unsigned i)
|
|
{
|
|
- void* const ret = array[i];
|
|
- memmove(array + i, array + i + 1, (n - i) * sizeof(ret));
|
|
- return ret;
|
|
+ void* const ret = array[i];
|
|
+ memmove(array + i, array + i + 1, (n - i) * sizeof(ret));
|
|
+ return ret;
|
|
}
|
|
|
|
/** Split lhs, the i'th child of `n`, into two nodes. */
|
|
-ZIX_PRIVATE ZixBTreeNode*
|
|
+static ZixBTreeNode*
|
|
zix_btree_split_child(ZixBTreeNode* const n,
|
|
const unsigned i,
|
|
ZixBTreeNode* const lhs)
|
|
{
|
|
- assert(lhs->n_vals == zix_btree_max_vals(lhs));
|
|
- assert(n->n_vals < ZIX_BTREE_INODE_VALS);
|
|
- assert(i < n->n_vals + 1U);
|
|
- assert(zix_btree_child(n, i) == lhs);
|
|
-
|
|
- const uint16_t max_n_vals = zix_btree_max_vals(lhs);
|
|
- ZixBTreeNode* rhs = zix_btree_node_new(lhs->is_leaf);
|
|
- if (!rhs) {
|
|
- return NULL;
|
|
- }
|
|
-
|
|
- // LHS and RHS get roughly half, less the middle value which moves up
|
|
- lhs->n_vals = max_n_vals / 2U;
|
|
- rhs->n_vals = (uint16_t)(max_n_vals - lhs->n_vals - 1);
|
|
-
|
|
- if (lhs->is_leaf) {
|
|
- // Copy large half from LHS to new RHS node
|
|
- memcpy(rhs->data.leaf.vals,
|
|
- lhs->data.leaf.vals + lhs->n_vals + 1,
|
|
- rhs->n_vals * sizeof(void*));
|
|
-
|
|
- // Move middle value up to parent
|
|
- zix_btree_ainsert(n->data.inode.vals,
|
|
- n->n_vals,
|
|
- i,
|
|
- lhs->data.leaf.vals[lhs->n_vals]);
|
|
- } else {
|
|
- // Copy large half from LHS to new RHS node
|
|
- memcpy(rhs->data.inode.vals,
|
|
- lhs->data.inode.vals + lhs->n_vals + 1,
|
|
- rhs->n_vals * sizeof(void*));
|
|
- memcpy(rhs->data.inode.children,
|
|
- lhs->data.inode.children + lhs->n_vals + 1,
|
|
- (rhs->n_vals + 1U) * sizeof(ZixBTreeNode*));
|
|
-
|
|
- // Move middle value up to parent
|
|
- zix_btree_ainsert(n->data.inode.vals,
|
|
- n->n_vals,
|
|
- i,
|
|
- lhs->data.inode.vals[lhs->n_vals]);
|
|
- }
|
|
-
|
|
- // Insert new RHS node in parent at position i
|
|
- zix_btree_ainsert((void**)n->data.inode.children, ++n->n_vals, i + 1U, rhs);
|
|
-
|
|
- return rhs;
|
|
+ assert(lhs->n_vals == zix_btree_max_vals(lhs));
|
|
+ assert(n->n_vals < ZIX_BTREE_INODE_VALS);
|
|
+ assert(i < n->n_vals + 1U);
|
|
+ assert(zix_btree_child(n, i) == lhs);
|
|
+
|
|
+ const uint16_t max_n_vals = zix_btree_max_vals(lhs);
|
|
+ ZixBTreeNode* rhs = zix_btree_node_new(lhs->is_leaf);
|
|
+ if (!rhs) {
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ // LHS and RHS get roughly half, less the middle value which moves up
|
|
+ lhs->n_vals = max_n_vals / 2U;
|
|
+ rhs->n_vals = (uint16_t)(max_n_vals - lhs->n_vals - 1);
|
|
+
|
|
+ if (lhs->is_leaf) {
|
|
+ // Copy large half from LHS to new RHS node
|
|
+ memcpy(rhs->data.leaf.vals,
|
|
+ lhs->data.leaf.vals + lhs->n_vals + 1,
|
|
+ rhs->n_vals * sizeof(void*));
|
|
+
|
|
+ // Move middle value up to parent
|
|
+ zix_btree_ainsert(
|
|
+ n->data.inode.vals, n->n_vals, i, lhs->data.leaf.vals[lhs->n_vals]);
|
|
+ } else {
|
|
+ // Copy large half from LHS to new RHS node
|
|
+ memcpy(rhs->data.inode.vals,
|
|
+ lhs->data.inode.vals + lhs->n_vals + 1,
|
|
+ rhs->n_vals * sizeof(void*));
|
|
+ memcpy(rhs->data.inode.children,
|
|
+ lhs->data.inode.children + lhs->n_vals + 1,
|
|
+ (rhs->n_vals + 1U) * sizeof(ZixBTreeNode*));
|
|
+
|
|
+ // Move middle value up to parent
|
|
+ zix_btree_ainsert(
|
|
+ n->data.inode.vals, n->n_vals, i, lhs->data.inode.vals[lhs->n_vals]);
|
|
+ }
|
|
+
|
|
+ // Insert new RHS node in parent at position i
|
|
+ zix_btree_ainsert((void**)n->data.inode.children, ++n->n_vals, i + 1U, rhs);
|
|
+
|
|
+ return rhs;
|
|
}
|
|
|
|
#ifdef ZIX_BTREE_SORTED_CHECK
|
|
/** Check that `n` is sorted with respect to search key `e`. */
|
|
-ZIX_PRIVATE bool
|
|
+static bool
|
|
zix_btree_node_is_sorted_with_respect_to(const ZixBTree* const t,
|
|
const ZixBTreeNode* const n,
|
|
const void* const e)
|
|
{
|
|
- if (n->n_vals <= 1) {
|
|
- return true;
|
|
- }
|
|
+ if (n->n_vals <= 1) {
|
|
+ return true;
|
|
+ }
|
|
|
|
- int cmp = t->cmp(zix_btree_value(n, 0), e, t->cmp_data);
|
|
- for (uint16_t i = 1; i < n->n_vals; ++i) {
|
|
- const int next_cmp = t->cmp(zix_btree_value(n, i), e, t->cmp_data);
|
|
- if ((cmp >= 0 && next_cmp < 0) ||
|
|
- (cmp > 0 && next_cmp <= 0)) {
|
|
- return false;
|
|
- }
|
|
- cmp = next_cmp;
|
|
- }
|
|
+ int cmp = t->cmp(zix_btree_value(n, 0), e, t->cmp_data);
|
|
+ for (uint16_t i = 1; i < n->n_vals; ++i) {
|
|
+ const int next_cmp = t->cmp(zix_btree_value(n, i), e, t->cmp_data);
|
|
+ if ((cmp >= 0 && next_cmp < 0) || (cmp > 0 && next_cmp <= 0)) {
|
|
+ return false;
|
|
+ }
|
|
+ cmp = next_cmp;
|
|
+ }
|
|
|
|
- return true;
|
|
+ return true;
|
|
}
|
|
#endif
|
|
|
|
/** Find the first value in `n` that is not less than `e` (lower bound). */
|
|
-ZIX_PRIVATE unsigned
|
|
+static unsigned
|
|
zix_btree_node_find(const ZixBTree* const t,
|
|
const ZixBTreeNode* const n,
|
|
const void* const e,
|
|
bool* const equal)
|
|
{
|
|
#ifdef ZIX_BTREE_SORTED_CHECK
|
|
- assert(zix_btree_node_is_sorted_with_respect_to(t, n, e));
|
|
+ assert(zix_btree_node_is_sorted_with_respect_to(t, n, e));
|
|
#endif
|
|
|
|
- unsigned first = 0U;
|
|
- unsigned len = n->n_vals;
|
|
- while (len > 0) {
|
|
- const unsigned half = len >> 1U;
|
|
- const unsigned i = first + half;
|
|
- const int cmp = t->cmp(zix_btree_value(n, i), e, t->cmp_data);
|
|
- if (cmp == 0) {
|
|
- *equal = true;
|
|
- len = half; // Keep searching for wildcard matches
|
|
- } else if (cmp < 0) {
|
|
- const unsigned chop = half + 1U;
|
|
- first += chop;
|
|
- len -= chop;
|
|
- } else {
|
|
- len = half;
|
|
- }
|
|
- }
|
|
- assert(!*equal || t->cmp(zix_btree_value(n, first), e, t->cmp_data) == 0);
|
|
- return first;
|
|
+ unsigned first = 0U;
|
|
+ unsigned len = n->n_vals;
|
|
+ while (len > 0) {
|
|
+ const unsigned half = len >> 1U;
|
|
+ const unsigned i = first + half;
|
|
+ const int cmp = t->cmp(zix_btree_value(n, i), e, t->cmp_data);
|
|
+ if (cmp == 0) {
|
|
+ *equal = true;
|
|
+ len = half; // Keep searching for wildcard matches
|
|
+ } else if (cmp < 0) {
|
|
+ const unsigned chop = half + 1U;
|
|
+ first += chop;
|
|
+ len -= chop;
|
|
+ } else {
|
|
+ len = half;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ assert(!*equal || t->cmp(zix_btree_value(n, first), e, t->cmp_data) == 0);
|
|
+ return first;
|
|
}
|
|
|
|
ZixStatus
|
|
zix_btree_insert(ZixBTree* const t, void* const e)
|
|
{
|
|
- ZixBTreeNode* parent = NULL; // Parent of n
|
|
- ZixBTreeNode* n = t->root; // Current node
|
|
- unsigned i = 0; // Index of n in parent
|
|
- while (n) {
|
|
- if (n->n_vals == zix_btree_max_vals(n)) {
|
|
- // Node is full, split to ensure there is space for a leaf split
|
|
- if (!parent) {
|
|
- // Root is full, grow tree upwards
|
|
- if (!(parent = zix_btree_node_new(false))) {
|
|
- return ZIX_STATUS_NO_MEM;
|
|
- }
|
|
- t->root = parent;
|
|
- parent->data.inode.children[0] = n;
|
|
- ++t->height;
|
|
- }
|
|
-
|
|
- ZixBTreeNode* const rhs = zix_btree_split_child(parent, i, n);
|
|
- if (!rhs) {
|
|
- return ZIX_STATUS_NO_MEM;
|
|
- }
|
|
-
|
|
- const int cmp = t->cmp(parent->data.inode.vals[i], e, t->cmp_data);
|
|
- if (cmp == 0) {
|
|
- return ZIX_STATUS_EXISTS;
|
|
- } else if (cmp < 0) {
|
|
- // Move to new RHS
|
|
- n = rhs;
|
|
- ++i;
|
|
- }
|
|
- }
|
|
-
|
|
- assert(!parent || zix_btree_child(parent, i) == n);
|
|
-
|
|
- bool equal = false;
|
|
- i = zix_btree_node_find(t, n, e, &equal);
|
|
- if (equal) {
|
|
- return ZIX_STATUS_EXISTS;
|
|
- } else if (!n->is_leaf) {
|
|
- // Descend to child node left of value
|
|
- parent = n;
|
|
- n = zix_btree_child(n, i);
|
|
- } else {
|
|
- // Insert into internal node
|
|
- zix_btree_ainsert(n->data.leaf.vals, n->n_vals++, i, e);
|
|
- break;
|
|
- }
|
|
- }
|
|
-
|
|
- ++t->size;
|
|
-
|
|
- return ZIX_STATUS_SUCCESS;
|
|
-}
|
|
-
|
|
-ZIX_PRIVATE ZixBTreeIter*
|
|
+ ZixBTreeNode* parent = NULL; // Parent of n
|
|
+ ZixBTreeNode* n = t->root; // Current node
|
|
+ unsigned i = 0; // Index of n in parent
|
|
+ while (n) {
|
|
+ if (n->n_vals == zix_btree_max_vals(n)) {
|
|
+ // Node is full, split to ensure there is space for a leaf split
|
|
+ if (!parent) {
|
|
+ // Root is full, grow tree upwards
|
|
+ if (!(parent = zix_btree_node_new(false))) {
|
|
+ return ZIX_STATUS_NO_MEM;
|
|
+ }
|
|
+ t->root = parent;
|
|
+ parent->data.inode.children[0] = n;
|
|
+ ++t->height;
|
|
+ }
|
|
+
|
|
+ ZixBTreeNode* const rhs = zix_btree_split_child(parent, i, n);
|
|
+ if (!rhs) {
|
|
+ return ZIX_STATUS_NO_MEM;
|
|
+ }
|
|
+
|
|
+ const int cmp = t->cmp(parent->data.inode.vals[i], e, t->cmp_data);
|
|
+ if (cmp == 0) {
|
|
+ return ZIX_STATUS_EXISTS;
|
|
+ }
|
|
+
|
|
+ if (cmp < 0) {
|
|
+ // Move to new RHS
|
|
+ n = rhs;
|
|
+ ++i;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ assert(!parent || zix_btree_child(parent, i) == n);
|
|
+
|
|
+ bool equal = false;
|
|
+ i = zix_btree_node_find(t, n, e, &equal);
|
|
+ if (equal) {
|
|
+ return ZIX_STATUS_EXISTS;
|
|
+ }
|
|
+
|
|
+ if (!n->is_leaf) {
|
|
+ // Descend to child node left of value
|
|
+ parent = n;
|
|
+ n = zix_btree_child(n, i);
|
|
+ } else {
|
|
+ // Insert into internal node
|
|
+ zix_btree_ainsert(n->data.leaf.vals, n->n_vals++, i, e);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ ++t->size;
|
|
+
|
|
+ return ZIX_STATUS_SUCCESS;
|
|
+}
|
|
+
|
|
+static ZixBTreeIter*
|
|
zix_btree_iter_new(const ZixBTree* const t)
|
|
{
|
|
- const size_t s = t->height * sizeof(ZixBTreeIterFrame);
|
|
+ const size_t s = t->height * sizeof(ZixBTreeIterFrame);
|
|
|
|
- ZixBTreeIter* i = (ZixBTreeIter*)calloc(1, sizeof(ZixBTreeIter) + s);
|
|
- if (i) {
|
|
- i->n_levels = t->height;
|
|
- }
|
|
- return i;
|
|
+ ZixBTreeIter* i = (ZixBTreeIter*)calloc(1, sizeof(ZixBTreeIter) + s);
|
|
+ if (i) {
|
|
+ i->n_levels = t->height;
|
|
+ }
|
|
+ return i;
|
|
}
|
|
|
|
-ZIX_PRIVATE void
|
|
+static void
|
|
zix_btree_iter_set_frame(ZixBTreeIter* const ti,
|
|
ZixBTreeNode* const n,
|
|
const unsigned i)
|
|
{
|
|
- if (ti) {
|
|
- ti->stack[ti->level].node = n;
|
|
- ti->stack[ti->level].index = i;
|
|
- }
|
|
+ if (ti) {
|
|
+ ti->stack[ti->level].node = n;
|
|
+ ti->stack[ti->level].index = i;
|
|
+ }
|
|
}
|
|
|
|
-ZIX_PRIVATE bool
|
|
+static bool
|
|
zix_btree_node_is_minimal(ZixBTreeNode* const n)
|
|
{
|
|
- assert(n->n_vals >= zix_btree_min_vals(n));
|
|
- return n->n_vals == zix_btree_min_vals(n);
|
|
+ assert(n->n_vals >= zix_btree_min_vals(n));
|
|
+ return n->n_vals == zix_btree_min_vals(n);
|
|
}
|
|
|
|
/** Enlarge left child by stealing a value from its right sibling. */
|
|
-ZIX_PRIVATE ZixBTreeNode*
|
|
+static ZixBTreeNode*
|
|
zix_btree_rotate_left(ZixBTreeNode* const parent, const unsigned i)
|
|
{
|
|
- ZixBTreeNode* const lhs = zix_btree_child(parent, i);
|
|
- ZixBTreeNode* const rhs = zix_btree_child(parent, i + 1);
|
|
+ ZixBTreeNode* const lhs = zix_btree_child(parent, i);
|
|
+ ZixBTreeNode* const rhs = zix_btree_child(parent, i + 1);
|
|
|
|
- assert(lhs->is_leaf == rhs->is_leaf);
|
|
+ assert(lhs->is_leaf == rhs->is_leaf);
|
|
|
|
- if (lhs->is_leaf) {
|
|
- // Move parent value to end of LHS
|
|
- lhs->data.leaf.vals[lhs->n_vals++] = parent->data.inode.vals[i];
|
|
+ if (lhs->is_leaf) {
|
|
+ // Move parent value to end of LHS
|
|
+ lhs->data.leaf.vals[lhs->n_vals++] = parent->data.inode.vals[i];
|
|
|
|
- // Move first value in RHS to parent
|
|
- parent->data.inode.vals[i] =
|
|
- zix_btree_aerase(rhs->data.leaf.vals, rhs->n_vals, 0);
|
|
- } else {
|
|
- // Move parent value to end of LHS
|
|
- lhs->data.inode.vals[lhs->n_vals++] = parent->data.inode.vals[i];
|
|
+ // Move first value in RHS to parent
|
|
+ parent->data.inode.vals[i] =
|
|
+ zix_btree_aerase(rhs->data.leaf.vals, rhs->n_vals, 0);
|
|
+ } else {
|
|
+ // Move parent value to end of LHS
|
|
+ lhs->data.inode.vals[lhs->n_vals++] = parent->data.inode.vals[i];
|
|
|
|
- // Move first value in RHS to parent
|
|
- parent->data.inode.vals[i] =
|
|
- zix_btree_aerase(rhs->data.inode.vals, rhs->n_vals, 0);
|
|
+ // Move first value in RHS to parent
|
|
+ parent->data.inode.vals[i] =
|
|
+ zix_btree_aerase(rhs->data.inode.vals, rhs->n_vals, 0);
|
|
|
|
- // Move first child pointer from RHS to end of LHS
|
|
- lhs->data.inode.children[lhs->n_vals] = (ZixBTreeNode*)zix_btree_aerase(
|
|
- (void**)rhs->data.inode.children, rhs->n_vals, 0);
|
|
- }
|
|
+ // Move first child pointer from RHS to end of LHS
|
|
+ lhs->data.inode.children[lhs->n_vals] = (ZixBTreeNode*)zix_btree_aerase(
|
|
+ (void**)rhs->data.inode.children, rhs->n_vals, 0);
|
|
+ }
|
|
|
|
- --rhs->n_vals;
|
|
+ --rhs->n_vals;
|
|
|
|
- return lhs;
|
|
+ return lhs;
|
|
}
|
|
|
|
/** Enlarge right child by stealing a value from its left sibling. */
|
|
-ZIX_PRIVATE ZixBTreeNode*
|
|
+static ZixBTreeNode*
|
|
zix_btree_rotate_right(ZixBTreeNode* const parent, const unsigned i)
|
|
{
|
|
- ZixBTreeNode* const lhs = zix_btree_child(parent, i - 1);
|
|
- ZixBTreeNode* const rhs = zix_btree_child(parent, i);
|
|
+ ZixBTreeNode* const lhs = zix_btree_child(parent, i - 1);
|
|
+ ZixBTreeNode* const rhs = zix_btree_child(parent, i);
|
|
|
|
- assert(lhs->is_leaf == rhs->is_leaf);
|
|
+ assert(lhs->is_leaf == rhs->is_leaf);
|
|
|
|
- if (lhs->is_leaf) {
|
|
- // Prepend parent value to RHS
|
|
- zix_btree_ainsert(rhs->data.leaf.vals,
|
|
- rhs->n_vals++,
|
|
- 0,
|
|
- parent->data.inode.vals[i - 1]);
|
|
+ if (lhs->is_leaf) {
|
|
+ // Prepend parent value to RHS
|
|
+ zix_btree_ainsert(
|
|
+ rhs->data.leaf.vals, rhs->n_vals++, 0, parent->data.inode.vals[i - 1]);
|
|
|
|
- // Move last value from LHS to parent
|
|
- parent->data.inode.vals[i - 1] = lhs->data.leaf.vals[--lhs->n_vals];
|
|
- } else {
|
|
- // Prepend parent value to RHS
|
|
- zix_btree_ainsert(rhs->data.inode.vals,
|
|
- rhs->n_vals++,
|
|
- 0,
|
|
- parent->data.inode.vals[i - 1]);
|
|
+ // Move last value from LHS to parent
|
|
+ parent->data.inode.vals[i - 1] = lhs->data.leaf.vals[--lhs->n_vals];
|
|
+ } else {
|
|
+ // Prepend parent value to RHS
|
|
+ zix_btree_ainsert(
|
|
+ rhs->data.inode.vals, rhs->n_vals++, 0, parent->data.inode.vals[i - 1]);
|
|
|
|
- // Move last child pointer from LHS and prepend to RHS
|
|
- zix_btree_ainsert((void**)rhs->data.inode.children,
|
|
- rhs->n_vals,
|
|
- 0,
|
|
- lhs->data.inode.children[lhs->n_vals]);
|
|
+ // Move last child pointer from LHS and prepend to RHS
|
|
+ zix_btree_ainsert((void**)rhs->data.inode.children,
|
|
+ rhs->n_vals,
|
|
+ 0,
|
|
+ lhs->data.inode.children[lhs->n_vals]);
|
|
|
|
- // Move last value from LHS to parent
|
|
- parent->data.inode.vals[i - 1] = lhs->data.inode.vals[--lhs->n_vals];
|
|
- }
|
|
+ // Move last value from LHS to parent
|
|
+ parent->data.inode.vals[i - 1] = lhs->data.inode.vals[--lhs->n_vals];
|
|
+ }
|
|
|
|
- return rhs;
|
|
+ return rhs;
|
|
}
|
|
|
|
/** Move n[i] down, merge the left and right child, return the merged node. */
|
|
-ZIX_PRIVATE ZixBTreeNode*
|
|
+static ZixBTreeNode*
|
|
zix_btree_merge(ZixBTree* const t, ZixBTreeNode* const n, const unsigned i)
|
|
{
|
|
- ZixBTreeNode* const lhs = zix_btree_child(n, i);
|
|
- ZixBTreeNode* const rhs = zix_btree_child(n, i + 1);
|
|
-
|
|
- assert(lhs->is_leaf == rhs->is_leaf);
|
|
- assert(zix_btree_node_is_minimal(lhs));
|
|
- assert(lhs->n_vals + rhs->n_vals < zix_btree_max_vals(lhs));
|
|
-
|
|
- // Move parent value to end of LHS
|
|
- if (lhs->is_leaf) {
|
|
- lhs->data.leaf.vals[lhs->n_vals++] =
|
|
- zix_btree_aerase(n->data.inode.vals, n->n_vals, i);
|
|
- } else {
|
|
- lhs->data.inode.vals[lhs->n_vals++] =
|
|
- zix_btree_aerase(n->data.inode.vals, n->n_vals, i);
|
|
- }
|
|
-
|
|
- // Erase corresponding child pointer (to RHS) in parent
|
|
- zix_btree_aerase((void**)n->data.inode.children, n->n_vals, i + 1U);
|
|
-
|
|
- // Add everything from RHS to end of LHS
|
|
- if (lhs->is_leaf) {
|
|
- memcpy(lhs->data.leaf.vals + lhs->n_vals,
|
|
- rhs->data.leaf.vals,
|
|
- rhs->n_vals * sizeof(void*));
|
|
- } else {
|
|
- memcpy(lhs->data.inode.vals + lhs->n_vals,
|
|
- rhs->data.inode.vals,
|
|
- rhs->n_vals * sizeof(void*));
|
|
- memcpy(lhs->data.inode.children + lhs->n_vals,
|
|
- rhs->data.inode.children,
|
|
- (rhs->n_vals + 1U) * sizeof(void*));
|
|
- }
|
|
-
|
|
- lhs->n_vals = (uint16_t)(lhs->n_vals + rhs->n_vals);
|
|
-
|
|
- if (--n->n_vals == 0) {
|
|
- // Root is now empty, replace it with its only child
|
|
- assert(n == t->root);
|
|
- t->root = lhs;
|
|
- free(n);
|
|
- }
|
|
-
|
|
- free(rhs);
|
|
- return lhs;
|
|
+ ZixBTreeNode* const lhs = zix_btree_child(n, i);
|
|
+ ZixBTreeNode* const rhs = zix_btree_child(n, i + 1);
|
|
+
|
|
+ assert(lhs->is_leaf == rhs->is_leaf);
|
|
+ assert(zix_btree_node_is_minimal(lhs));
|
|
+ assert(lhs->n_vals + rhs->n_vals < zix_btree_max_vals(lhs));
|
|
+
|
|
+ // Move parent value to end of LHS
|
|
+ if (lhs->is_leaf) {
|
|
+ lhs->data.leaf.vals[lhs->n_vals++] =
|
|
+ zix_btree_aerase(n->data.inode.vals, n->n_vals, i);
|
|
+ } else {
|
|
+ lhs->data.inode.vals[lhs->n_vals++] =
|
|
+ zix_btree_aerase(n->data.inode.vals, n->n_vals, i);
|
|
+ }
|
|
+
|
|
+ // Erase corresponding child pointer (to RHS) in parent
|
|
+ zix_btree_aerase((void**)n->data.inode.children, n->n_vals, i + 1U);
|
|
+
|
|
+ // Add everything from RHS to end of LHS
|
|
+ if (lhs->is_leaf) {
|
|
+ memcpy(lhs->data.leaf.vals + lhs->n_vals,
|
|
+ rhs->data.leaf.vals,
|
|
+ rhs->n_vals * sizeof(void*));
|
|
+ } else {
|
|
+ memcpy(lhs->data.inode.vals + lhs->n_vals,
|
|
+ rhs->data.inode.vals,
|
|
+ rhs->n_vals * sizeof(void*));
|
|
+ memcpy(lhs->data.inode.children + lhs->n_vals,
|
|
+ rhs->data.inode.children,
|
|
+ (rhs->n_vals + 1U) * sizeof(void*));
|
|
+ }
|
|
+
|
|
+ lhs->n_vals = (uint16_t)(lhs->n_vals + rhs->n_vals);
|
|
+
|
|
+ if (--n->n_vals == 0) {
|
|
+ // Root is now empty, replace it with its only child
|
|
+ assert(n == t->root);
|
|
+ t->root = lhs;
|
|
+ free(n);
|
|
+ }
|
|
+
|
|
+ free(rhs);
|
|
+ return lhs;
|
|
}
|
|
|
|
/** Remove and return the min value from the subtree rooted at `n`. */
|
|
-ZIX_PRIVATE void*
|
|
+static void*
|
|
zix_btree_remove_min(ZixBTree* const t, ZixBTreeNode* n)
|
|
{
|
|
- while (!n->is_leaf) {
|
|
- if (zix_btree_node_is_minimal(zix_btree_child(n, 0))) {
|
|
- // Leftmost child is minimal, must expand
|
|
- if (!zix_btree_node_is_minimal(zix_btree_child(n, 1))) {
|
|
- // Child's right sibling has at least one key to steal
|
|
- n = zix_btree_rotate_left(n, 0);
|
|
- } else {
|
|
- // Both child and right sibling are minimal, merge
|
|
- n = zix_btree_merge(t, n, 0);
|
|
- }
|
|
- } else {
|
|
- n = zix_btree_child(n, 0);
|
|
- }
|
|
- }
|
|
+ while (!n->is_leaf) {
|
|
+ if (zix_btree_node_is_minimal(zix_btree_child(n, 0))) {
|
|
+ // Leftmost child is minimal, must expand
|
|
+ if (!zix_btree_node_is_minimal(zix_btree_child(n, 1))) {
|
|
+ // Child's right sibling has at least one key to steal
|
|
+ n = zix_btree_rotate_left(n, 0);
|
|
+ } else {
|
|
+ // Both child and right sibling are minimal, merge
|
|
+ n = zix_btree_merge(t, n, 0);
|
|
+ }
|
|
+ } else {
|
|
+ n = zix_btree_child(n, 0);
|
|
+ }
|
|
+ }
|
|
|
|
- return zix_btree_aerase(n->data.leaf.vals, --n->n_vals, 0);
|
|
+ return zix_btree_aerase(n->data.leaf.vals, --n->n_vals, 0);
|
|
}
|
|
|
|
/** Remove and return the max value from the subtree rooted at `n`. */
|
|
-ZIX_PRIVATE void*
|
|
+static void*
|
|
zix_btree_remove_max(ZixBTree* const t, ZixBTreeNode* n)
|
|
{
|
|
- while (!n->is_leaf) {
|
|
- if (zix_btree_node_is_minimal(zix_btree_child(n, n->n_vals))) {
|
|
- // Leftmost child is minimal, must expand
|
|
- if (!zix_btree_node_is_minimal(zix_btree_child(n, n->n_vals - 1))) {
|
|
- // Child's left sibling has at least one key to steal
|
|
- n = zix_btree_rotate_right(n, n->n_vals);
|
|
- } else {
|
|
- // Both child and left sibling are minimal, merge
|
|
- n = zix_btree_merge(t, n, n->n_vals - 1U);
|
|
- }
|
|
- } else {
|
|
- n = zix_btree_child(n, n->n_vals);
|
|
- }
|
|
- }
|
|
+ while (!n->is_leaf) {
|
|
+ if (zix_btree_node_is_minimal(zix_btree_child(n, n->n_vals))) {
|
|
+ // Leftmost child is minimal, must expand
|
|
+ if (!zix_btree_node_is_minimal(zix_btree_child(n, n->n_vals - 1))) {
|
|
+ // Child's left sibling has at least one key to steal
|
|
+ n = zix_btree_rotate_right(n, n->n_vals);
|
|
+ } else {
|
|
+ // Both child and left sibling are minimal, merge
|
|
+ n = zix_btree_merge(t, n, n->n_vals - 1U);
|
|
+ }
|
|
+ } else {
|
|
+ n = zix_btree_child(n, n->n_vals);
|
|
+ }
|
|
+ }
|
|
|
|
- return n->data.leaf.vals[--n->n_vals];
|
|
+ return n->data.leaf.vals[--n->n_vals];
|
|
}
|
|
|
|
ZixStatus
|
|
@@ -604,120 +599,120 @@ zix_btree_remove(ZixBTree* const t,
|
|
void** const out,
|
|
ZixBTreeIter** const next)
|
|
{
|
|
- ZixBTreeNode* n = t->root;
|
|
- ZixBTreeIter* ti = NULL;
|
|
- const bool user_iter = next && *next;
|
|
- if (next) {
|
|
- if (!*next && !(*next = zix_btree_iter_new(t))) {
|
|
- return ZIX_STATUS_NO_MEM;
|
|
- }
|
|
- ti = *next;
|
|
- ti->level = 0;
|
|
- }
|
|
-
|
|
- while (true) {
|
|
- /* To remove in a single walk down, the tree is adjusted along the way
|
|
- so that the current node always has at least one more value than the
|
|
- minimum required in general. Thus, there is always room to remove
|
|
- without adjusting on the way back up. */
|
|
- assert(n == t->root || !zix_btree_node_is_minimal(n));
|
|
-
|
|
- bool equal = false;
|
|
- const unsigned i = zix_btree_node_find(t, n, e, &equal);
|
|
- zix_btree_iter_set_frame(ti, n, i);
|
|
- if (n->is_leaf) {
|
|
- if (equal) {
|
|
- // Found in leaf node
|
|
- *out = zix_btree_aerase(n->data.leaf.vals, --n->n_vals, i);
|
|
- if (ti && i == n->n_vals) {
|
|
- if (i == 0) {
|
|
- ti->level = 0;
|
|
- ti->stack[0].node = NULL;
|
|
- } else {
|
|
- --ti->stack[ti->level].index;
|
|
- zix_btree_iter_increment(ti);
|
|
- }
|
|
- }
|
|
- --t->size;
|
|
- return ZIX_STATUS_SUCCESS;
|
|
- } else {
|
|
- // Not found in leaf node, or tree
|
|
- if (ti && !user_iter) {
|
|
- zix_btree_iter_free(ti);
|
|
- *next = NULL;
|
|
- }
|
|
- return ZIX_STATUS_NOT_FOUND;
|
|
- }
|
|
- } else if (equal) {
|
|
- // Found in internal node
|
|
- ZixBTreeNode* const lhs = zix_btree_child(n, i);
|
|
- ZixBTreeNode* const rhs = zix_btree_child(n, i + 1);
|
|
- const size_t l_size = lhs->n_vals;
|
|
- const size_t r_size = rhs->n_vals;
|
|
- if (zix_btree_node_is_minimal(lhs) &&
|
|
- zix_btree_node_is_minimal(rhs)) {
|
|
- // Both preceding and succeeding child are minimal
|
|
- n = zix_btree_merge(t, n, i);
|
|
- } else if (l_size >= r_size) {
|
|
- // Left child can remove without merge
|
|
- assert(!zix_btree_node_is_minimal(lhs));
|
|
- *out = n->data.inode.vals[i];
|
|
- n->data.inode.vals[i] = zix_btree_remove_max(t, lhs);
|
|
- --t->size;
|
|
- return ZIX_STATUS_SUCCESS;
|
|
- } else {
|
|
- // Right child can remove without merge
|
|
- assert(!zix_btree_node_is_minimal(rhs));
|
|
- *out = n->data.inode.vals[i];
|
|
- n->data.inode.vals[i] = zix_btree_remove_min(t, rhs);
|
|
- --t->size;
|
|
- return ZIX_STATUS_SUCCESS;
|
|
- }
|
|
- } else {
|
|
- // Not found in internal node, key is in/under children[i]
|
|
- if (zix_btree_node_is_minimal(zix_btree_child(n, i))) {
|
|
- if (i > 0 &&
|
|
- !zix_btree_node_is_minimal(zix_btree_child(n, i - 1))) {
|
|
- // Steal a key from child's left sibling
|
|
- n = zix_btree_rotate_right(n, i);
|
|
- } else if (i < n->n_vals &&
|
|
- !zix_btree_node_is_minimal(
|
|
- zix_btree_child(n, i + 1))) {
|
|
- // Steal a key from child's right sibling
|
|
- n = zix_btree_rotate_left(n, i);
|
|
- } else if (n == t->root && n->n_vals == 1) {
|
|
- // Root has two children, both minimal, delete it
|
|
- assert(i == 0 || i == 1);
|
|
- const uint16_t counts[2] = {zix_btree_child(n, 0)->n_vals,
|
|
- zix_btree_child(n, 1)->n_vals};
|
|
-
|
|
- n = zix_btree_merge(t, n, 0);
|
|
- if (ti) {
|
|
- ti->stack[ti->level].node = n;
|
|
- ti->stack[ti->level].index = counts[i];
|
|
- }
|
|
- } else {
|
|
- // Both child's siblings are minimal, merge them
|
|
- if (i < n->n_vals) {
|
|
- n = zix_btree_merge(t, n, i);
|
|
- } else {
|
|
- n = zix_btree_merge(t, n, i - 1U);
|
|
- if (ti) {
|
|
- --ti->stack[ti->level].index;
|
|
- }
|
|
- }
|
|
- }
|
|
- } else {
|
|
- n = zix_btree_child(n, i);
|
|
- }
|
|
- }
|
|
- if (ti) {
|
|
- ++ti->level;
|
|
- }
|
|
- }
|
|
-
|
|
- assert(false); // Not reached
|
|
- return ZIX_STATUS_ERROR;
|
|
+ ZixBTreeNode* n = t->root;
|
|
+ ZixBTreeIter* ti = NULL;
|
|
+ const bool user_iter = next && *next;
|
|
+ if (next) {
|
|
+ if (!*next && !(*next = zix_btree_iter_new(t))) {
|
|
+ return ZIX_STATUS_NO_MEM;
|
|
+ }
|
|
+ ti = *next;
|
|
+ ti->level = 0;
|
|
+ }
|
|
+
|
|
+ while (true) {
|
|
+ /* To remove in a single walk down, the tree is adjusted along the way
|
|
+ so that the current node always has at least one more value than the
|
|
+ minimum required in general. Thus, there is always room to remove
|
|
+ without adjusting on the way back up. */
|
|
+ assert(n == t->root || !zix_btree_node_is_minimal(n));
|
|
+
|
|
+ bool equal = false;
|
|
+ const unsigned i = zix_btree_node_find(t, n, e, &equal);
|
|
+ zix_btree_iter_set_frame(ti, n, i);
|
|
+ if (n->is_leaf) {
|
|
+ if (equal) {
|
|
+ // Found in leaf node
|
|
+ *out = zix_btree_aerase(n->data.leaf.vals, --n->n_vals, i);
|
|
+ if (ti && i == n->n_vals) {
|
|
+ if (i == 0) {
|
|
+ ti->level = 0;
|
|
+ ti->stack[0].node = NULL;
|
|
+ } else {
|
|
+ --ti->stack[ti->level].index;
|
|
+ zix_btree_iter_increment(ti);
|
|
+ }
|
|
+ }
|
|
+ --t->size;
|
|
+ return ZIX_STATUS_SUCCESS;
|
|
+ }
|
|
+
|
|
+ // Not found in leaf node, or tree
|
|
+ if (ti && !user_iter) {
|
|
+ zix_btree_iter_free(ti);
|
|
+ *next = NULL;
|
|
+ }
|
|
+
|
|
+ return ZIX_STATUS_NOT_FOUND;
|
|
+ }
|
|
+
|
|
+ if (equal) {
|
|
+ // Found in internal node
|
|
+ ZixBTreeNode* const lhs = zix_btree_child(n, i);
|
|
+ ZixBTreeNode* const rhs = zix_btree_child(n, i + 1);
|
|
+ const size_t l_size = lhs->n_vals;
|
|
+ const size_t r_size = rhs->n_vals;
|
|
+ if (zix_btree_node_is_minimal(lhs) && zix_btree_node_is_minimal(rhs)) {
|
|
+ // Both preceding and succeeding child are minimal
|
|
+ n = zix_btree_merge(t, n, i);
|
|
+ } else if (l_size >= r_size) {
|
|
+ // Left child can remove without merge
|
|
+ assert(!zix_btree_node_is_minimal(lhs));
|
|
+ *out = n->data.inode.vals[i];
|
|
+ n->data.inode.vals[i] = zix_btree_remove_max(t, lhs);
|
|
+ --t->size;
|
|
+ return ZIX_STATUS_SUCCESS;
|
|
+ } else {
|
|
+ // Right child can remove without merge
|
|
+ assert(!zix_btree_node_is_minimal(rhs));
|
|
+ *out = n->data.inode.vals[i];
|
|
+ n->data.inode.vals[i] = zix_btree_remove_min(t, rhs);
|
|
+ --t->size;
|
|
+ return ZIX_STATUS_SUCCESS;
|
|
+ }
|
|
+ } else {
|
|
+ // Not found in internal node, key is in/under children[i]
|
|
+ if (zix_btree_node_is_minimal(zix_btree_child(n, i))) {
|
|
+ if (i > 0 && !zix_btree_node_is_minimal(zix_btree_child(n, i - 1))) {
|
|
+ // Steal a key from child's left sibling
|
|
+ n = zix_btree_rotate_right(n, i);
|
|
+ } else if (i < n->n_vals &&
|
|
+ !zix_btree_node_is_minimal(zix_btree_child(n, i + 1))) {
|
|
+ // Steal a key from child's right sibling
|
|
+ n = zix_btree_rotate_left(n, i);
|
|
+ } else if (n == t->root && n->n_vals == 1) {
|
|
+ // Root has two children, both minimal, delete it
|
|
+ assert(i == 0 || i == 1);
|
|
+ const uint16_t counts[2] = {zix_btree_child(n, 0)->n_vals,
|
|
+ zix_btree_child(n, 1)->n_vals};
|
|
+
|
|
+ n = zix_btree_merge(t, n, 0);
|
|
+ if (ti) {
|
|
+ ti->stack[ti->level].node = n;
|
|
+ ti->stack[ti->level].index = counts[i];
|
|
+ }
|
|
+ } else {
|
|
+ // Both child's siblings are minimal, merge them
|
|
+ if (i < n->n_vals) {
|
|
+ n = zix_btree_merge(t, n, i);
|
|
+ } else {
|
|
+ n = zix_btree_merge(t, n, i - 1U);
|
|
+ if (ti) {
|
|
+ --ti->stack[ti->level].index;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ n = zix_btree_child(n, i);
|
|
+ }
|
|
+ }
|
|
+ if (ti) {
|
|
+ ++ti->level;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ assert(false); // Not reached
|
|
+ return ZIX_STATUS_ERROR;
|
|
}
|
|
|
|
ZixStatus
|
|
@@ -725,30 +720,32 @@ zix_btree_find(const ZixBTree* const t,
|
|
const void* const e,
|
|
ZixBTreeIter** const ti)
|
|
{
|
|
- ZixBTreeNode* n = t->root;
|
|
- if (!(*ti = zix_btree_iter_new(t))) {
|
|
- return ZIX_STATUS_NO_MEM;
|
|
- }
|
|
+ ZixBTreeNode* n = t->root;
|
|
+ if (!(*ti = zix_btree_iter_new(t))) {
|
|
+ return ZIX_STATUS_NO_MEM;
|
|
+ }
|
|
+
|
|
+ while (n) {
|
|
+ bool equal = false;
|
|
+ const unsigned i = zix_btree_node_find(t, n, e, &equal);
|
|
+
|
|
+ zix_btree_iter_set_frame(*ti, n, i);
|
|
|
|
- while (n) {
|
|
- bool equal = false;
|
|
- const unsigned i = zix_btree_node_find(t, n, e, &equal);
|
|
+ if (equal) {
|
|
+ return ZIX_STATUS_SUCCESS;
|
|
+ }
|
|
|
|
- zix_btree_iter_set_frame(*ti, n, i);
|
|
+ if (n->is_leaf) {
|
|
+ break;
|
|
+ }
|
|
|
|
- if (equal) {
|
|
- return ZIX_STATUS_SUCCESS;
|
|
- } else if (n->is_leaf) {
|
|
- break;
|
|
- } else {
|
|
- ++(*ti)->level;
|
|
- n = zix_btree_child(n, i);
|
|
- }
|
|
- }
|
|
+ ++(*ti)->level;
|
|
+ n = zix_btree_child(n, i);
|
|
+ }
|
|
|
|
- zix_btree_iter_free(*ti);
|
|
- *ti = NULL;
|
|
- return ZIX_STATUS_NOT_FOUND;
|
|
+ zix_btree_iter_free(*ti);
|
|
+ *ti = NULL;
|
|
+ return ZIX_STATUS_NOT_FOUND;
|
|
}
|
|
|
|
ZixStatus
|
|
@@ -756,175 +753,184 @@ zix_btree_lower_bound(const ZixBTree* const t,
|
|
const void* const e,
|
|
ZixBTreeIter** const ti)
|
|
{
|
|
- if (!t) {
|
|
- *ti = NULL;
|
|
- return ZIX_STATUS_BAD_ARG;
|
|
- } else if (!t->root) {
|
|
- *ti = NULL;
|
|
- return ZIX_STATUS_SUCCESS;
|
|
- }
|
|
-
|
|
- ZixBTreeNode* n = t->root;
|
|
- bool found = false;
|
|
- unsigned found_level = 0;
|
|
- if (!(*ti = zix_btree_iter_new(t))) {
|
|
- return ZIX_STATUS_NO_MEM;
|
|
- }
|
|
-
|
|
- while (n) {
|
|
- bool equal = false;
|
|
- const unsigned i = zix_btree_node_find(t, n, e, &equal);
|
|
-
|
|
- zix_btree_iter_set_frame(*ti, n, i);
|
|
-
|
|
- if (equal) {
|
|
- found_level = (*ti)->level;
|
|
- found = true;
|
|
- }
|
|
-
|
|
- if (n->is_leaf) {
|
|
- break;
|
|
- } else {
|
|
- ++(*ti)->level;
|
|
- n = zix_btree_child(n, i);
|
|
- assert(n);
|
|
- }
|
|
- }
|
|
-
|
|
- const ZixBTreeIterFrame* const frame = &(*ti)->stack[(*ti)->level];
|
|
- assert(frame->node);
|
|
- if (frame->index == frame->node->n_vals) {
|
|
- if (found) {
|
|
- // Found on a previous level but went too far
|
|
- (*ti)->level = found_level;
|
|
- } else {
|
|
- // Reached end (key is greater than everything in tree)
|
|
- (*ti)->level = 0;
|
|
- (*ti)->stack[0].node = NULL;
|
|
- }
|
|
- }
|
|
-
|
|
- return ZIX_STATUS_SUCCESS;
|
|
+ if (!t) {
|
|
+ *ti = NULL;
|
|
+ return ZIX_STATUS_BAD_ARG;
|
|
+ }
|
|
+
|
|
+ if (!t->root) {
|
|
+ *ti = NULL;
|
|
+ return ZIX_STATUS_SUCCESS;
|
|
+ }
|
|
+
|
|
+ ZixBTreeNode* n = t->root;
|
|
+ bool found = false;
|
|
+ unsigned found_level = 0;
|
|
+ if (!(*ti = zix_btree_iter_new(t))) {
|
|
+ return ZIX_STATUS_NO_MEM;
|
|
+ }
|
|
+
|
|
+ while (n) {
|
|
+ bool equal = false;
|
|
+ const unsigned i = zix_btree_node_find(t, n, e, &equal);
|
|
+
|
|
+ zix_btree_iter_set_frame(*ti, n, i);
|
|
+
|
|
+ if (equal) {
|
|
+ found_level = (*ti)->level;
|
|
+ found = true;
|
|
+ }
|
|
+
|
|
+ if (n->is_leaf) {
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ ++(*ti)->level;
|
|
+ n = zix_btree_child(n, i);
|
|
+ assert(n);
|
|
+ }
|
|
+
|
|
+ const ZixBTreeIterFrame* const frame = &(*ti)->stack[(*ti)->level];
|
|
+ assert(frame->node);
|
|
+ if (frame->index == frame->node->n_vals) {
|
|
+ if (found) {
|
|
+ // Found on a previous level but went too far
|
|
+ (*ti)->level = found_level;
|
|
+ } else {
|
|
+ // Reached end (key is greater than everything in tree)
|
|
+ (*ti)->level = 0;
|
|
+ (*ti)->stack[0].node = NULL;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return ZIX_STATUS_SUCCESS;
|
|
}
|
|
|
|
void*
|
|
zix_btree_get(const ZixBTreeIter* const ti)
|
|
{
|
|
- const ZixBTreeIterFrame* const frame = &ti->stack[ti->level];
|
|
- assert(frame->node);
|
|
- assert(frame->index < frame->node->n_vals);
|
|
- return zix_btree_value(frame->node, frame->index);
|
|
+ const ZixBTreeIterFrame* const frame = &ti->stack[ti->level];
|
|
+ assert(frame->node);
|
|
+ assert(frame->index < frame->node->n_vals);
|
|
+ return zix_btree_value(frame->node, frame->index);
|
|
}
|
|
|
|
ZixBTreeIter*
|
|
zix_btree_begin(const ZixBTree* const t)
|
|
{
|
|
- ZixBTreeIter* const i = zix_btree_iter_new(t);
|
|
- if (!i) {
|
|
- return NULL;
|
|
- } else if (t->size == 0) {
|
|
- i->level = 0;
|
|
- i->stack[0].node = NULL;
|
|
- } else {
|
|
- ZixBTreeNode* n = t->root;
|
|
- i->stack[0].node = n;
|
|
- i->stack[0].index = 0;
|
|
- while (!n->is_leaf) {
|
|
- n = zix_btree_child(n, 0);
|
|
- ++i->level;
|
|
- i->stack[i->level].node = n;
|
|
- i->stack[i->level].index = 0;
|
|
- }
|
|
- }
|
|
- return i;
|
|
+ ZixBTreeIter* const i = zix_btree_iter_new(t);
|
|
+ if (!i) {
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ if (t->size == 0) {
|
|
+ i->level = 0;
|
|
+ i->stack[0].node = NULL;
|
|
+ } else {
|
|
+ ZixBTreeNode* n = t->root;
|
|
+ i->stack[0].node = n;
|
|
+ i->stack[0].index = 0;
|
|
+ while (!n->is_leaf) {
|
|
+ n = zix_btree_child(n, 0);
|
|
+ ++i->level;
|
|
+ i->stack[i->level].node = n;
|
|
+ i->stack[i->level].index = 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return i;
|
|
}
|
|
|
|
ZixBTreeIter*
|
|
zix_btree_end(const ZixBTree* const t)
|
|
{
|
|
- return zix_btree_iter_new(t);
|
|
+ return zix_btree_iter_new(t);
|
|
}
|
|
|
|
ZixBTreeIter*
|
|
zix_btree_iter_copy(const ZixBTreeIter* const i)
|
|
{
|
|
- if (!i) {
|
|
- return NULL;
|
|
- }
|
|
+ if (!i) {
|
|
+ return NULL;
|
|
+ }
|
|
|
|
- const size_t s = i->n_levels * sizeof(ZixBTreeIterFrame);
|
|
- ZixBTreeIter* j = (ZixBTreeIter*)calloc(1, sizeof(ZixBTreeIter) + s);
|
|
- if (j) {
|
|
- memcpy(j, i, sizeof(ZixBTreeIter) + s);
|
|
- }
|
|
- return j;
|
|
+ const size_t s = i->n_levels * sizeof(ZixBTreeIterFrame);
|
|
+ ZixBTreeIter* j = (ZixBTreeIter*)calloc(1, sizeof(ZixBTreeIter) + s);
|
|
+ if (j) {
|
|
+ memcpy(j, i, sizeof(ZixBTreeIter) + s);
|
|
+ }
|
|
+
|
|
+ return j;
|
|
}
|
|
|
|
bool
|
|
zix_btree_iter_is_end(const ZixBTreeIter* const i)
|
|
{
|
|
- return !i || i->stack[0].node == NULL;
|
|
+ return !i || (i->level == 0 && i->stack[0].node == NULL);
|
|
}
|
|
|
|
bool
|
|
-zix_btree_iter_equals(const ZixBTreeIter* const lhs, const ZixBTreeIter* const rhs)
|
|
+zix_btree_iter_equals(const ZixBTreeIter* const lhs,
|
|
+ const ZixBTreeIter* const rhs)
|
|
{
|
|
- if (zix_btree_iter_is_end(lhs) && zix_btree_iter_is_end(rhs)) {
|
|
- return true;
|
|
- } else if (zix_btree_iter_is_end(lhs) || zix_btree_iter_is_end(rhs) ||
|
|
- lhs->level != rhs->level) {
|
|
- return false;
|
|
- }
|
|
+ if (zix_btree_iter_is_end(lhs) && zix_btree_iter_is_end(rhs)) {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ if (zix_btree_iter_is_end(lhs) || zix_btree_iter_is_end(rhs) ||
|
|
+ lhs->level != rhs->level) {
|
|
+ return false;
|
|
+ }
|
|
|
|
- return !memcmp(lhs,
|
|
- rhs,
|
|
- sizeof(ZixBTreeIter) +
|
|
- (lhs->level + 1) * sizeof(ZixBTreeIterFrame));
|
|
+ return !memcmp(lhs,
|
|
+ rhs,
|
|
+ sizeof(ZixBTreeIter) +
|
|
+ (lhs->level + 1) * sizeof(ZixBTreeIterFrame));
|
|
}
|
|
|
|
void
|
|
zix_btree_iter_increment(ZixBTreeIter* const i)
|
|
{
|
|
- ZixBTreeIterFrame* f = &i->stack[i->level];
|
|
- if (f->node->is_leaf) {
|
|
- // Leaf, move right
|
|
- assert(f->index < f->node->n_vals);
|
|
- if (++f->index == f->node->n_vals) {
|
|
- // Reached end of leaf, move up
|
|
- f = &i->stack[i->level];
|
|
- while (i->level > 0 && f->index == f->node->n_vals) {
|
|
- f = &i->stack[--i->level];
|
|
- assert(f->index <= f->node->n_vals);
|
|
- }
|
|
-
|
|
- if (f->index == f->node->n_vals) {
|
|
- // Reached end of tree
|
|
- assert(i->level == 0);
|
|
- f->node = NULL;
|
|
- f->index = 0;
|
|
- }
|
|
- }
|
|
- } else {
|
|
- // Internal node, move down to next child
|
|
- assert(f->index < f->node->n_vals);
|
|
- ZixBTreeNode* child = zix_btree_child(f->node, ++f->index);
|
|
-
|
|
- f = &i->stack[++i->level];
|
|
- f->node = child;
|
|
- f->index = 0;
|
|
-
|
|
- // Move down and left until we hit a leaf
|
|
- while (!f->node->is_leaf) {
|
|
- child = zix_btree_child(f->node, 0);
|
|
- f = &i->stack[++i->level];
|
|
- f->node = child;
|
|
- f->index = 0;
|
|
- }
|
|
- }
|
|
+ ZixBTreeIterFrame* f = &i->stack[i->level];
|
|
+ if (f->node->is_leaf) {
|
|
+ // Leaf, move right
|
|
+ assert(f->index < f->node->n_vals);
|
|
+ if (++f->index == f->node->n_vals) {
|
|
+ // Reached end of leaf, move up
|
|
+ f = &i->stack[i->level];
|
|
+ while (i->level > 0 && f->index == f->node->n_vals) {
|
|
+ f = &i->stack[--i->level];
|
|
+ assert(f->index <= f->node->n_vals);
|
|
+ }
|
|
+
|
|
+ if (f->index == f->node->n_vals) {
|
|
+ // Reached end of tree
|
|
+ assert(i->level == 0);
|
|
+ f->node = NULL;
|
|
+ f->index = 0;
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ // Internal node, move down to next child
|
|
+ assert(f->index < f->node->n_vals);
|
|
+ ZixBTreeNode* child = zix_btree_child(f->node, ++f->index);
|
|
+
|
|
+ f = &i->stack[++i->level];
|
|
+ f->node = child;
|
|
+ f->index = 0;
|
|
+
|
|
+ // Move down and left until we hit a leaf
|
|
+ while (!f->node->is_leaf) {
|
|
+ child = zix_btree_child(f->node, 0);
|
|
+ f = &i->stack[++i->level];
|
|
+ f->node = child;
|
|
+ f->index = 0;
|
|
+ }
|
|
+ }
|
|
}
|
|
|
|
void
|
|
zix_btree_iter_free(ZixBTreeIter* const i)
|
|
{
|
|
- free(i);
|
|
+ free(i);
|
|
}
|
|
diff --git a/src/zix/btree.h b/src/zix/btree.h
|
|
index 6333a91..bec0698 100644
|
|
--- a/src/zix/btree.h
|
|
+++ b/src/zix/btree.h
|
|
@@ -1,5 +1,5 @@
|
|
/*
|
|
- Copyright 2011-2016 David Robillard <d@drobilla.net>
|
|
+ Copyright 2011-2020 David Robillard <d@drobilla.net>
|
|
|
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
@@ -54,27 +54,29 @@ typedef struct ZixBTreeIterImpl ZixBTreeIter;
|
|
/**
|
|
Create a new (empty) B-Tree.
|
|
*/
|
|
-ZIX_API ZixBTree*
|
|
-zix_btree_new(ZixComparator cmp,
|
|
- const void* cmp_data,
|
|
- ZixDestroyFunc destroy);
|
|
+ZIX_API
|
|
+ZixBTree*
|
|
+zix_btree_new(ZixComparator cmp, const void* cmp_data, ZixDestroyFunc destroy);
|
|
|
|
/**
|
|
Free `t`.
|
|
*/
|
|
-ZIX_API void
|
|
+ZIX_API
|
|
+void
|
|
zix_btree_free(ZixBTree* t);
|
|
|
|
/**
|
|
Return the number of elements in `t`.
|
|
*/
|
|
-ZIX_PURE_API size_t
|
|
+ZIX_PURE_API
|
|
+size_t
|
|
zix_btree_size(const ZixBTree* t);
|
|
|
|
/**
|
|
Insert the element `e` into `t`.
|
|
*/
|
|
-ZIX_API ZixStatus
|
|
+ZIX_API
|
|
+ZixStatus
|
|
zix_btree_insert(ZixBTree* t, void* e);
|
|
|
|
/**
|
|
@@ -90,14 +92,16 @@ zix_btree_insert(ZixBTree* t, void* e);
|
|
also non-NULL, the iterator is reused, otherwise a new one is allocated. To
|
|
reuse an iterator, no items may have been added since its creation.
|
|
*/
|
|
-ZIX_API ZixStatus
|
|
+ZIX_API
|
|
+ZixStatus
|
|
zix_btree_remove(ZixBTree* t, const void* e, void** out, ZixBTreeIter** next);
|
|
|
|
/**
|
|
Set `ti` to an element equal to `e` in `t`.
|
|
If no such item exists, `ti` is set to NULL.
|
|
*/
|
|
-ZIX_API ZixStatus
|
|
+ZIX_API
|
|
+ZixStatus
|
|
zix_btree_find(const ZixBTree* t, const void* e, ZixBTreeIter** ti);
|
|
|
|
/**
|
|
@@ -107,13 +111,15 @@ zix_btree_find(const ZixBTree* t, const void* e, ZixBTreeIter** ti);
|
|
values in the tree, `ti` will be set to the least such element. The search
|
|
key `e` is always passed as the second argument to the comparator.
|
|
*/
|
|
-ZIX_API ZixStatus
|
|
+ZIX_API
|
|
+ZixStatus
|
|
zix_btree_lower_bound(const ZixBTree* t, const void* e, ZixBTreeIter** ti);
|
|
|
|
/**
|
|
Return the data associated with the given tree item.
|
|
*/
|
|
-ZIX_PURE_API void*
|
|
+ZIX_PURE_API
|
|
+void*
|
|
zix_btree_get(const ZixBTreeIter* ti);
|
|
|
|
/**
|
|
@@ -121,7 +127,8 @@ zix_btree_get(const ZixBTreeIter* ti);
|
|
|
|
The returned iterator must be freed with zix_btree_iter_free().
|
|
*/
|
|
-ZIX_PURE_API ZixBTreeIter*
|
|
+ZIX_PURE_API
|
|
+ZixBTreeIter*
|
|
zix_btree_begin(const ZixBTree* t);
|
|
|
|
/**
|
|
@@ -129,37 +136,43 @@ zix_btree_begin(const ZixBTree* t);
|
|
|
|
The returned iterator must be freed with zix_btree_iter_free().
|
|
*/
|
|
-ZIX_API ZixBTreeIter*
|
|
+ZIX_API
|
|
+ZixBTreeIter*
|
|
zix_btree_end(const ZixBTree* t);
|
|
|
|
/**
|
|
Return a new copy of `i`.
|
|
*/
|
|
-ZIX_API ZixBTreeIter*
|
|
+ZIX_API
|
|
+ZixBTreeIter*
|
|
zix_btree_iter_copy(const ZixBTreeIter* i);
|
|
|
|
/**
|
|
Return true iff `lhs` is equal to `rhs`.
|
|
*/
|
|
-ZIX_PURE_API bool
|
|
+ZIX_PURE_API
|
|
+bool
|
|
zix_btree_iter_equals(const ZixBTreeIter* lhs, const ZixBTreeIter* rhs);
|
|
|
|
/**
|
|
Return true iff `i` is an iterator to the end of its tree.
|
|
*/
|
|
-ZIX_PURE_API bool
|
|
+ZIX_PURE_API
|
|
+bool
|
|
zix_btree_iter_is_end(const ZixBTreeIter* i);
|
|
|
|
/**
|
|
Increment `i` to point to the next element in the tree.
|
|
*/
|
|
-ZIX_API void
|
|
+ZIX_API
|
|
+void
|
|
zix_btree_iter_increment(ZixBTreeIter* i);
|
|
|
|
/**
|
|
Free `i`.
|
|
*/
|
|
-ZIX_API void
|
|
+ZIX_API
|
|
+void
|
|
zix_btree_iter_free(ZixBTreeIter* i);
|
|
|
|
/**
|
|
@@ -168,7 +181,7 @@ zix_btree_iter_free(ZixBTreeIter* i);
|
|
*/
|
|
|
|
#ifdef __cplusplus
|
|
-} /* extern "C" */
|
|
+} /* extern "C" */
|
|
#endif
|
|
|
|
-#endif /* ZIX_BTREE_H */
|
|
+#endif /* ZIX_BTREE_H */
|
|
diff --git a/src/zix/common.h b/src/zix/common.h
|
|
index cf07451..d47586c 100644
|
|
--- a/src/zix/common.h
|
|
+++ b/src/zix/common.h
|
|
@@ -1,5 +1,5 @@
|
|
/*
|
|
- Copyright 2016 David Robillard <d@drobilla.net>
|
|
+ Copyright 2016-2020 David Robillard <d@drobilla.net>
|
|
|
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
@@ -25,41 +25,37 @@
|
|
*/
|
|
|
|
/** @cond */
|
|
-#ifdef ZIX_SHARED
|
|
-# ifdef _WIN32
|
|
-# define ZIX_LIB_IMPORT __declspec(dllimport)
|
|
-# define ZIX_LIB_EXPORT __declspec(dllexport)
|
|
-# else
|
|
-# define ZIX_LIB_IMPORT __attribute__((visibility("default")))
|
|
-# define ZIX_LIB_EXPORT __attribute__((visibility("default")))
|
|
-# endif
|
|
-# ifdef ZIX_INTERNAL
|
|
-# define ZIX_API ZIX_LIB_EXPORT
|
|
-# else
|
|
-# define ZIX_API ZIX_LIB_IMPORT
|
|
-# endif
|
|
-# define ZIX_PRIVATE static
|
|
-#elif defined(ZIX_INLINE)
|
|
-# define ZIX_API static inline
|
|
-# define ZIX_PRIVATE static inline
|
|
+#if defined(_WIN32) && !defined(ZIX_STATIC) && defined(ZIX_INTERNAL)
|
|
+# define ZIX_API __declspec(dllexport)
|
|
+#elif defined(_WIN32) && !defined(ZIX_STATIC)
|
|
+# define ZIX_API __declspec(dllimport)
|
|
+#elif defined(__GNUC__)
|
|
+# define ZIX_API __attribute__((visibility("default")))
|
|
#else
|
|
-# define ZIX_API
|
|
-# define ZIX_PRIVATE static
|
|
+# define ZIX_API
|
|
#endif
|
|
|
|
#ifdef __GNUC__
|
|
-# define ZIX_PURE_FUNC __attribute__((pure))
|
|
-# define ZIX_CONST_FUNC __attribute__((const))
|
|
-# define ZIX_MALLOC_FUNC __attribute__((malloc))
|
|
+# define ZIX_PURE_FUNC __attribute__((pure))
|
|
+# define ZIX_CONST_FUNC __attribute__((const))
|
|
+# define ZIX_MALLOC_FUNC __attribute__((malloc))
|
|
#else
|
|
-# define ZIX_PURE_FUNC
|
|
-# define ZIX_CONST_FUNC
|
|
-# define ZIX_MALLOC_FUNC
|
|
+# define ZIX_PURE_FUNC
|
|
+# define ZIX_CONST_FUNC
|
|
+# define ZIX_MALLOC_FUNC
|
|
#endif
|
|
|
|
-#define ZIX_PURE_API ZIX_API ZIX_PURE_FUNC
|
|
-#define ZIX_CONST_API ZIX_API ZIX_CONST_FUNC
|
|
-#define ZIX_MALLOC_API ZIX_API ZIX_MALLOC_FUNC
|
|
+#define ZIX_PURE_API \
|
|
+ ZIX_API \
|
|
+ ZIX_PURE_FUNC
|
|
+
|
|
+#define ZIX_CONST_API \
|
|
+ ZIX_API \
|
|
+ ZIX_CONST_FUNC
|
|
+
|
|
+#define ZIX_MALLOC_API \
|
|
+ ZIX_API \
|
|
+ ZIX_MALLOC_FUNC
|
|
|
|
/** @endcond */
|
|
|
|
@@ -68,43 +64,50 @@ extern "C" {
|
|
#endif
|
|
|
|
#ifdef __GNUC__
|
|
-#define ZIX_LOG_FUNC(fmt, arg1) __attribute__((format(printf, fmt, arg1)))
|
|
+# define ZIX_LOG_FUNC(fmt, arg1) __attribute__((format(printf, fmt, arg1)))
|
|
#else
|
|
-#define ZIX_LOG_FUNC(fmt, arg1)
|
|
+# define ZIX_LOG_FUNC(fmt, arg1)
|
|
#endif
|
|
|
|
// Unused parameter macro to suppresses warnings and make it impossible to use
|
|
#if defined(__cplusplus)
|
|
-# define ZIX_UNUSED(name)
|
|
+# define ZIX_UNUSED(name)
|
|
#elif defined(__GNUC__)
|
|
-# define ZIX_UNUSED(name) name##_unused __attribute__((__unused__))
|
|
+# define ZIX_UNUSED(name) name##_unused __attribute__((__unused__))
|
|
#else
|
|
-# define ZIX_UNUSED(name) name
|
|
+# define ZIX_UNUSED(name) name
|
|
#endif
|
|
|
|
typedef enum {
|
|
- ZIX_STATUS_SUCCESS,
|
|
- ZIX_STATUS_ERROR,
|
|
- ZIX_STATUS_NO_MEM,
|
|
- ZIX_STATUS_NOT_FOUND,
|
|
- ZIX_STATUS_EXISTS,
|
|
- ZIX_STATUS_BAD_ARG,
|
|
- ZIX_STATUS_BAD_PERMS
|
|
+ ZIX_STATUS_SUCCESS,
|
|
+ ZIX_STATUS_ERROR,
|
|
+ ZIX_STATUS_NO_MEM,
|
|
+ ZIX_STATUS_NOT_FOUND,
|
|
+ ZIX_STATUS_EXISTS,
|
|
+ ZIX_STATUS_BAD_ARG,
|
|
+ ZIX_STATUS_BAD_PERMS
|
|
} ZixStatus;
|
|
|
|
static inline const char*
|
|
zix_strerror(const ZixStatus status)
|
|
{
|
|
- switch (status) {
|
|
- case ZIX_STATUS_SUCCESS: return "Success";
|
|
- case ZIX_STATUS_ERROR: return "Unknown error";
|
|
- case ZIX_STATUS_NO_MEM: return "Out of memory";
|
|
- case ZIX_STATUS_NOT_FOUND: return "Not found";
|
|
- case ZIX_STATUS_EXISTS: return "Exists";
|
|
- case ZIX_STATUS_BAD_ARG: return "Bad argument";
|
|
- case ZIX_STATUS_BAD_PERMS: return "Bad permissions";
|
|
- }
|
|
- return "Unknown error";
|
|
+ switch (status) {
|
|
+ case ZIX_STATUS_SUCCESS:
|
|
+ return "Success";
|
|
+ case ZIX_STATUS_ERROR:
|
|
+ return "Unknown error";
|
|
+ case ZIX_STATUS_NO_MEM:
|
|
+ return "Out of memory";
|
|
+ case ZIX_STATUS_NOT_FOUND:
|
|
+ return "Not found";
|
|
+ case ZIX_STATUS_EXISTS:
|
|
+ return "Exists";
|
|
+ case ZIX_STATUS_BAD_ARG:
|
|
+ return "Bad argument";
|
|
+ case ZIX_STATUS_BAD_PERMS:
|
|
+ return "Bad permissions";
|
|
+ }
|
|
+ return "Unknown error";
|
|
}
|
|
|
|
/**
|
|
@@ -129,7 +132,7 @@ typedef void (*ZixDestroyFunc)(void* ptr);
|
|
*/
|
|
|
|
#ifdef __cplusplus
|
|
-} /* extern "C" */
|
|
+} /* extern "C" */
|
|
#endif
|
|
|
|
-#endif /* ZIX_COMMON_H */
|
|
+#endif /* ZIX_COMMON_H */
|
|
diff --git a/src/zix/digest.c b/src/zix/digest.c
|
|
index 8d08ae2..47d27b9 100644
|
|
--- a/src/zix/digest.c
|
|
+++ b/src/zix/digest.c
|
|
@@ -17,7 +17,7 @@
|
|
#include "zix/digest.h"
|
|
|
|
#ifdef __SSE4_2__
|
|
-# include <smmintrin.h>
|
|
+# include <smmintrin.h>
|
|
#endif
|
|
|
|
#include <assert.h>
|
|
@@ -30,75 +30,75 @@
|
|
uint32_t
|
|
zix_digest_start(void)
|
|
{
|
|
- return 1;
|
|
+ return 1;
|
|
}
|
|
|
|
uint32_t
|
|
zix_digest_add(uint32_t hash, const void* const buf, const size_t len)
|
|
{
|
|
- const uint8_t* str = (const uint8_t*)buf;
|
|
-
|
|
-#ifdef __x86_64__
|
|
- for (size_t i = 0; i < (len / sizeof(uint64_t)); ++i) {
|
|
- hash = (uint32_t)_mm_crc32_u64(hash, *(const uint64_t*)str);
|
|
- str += sizeof(uint64_t);
|
|
- }
|
|
- if (len & sizeof(uint32_t)) {
|
|
- hash = _mm_crc32_u32(hash, *(const uint32_t*)str);
|
|
- str += sizeof(uint32_t);
|
|
- }
|
|
-#else
|
|
- for (size_t i = 0; i < (len / sizeof(uint32_t)); ++i) {
|
|
- hash = _mm_crc32_u32(hash, *(const uint32_t*)str);
|
|
- str += sizeof(uint32_t);
|
|
- }
|
|
-#endif
|
|
- if (len & sizeof(uint16_t)) {
|
|
- hash = _mm_crc32_u16(hash, *(const uint16_t*)str);
|
|
- str += sizeof(uint16_t);
|
|
- }
|
|
- if (len & sizeof(uint8_t)) {
|
|
- hash = _mm_crc32_u8(hash, *(const uint8_t*)str);
|
|
- }
|
|
-
|
|
- return hash;
|
|
+ const uint8_t* str = (const uint8_t*)buf;
|
|
+
|
|
+# ifdef __x86_64__
|
|
+ for (size_t i = 0; i < (len / sizeof(uint64_t)); ++i) {
|
|
+ hash = (uint32_t)_mm_crc32_u64(hash, *(const uint64_t*)str);
|
|
+ str += sizeof(uint64_t);
|
|
+ }
|
|
+ if (len & sizeof(uint32_t)) {
|
|
+ hash = _mm_crc32_u32(hash, *(const uint32_t*)str);
|
|
+ str += sizeof(uint32_t);
|
|
+ }
|
|
+# else
|
|
+ for (size_t i = 0; i < (len / sizeof(uint32_t)); ++i) {
|
|
+ hash = _mm_crc32_u32(hash, *(const uint32_t*)str);
|
|
+ str += sizeof(uint32_t);
|
|
+ }
|
|
+# endif
|
|
+ if (len & sizeof(uint16_t)) {
|
|
+ hash = _mm_crc32_u16(hash, *(const uint16_t*)str);
|
|
+ str += sizeof(uint16_t);
|
|
+ }
|
|
+ if (len & sizeof(uint8_t)) {
|
|
+ hash = _mm_crc32_u8(hash, *(const uint8_t*)str);
|
|
+ }
|
|
+
|
|
+ return hash;
|
|
}
|
|
|
|
uint32_t
|
|
zix_digest_add_64(uint32_t hash, const void* const buf, const size_t len)
|
|
{
|
|
- assert((uintptr_t)buf % sizeof(uint64_t) == 0);
|
|
- assert(len % sizeof(uint64_t) == 0);
|
|
+ assert((uintptr_t)buf % sizeof(uint64_t) == 0);
|
|
+ assert(len % sizeof(uint64_t) == 0);
|
|
|
|
-#ifdef __x86_64__
|
|
- const uint64_t* ptr = (const uint64_t*)buf;
|
|
+# ifdef __x86_64__
|
|
+ const uint64_t* ptr = (const uint64_t*)buf;
|
|
|
|
- for (size_t i = 0; i < (len / sizeof(uint64_t)); ++i) {
|
|
- hash = (uint32_t)_mm_crc32_u64(hash, *ptr);
|
|
- ++ptr;
|
|
- }
|
|
+ for (size_t i = 0; i < (len / sizeof(uint64_t)); ++i) {
|
|
+ hash = (uint32_t)_mm_crc32_u64(hash, *ptr);
|
|
+ ++ptr;
|
|
+ }
|
|
|
|
- return hash;
|
|
-#else
|
|
- const uint32_t* ptr = (const uint32_t*)buf;
|
|
+ return hash;
|
|
+# else
|
|
+ const uint32_t* ptr = (const uint32_t*)buf;
|
|
|
|
- for (size_t i = 0; i < (len / sizeof(uint32_t)); ++i) {
|
|
- hash = _mm_crc32_u32(hash, *ptr);
|
|
- ++ptr;
|
|
- }
|
|
+ for (size_t i = 0; i < (len / sizeof(uint32_t)); ++i) {
|
|
+ hash = _mm_crc32_u32(hash, *ptr);
|
|
+ ++ptr;
|
|
+ }
|
|
|
|
- return hash;
|
|
-#endif
|
|
+ return hash;
|
|
+# endif
|
|
}
|
|
|
|
uint32_t
|
|
zix_digest_add_ptr(const uint32_t hash, const void* const ptr)
|
|
{
|
|
-#ifdef __x86_64__
|
|
- return (uint32_t)_mm_crc32_u64(hash, (uintptr_t)ptr);
|
|
-#else
|
|
- return _mm_crc32_u32(hash, (uintptr_t)ptr);
|
|
-#endif
|
|
+# ifdef __x86_64__
|
|
+ return (uint32_t)_mm_crc32_u64(hash, (uintptr_t)ptr);
|
|
+# else
|
|
+ return _mm_crc32_u32(hash, (uintptr_t)ptr);
|
|
+# endif
|
|
}
|
|
|
|
#else
|
|
@@ -108,34 +108,34 @@ zix_digest_add_ptr(const uint32_t hash, const void* const ptr)
|
|
uint32_t
|
|
zix_digest_start(void)
|
|
{
|
|
- return 5381;
|
|
+ return 5381;
|
|
}
|
|
|
|
uint32_t
|
|
zix_digest_add(uint32_t hash, const void* const buf, const size_t len)
|
|
{
|
|
- const uint8_t* str = (const uint8_t*)buf;
|
|
+ const uint8_t* str = (const uint8_t*)buf;
|
|
|
|
- for (size_t i = 0; i < len; ++i) {
|
|
- hash = (hash << 5u) + hash + str[i];
|
|
- }
|
|
+ for (size_t i = 0; i < len; ++i) {
|
|
+ hash = (hash << 5u) + hash + str[i];
|
|
+ }
|
|
|
|
- return hash;
|
|
+ return hash;
|
|
}
|
|
|
|
uint32_t
|
|
zix_digest_add_64(uint32_t hash, const void* const buf, const size_t len)
|
|
{
|
|
- assert((uintptr_t)buf % sizeof(uint64_t) == 0);
|
|
- assert(len % sizeof(uint64_t) == 0);
|
|
+ assert((uintptr_t)buf % sizeof(uint64_t) == 0);
|
|
+ assert(len % sizeof(uint64_t) == 0);
|
|
|
|
- return zix_digest_add(hash, buf, len);
|
|
+ return zix_digest_add(hash, buf, len);
|
|
}
|
|
|
|
uint32_t
|
|
zix_digest_add_ptr(const uint32_t hash, const void* const ptr)
|
|
{
|
|
- return zix_digest_add(hash, &ptr, sizeof(ptr));
|
|
+ return zix_digest_add(hash, &ptr, sizeof(ptr));
|
|
}
|
|
|
|
#endif
|
|
diff --git a/src/zix/digest.h b/src/zix/digest.h
|
|
index 74d13f9..1fde77a 100644
|
|
--- a/src/zix/digest.h
|
|
+++ b/src/zix/digest.h
|
|
@@ -29,7 +29,8 @@ extern "C" {
|
|
/**
|
|
Return an initial empty digest value.
|
|
*/
|
|
-ZIX_CONST_API uint32_t
|
|
+ZIX_CONST_API
|
|
+uint32_t
|
|
zix_digest_start(void);
|
|
|
|
/**
|
|
@@ -37,7 +38,8 @@ zix_digest_start(void);
|
|
|
|
This can be used for any size or alignment.
|
|
*/
|
|
-ZIX_PURE_API uint32_t
|
|
+ZIX_PURE_API
|
|
+uint32_t
|
|
zix_digest_add(uint32_t hash, const void* buf, size_t len);
|
|
|
|
/**
|
|
@@ -45,7 +47,8 @@ zix_digest_add(uint32_t hash, const void* buf, size_t len);
|
|
|
|
Both `buf` and `len` must be evenly divisible by 8 (64 bits).
|
|
*/
|
|
-ZIX_PURE_API uint32_t
|
|
+ZIX_PURE_API
|
|
+uint32_t
|
|
zix_digest_add_64(uint32_t hash, const void* buf, size_t len);
|
|
|
|
/**
|
|
@@ -53,11 +56,12 @@ zix_digest_add_64(uint32_t hash, const void* buf, size_t len);
|
|
|
|
This hashes the value of the pointer itself, and does not dereference `ptr`.
|
|
*/
|
|
-ZIX_CONST_API uint32_t
|
|
+ZIX_CONST_API
|
|
+uint32_t
|
|
zix_digest_add_ptr(uint32_t hash, const void* ptr);
|
|
|
|
#ifdef __cplusplus
|
|
-} /* extern "C" */
|
|
+} /* extern "C" */
|
|
#endif
|
|
|
|
-#endif /* ZIX_DIGEST_H */
|
|
+#endif /* ZIX_DIGEST_H */
|
|
diff --git a/src/zix/hash.c b/src/zix/hash.c
|
|
index a498b0f..8183082 100644
|
|
--- a/src/zix/hash.c
|
|
+++ b/src/zix/hash.c
|
|
@@ -1,5 +1,5 @@
|
|
/*
|
|
- Copyright 2011-2018 David Robillard <d@drobilla.net>
|
|
+ Copyright 2011-2020 David Robillard <d@drobilla.net>
|
|
|
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
@@ -25,109 +25,107 @@
|
|
from powers of two as possible.
|
|
*/
|
|
static const unsigned sizes[] = {
|
|
- 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593, 49157, 98317,
|
|
- 196613, 393241, 786433, 1572869, 3145739, 6291469, 12582917, 25165843,
|
|
- 50331653, 100663319, 201326611, 402653189, 805306457, 1610612741, 0
|
|
-};
|
|
+ 53, 97, 193, 389, 769, 1543, 3079,
|
|
+ 6151, 12289, 24593, 49157, 98317, 196613, 393241,
|
|
+ 786433, 1572869, 3145739, 6291469, 12582917, 25165843, 50331653,
|
|
+ 100663319, 201326611, 402653189, 805306457, 1610612741, 0};
|
|
|
|
typedef struct ZixHashEntry {
|
|
- struct ZixHashEntry* next; ///< Next entry in bucket
|
|
- uint32_t hash; ///< Non-modulo hash value
|
|
- // Value follows here (access with zix_hash_value)
|
|
+ struct ZixHashEntry* next; ///< Next entry in bucket
|
|
+ uint32_t hash; ///< Non-modulo hash value
|
|
+ // Value follows here (access with zix_hash_value)
|
|
} ZixHashEntry;
|
|
|
|
struct ZixHashImpl {
|
|
- ZixHashFunc hash_func;
|
|
- ZixEqualFunc equal_func;
|
|
- ZixHashEntry** buckets;
|
|
- const unsigned* n_buckets;
|
|
- size_t value_size;
|
|
- unsigned count;
|
|
+ ZixHashFunc hash_func;
|
|
+ ZixEqualFunc equal_func;
|
|
+ ZixHashEntry** buckets;
|
|
+ const unsigned* n_buckets;
|
|
+ size_t value_size;
|
|
+ unsigned count;
|
|
};
|
|
|
|
static inline void*
|
|
zix_hash_value(ZixHashEntry* entry)
|
|
{
|
|
- return entry + 1;
|
|
+ return entry + 1;
|
|
}
|
|
|
|
ZixHash*
|
|
-zix_hash_new(ZixHashFunc hash_func,
|
|
- ZixEqualFunc equal_func,
|
|
- size_t value_size)
|
|
+zix_hash_new(ZixHashFunc hash_func, ZixEqualFunc equal_func, size_t value_size)
|
|
{
|
|
- ZixHash* hash = (ZixHash*)malloc(sizeof(ZixHash));
|
|
- if (hash) {
|
|
- hash->hash_func = hash_func;
|
|
- hash->equal_func = equal_func;
|
|
- hash->n_buckets = &sizes[0];
|
|
- hash->value_size = value_size;
|
|
- hash->count = 0;
|
|
- if (!(hash->buckets = (ZixHashEntry**)calloc(*hash->n_buckets,
|
|
- sizeof(ZixHashEntry*)))) {
|
|
- free(hash);
|
|
- return NULL;
|
|
- }
|
|
- }
|
|
- return hash;
|
|
+ ZixHash* hash = (ZixHash*)malloc(sizeof(ZixHash));
|
|
+ if (hash) {
|
|
+ hash->hash_func = hash_func;
|
|
+ hash->equal_func = equal_func;
|
|
+ hash->n_buckets = &sizes[0];
|
|
+ hash->value_size = value_size;
|
|
+ hash->count = 0;
|
|
+ if (!(hash->buckets =
|
|
+ (ZixHashEntry**)calloc(*hash->n_buckets, sizeof(ZixHashEntry*)))) {
|
|
+ free(hash);
|
|
+ return NULL;
|
|
+ }
|
|
+ }
|
|
+ return hash;
|
|
}
|
|
|
|
void
|
|
zix_hash_free(ZixHash* hash)
|
|
{
|
|
- if (!hash) {
|
|
- return;
|
|
- }
|
|
-
|
|
- for (unsigned b = 0; b < *hash->n_buckets; ++b) {
|
|
- ZixHashEntry* bucket = hash->buckets[b];
|
|
- for (ZixHashEntry* e = bucket; e;) {
|
|
- ZixHashEntry* next = e->next;
|
|
- free(e);
|
|
- e = next;
|
|
- }
|
|
- }
|
|
-
|
|
- free(hash->buckets);
|
|
- free(hash);
|
|
+ if (!hash) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ for (unsigned b = 0; b < *hash->n_buckets; ++b) {
|
|
+ ZixHashEntry* bucket = hash->buckets[b];
|
|
+ for (ZixHashEntry* e = bucket; e;) {
|
|
+ ZixHashEntry* next = e->next;
|
|
+ free(e);
|
|
+ e = next;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ free(hash->buckets);
|
|
+ free(hash);
|
|
}
|
|
|
|
size_t
|
|
zix_hash_size(const ZixHash* hash)
|
|
{
|
|
- return hash->count;
|
|
+ return hash->count;
|
|
}
|
|
|
|
static inline void
|
|
insert_entry(ZixHashEntry** bucket, ZixHashEntry* entry)
|
|
{
|
|
- entry->next = *bucket;
|
|
- *bucket = entry;
|
|
+ entry->next = *bucket;
|
|
+ *bucket = entry;
|
|
}
|
|
|
|
static inline ZixStatus
|
|
rehash(ZixHash* hash, unsigned new_n_buckets)
|
|
{
|
|
- ZixHashEntry** new_buckets = (ZixHashEntry**)calloc(
|
|
- new_n_buckets, sizeof(ZixHashEntry*));
|
|
- if (!new_buckets) {
|
|
- return ZIX_STATUS_NO_MEM;
|
|
- }
|
|
-
|
|
- const unsigned old_n_buckets = *hash->n_buckets;
|
|
- for (unsigned b = 0; b < old_n_buckets; ++b) {
|
|
- for (ZixHashEntry* e = hash->buckets[b]; e;) {
|
|
- ZixHashEntry* const next = e->next;
|
|
- const unsigned h = e->hash % new_n_buckets;
|
|
- insert_entry(&new_buckets[h], e);
|
|
- e = next;
|
|
- }
|
|
- }
|
|
-
|
|
- free(hash->buckets);
|
|
- hash->buckets = new_buckets;
|
|
-
|
|
- return ZIX_STATUS_SUCCESS;
|
|
+ ZixHashEntry** new_buckets =
|
|
+ (ZixHashEntry**)calloc(new_n_buckets, sizeof(ZixHashEntry*));
|
|
+ if (!new_buckets) {
|
|
+ return ZIX_STATUS_NO_MEM;
|
|
+ }
|
|
+
|
|
+ const unsigned old_n_buckets = *hash->n_buckets;
|
|
+ for (unsigned b = 0; b < old_n_buckets; ++b) {
|
|
+ for (ZixHashEntry* e = hash->buckets[b]; e;) {
|
|
+ ZixHashEntry* const next = e->next;
|
|
+ const unsigned h = e->hash % new_n_buckets;
|
|
+ insert_entry(&new_buckets[h], e);
|
|
+ e = next;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ free(hash->buckets);
|
|
+ hash->buckets = new_buckets;
|
|
+
|
|
+ return ZIX_STATUS_SUCCESS;
|
|
}
|
|
|
|
static inline ZixHashEntry*
|
|
@@ -136,100 +134,97 @@ find_entry(const ZixHash* hash,
|
|
const unsigned h,
|
|
const unsigned h_nomod)
|
|
{
|
|
- for (ZixHashEntry* e = hash->buckets[h]; e; e = e->next) {
|
|
- if (e->hash == h_nomod && hash->equal_func(zix_hash_value(e), key)) {
|
|
- return e;
|
|
- }
|
|
- }
|
|
- return NULL;
|
|
+ for (ZixHashEntry* e = hash->buckets[h]; e; e = e->next) {
|
|
+ if (e->hash == h_nomod && hash->equal_func(zix_hash_value(e), key)) {
|
|
+ return e;
|
|
+ }
|
|
+ }
|
|
+ return NULL;
|
|
}
|
|
|
|
void*
|
|
zix_hash_find(const ZixHash* hash, const void* value)
|
|
{
|
|
- const unsigned h_nomod = hash->hash_func(value);
|
|
- const unsigned h = h_nomod % *hash->n_buckets;
|
|
- ZixHashEntry* const entry = find_entry(hash, value, h, h_nomod);
|
|
- return entry ? zix_hash_value(entry) : 0;
|
|
+ const unsigned h_nomod = hash->hash_func(value);
|
|
+ const unsigned h = h_nomod % *hash->n_buckets;
|
|
+ ZixHashEntry* const entry = find_entry(hash, value, h, h_nomod);
|
|
+ return entry ? zix_hash_value(entry) : 0;
|
|
}
|
|
|
|
ZixStatus
|
|
zix_hash_insert(ZixHash* hash, const void* value, void** inserted)
|
|
{
|
|
- unsigned h_nomod = hash->hash_func(value);
|
|
- unsigned h = h_nomod % *hash->n_buckets;
|
|
-
|
|
- ZixHashEntry* elem = find_entry(hash, value, h, h_nomod);
|
|
- if (elem) {
|
|
- assert(elem->hash == h_nomod);
|
|
- if (inserted) {
|
|
- *inserted = zix_hash_value(elem);
|
|
- }
|
|
- return ZIX_STATUS_EXISTS;
|
|
- }
|
|
-
|
|
- elem = (ZixHashEntry*)malloc(sizeof(ZixHashEntry) + hash->value_size);
|
|
- if (!elem) {
|
|
- return ZIX_STATUS_NO_MEM;
|
|
- }
|
|
- elem->next = NULL;
|
|
- elem->hash = h_nomod;
|
|
- memcpy(elem + 1, value, hash->value_size);
|
|
-
|
|
- const unsigned next_n_buckets = *(hash->n_buckets + 1);
|
|
- if (next_n_buckets != 0 && (hash->count + 1) >= next_n_buckets) {
|
|
- if (!rehash(hash, next_n_buckets)) {
|
|
- h = h_nomod % *(++hash->n_buckets);
|
|
- }
|
|
- }
|
|
-
|
|
- insert_entry(&hash->buckets[h], elem);
|
|
- ++hash->count;
|
|
- if (inserted) {
|
|
- *inserted = zix_hash_value(elem);
|
|
- }
|
|
- return ZIX_STATUS_SUCCESS;
|
|
+ unsigned h_nomod = hash->hash_func(value);
|
|
+ unsigned h = h_nomod % *hash->n_buckets;
|
|
+
|
|
+ ZixHashEntry* elem = find_entry(hash, value, h, h_nomod);
|
|
+ if (elem) {
|
|
+ assert(elem->hash == h_nomod);
|
|
+ if (inserted) {
|
|
+ *inserted = zix_hash_value(elem);
|
|
+ }
|
|
+ return ZIX_STATUS_EXISTS;
|
|
+ }
|
|
+
|
|
+ elem = (ZixHashEntry*)malloc(sizeof(ZixHashEntry) + hash->value_size);
|
|
+ if (!elem) {
|
|
+ return ZIX_STATUS_NO_MEM;
|
|
+ }
|
|
+ elem->next = NULL;
|
|
+ elem->hash = h_nomod;
|
|
+ memcpy(elem + 1, value, hash->value_size);
|
|
+
|
|
+ const unsigned next_n_buckets = *(hash->n_buckets + 1);
|
|
+ if (next_n_buckets != 0 && (hash->count + 1) >= next_n_buckets) {
|
|
+ if (!rehash(hash, next_n_buckets)) {
|
|
+ h = h_nomod % *(++hash->n_buckets);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ insert_entry(&hash->buckets[h], elem);
|
|
+ ++hash->count;
|
|
+ if (inserted) {
|
|
+ *inserted = zix_hash_value(elem);
|
|
+ }
|
|
+ return ZIX_STATUS_SUCCESS;
|
|
}
|
|
|
|
ZixStatus
|
|
zix_hash_remove(ZixHash* hash, const void* value)
|
|
{
|
|
- const unsigned h_nomod = hash->hash_func(value);
|
|
- const unsigned h = h_nomod % *hash->n_buckets;
|
|
-
|
|
- ZixHashEntry** next_ptr = &hash->buckets[h];
|
|
- for (ZixHashEntry* e = hash->buckets[h]; e; e = e->next) {
|
|
- if (h_nomod == e->hash &&
|
|
- hash->equal_func(zix_hash_value(e), value)) {
|
|
- *next_ptr = e->next;
|
|
- free(e);
|
|
- return ZIX_STATUS_SUCCESS;
|
|
- }
|
|
- next_ptr = &e->next;
|
|
- }
|
|
-
|
|
- if (hash->n_buckets != sizes) {
|
|
- const unsigned prev_n_buckets = *(hash->n_buckets - 1);
|
|
- if (hash->count - 1 <= prev_n_buckets) {
|
|
- if (!rehash(hash, prev_n_buckets)) {
|
|
- --hash->n_buckets;
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- --hash->count;
|
|
- return ZIX_STATUS_NOT_FOUND;
|
|
+ const unsigned h_nomod = hash->hash_func(value);
|
|
+ const unsigned h = h_nomod % *hash->n_buckets;
|
|
+
|
|
+ ZixHashEntry** next_ptr = &hash->buckets[h];
|
|
+ for (ZixHashEntry* e = hash->buckets[h]; e; e = e->next) {
|
|
+ if (h_nomod == e->hash && hash->equal_func(zix_hash_value(e), value)) {
|
|
+ *next_ptr = e->next;
|
|
+ free(e);
|
|
+ return ZIX_STATUS_SUCCESS;
|
|
+ }
|
|
+ next_ptr = &e->next;
|
|
+ }
|
|
+
|
|
+ if (hash->n_buckets != sizes) {
|
|
+ const unsigned prev_n_buckets = *(hash->n_buckets - 1);
|
|
+ if (hash->count - 1 <= prev_n_buckets) {
|
|
+ if (!rehash(hash, prev_n_buckets)) {
|
|
+ --hash->n_buckets;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ --hash->count;
|
|
+ return ZIX_STATUS_NOT_FOUND;
|
|
}
|
|
|
|
void
|
|
-zix_hash_foreach(ZixHash* hash,
|
|
- ZixHashVisitFunc f,
|
|
- void* user_data)
|
|
+zix_hash_foreach(ZixHash* hash, ZixHashVisitFunc f, void* user_data)
|
|
{
|
|
- for (unsigned b = 0; b < *hash->n_buckets; ++b) {
|
|
- ZixHashEntry* bucket = hash->buckets[b];
|
|
- for (ZixHashEntry* e = bucket; e; e = e->next) {
|
|
- f(zix_hash_value(e), user_data);
|
|
- }
|
|
- }
|
|
+ for (unsigned b = 0; b < *hash->n_buckets; ++b) {
|
|
+ ZixHashEntry* bucket = hash->buckets[b];
|
|
+ for (ZixHashEntry* e = bucket; e; e = e->next) {
|
|
+ f(zix_hash_value(e), user_data);
|
|
+ }
|
|
+ }
|
|
}
|
|
diff --git a/src/zix/hash.h b/src/zix/hash.h
|
|
index ce7fdc6..3905038 100644
|
|
--- a/src/zix/hash.h
|
|
+++ b/src/zix/hash.h
|
|
@@ -1,5 +1,5 @@
|
|
/*
|
|
- Copyright 2011-2015 David Robillard <d@drobilla.net>
|
|
+ Copyright 2011-2020 David Robillard <d@drobilla.net>
|
|
|
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
@@ -43,8 +43,7 @@ typedef uint32_t (*ZixHashFunc)(const void* value);
|
|
/**
|
|
Function to visit a hash element.
|
|
*/
|
|
-typedef void (*ZixHashVisitFunc)(void* value,
|
|
- void* user_data);
|
|
+typedef void (*ZixHashVisitFunc)(void* value, void* user_data);
|
|
|
|
/**
|
|
Create a new hash table.
|
|
@@ -59,21 +58,22 @@ typedef void (*ZixHashVisitFunc)(void* value,
|
|
@param equal_func A function to test value equality.
|
|
@param value_size The size of the values to be stored.
|
|
*/
|
|
-ZIX_API ZixHash*
|
|
-zix_hash_new(ZixHashFunc hash_func,
|
|
- ZixEqualFunc equal_func,
|
|
- size_t value_size);
|
|
+ZIX_API
|
|
+ZixHash*
|
|
+zix_hash_new(ZixHashFunc hash_func, ZixEqualFunc equal_func, size_t value_size);
|
|
|
|
/**
|
|
Free `hash`.
|
|
*/
|
|
-ZIX_API void
|
|
+ZIX_API
|
|
+void
|
|
zix_hash_free(ZixHash* hash);
|
|
|
|
/**
|
|
Return the number of elements in `hash`.
|
|
*/
|
|
-ZIX_PURE_API size_t
|
|
+ZIX_PURE_API
|
|
+size_t
|
|
zix_hash_size(const ZixHash* hash);
|
|
|
|
/**
|
|
@@ -90,10 +90,9 @@ zix_hash_size(const ZixHash* hash);
|
|
@param inserted The copy of `value` in the hash table.
|
|
@return ZIX_STATUS_SUCCESS, ZIX_STATUS_EXISTS, or ZIX_STATUS_NO_MEM.
|
|
*/
|
|
-ZIX_API ZixStatus
|
|
-zix_hash_insert(ZixHash* hash,
|
|
- const void* value,
|
|
- void** inserted);
|
|
+ZIX_API
|
|
+ZixStatus
|
|
+zix_hash_insert(ZixHash* hash, const void* value, void** inserted);
|
|
|
|
/**
|
|
Remove an item from `hash`.
|
|
@@ -102,9 +101,9 @@ zix_hash_insert(ZixHash* hash,
|
|
@param value The value to remove.
|
|
@return ZIX_STATUS_SUCCES or ZIX_STATUS_NOT_FOUND.
|
|
*/
|
|
-ZIX_API ZixStatus
|
|
-zix_hash_remove(ZixHash* hash,
|
|
- const void* value);
|
|
+ZIX_API
|
|
+ZixStatus
|
|
+zix_hash_remove(ZixHash* hash, const void* value);
|
|
|
|
/**
|
|
Search for an item in `hash`.
|
|
@@ -112,9 +111,9 @@ zix_hash_remove(ZixHash* hash,
|
|
@param hash The hash table.
|
|
@param value The value to search for.
|
|
*/
|
|
-ZIX_API void*
|
|
-zix_hash_find(const ZixHash* hash,
|
|
- const void* value);
|
|
+ZIX_API
|
|
+void*
|
|
+zix_hash_find(const ZixHash* hash, const void* value);
|
|
|
|
/**
|
|
Call `f` on each value in `hash`.
|
|
@@ -123,10 +122,9 @@ zix_hash_find(const ZixHash* hash,
|
|
@param f The function to call on each value.
|
|
@param user_data The user_data parameter passed to `f`.
|
|
*/
|
|
-ZIX_API void
|
|
-zix_hash_foreach(ZixHash* hash,
|
|
- ZixHashVisitFunc f,
|
|
- void* user_data);
|
|
+ZIX_API
|
|
+void
|
|
+zix_hash_foreach(ZixHash* hash, ZixHashVisitFunc f, void* user_data);
|
|
|
|
/**
|
|
@}
|
|
@@ -134,7 +132,7 @@ zix_hash_foreach(ZixHash* hash,
|
|
*/
|
|
|
|
#ifdef __cplusplus
|
|
-} /* extern "C" */
|
|
+} /* extern "C" */
|
|
#endif
|
|
|
|
-#endif /* ZIX_HASH_H */
|
|
+#endif /* ZIX_HASH_H */
|
|
2.29.2
|
|
|