diff options
Diffstat (limited to 'src/zix')
-rw-r--r-- | src/zix/btree.c | 923 | ||||
-rw-r--r-- | src/zix/btree.h | 174 | ||||
-rw-r--r-- | src/zix/common.h | 127 | ||||
-rw-r--r-- | src/zix/digest.c | 128 | ||||
-rw-r--r-- | src/zix/digest.h | 54 | ||||
-rw-r--r-- | src/zix/hash.c | 217 | ||||
-rw-r--r-- | src/zix/hash.h | 125 |
7 files changed, 0 insertions, 1748 deletions
diff --git a/src/zix/btree.c b/src/zix/btree.c deleted file mode 100644 index 05bbe6f..0000000 --- a/src/zix/btree.c +++ /dev/null @@ -1,923 +0,0 @@ -// Copyright 2011-2020 David Robillard <d@drobilla.net> -// SPDX-License-Identifier: ISC - -#include "zix/btree.h" - -#include <assert.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> - -// #define ZIX_BTREE_DEBUG 1 -// #define ZIX_BTREE_SORTED_CHECK 1 - -#ifndef ZIX_BTREE_PAGE_SIZE -# 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_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 -}; - -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; -}; - -#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112l) || \ - (defined(__cplusplus) && __cplusplus >= 201103L) -static_assert(sizeof(ZixBTreeNode) == ZIX_BTREE_PAGE_SIZE, ""); -#endif - -typedef struct { - ZixBTreeNode* node; - unsigned index; -} ZixBTreeIterFrame; - -struct ZixBTreeIterImpl { - unsigned n_levels; ///< Maximum depth of stack - unsigned level; ///< Current level in stack - ZixBTreeIterFrame stack[]; ///< Position stack -}; - -#ifdef ZIX_BTREE_DEBUG - -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"); -} - -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 - -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); -#endif - - ZixBTreeNode* node = (ZixBTreeNode*)malloc(sizeof(ZixBTreeNode)); - if (node) { - node->is_leaf = leaf; - node->n_vals = 0; - } - return node; -} - -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]; -} - -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]; -} - -ZixBTree* -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; -} - -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); - } -} - -void -zix_btree_free(ZixBTree* const t) -{ - if (t) { - zix_btree_free_rec(t, t->root); - free(t); - } -} - -size_t -zix_btree_size(const ZixBTree* const t) -{ - return t->size; -} - -static uint16_t -zix_btree_max_vals(const ZixBTreeNode* const node) -{ - return node->is_leaf ? ZIX_BTREE_LEAF_VALS : ZIX_BTREE_INODE_VALS; -} - -static uint16_t -zix_btree_min_vals(const ZixBTreeNode* const node) -{ - return (uint16_t)(((zix_btree_max_vals(node) + 1U) / 2U) - 1U); -} - -/** Shift pointers in `array` of length `n` right starting at `i`. */ -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; -} - -/** Erase element `i` in `array` of length `n` and return erased element. */ -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; -} - -/** Split lhs, the i'th child of `n`, into two nodes. */ -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; -} - -#ifdef ZIX_BTREE_SORTED_CHECK -/** Check that `n` is sorted with respect to search key `e`. */ -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; - } - - 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; -} -#endif - -/** Find the first value in `n` that is not less than `e` (lower bound). */ -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)); -#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; -} - -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; - } - - 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); - - ZixBTreeIter* i = (ZixBTreeIter*)calloc(1, sizeof(ZixBTreeIter) + s); - if (i) { - i->n_levels = t->height; - } - return i; -} - -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; - } -} - -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); -} - -/** Enlarge left child by stealing a value from its right sibling. */ -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); - - 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]; - - // 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 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; - - return lhs; -} - -/** Enlarge right child by stealing a value from its left sibling. */ -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); - - 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]); - - // 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 value from LHS to parent - parent->data.inode.vals[i - 1] = lhs->data.inode.vals[--lhs->n_vals]; - } - - return rhs; -} - -/** Move n[i] down, merge the left and right child, return the merged node. */ -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; -} - -/** Remove and return the min value from the subtree rooted at `n`. */ -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); - } - } - - return zix_btree_aerase(n->data.leaf.vals, --n->n_vals, 0); -} - -/** Remove and return the max value from the subtree rooted at `n`. */ -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); - } - } - - return n->data.leaf.vals[--n->n_vals]; -} - -ZixStatus -zix_btree_remove(ZixBTree* const t, - const void* const e, - 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; - } - - // 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 -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; - } - - 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) { - return ZIX_STATUS_SUCCESS; - } - - if (n->is_leaf) { - break; - } - - ++(*ti)->level; - n = zix_btree_child(n, i); - } - - zix_btree_iter_free(*ti); - *ti = NULL; - return ZIX_STATUS_NOT_FOUND; -} - -ZixStatus -zix_btree_lower_bound(const ZixBTree* const t, - const void* const e, - ZixBTreeIter** const ti) -{ - 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); -} - -ZixBTreeIter* -zix_btree_begin(const ZixBTree* const t) -{ - 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); -} - -ZixBTreeIter* -zix_btree_iter_copy(const ZixBTreeIter* const i) -{ - 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; -} - -bool -zix_btree_iter_is_end(const ZixBTreeIter* const i) -{ - return !i || (i->level == 0 && i->stack[0].node == NULL); -} - -bool -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; - } - - 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)); -} - -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; - } - } -} - -void -zix_btree_iter_free(ZixBTreeIter* const i) -{ - free(i); -} diff --git a/src/zix/btree.h b/src/zix/btree.h deleted file mode 100644 index b70f210..0000000 --- a/src/zix/btree.h +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright 2011-2020 David Robillard <d@drobilla.net> -// SPDX-License-Identifier: ISC - -#ifndef ZIX_BTREE_H -#define ZIX_BTREE_H - -#include "zix/common.h" - -#include <stdbool.h> -#include <stddef.h> - -#ifdef __cplusplus -extern "C" { -#endif - -/** - @addtogroup zix - @{ - @name BTree - @{ -*/ - -/** - A B-Tree. -*/ -typedef struct ZixBTreeImpl ZixBTree; - -/** - A B-Tree node (opaque). -*/ -typedef struct ZixBTreeNodeImpl ZixBTreeNode; - -/** - An iterator over a B-Tree. - - Note that modifying the trees invalidates all iterators, so all iterators - are const iterators. -*/ -typedef struct ZixBTreeIterImpl ZixBTreeIter; - -/** - Create a new (empty) B-Tree. -*/ -ZIX_API -ZixBTree* -zix_btree_new(ZixComparator cmp, const void* cmp_data, ZixDestroyFunc destroy); - -/** - Free `t`. -*/ -ZIX_API -void -zix_btree_free(ZixBTree* t); - -/** - Return the number of elements in `t`. -*/ -ZIX_PURE_API -size_t -zix_btree_size(const ZixBTree* t); - -/** - Insert the element `e` into `t`. -*/ -ZIX_API -ZixStatus -zix_btree_insert(ZixBTree* t, void* e); - -/** - Remove the value `e` from `t`. - - @param t Tree to remove from. - - @param e Value to remove. - - @param out Set to point to the removed pointer (which may not equal `e`). - - @param next If non-NULL, pointed to the value following `e`. If *next is - 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_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_btree_find(const ZixBTree* t, const void* e, ZixBTreeIter** ti); - -/** - Set `ti` to the smallest element in `t` that is not less than `e`. - - Wildcards are supported, so if the search key `e` compares equal to many - 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_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_btree_get(const ZixBTreeIter* ti); - -/** - Return an iterator to the first (smallest) element in `t`. - - The returned iterator must be freed with zix_btree_iter_free(). -*/ -ZIX_PURE_API -ZixBTreeIter* -zix_btree_begin(const ZixBTree* t); - -/** - Return an iterator to the end of `t` (one past the last element). - - The returned iterator must be freed with zix_btree_iter_free(). -*/ -ZIX_API -ZixBTreeIter* -zix_btree_end(const ZixBTree* t); - -/** - Return a new copy of `i`. -*/ -ZIX_API -ZixBTreeIter* -zix_btree_iter_copy(const ZixBTreeIter* i); - -/** - Return true iff `lhs` is equal to `rhs`. -*/ -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_btree_iter_is_end(const ZixBTreeIter* i); - -/** - Increment `i` to point to the next element in the tree. -*/ -ZIX_API -void -zix_btree_iter_increment(ZixBTreeIter* i); - -/** - Free `i`. -*/ -ZIX_API -void -zix_btree_iter_free(ZixBTreeIter* i); - -/** - @} - @} -*/ - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* ZIX_BTREE_H */ diff --git a/src/zix/common.h b/src/zix/common.h deleted file mode 100644 index 54f2303..0000000 --- a/src/zix/common.h +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2016-2020 David Robillard <d@drobilla.net> -// SPDX-License-Identifier: ISC - -#ifndef ZIX_COMMON_H -#define ZIX_COMMON_H - -#include <stdbool.h> - -/** - @addtogroup zix - @{ -*/ - -/** @cond */ -#ifndef ZIX_API -# 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 -# endif -#endif - -#ifdef __GNUC__ -# 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 -#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 - -/** @endcond */ - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __GNUC__ -# define ZIX_LOG_FUNC(fmt, arg1) __attribute__((format(printf, fmt, arg1))) -#else -# 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) -#elif defined(__GNUC__) -# define ZIX_UNUSED(name) name##_unused __attribute__((__unused__)) -#else -# 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 -} 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"; -} - -/** - Function for comparing two elements. -*/ -typedef int (*ZixComparator)(const void* a, - const void* b, - const void* user_data); - -/** - Function for testing equality of two elements. -*/ -typedef bool (*ZixEqualFunc)(const void* a, const void* b); - -/** - Function to destroy an element. -*/ -typedef void (*ZixDestroyFunc)(void* ptr); - -/** - @} -*/ - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* ZIX_COMMON_H */ diff --git a/src/zix/digest.c b/src/zix/digest.c deleted file mode 100644 index a7f983b..0000000 --- a/src/zix/digest.c +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2012-2020 David Robillard <d@drobilla.net> -// SPDX-License-Identifier: ISC - -#include "zix/digest.h" - -#ifdef __SSE4_2__ -# include <smmintrin.h> -#endif - -#include <assert.h> -#include <stdint.h> - -#ifdef __SSE4_2__ - -// SSE 4.2 CRC32 - -uint32_t -zix_digest_start(void) -{ - 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; -} - -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); - -# 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; - } - - 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; - } - - 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 -} - -#else - -// Classic DJB hash - -uint32_t -zix_digest_start(void) -{ - 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; - - for (size_t i = 0; i < len; ++i) { - hash = (hash << 5u) + hash + str[i]; - } - - 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); - - 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)); -} - -#endif diff --git a/src/zix/digest.h b/src/zix/digest.h deleted file mode 100644 index 3c294d9..0000000 --- a/src/zix/digest.h +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2012-2020 David Robillard <d@drobilla.net> -// SPDX-License-Identifier: ISC - -#ifndef ZIX_DIGEST_H -#define ZIX_DIGEST_H - -#include "zix/common.h" - -#include <stddef.h> -#include <stdint.h> - -#ifdef __cplusplus -extern "C" { -#endif - -/** - Return an initial empty digest value. -*/ -ZIX_CONST_API -uint32_t -zix_digest_start(void); - -/** - Update `hash` to include `buf`, a buffer of `len` bytes. - - This can be used for any size or alignment. -*/ -ZIX_PURE_API -uint32_t -zix_digest_add(uint32_t hash, const void* buf, size_t len); - -/** - Update `hash` to include `buf`, a 64-bit aligned buffer of `len` bytes. - - Both `buf` and `len` must be evenly divisible by 8 (64 bits). -*/ -ZIX_PURE_API -uint32_t -zix_digest_add_64(uint32_t hash, const void* buf, size_t len); - -/** - Update `hash` to include `ptr`. - - This hashes the value of the pointer itself, and does not dereference `ptr`. -*/ -ZIX_CONST_API -uint32_t -zix_digest_add_ptr(uint32_t hash, const void* ptr); - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* ZIX_DIGEST_H */ diff --git a/src/zix/hash.c b/src/zix/hash.c deleted file mode 100644 index 5952574..0000000 --- a/src/zix/hash.c +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright 2011-2020 David Robillard <d@drobilla.net> -// SPDX-License-Identifier: ISC - -#include "zix/hash.h" - -#include <assert.h> -#include <stdlib.h> -#include <string.h> - -/** - Primes, each slightly less than twice its predecessor, and as far away - 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}; - -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) -} ZixHashEntry; - -struct ZixHashImpl { - 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; -} - -ZixHash* -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; -} - -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); -} - -size_t -zix_hash_size(const ZixHash* hash) -{ - return hash->count; -} - -static inline void -insert_entry(ZixHashEntry** bucket, ZixHashEntry* 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; -} - -static inline ZixHashEntry* -find_entry(const ZixHash* hash, - const void* key, - 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; -} - -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; -} - -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; -} - -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; -} - -void -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); - } - } -} diff --git a/src/zix/hash.h b/src/zix/hash.h deleted file mode 100644 index 3c936f6..0000000 --- a/src/zix/hash.h +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright 2011-2020 David Robillard <d@drobilla.net> -// SPDX-License-Identifier: ISC - -#ifndef ZIX_HASH_H -#define ZIX_HASH_H - -#include "zix/common.h" - -#include <stddef.h> -#include <stdint.h> - -#ifdef __cplusplus -extern "C" { -#endif - -/** - @addtogroup zix - @{ - @name Hash - @{ -*/ - -typedef struct ZixHashImpl ZixHash; - -/** - Function for computing the hash of an element. -*/ -typedef uint32_t (*ZixHashFunc)(const void* value); - -/** - Function to visit a hash element. -*/ -typedef void (*ZixHashVisitFunc)(void* value, void* user_data); - -/** - Create a new hash table. - - To minimize space overhead, unlike many hash tables this stores a single - value, not a key and a value. Any size of value can be stored, but all the - values in the hash table must be the same size, and the values must be safe - to copy with memcpy. To get key:value behaviour, simply insert a struct - with a key and value into the hash. - - @param hash_func The hashing function. - @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); - -/** - Free `hash`. -*/ -ZIX_API -void -zix_hash_free(ZixHash* hash); - -/** - Return the number of elements in `hash`. -*/ -ZIX_PURE_API -size_t -zix_hash_size(const ZixHash* hash); - -/** - Insert an item into `hash`. - - If no matching value is found, ZIX_STATUS_SUCCESS will be returned, and @p - inserted will be pointed to the copy of `value` made in the new hash node. - - If a matching value already exists, ZIX_STATUS_EXISTS will be returned, and - `inserted` will be pointed to the existing value. - - @param hash The hash table. - @param value The value to be inserted. - @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); - -/** - Remove an item from `hash`. - - @param hash The hash table. - @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); - -/** - Search for an item in `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); - -/** - Call `f` on each value in `hash`. - - @param hash The hash table. - @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); - -/** - @} - @} -*/ - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* ZIX_HASH_H */ |