aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2021-09-11 01:28:39 -0400
committerDavid Robillard <d@drobilla.net>2022-01-28 21:57:07 -0500
commit56cceb103dc633d6af957472945e792187a5dd4e (patch)
tree154a7b228f4f2bfda422d16feb9863d8a6d6992a
parenteb804125430e3445e85c423b28e1c41346772ed0 (diff)
downloadserd-56cceb103dc633d6af957472945e792187a5dd4e.tar.gz
serd-56cceb103dc633d6af957472945e792187a5dd4e.tar.bz2
serd-56cceb103dc633d6af957472945e792187a5dd4e.zip
Update zix and make it a proper subproject
-rw-r--r--.gitmodules4
-rw-r--r--meson.build9
-rw-r--r--src/describe.c10
-rw-r--r--src/env.c1
-rw-r--r--src/model.c15
-rw-r--r--src/nodes.c2
-rw-r--r--src/zix/btree.c975
-rw-r--r--src/zix/btree.h213
-rw-r--r--src/zix/common.h137
-rw-r--r--src/zix/digest.c233
-rw-r--r--src/zix/digest.h109
-rw-r--r--src/zix/hash.c348
-rw-r--r--src/zix/hash.h329
m---------subprojects/zix0
-rw-r--r--subprojects/zix.wrap5
15 files changed, 33 insertions, 2357 deletions
diff --git a/.gitmodules b/.gitmodules
index 17929c01..3f314507 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,7 @@
[submodule "subprojects/exess"]
path = subprojects/exess
url = ../exess.git
+
+[submodule "subprojects/zix"]
+ path = subprojects/zix
+ url = ../zix.git
diff --git a/meson.build b/meson.build
index da14eb27..7d1d3c84 100644
--- a/meson.build
+++ b/meson.build
@@ -118,9 +118,6 @@ sources = [
'src/uri.c',
'src/world.c',
'src/writer.c',
- 'src/zix/btree.c',
- 'src/zix/digest.c',
- 'src/zix/hash.c',
]
# System libraries
@@ -170,6 +167,10 @@ library_args += ['-DSERD_MAJOR_VERSION=@0@'.format(serd_major_version),
'-DSERD_MINOR_VERSION=@0@'.format(serd_minor_version),
'-DSERD_MICRO_VERSION=@0@'.format(serd_micro_version)]
+zix_dep = dependency('zix-0',
+ version: '>= 0.0.1',
+ fallback: ['zix', 'zix_dep'])
+
exess_dep = dependency('exess-0',
version: '>= 0.0.1',
fallback: ['exess', 'exess_dep'])
@@ -181,7 +182,7 @@ libserd = build_target(
version: meson.project_version(),
include_directories: include_directories('include', 'src'),
c_args: c_warnings + platform_args + library_args,
- dependencies: [exess_dep, m_dep],
+ dependencies: [exess_dep, m_dep, zix_dep],
gnu_symbol_visibility: 'hidden',
install: true,
target_type: library_type)
diff --git a/src/describe.c b/src/describe.c
index 4a087222..695217fd 100644
--- a/src/describe.c
+++ b/src/describe.c
@@ -295,10 +295,12 @@ serd_describe_range(const SerdCursor* const range,
assert(sink);
- SerdStatus st = SERD_SUCCESS;
- SerdCursor copy = *range;
- ZixHash* const list_subjects = zix_hash_new(identity, ptr_hash, ptr_equals);
- DescribeContext ctx = {range->model, sink, list_subjects, flags};
+ SerdStatus st = SERD_SUCCESS;
+ SerdCursor copy = *range;
+ ZixHash* const list_subjects =
+ zix_hash_new(NULL, identity, ptr_hash, ptr_equals);
+
+ DescribeContext ctx = {range->model, sink, list_subjects, flags};
st = write_pretty_range(&ctx, 0, &copy, NULL, (flags & SERD_NO_TYPE_FIRST));
diff --git a/src/env.c b/src/env.c
index 71cb196f..df82381c 100644
--- a/src/env.c
+++ b/src/env.c
@@ -44,6 +44,7 @@ SerdEnv*
serd_env_new(SerdWorld* const world, const SerdStringView base_uri)
{
assert(world);
+ (void)world;
SerdEnv* env = (SerdEnv*)calloc(1, sizeof(struct SerdEnvImpl));
diff --git a/src/model.c b/src/model.c
index 0cf3b842..4e79fa77 100644
--- a/src/model.c
+++ b/src/model.c
@@ -73,7 +73,7 @@ serd_model_add_index(SerdModel* const model, const SerdStatementOrder order)
const unsigned* const ordering = orderings[order];
const ZixComparator comparator = serd_model_index_comparator(model, order);
- model->indices[order] = zix_btree_new(comparator, ordering);
+ model->indices[order] = zix_btree_new(NULL, comparator, ordering);
ZixStatus zst = model->indices[order] ? ZIX_STATUS_SUCCESS : ZIX_STATUS_ERROR;
@@ -103,7 +103,7 @@ serd_model_drop_index(SerdModel* const model, const SerdStatementOrder order)
return SERD_BAD_CALL;
}
- zix_btree_free(model->indices[order], NULL);
+ zix_btree_free(model->indices[order], NULL, NULL);
model->indices[order] = NULL;
return SERD_SUCCESS;
}
@@ -246,6 +246,13 @@ serd_model_drop_statement(SerdModel* const model,
serd_statement_free(statement);
}
+static void
+destroy_tree_statement(void* ptr, const void* user_data)
+{
+ (void)user_data;
+ serd_statement_free((SerdStatement*)ptr);
+}
+
void
serd_model_free(SerdModel* const model)
{
@@ -255,11 +262,11 @@ serd_model_free(SerdModel* const model)
// Free all statements (which are owned by the default index)
ZixBTree* const default_index = model->indices[model->default_order];
- zix_btree_clear(default_index, (ZixDestroyFunc)serd_statement_free);
+ zix_btree_clear(default_index, destroy_tree_statement, NULL);
// Free indices themselves
for (unsigned i = 0u; i < N_STATEMENT_ORDERS; ++i) {
- zix_btree_free(model->indices[i], NULL);
+ zix_btree_free(model->indices[i], NULL, NULL);
}
serd_nodes_free(model->nodes);
diff --git a/src/nodes.c b/src/nodes.c
index b821c004..529af688 100644
--- a/src/nodes.c
+++ b/src/nodes.c
@@ -202,7 +202,7 @@ serd_nodes_new(void)
{
SerdNodes* const nodes = (SerdNodes*)calloc(1, sizeof(SerdNodes));
- nodes->hash = zix_hash_new(nodes_key, nodes_hash, nodes_equal);
+ nodes->hash = zix_hash_new(NULL, nodes_key, nodes_hash, nodes_equal);
return nodes;
}
diff --git a/src/zix/btree.c b/src/zix/btree.c
deleted file mode 100644
index 56dfa98c..00000000
--- a/src/zix/btree.c
+++ /dev/null
@@ -1,975 +0,0 @@
-/*
- Copyright 2011-2021 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
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-
-#include "zix/btree.h"
-
-#include <assert.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
-// #define ZIX_BTREE_SORTED_CHECK 1
-
-// Define ZixShort as an integer type half the size of a pointer
-#if UINTPTR_MAX >= UINT32_MAX
-typedef uint32_t ZixShort;
-#else
-typedef uint16_t ZixShort;
-#endif
-
-#ifndef ZIX_BTREE_PAGE_SIZE
-# define ZIX_BTREE_PAGE_SIZE 4096u
-#endif
-
-#define ZIX_BTREE_NODE_SPACE (ZIX_BTREE_PAGE_SIZE - 2u * sizeof(ZixShort))
-#define ZIX_BTREE_LEAF_VALS ((ZIX_BTREE_NODE_SPACE / sizeof(void*)) - 1u)
-#define ZIX_BTREE_INODE_VALS (ZIX_BTREE_LEAF_VALS / 2u)
-
-struct ZixBTreeImpl {
- ZixBTreeNode* root;
- ZixComparator cmp;
- const void* cmp_data;
- size_t size;
-};
-
-struct ZixBTreeNodeImpl {
- ZixShort is_leaf;
- ZixShort n_vals;
-
- union {
- struct {
- void* vals[ZIX_BTREE_LEAF_VALS];
- } leaf;
-
- struct {
- void* vals[ZIX_BTREE_INODE_VALS];
- ZixBTreeNode* children[ZIX_BTREE_INODE_VALS + 1u];
- } inode;
- } data;
-};
-
-#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112l) || \
- (defined(__cplusplus) && __cplusplus >= 201103L)
-static_assert(sizeof(ZixBTreeNode) == ZIX_BTREE_PAGE_SIZE, "");
-#endif
-
-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 - 2u * sizeof(ZixBTreeNode*));
-#endif
-
- ZixBTreeNode* const node = (ZixBTreeNode*)malloc(sizeof(ZixBTreeNode));
- if (node) {
- node->is_leaf = leaf;
- node->n_vals = 0u;
- }
-
- return node;
-}
-
-ZIX_PURE_FUNC
-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)
-{
- ZixBTree* const t = (ZixBTree*)malloc(sizeof(ZixBTree));
- if (!t) {
- return NULL;
- }
-
- if (!(t->root = zix_btree_node_new(true))) {
- free(t);
- return NULL;
- }
-
- t->cmp = cmp;
- t->cmp_data = cmp_data;
- t->size = 0;
-
- return t;
-}
-
-static void
-zix_btree_free_children(ZixBTree* const t,
- ZixBTreeNode* const n,
- ZixDestroyFunc destroy)
-{
- if (!n->is_leaf) {
- for (ZixShort i = 0; i < n->n_vals + 1u; ++i) {
- zix_btree_free_children(t, zix_btree_child(n, i), destroy);
- free(zix_btree_child(n, i));
- }
- }
-
- if (destroy) {
- if (n->is_leaf) {
- for (ZixShort i = 0u; i < n->n_vals; ++i) {
- destroy(n->data.leaf.vals[i]);
- }
- } else {
- for (ZixShort i = 0u; i < n->n_vals; ++i) {
- destroy(n->data.inode.vals[i]);
- }
- }
- }
-}
-
-void
-zix_btree_free(ZixBTree* const t, ZixDestroyFunc destroy)
-{
- if (t) {
- zix_btree_clear(t, destroy);
- free(t->root);
- free(t);
- }
-}
-
-void
-zix_btree_clear(ZixBTree* const t, ZixDestroyFunc destroy)
-{
- zix_btree_free_children(t, t->root, destroy);
-
- memset(t->root, 0, sizeof(ZixBTreeNode));
- t->root->is_leaf = true;
- t->size = 0u;
-}
-
-size_t
-zix_btree_size(const ZixBTree* const t)
-{
- return t->size;
-}
-
-static ZixShort
-zix_btree_max_vals(const ZixBTreeNode* const node)
-{
- return node->is_leaf ? ZIX_BTREE_LEAF_VALS : ZIX_BTREE_INODE_VALS;
-}
-
-static ZixShort
-zix_btree_min_vals(const ZixBTreeNode* const node)
-{
- return (ZixShort)(((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 ZixShort 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 = (ZixShort)(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 ZixComparator compare,
- const void* const compare_user_data,
- void* const* const values,
- const unsigned n_values,
- const void* const key)
-{
- if (n_values <= 1u) {
- return true;
- }
-
- int cmp = compare(values[0], key, compare_user_data);
- for (unsigned i = 1u; i < n_values; ++i) {
- const int next_cmp = compare(values[i], key, compare_user_data);
- if ((cmp >= 0 && next_cmp < 0) || (cmp > 0 && next_cmp <= 0)) {
- return false;
- }
-
- cmp = next_cmp;
- }
-
- return true;
-}
-#endif
-
-static unsigned
-zix_btree_find_value(const ZixComparator compare,
- const void* const compare_user_data,
- void* const* const values,
- const unsigned n_values,
- const void* const key,
- bool* const equal)
-{
- unsigned first = 0u;
- unsigned count = n_values;
-
- while (count > 0u) {
- const unsigned half = count >> 1u;
- const unsigned i = first + half;
- void* const value = values[i];
- const int cmp = compare(value, key, compare_user_data);
-
- if (!cmp) {
- *equal = true;
- return i;
- }
-
- if (cmp < 0) {
- first += half + 1u;
- count -= half + 1u;
- } else {
- count = half;
- }
- }
-
- assert(first == n_values || compare(values[first], key, compare_user_data));
- *equal = false;
- return first;
-}
-
-static unsigned
-zix_btree_find_pattern(const ZixComparator compare_key,
- const void* const compare_key_user_data,
- void* const* const values,
- const unsigned n_values,
- const void* const key,
- bool* const equal)
-{
-#ifdef ZIX_BTREE_SORTED_CHECK
- assert(zix_btree_node_is_sorted_with_respect_to(
- compare_key, compare_key_user_data, values, n_values, key));
-#endif
-
- unsigned first = 0u;
- unsigned count = n_values;
-
- while (count > 0u) {
- const unsigned half = count >> 1u;
- const unsigned i = first + half;
- void* const value = values[i];
- const int cmp = compare_key(value, key, compare_key_user_data);
-
- if (cmp == 0) {
- // Found a match, but keep searching for the leftmost one
- *equal = true;
- count = half;
-
- } else if (cmp < 0) {
- // Search right half
- first += half + 1u;
- count -= half + 1u;
-
- } else {
- // Search left half
- count = half;
- }
- }
-
- assert(!*equal ||
- (compare_key(values[first], key, compare_key_user_data) == 0 &&
- (first == 0u ||
- (compare_key(values[first - 1u], key, compare_key_user_data) < 0))));
-
- return first;
-}
-
-/// Convenience wrapper to find a value in an internal node
-static unsigned
-zix_btree_inode_find(const ZixBTree* const t,
- const ZixBTreeNode* const n,
- const void* const e,
- bool* const equal)
-{
- assert(!n->is_leaf);
-
- return zix_btree_find_value(
- t->cmp, t->cmp_data, n->data.inode.vals, n->n_vals, e, equal);
-}
-
-/// Convenience wrapper to find a value in a leaf node
-static unsigned
-zix_btree_leaf_find(const ZixBTree* const t,
- const ZixBTreeNode* const n,
- const void* const e,
- bool* const equal)
-{
- assert(n->is_leaf);
-
- return zix_btree_find_value(
- t->cmp, t->cmp_data, n->data.leaf.vals, n->n_vals, e, equal);
-}
-
-static inline bool
-zix_btree_can_remove_from(const ZixBTreeNode* const n)
-{
- assert(n->n_vals >= zix_btree_min_vals(n));
- return n->n_vals > zix_btree_min_vals(n);
-}
-
-static inline bool
-zix_btree_is_full(const ZixBTreeNode* const n)
-{
- assert(n->n_vals <= zix_btree_max_vals(n));
- return n->n_vals == zix_btree_max_vals(n);
-}
-
-static ZixStatus
-zix_btree_grow_up(ZixBTree* const t)
-{
- ZixBTreeNode* const new_root = zix_btree_node_new(false);
- if (!new_root) {
- return ZIX_STATUS_NO_MEM;
- }
-
- // Set old root as the only child of the new root
- new_root->data.inode.children[0] = t->root;
-
- // Split the old root to get two balanced siblings
- zix_btree_split_child(new_root, 0, t->root);
- t->root = new_root;
-
- return ZIX_STATUS_SUCCESS;
-}
-
-ZixStatus
-zix_btree_insert(ZixBTree* const t, void* const e)
-{
- ZixStatus st = ZIX_STATUS_SUCCESS;
-
- // Grow up if necessary to ensure the root is not full
- if (zix_btree_is_full(t->root)) {
- if ((st = zix_btree_grow_up(t))) {
- return st;
- }
- }
-
- // Walk down from the root until we reach a suitable leaf
- ZixBTreeNode* node = t->root;
- while (!node->is_leaf) {
- // Search for the value in this node
- bool equal = false;
- const unsigned i = zix_btree_inode_find(t, node, e, &equal);
- if (equal) {
- return ZIX_STATUS_EXISTS;
- }
-
- // Value not in this node, but may be in the ith child
- ZixBTreeNode* child = node->data.inode.children[i];
- if (zix_btree_is_full(child)) {
- // The child is full, split it before continuing
- ZixBTreeNode* const rhs = zix_btree_split_child(node, i, child);
- if (!rhs) {
- return ZIX_STATUS_NO_MEM;
- }
-
- // Compare with new split value to determine which side to use
- const int cmp = t->cmp(node->data.inode.vals[i], e, t->cmp_data);
- if (cmp < 0) {
- child = rhs; // Split value is less than the new value, move right
- } else if (cmp == 0) {
- return ZIX_STATUS_EXISTS; // Split value is exactly the value to insert
- }
- }
-
- // Descend to child node and continue
- node = child;
- }
-
- // Search for the value in the leaf
- bool equal = false;
- const unsigned i = zix_btree_leaf_find(t, node, e, &equal);
- if (equal) {
- return ZIX_STATUS_EXISTS;
- }
-
- // The value is not in the tree, insert into the leaf
- zix_btree_ainsert(node->data.leaf.vals, node->n_vals++, i, e);
- ++t->size;
- return ZIX_STATUS_SUCCESS;
-}
-
-static void
-zix_btree_iter_set_frame(ZixBTreeIter* const ti,
- ZixBTreeNode* const n,
- const ZixShort i)
-{
- ti->nodes[ti->level] = n;
- ti->indexes[ti->level] = (uint16_t)i;
-}
-
-static void
-zix_btree_iter_push(ZixBTreeIter* const ti,
- ZixBTreeNode* const n,
- const ZixShort i)
-{
- assert(ti->level < ZIX_BTREE_MAX_HEIGHT);
- ++ti->level;
- ti->nodes[ti->level] = n;
- ti->indexes[ti->level] = (uint16_t)i;
-}
-
-static void
-zix_btree_iter_pop(ZixBTreeIter* const ti)
-{
- assert(ti->level > 0u);
- ti->nodes[ti->level] = NULL;
- ti->indexes[ti->level] = 0u;
- --ti->level;
-}
-
-/// 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 a 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(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 = (ZixShort)(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)
-{
- assert(zix_btree_can_remove_from(n));
-
- while (!n->is_leaf) {
- ZixBTreeNode* const* const children = n->data.inode.children;
-
- n = zix_btree_can_remove_from(children[0]) ? children[0]
- : zix_btree_can_remove_from(children[1]) ? zix_btree_rotate_left(n, 0)
- : zix_btree_merge(t, 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)
-{
- assert(zix_btree_can_remove_from(n));
-
- while (!n->is_leaf) {
- ZixBTreeNode* const* const children = n->data.inode.children;
-
- const unsigned y = n->n_vals - 1u;
- const unsigned z = n->n_vals;
-
- n = zix_btree_can_remove_from(children[z]) ? children[z]
- : zix_btree_can_remove_from(children[y]) ? zix_btree_rotate_right(n, z)
- : zix_btree_merge(t, n, y);
- }
-
- return n->data.leaf.vals[--n->n_vals];
-}
-
-static ZixBTreeNode*
-zix_btree_fatten_child(ZixBTree* const t, ZixBTreeIter* const iter)
-{
- ZixBTreeNode* const n = iter->nodes[iter->level];
- const ZixShort i = iter->indexes[iter->level];
-
- assert(!n->is_leaf);
- ZixBTreeNode* const* const children = n->data.inode.children;
-
- if (i > 0 && zix_btree_can_remove_from(children[i - 1u])) {
- return zix_btree_rotate_right(n, i); // Steal a key from left sibling
- }
-
- if (i < n->n_vals && zix_btree_can_remove_from(children[i + 1u])) {
- return zix_btree_rotate_left(n, i); // Steal a key from right sibling
- }
-
- // Both child's siblings are minimal, merge them
-
- if (i == n->n_vals) {
- --iter->indexes[iter->level];
- return zix_btree_merge(t, n, i - 1u); // Merge last two children
- }
-
- return zix_btree_merge(t, n, i); // Merge left and right siblings
-}
-
-/// Replace the ith value in `n` with one from a child if possible
-static ZixStatus
-zix_btree_replace_value(ZixBTree* const t,
- ZixBTreeNode* const n,
- const unsigned i,
- void** const out)
-{
- ZixBTreeNode* const lhs = zix_btree_child(n, i);
- ZixBTreeNode* const rhs = zix_btree_child(n, i + 1);
- if (!zix_btree_can_remove_from(lhs) && !zix_btree_can_remove_from(rhs)) {
- return ZIX_STATUS_NOT_FOUND;
- }
-
- // Stash the value for the caller before it is replaced
- *out = n->data.inode.vals[i];
-
- n->data.inode.vals[i] =
- // Left child has more values, steal its largest
- (lhs->n_vals > rhs->n_vals) ? zix_btree_remove_max(t, lhs)
-
- // Right child has more values, steal its smallest
- : (rhs->n_vals > lhs->n_vals) ? zix_btree_remove_min(t, rhs)
-
- // Children are balanced, use index parity as a low-bias tie breaker
- : (i & 1u) ? zix_btree_remove_max(t, lhs)
- : zix_btree_remove_min(t, rhs);
-
- return ZIX_STATUS_SUCCESS;
-}
-
-ZixStatus
-zix_btree_remove(ZixBTree* const t,
- const void* const e,
- void** const out,
- ZixBTreeIter* const next)
-{
- ZixBTreeNode* n = t->root;
- ZixBTreeIter* ti = next;
- ZixStatus st = ZIX_STATUS_SUCCESS;
-
- *ti = zix_btree_end_iter;
-
- /* 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. This ensures that there is always room to remove, without
- having to merge nodes again on a traversal back up. */
-
- if (!n->is_leaf && n->n_vals == 1u &&
- !zix_btree_can_remove_from(n->data.inode.children[0u]) &&
- !zix_btree_can_remove_from(n->data.inode.children[1u])) {
- // Root has only two children, both minimal, merge them into a new root
- n = zix_btree_merge(t, n, 0);
- }
-
- while (!n->is_leaf) {
- assert(n == t->root || zix_btree_can_remove_from(n));
-
- // Search for the value in the current node and update the iterator
- bool equal = false;
- const unsigned i = zix_btree_inode_find(t, n, e, &equal);
-
- zix_btree_iter_set_frame(ti, n, i);
-
- if (equal) {
- // Found in internal node
- if (!(st = zix_btree_replace_value(t, n, i, out))) {
- // Replaced hole with a value from a direct child
- --t->size;
- return st;
- }
-
- // Both preceding and succeeding child are minimal, merge and continue
- n = zix_btree_merge(t, n, i);
-
- } else {
- // Not found in internal node, is in the ith child if anywhere
- n = zix_btree_can_remove_from(zix_btree_child(n, i))
- ? zix_btree_child(n, i)
- : zix_btree_fatten_child(t, ti);
- }
-
- ++ti->level;
- }
-
- // We're at the leaf the value may be in, search for the value in it
- bool equal = false;
- const unsigned i = zix_btree_leaf_find(t, n, e, &equal);
-
- if (!equal) { // Not found in tree
- *ti = zix_btree_end_iter;
- return ZIX_STATUS_NOT_FOUND;
- }
-
- // Erase from leaf node
- *out = zix_btree_aerase(n->data.leaf.vals, --n->n_vals, i);
-
- // Update next iterator
- if (n->n_vals == 0u) {
- // Removed the last element in the tree
- assert(n == t->root);
- assert(t->size == 1u);
- *ti = zix_btree_end_iter;
- } else if (i == n->n_vals) {
- // Removed the largest element in this leaf, increment to the next
- zix_btree_iter_set_frame(ti, n, i - 1);
- zix_btree_iter_increment(ti);
- } else {
- zix_btree_iter_set_frame(ti, n, i);
- }
-
- --t->size;
- return ZIX_STATUS_SUCCESS;
-}
-
-ZixStatus
-zix_btree_find(const ZixBTree* const t,
- const void* const e,
- ZixBTreeIter* const ti)
-{
- ZixBTreeNode* n = t->root;
-
- *ti = zix_btree_end_iter;
-
- while (!n->is_leaf) {
- bool equal = false;
- const unsigned i = zix_btree_inode_find(t, n, e, &equal);
-
- zix_btree_iter_set_frame(ti, n, i);
-
- if (equal) {
- return ZIX_STATUS_SUCCESS;
- }
-
- ++ti->level;
- n = zix_btree_child(n, i);
- }
-
- bool equal = false;
- const unsigned i = zix_btree_leaf_find(t, n, e, &equal);
- if (equal) {
- zix_btree_iter_set_frame(ti, n, i);
- return ZIX_STATUS_SUCCESS;
- }
-
- *ti = zix_btree_end_iter;
- return ZIX_STATUS_NOT_FOUND;
-}
-
-ZixStatus
-zix_btree_lower_bound(const ZixBTree* const t,
- const ZixComparator compare,
- const void* const compare_user_data,
- const void* const key,
- ZixBTreeIter* const ti)
-{
- *ti = zix_btree_end_iter;
-
- ZixBTreeNode* n = t->root; // Current node
- uint16_t found_level = 0u; // Lowest level a match was found at
- bool found = false; // True if a match was ever found
-
- // Search down until we reach a leaf
- while (!n->is_leaf) {
- bool equal = false;
- const unsigned i = zix_btree_find_pattern(
- compare, compare_user_data, n->data.inode.vals, n->n_vals, key, &equal);
-
- zix_btree_iter_set_frame(ti, n, i);
- if (equal) {
- found_level = ti->level;
- found = true;
- }
-
- ++ti->level;
- n = zix_btree_child(n, i);
- }
-
- bool equal = false;
- const unsigned i = zix_btree_find_pattern(
- compare, compare_user_data, n->data.leaf.vals, n->n_vals, key, &equal);
-
- zix_btree_iter_set_frame(ti, n, i);
- if (equal) {
- return ZIX_STATUS_SUCCESS;
- }
-
- if (ti->indexes[ti->level] == ti->nodes[ti->level]->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 = zix_btree_end_iter;
- }
- }
-
- return ZIX_STATUS_SUCCESS;
-}
-
-void*
-zix_btree_get(const ZixBTreeIter ti)
-{
- const ZixBTreeNode* const node = ti.nodes[ti.level];
- const unsigned index = ti.indexes[ti.level];
-
- assert(node);
- assert(index < node->n_vals);
-
- return node->is_leaf ? node->data.leaf.vals[index]
- : node->data.inode.vals[index];
-}
-
-ZixBTreeIter
-zix_btree_begin(const ZixBTree* const t)
-{
- ZixBTreeIter iter = zix_btree_end_iter;
-
- if (t->size > 0u) {
- ZixBTreeNode* n = t->root;
- zix_btree_iter_set_frame(&iter, n, 0u);
-
- while (!n->is_leaf) {
- n = zix_btree_child(n, 0);
- zix_btree_iter_push(&iter, n, 0u);
- }
- }
-
- return iter;
-}
-
-ZixBTreeIter
-zix_btree_end(const ZixBTree* const t)
-{
- (void)t;
-
- return zix_btree_end_iter;
-}
-
-bool
-zix_btree_iter_equals(const ZixBTreeIter lhs, const ZixBTreeIter rhs)
-{
- const size_t indexes_size = (lhs.level + 1u) * sizeof(uint16_t);
-
- return (lhs.level == rhs.level) && (lhs.nodes[0] == rhs.nodes[0]) &&
- (!lhs.nodes[0] || !memcmp(lhs.indexes, rhs.indexes, indexes_size));
-}
-
-ZixStatus
-zix_btree_iter_increment(ZixBTreeIter* const i)
-{
- assert(!zix_btree_iter_is_end(*i));
-
- // Move to the next value in the current node
- const uint16_t index = ++i->indexes[i->level];
-
- if (i->nodes[i->level]->is_leaf) {
- // Leaf, move up if necessary until we're not at the end of the node
- while (i->indexes[i->level] >= i->nodes[i->level]->n_vals) {
- if (i->level == 0) {
- // End of root, end of tree
- i->nodes[0] = NULL;
- return ZIX_STATUS_REACHED_END;
- }
-
- // At end of internal node, move up
- zix_btree_iter_pop(i);
- }
-
- } else {
- // Internal node, move down to next child
- const ZixBTreeNode* const node = i->nodes[i->level];
- ZixBTreeNode* const child = node->data.inode.children[index];
-
- zix_btree_iter_push(i, child, 0u);
-
- // Move down and left until we hit a leaf
- while (!i->nodes[i->level]->is_leaf) {
- zix_btree_iter_push(i, i->nodes[i->level]->data.inode.children[0], 0u);
- }
- }
-
- return ZIX_STATUS_SUCCESS;
-}
-
-ZixBTreeIter
-zix_btree_iter_next(const ZixBTreeIter iter)
-{
- ZixBTreeIter next = iter;
-
- zix_btree_iter_increment(&next);
-
- return next;
-}
diff --git a/src/zix/btree.h b/src/zix/btree.h
deleted file mode 100644
index 3576789c..00000000
--- a/src/zix/btree.h
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- 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
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-
-#ifndef ZIX_BTREE_H
-#define ZIX_BTREE_H
-
-#include "zix/common.h"
-
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdint.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- @addtogroup zix
- @{
- @name BTree
- @{
-*/
-
-/**
- The maximum height of a ZixBTree.
-
- This is exposed because it determines the size of iterators, which are
- statically sized so they can used on the stack. The usual degree (or
- "fanout") of a B-Tree is high enough that a relatively short tree can
- contain many elements. With the default page size of 4 KiB, the default
- height of 6 is enough to store trillions.
-*/
-#ifndef ZIX_BTREE_MAX_HEIGHT
-# define ZIX_BTREE_MAX_HEIGHT 6u
-#endif
-
-/// 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 tree invalidates all iterators.
-
- The contents of this type are considered an implementation detail and should
- not be used directly by clients. They are nevertheless exposed here so that
- iterators can be allocated on the stack.
-*/
-typedef struct {
- ZixBTreeNode* nodes[ZIX_BTREE_MAX_HEIGHT]; ///< Parallel node pointer stack
- uint16_t indexes[ZIX_BTREE_MAX_HEIGHT]; ///< Parallel child index stack
- uint16_t level; ///< Current level in stack
-} ZixBTreeIter;
-
-/// A static end iterator for convenience
-static const ZixBTreeIter zix_btree_end_iter = {
- {NULL, NULL, NULL, NULL, NULL, NULL},
- {0u, 0u, 0u, 0u, 0u, 0u},
- 0u};
-
-/**
- Create a new (empty) B-Tree.
-
- The given comparator must be a total ordering and is used to internally
- organize the tree and look for values exactly.
-
- Searching can be done with a custom comparator that supports wildcards, see
- zix_btree_lower_bound() for details.
-*/
-ZIX_API
-ZixBTree*
-zix_btree_new(ZixComparator cmp, const void* cmp_data);
-
-/**
- Free `t` and all the nodes it contains.
-
- @param destroy Function to call once for every value in the tree. This can
- be used to free values if they are dynamically allocated.
-*/
-ZIX_API
-void
-zix_btree_free(ZixBTree* t, ZixDestroyFunc destroy);
-
-/**
- Clear everything from `t`, leaving it empty.
-
- @param destroy Function called exactly once for every value in the tree,
- just before that value is removed from the tree.
-*/
-ZIX_API
-void
-zix_btree_clear(ZixBTree* t, ZixDestroyFunc destroy);
-
-/// 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 On successful return, set to point at the value that immediately
- followed `e`.
-*/
-ZIX_API
-ZixStatus
-zix_btree_remove(ZixBTree* t, const void* e, void** out, ZixBTreeIter* next);
-
-/**
- Set `ti` to an element exactly equal to `e` in `t`.
-
- If no such item exists, `ti` is set to the end.
-*/
-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`.
-
- The given comparator must be compatible with the tree comparator, that is,
- any two values must have the same ordering according to both. Within this
- constraint, it may implement fuzzier searching by handling special search
- key values, for example with wildcards.
-
- If the search key `e` compares equal to many values in the tree, then `ti`
- will be set to the least such element.
-
- The comparator is always called with an actual value in the tree as the
- first argument, and `key` as the second argument.
-*/
-ZIX_API
-ZixStatus
-zix_btree_lower_bound(const ZixBTree* t,
- ZixComparator compare,
- const void* compare_user_data,
- const void* key,
- ZixBTreeIter* ti);
-
-/// Return the data at the given position in the tree
-ZIX_PURE_API
-void*
-zix_btree_get(ZixBTreeIter ti);
-
-/// Return an iterator to the first (smallest) element in `t`
-ZIX_PURE_API
-ZixBTreeIter
-zix_btree_begin(const ZixBTree* t);
-
-/// Return an iterator to the end of `t` (one past the last element)
-ZIX_CONST_API
-ZixBTreeIter
-zix_btree_end(const ZixBTree* t);
-
-/// Return true iff `lhs` is equal to `rhs`
-ZIX_PURE_API
-bool
-zix_btree_iter_equals(ZixBTreeIter lhs, ZixBTreeIter rhs);
-
-/// Return true iff `i` is an iterator at the end of a tree
-static inline bool
-zix_btree_iter_is_end(const ZixBTreeIter i)
-{
- return i.level == 0 && !i.nodes[0];
-}
-
-/// Increment `i` to point to the next element in the tree
-ZIX_API
-ZixStatus
-zix_btree_iter_increment(ZixBTreeIter* i);
-
-/// Return an iterator one past `iter`
-ZIX_PURE_API
-ZixBTreeIter
-zix_btree_iter_next(ZixBTreeIter iter);
-
-/**
- @}
- @}
-*/
-
-#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 02f00b45..00000000
--- a/src/zix/common.h
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- 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
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-
-#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,
- ZIX_STATUS_REACHED_END
-} 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";
- case ZIX_STATUS_REACHED_END:
- return "Reached end";
- }
- 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 dcfadf8f..00000000
--- a/src/zix/digest.c
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- Copyright 2012-2021 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
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-
-#include "zix/digest.h"
-
-#include <assert.h>
-#include <stdint.h>
-#include <string.h>
-
-#if defined(__clang__) && __clang_major__ >= 12
-# define FALLTHROUGH() __attribute__((fallthrough))
-#elif defined(__GNUC__) && !defined(__clang__)
-# define FALLTHROUGH() __attribute__((fallthrough))
-#else
-# define FALLTHROUGH()
-#endif
-
-/*
- 64-bit hash: Essentially fasthash64, implemented here in an aligned/padded
- and a general UB-free variant.
-*/
-
-static inline uint64_t
-mix64(uint64_t h)
-{
- h ^= h >> 23u;
- h *= 0x2127599BF4325C37ull;
- h ^= h >> 47u;
- return h;
-}
-
-uint64_t
-zix_digest64(const uint64_t seed, const void* const key, const size_t len)
-{
- static const uint64_t m = 0x880355F21E6D1965ull;
-
- const uint64_t* const blocks = (const uint64_t*)key;
- const size_t n_blocks = len / sizeof(uint64_t);
-
- uint64_t h = seed ^ (len * m);
- for (size_t i = 0u; i < n_blocks; ++i) {
- h ^= mix64(blocks[i]);
- h *= m;
- }
-
- const uint8_t* const tail = (const unsigned char*)(blocks + n_blocks);
- uint64_t v = 0u;
-
- switch (len & 7u) {
- case 7:
- v |= (uint64_t)tail[6] << 48u;
- FALLTHROUGH();
- case 6:
- v |= (uint64_t)tail[5] << 40u;
- FALLTHROUGH();
- case 5:
- v |= (uint64_t)tail[4] << 32u;
- FALLTHROUGH();
- case 4:
- v |= (uint64_t)tail[3] << 24u;
- FALLTHROUGH();
- case 3:
- v |= (uint64_t)tail[2] << 16u;
- FALLTHROUGH();
- case 2:
- v |= (uint64_t)tail[1] << 8u;
- FALLTHROUGH();
- case 1:
- v |= (uint64_t)tail[0];
-
- h ^= mix64(v);
- h *= m;
- }
-
- return mix64(h);
-}
-
-uint64_t
-zix_digest64_aligned(const uint64_t seed, const void* const key, size_t len)
-{
- static const uint64_t m = 0x880355F21E6D1965ull;
-
- assert((uintptr_t)key % sizeof(uint64_t) == 0u);
- assert(len % sizeof(uint64_t) == 0u);
-
- const uint64_t* const blocks = (const uint64_t*)key;
- const size_t n_blocks = len / sizeof(uint64_t);
- uint64_t h = seed ^ (len * m);
-
- for (size_t i = 0u; i < n_blocks; ++i) {
- h ^= mix64(blocks[i]);
- h *= m;
- }
-
- return mix64(h);
-}
-
-/*
- 32-bit hash: Essentially murmur3, reimplemented here in an aligned/padded and
- a general UB-free variant.
-*/
-
-/**
- Rotate left by some count of bits.
-
- This is recognized by any halfway decent compiler and compiled to a single
- instruction on architectures that have one.
-*/
-static inline uint32_t
-rotl32(const uint32_t val, const uint32_t bits)
-{
- return ((val << bits) | (val >> (32 - bits)));
-}
-
-static inline uint32_t
-mix32(uint32_t h)
-{
- h ^= h >> 16u;
- h *= 0x85EBCA6Bu;
- h ^= h >> 13u;
- h *= 0xC2B2AE35u;
- h ^= h >> 16u;
- return h;
-}
-
-uint32_t
-zix_digest32(const uint32_t seed, const void* const key, const size_t len)
-{
- static const uint32_t c1 = 0xCC9E2D51u;
- static const uint32_t c2 = 0x1B873593u;
-
- // Process as many 32-bit blocks as possible
- const size_t n_blocks = len / sizeof(uint32_t);
- const uint8_t* data = (const uint8_t*)key;
- const uint8_t* const blocks_end = data + (n_blocks * sizeof(uint32_t));
- uint32_t h = seed;
- for (; data != blocks_end; data += sizeof(uint32_t)) {
- uint32_t k = 0u;
- memcpy(&k, data, sizeof(uint32_t));
-
- k *= c1;
- k = rotl32(k, 15);
- k *= c2;
-
- h ^= k;
- h = rotl32(h, 13);
- h = h * 5u + 0xE6546B64u;
- }
-
- // Process any trailing bytes
- uint32_t k = 0u;
- switch (len & 3u) {
- case 3u:
- k ^= (uint32_t)data[2u] << 16u;
- FALLTHROUGH();
- case 2u:
- k ^= (uint32_t)data[1u] << 8u;
- FALLTHROUGH();
- case 1u:
- k ^= (uint32_t)data[0u];
-
- k *= c1;
- k = rotl32(k, 15u);
- k *= c2;
- h ^= k;
- }
-
- return mix32(h ^ (uint32_t)len);
-}
-
-uint32_t
-zix_digest32_aligned(const uint32_t seed,
- const void* const key,
- const size_t len)
-{
- static const uint32_t c1 = 0xCC9E2D51u;
- static const uint32_t c2 = 0x1B873593u;
-
- assert((uintptr_t)key % sizeof(uint32_t) == 0u);
- assert(len % sizeof(uint32_t) == 0u);
-
- const uint32_t* const blocks = (const uint32_t*)key;
- const size_t n_blocks = len / sizeof(uint32_t);
- uint32_t h = seed;
- for (size_t i = 0u; i < n_blocks; ++i) {
- uint32_t k = blocks[i];
-
- k *= c1;
- k = rotl32(k, 15);
- k *= c2;
-
- h ^= k;
- h = rotl32(h, 13);
- h = h * 5u + 0xE6546B64u;
- }
-
- return mix32(h ^ (uint32_t)len);
-}
-
-// Native word size wrapper
-
-size_t
-zix_digest(const size_t seed, const void* const buf, const size_t len)
-{
-#if UINTPTR_MAX >= UINT64_MAX
- return zix_digest64(seed, buf, len);
-#else
- return zix_digest32(seed, buf, len);
-#endif
-}
-
-size_t
-zix_digest_aligned(const size_t seed, const void* const buf, const size_t len)
-{
-#if UINTPTR_MAX >= UINT64_MAX
- return zix_digest64_aligned(seed, buf, len);
-#else
- return zix_digest32_aligned(seed, buf, len);
-#endif
-}
diff --git a/src/zix/digest.h b/src/zix/digest.h
deleted file mode 100644
index 6df70024..00000000
--- a/src/zix/digest.h
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- Copyright 2012-2021 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
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-
-#ifndef ZIX_DIGEST_H
-#define ZIX_DIGEST_H
-
-#include "zix/common.h"
-
-#include <stddef.h>
-#include <stdint.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- @addtogroup zix
- @{
- @name Digest
- Functions to generate a short "digest" of data with minimal collisions.
-
- These are good general-purpose hash functions for indexing arbitrary data,
- but are not necessarily stable across platforms and should never be used for
- cryptographic purposes.
- @{
-*/
-
-/**
- Return a 32-bit hash of a buffer.
-
- This can be used for any size or alignment.
-*/
-ZIX_PURE_API
-uint32_t
-zix_digest32(uint32_t seed, const void* buf, size_t len);
-
-/**
- Return a 32-bit hash of an aligned buffer.
-
- Both the buffer and size must be aligned to 32 bits. For data that fits
- these requirements, this is equivalent to, but faster than, zix_digest32().
-*/
-ZIX_PURE_API
-uint32_t
-zix_digest32_aligned(uint32_t seed, const void* buf, size_t len);
-
-/**
- Return a 64-bit hash of a buffer.
-
- This can be used for any size or alignment.
-*/
-ZIX_PURE_API
-uint64_t
-zix_digest64(uint64_t seed, const void* buf, size_t len);
-
-/**
- Return a 64-bit hash of an aligned buffer.
-
- Both the buffer and size must be aligned to 64 bits. For data that fits
- these requirements, this is equivalent to, but faster than, zix_digest64().
-*/
-ZIX_PURE_API
-uint64_t
-zix_digest64_aligned(uint64_t seed, const void* buf, size_t len);
-
-/**
- Return a pointer-sized hash of a buffer.
-
- This can be used for any size or alignment.
-
- Internally, this simply dispatches to zix_digest32() or zix_digest64() as
- appropriate.
-*/
-ZIX_PURE_API
-size_t
-zix_digest(size_t seed, const void* buf, size_t len);
-
-/**
- Return a pointer-sized hash of an aligned buffer.
-
- Both the buffer and size must be aligned to the pointer size. For data that
- fits these requirements, this is equivalent to, but faster than,
- zix_digest().
-
- Internally, this simply dispatches to zix_digest32_aligned() or
- zix_digest64_aligned() as appropriate.
-*/
-ZIX_PURE_API
-size_t
-zix_digest_aligned(size_t seed, const void* buf, size_t len);
-
-#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 8837378e..00000000
--- a/src/zix/hash.c
+++ /dev/null
@@ -1,348 +0,0 @@
-/*
- Copyright 2011-2021 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
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-
-#include "zix/hash.h"
-
-#include <assert.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-
-typedef struct ZixHashEntry {
- ZixHashCode hash; ///< Non-folded hash value
- ZixHashRecord* value; ///< Pointer to user-owned record
-} ZixHashEntry;
-
-struct ZixHashImpl {
- ZixKeyFunc key_func; ///< User-provided key accessor
- ZixHashFunc hash_func; ///< User-provided hashing function
- ZixKeyEqualFunc equal_func; ///< User-provided equality comparison function
- size_t count; ///< Number of records stored in the table
- size_t mask; ///< Bit mask for fast modulo (n_entries - 1)
- size_t n_entries; ///< Power of two table size
- ZixHashEntry* entries; ///< Pointer to dynamically allocated table
-};
-
-static const size_t min_n_entries = 4u;
-static const size_t tombstone = 0xDEADu;
-
-ZixHash*
-zix_hash_new(const ZixKeyFunc key_func,
- const ZixHashFunc hash_func,
- const ZixKeyEqualFunc equal_func)
-{
- ZixHash* const hash = (ZixHash*)malloc(sizeof(ZixHash));
- if (!hash) {
- return NULL;
- }
-
- hash->key_func = key_func;
- hash->hash_func = hash_func;
- hash->equal_func = equal_func;
- hash->count = 0u;
- hash->n_entries = min_n_entries;
- hash->mask = hash->n_entries - 1u;
-
- hash->entries = (ZixHashEntry*)calloc(hash->n_entries, sizeof(ZixHashEntry));
- if (!hash->entries) {
- free(hash);
- return NULL;
- }
-
- return hash;
-}
-
-void
-zix_hash_free(ZixHash* const hash)
-{
- if (hash) {
- free(hash->entries);
- free(hash);
- }
-}
-
-ZixHashIter
-zix_hash_begin(const ZixHash* const hash)
-{
- return hash->entries[0u].value ? 0u : zix_hash_next(hash, 0u);
-}
-
-ZixHashIter
-zix_hash_end(const ZixHash* const hash)
-{
- return hash->n_entries;
-}
-
-ZixHashRecord*
-zix_hash_get(const ZixHash* hash, const ZixHashIter i)
-{
- assert(i < hash->n_entries);
-
- return hash->entries[i].value;
-}
-
-ZixHashIter
-zix_hash_next(const ZixHash* const hash, ZixHashIter i)
-{
- do {
- ++i;
- } while (i < hash->n_entries && !hash->entries[i].value);
-
- return i;
-}
-
-size_t
-zix_hash_size(const ZixHash* const hash)
-{
- return hash->count;
-}
-
-static inline size_t
-fold_hash(const ZixHashCode h_nomod, const size_t mask)
-{
- return h_nomod & mask;
-}
-
-static inline bool
-is_tombstone(const ZixHashEntry* const entry)
-{
- return !entry->value && entry->hash == tombstone;
-}
-
-static inline bool
-is_empty(const ZixHashEntry* const entry)
-{
- return !entry->value && !entry->hash;
-}
-
-static inline bool
-is_match(const ZixHash* const hash,
- const ZixHashCode code,
- const size_t entry_index,
- ZixKeyEqualFunc predicate,
- const void* const user_data)
-{
- const ZixHashEntry* const entry = &hash->entries[entry_index];
-
- return entry->value && entry->hash == code &&
- predicate(hash->key_func(entry->value), user_data);
-}
-
-static inline size_t
-next_index(const ZixHash* const hash, const size_t i)
-{
- return (i == hash->mask) ? 0u : (i + 1u);
-}
-
-static inline ZixHashIter
-find_entry(const ZixHash* const hash,
- const ZixHashKey* const key,
- const size_t h,
- const ZixHashCode code)
-{
- size_t i = h;
-
- while (!is_match(hash, code, i, hash->equal_func, key) &&
- !is_empty(&hash->entries[i])) {
- i = next_index(hash, i);
- }
-
- return i;
-}
-
-static ZixStatus
-rehash(ZixHash* const hash, const size_t old_n_entries)
-{
- ZixHashEntry* const old_entries = hash->entries;
- const size_t new_n_entries = hash->n_entries;
-
- // Replace the array in the hash first so we can use find_entry() normally
- hash->entries = (ZixHashEntry*)calloc(new_n_entries, sizeof(ZixHashEntry));
- if (!hash->entries) {
- return ZIX_STATUS_NO_MEM;
- }
-
- // Reinsert every element into the new array
- for (size_t i = 0u; i < old_n_entries; ++i) {
- ZixHashEntry* const entry = &old_entries[i];
-
- if (entry->value) {
- assert(hash->mask == hash->n_entries - 1u);
- const size_t new_h = fold_hash(entry->hash, hash->mask);
- const size_t new_i = find_entry(hash, entry->value, new_h, entry->hash);
-
- hash->entries[new_i] = *entry;
- }
- }
-
- free(old_entries);
- return ZIX_STATUS_SUCCESS;
-}
-
-static ZixStatus
-grow(ZixHash* const hash)
-{
- const size_t old_n_entries = hash->n_entries;
-
- hash->n_entries <<= 1u;
- hash->mask = hash->n_entries - 1u;
-
- return rehash(hash, old_n_entries);
-}
-
-static ZixStatus
-shrink(ZixHash* const hash)
-{
- if (hash->n_entries > min_n_entries) {
- const size_t old_n_entries = hash->n_entries;
-
- hash->n_entries >>= 1u;
- hash->mask = hash->n_entries - 1u;
-
- return rehash(hash, old_n_entries);
- }
-
- return ZIX_STATUS_SUCCESS;
-}
-
-ZixHashIter
-zix_hash_find(const ZixHash* const hash, const ZixHashKey* const key)
-{
- const ZixHashCode h_nomod = hash->hash_func(key);
- const size_t h = fold_hash(h_nomod, hash->mask);
- const ZixHashIter i = find_entry(hash, key, h, h_nomod);
-
- return is_empty(&hash->entries[i]) ? hash->n_entries : i;
-}
-
-ZixHashRecord*
-zix_hash_find_record(const ZixHash* const hash, const ZixHashKey* const key)
-{
- const ZixHashCode h_nomod = hash->hash_func(key);
- const size_t h = fold_hash(h_nomod, hash->mask);
-
- return hash->entries[find_entry(hash, key, h, h_nomod)].value;
-}
-
-ZixHashInsertPlan
-zix_hash_plan_insert_prehashed(const ZixHash* const hash,
- const ZixHashCode code,
- const ZixKeyMatchFunc predicate,
- const void* const user_data)
-{
- // Calculate an ideal initial position
- ZixHashInsertPlan pos = {code, fold_hash(code, hash->mask)};
-
- // Search for a free position starting at the ideal one
- const size_t start_index = pos.index;
- size_t first_tombstone = SIZE_MAX;
- while (!is_empty(&hash->entries[pos.index])) {
- if (is_match(hash, code, pos.index, predicate, user_data)) {
- return pos;
- }
-
- if (first_tombstone == SIZE_MAX &&
- is_tombstone(&hash->entries[pos.index])) {
- first_tombstone = pos.index; // Remember the first/best free index
- }
-
- if ((pos.index = next_index(hash, pos.index)) == start_index) {
- break;
- }
- }
-
- // If we found a tombstone before an empty slot, place the new element there
- pos.index = first_tombstone < SIZE_MAX ? first_tombstone : pos.index;
- assert(!hash->entries[pos.index].value);
-
- return pos;
-}
-
-ZixHashInsertPlan
-zix_hash_plan_insert(const ZixHash* const hash, const ZixHashKey* const key)
-{
- return zix_hash_plan_insert_prehashed(
- hash, hash->hash_func(key), hash->equal_func, key);
-}
-
-ZixHashRecord*
-zix_hash_record_at(const ZixHash* const hash, const ZixHashInsertPlan position)
-{
- return hash->entries[position.index].value;
-}
-
-ZixStatus
-zix_hash_insert_at(ZixHash* const hash,
- const ZixHashInsertPlan position,
- ZixHashRecord* const record)
-{
- if (hash->entries[position.index].value) {
- return ZIX_STATUS_EXISTS;
- }
-
- // Set entry to new value
- ZixHashEntry* const entry = &hash->entries[position.index];
- assert(!entry->value);
- entry->hash = position.code;
- entry->value = record;
-
- // Update size and rehash if we exceeded the maximum load
- const size_t max_load = hash->n_entries / 2u + hash->n_entries / 8u;
- if (++hash->count >= max_load) {
- return grow(hash);
- }
-
- return ZIX_STATUS_SUCCESS;
-}
-
-ZixStatus
-zix_hash_insert(ZixHash* const hash, ZixHashRecord* const record)
-{
- const ZixHashKey* const key = hash->key_func(record);
- const ZixHashInsertPlan position = zix_hash_plan_insert(hash, key);
-
- return zix_hash_insert_at(hash, position, record);
-}
-
-ZixStatus
-zix_hash_erase(ZixHash* const hash,
- const ZixHashIter i,
- ZixHashRecord** const removed)
-{
- // Replace entry with a tombstone
- *removed = hash->entries[i].value;
- hash->entries[i].hash = tombstone;
- hash->entries[i].value = NULL;
-
- // Decrease element count and rehash if necessary
- --hash->count;
- if (hash->count < hash->n_entries / 4u) {
- return shrink(hash);
- }
-
- return ZIX_STATUS_SUCCESS;
-}
-
-ZixStatus
-zix_hash_remove(ZixHash* const hash,
- const ZixHashKey* const key,
- ZixHashRecord** const removed)
-{
- const ZixHashIter i = zix_hash_find(hash, key);
-
- return i == hash->n_entries ? ZIX_STATUS_NOT_FOUND
- : zix_hash_erase(hash, i, removed);
-}
diff --git a/src/zix/hash.h b/src/zix/hash.h
deleted file mode 100644
index f08d267b..00000000
--- a/src/zix/hash.h
+++ /dev/null
@@ -1,329 +0,0 @@
-/*
- Copyright 2011-2021 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
- copyright notice and this permission notice appear in all copies.
-
- THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-
-#ifndef ZIX_HASH_H
-#define ZIX_HASH_H
-
-#include "zix/common.h"
-
-#include <stdbool.h>
-#include <stddef.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- @addtogroup zix
- @{
- @name Hash
- @{
-*/
-
-// ZIX_HASH_KEY_TYPE can be defined to make the API more type-safe
-#if defined(ZIX_HASH_KEY_TYPE)
-typedef ZIX_HASH_KEY_TYPE ZixHashKey;
-#else
-typedef void ZixHashKey;
-#endif
-
-// ZIX_HASH_RECORD_TYPE can be defined to make the API more type-safe
-#if defined(ZIX_HASH_RECORD_TYPE)
-typedef ZIX_HASH_RECORD_TYPE ZixHashRecord;
-#else
-typedef void ZixHashRecord;
-#endif
-
-// ZIX_HASH_SEARCH_DATA_TYPE can be defined to make the API more type-safe
-#if defined(ZIX_HASH_SEARCH_DATA_TYPE)
-typedef ZIX_HASH_SEARCH_DATA_TYPE ZixHashSearchData;
-#else
-typedef void ZixHashSearchData;
-#endif
-
-/**
- A hash table.
-
- This is an open addressing hash table that stores pointers to arbitrary user
- data. Internally, everything is stored in a single flat array that is
- resized when necessary.
-
- The single user-provided pointer that is stored in the table is called a
- "record". A record contains a "key", which is accessed via a user-provided
- function. This design supports storing large records (which may be
- expensive to allocate/construct) without requiring an entire record to
- search. Simple atomic values can be stored by providing a trivial identity
- function as a key function.
-
- The table uses power of 2 sizes with a growth factor of 2, so that hash
- values can be folded into an array index using bitwise AND as a fast modulo.
- This means that only the necessary low bits of the hash value will be used,
- so the hash function must be well-balanced within this range. More or less
- any good modern hash algorithm will be fine, but beware, for example, hash
- functions that assume they are targeting a table with a prime size.
-
- Since this doubles and halves in size, it may not be an optimal choice if
- memory reuse is a priority. A growth factor of 1.5 with fast range
- reduction may be a better choice there, at the cost of requiring 128-bit
- arithmetic on 64-bit platforms, and indexing operations being slightly more
- expensive.
-*/
-typedef struct ZixHashImpl ZixHash;
-
-/// A full hash code for a key which is not folded down to the table size
-typedef size_t ZixHashCode;
-
-/**
- An iterator to an entry in a hash table.
-
- This is really just an index, but should be considered opaque to the user
- and only used via the provided API and equality comparison.
-*/
-typedef size_t ZixHashIter;
-
-/// User function for computing the hash of a key
-typedef ZixHashCode (*ZixHashFunc)(const ZixHashKey* key);
-
-/// User function for determining if two keys are truly equal
-typedef bool (*ZixKeyEqualFunc)(const ZixHashKey* a, const ZixHashKey* b);
-
-/// User function for determining if a key matches in a custom search
-typedef bool (*ZixKeyMatchFunc)(const ZixHashKey* key,
- const ZixHashSearchData* user_data);
-
-/// User function for getting the key of a record
-typedef const ZixHashKey* (*ZixKeyFunc)(const ZixHashRecord* record);
-
-/**
- A "plan" (position) to insert a record in a hash table.
-
- This contains everything necessary to insert a record, except the record
- itself. It is exposed to split up insertion so that records only need to be
- allocated if an existing record is not found, but the contents should be
- considered opaque to the user.
-*/
-typedef struct {
- ZixHashCode code; ///< Hash code used to find this position
- ZixHashIter index; ///< Index into hash table
-} ZixHashInsertPlan;
-
-/**
- Create a new hash table.
-
- @param key_func A function to retrieve the key from a record.
- @param hash_func The key hashing function.
- @param equal_func A function to test keys for equality.
-*/
-ZIX_API
-ZixHash*
-zix_hash_new(ZixKeyFunc key_func,
- ZixHashFunc hash_func,
- ZixKeyEqualFunc equal_func);
-
-/// Free `hash`
-ZIX_API
-void
-zix_hash_free(ZixHash* hash);
-
-/// Return an iterator to the first record in a hash, or the end if it is empty
-ZIX_PURE_API
-ZixHashIter
-zix_hash_begin(const ZixHash* hash);
-
-/// Return an iterator one past the last possible record in a hash
-ZIX_PURE_API
-ZixHashIter
-zix_hash_end(const ZixHash* hash);
-
-/// Return the record pointed to by an iterator
-ZIX_PURE_API
-ZixHashRecord*
-zix_hash_get(const ZixHash* hash, ZixHashIter i);
-
-/// Return an iterator that has been advanced to the next record in a hash
-ZIX_PURE_API
-ZixHashIter
-zix_hash_next(const ZixHash* hash, ZixHashIter i);
-
-/// Return the number of elements in a hash
-ZIX_PURE_API
-size_t
-zix_hash_size(const ZixHash* hash);
-
-/**
- Find the best position to insert a record with the given key.
-
- This searches the hash table and returns either the position of an existing
- equivalent record, or the best available position to insert a new one. The
- difference can be determined with zix_hash_record_at().
-
- If the returned position is not occupied, then it is valid to insert a
- record with this key using zix_hash_insert_at() until the hash table is
- modified (which invalidates the position).
-*/
-ZIX_API
-ZixHashInsertPlan
-zix_hash_plan_insert(const ZixHash* hash, const ZixHashKey* key);
-
-/**
- Find the best position to insert a record with a custom search.
-
- This is an advanced low-level version of zix_hash_plan_insert(). It allows
- a precalculated hash code to be given, along with a custom search predicate.
- These must be compatible with the functions that manage the hash table:
- trivial usage would be to pass the equality function used by the hash and
- the key to search for.
-
- This is exposed to make it possible to avoid constructing a key at all when
- potentially inserting. For example, if keys are structures with a fixed
- header and a variably-sized body, and you have a separate header and body
- this can be used to find an insert position without having to allocate a
- contiguous key.
-
- Note that care must be taken when using this function: improper use can
- corrupt the hash table. The hash code given must be correct for the key to
- be inserted, and the predicate must return true only if the key it is called
- with (the first argument) matches the key to be inserted.
-*/
-ZIX_API
-ZixHashInsertPlan
-zix_hash_plan_insert_prehashed(const ZixHash* hash,
- ZixHashCode code,
- ZixKeyMatchFunc predicate,
- const ZixHashSearchData* user_data);
-
-/**
- Return the record at the given position, or null.
-
- This is used to determine if a position returned from zix_hash_plan_insert()
- can be used to insert a new record, or to access the existing matching
- record.
-*/
-ZIX_PURE_API
-ZixHashRecord*
-zix_hash_record_at(const ZixHash* hash, ZixHashInsertPlan position);
-
-/**
- Insert a record at a specific position.
-
- The position must have been found with an earlier call to
- zix_hash_plan_insert(), and no modifications must have been made to the hash
- table since.
-
- @param hash The hash table.
-
- @param position The position for the new record.
-
- @param record The record to insert which, on success, can now be considered
- owned by the hash table.
-
- @return ZIX_STATUS_SUCCESS, ZIX_STATUS_EXISTS if a record already exists at
- this position, or ZIX_STATUS_NO_MEM if growing the hash table failed.
-*/
-ZIX_API
-ZixStatus
-zix_hash_insert_at(ZixHash* hash,
- ZixHashInsertPlan position,
- ZixHashRecord* record);
-
-/**
- Insert a record.
-
- This is a trivial wrapper for zix_hash_plan_insert() and
- zix_hash_insert_at() that is more convenient when record construction is not
- expensive.
-
- @param hash The hash table.
-
- @param record The record to insert which, on success, can now be considered
- owned by the hash table.
-
- @return ZIX_STATUS_SUCCESS, ZIX_STATUS_EXISTS, or ZIX_STATUS_NO_MEM.
-*/
-ZIX_API
-ZixStatus
-zix_hash_insert(ZixHash* hash, ZixHashRecord* record);
-
-/**
- Erase a record at a specific position.
-
- @param hash The hash table to remove the record from.
-
- @param i Iterator to the record to remove. This must be a valid iterator
- from an earlier call to zix_hash_find(), that is, the hash table must not
- have been modified since.
-
- @param removed Set to the removed record, or null.
-
- @return ZIX_STATUS_SUCCES or ZIX_STATUS_BAD_ARG if `i` does not point at a
- removable record.
-*/
-ZIX_API
-ZixStatus
-zix_hash_erase(ZixHash* hash, ZixHashIter i, ZixHashRecord** removed);
-
-/**
- Remove a record.
-
- @param hash The hash table.
- @param key The key of the record to remove.
- @param removed Set to the removed record, or null.
- @return ZIX_STATUS_SUCCES or ZIX_STATUS_NOT_FOUND.
-*/
-ZIX_API
-ZixStatus
-zix_hash_remove(ZixHash* hash, const ZixHashKey* key, ZixHashRecord** removed);
-
-/**
- Find the position of a record with a given key.
-
- @param hash The hash table to search.
-
- @param key The key of the desired record.
-
- @return An iterator to the matching record, or the end iterator if no such
- record exists.
-*/
-ZIX_API
-ZixHashIter
-zix_hash_find(const ZixHash* hash, const ZixHashKey* key);
-
-/**
- Find a record with a given key.
-
- This is essentially the same as zix_hash_find(), but returns a pointer to
- the record for convenience.
-
- @param hash The hash table to search.
-
- @param key The key of the desired record.
-
- @return A pointer to the matching record, of null if no such record exists.
-*/
-ZIX_API
-ZixHashRecord*
-zix_hash_find_record(const ZixHash* hash, const ZixHashKey* key);
-
-/**
- @}
- @}
-*/
-
-#ifdef __cplusplus
-} /* extern "C" */
-#endif
-
-#endif /* ZIX_HASH_H */
diff --git a/subprojects/zix b/subprojects/zix
new file mode 160000
+Subproject 335fe063ea9b98d8d94cb60e63a08eeb5303f0d
diff --git a/subprojects/zix.wrap b/subprojects/zix.wrap
new file mode 100644
index 00000000..cbbba4df
--- /dev/null
+++ b/subprojects/zix.wrap
@@ -0,0 +1,5 @@
+[wrap-git]
+url = https://gitlab.com/drobilla/zix.git
+push-url = git@gitlab.com:drobilla/zix.git
+revision = main
+depth = 1