src/cache.o src/stconn.o src/http_htx.o src/debug.o \
src/check.o src/stats-html.o src/haproxy.o src/listener.o \
src/applet.o src/pattern.o src/cfgparse-listen.o \
- src/flt_spoe.o src/cebuis_tree.o src/http_ext.o \
- src/http_act.o src/http_fetch.o src/cebus_tree.o \
- src/cebuib_tree.o src/http_client.o src/dns.o \
- src/cebub_tree.o src/vars.o src/event_hdl.o src/tcp_rules.o \
+ src/flt_spoe.o src/cebis_tree.o src/http_ext.o \
+ src/http_act.o src/http_fetch.o src/cebs_tree.o \
+ src/cebib_tree.o src/http_client.o src/dns.o \
+ src/cebb_tree.o src/vars.o src/event_hdl.o src/tcp_rules.o \
src/trace.o src/stats-proxy.o src/pool.o src/stats.o \
src/cfgparse-global.o src/filters.o src/mux_pt.o \
src/flt_http_comp.o src/sock.o src/h1.o src/sink.o \
- src/cebua_tree.o src/session.o src/payload.o src/htx.o \
- src/cebul_tree.o src/cebu32_tree.o src/cebu64_tree.o \
+ src/ceba_tree.o src/session.o src/payload.o src/htx.o \
+ src/cebl_tree.o src/ceb32_tree.o src/ceb64_tree.o \
src/server_state.o src/proto_rhttp.o src/flt_trace.o src/fd.o \
src/task.o src/map.o src/fcgi-app.o src/h2.o src/mworker.o \
src/tcp_sample.o src/mjson.o src/h1_htx.o src/tcp_act.o \
#define VAR_NAME_ROOTS 4
struct vars {
- struct ceb_node *name_root[VAR_NAME_ROOTS];
+ struct ceb_root *name_root[VAR_NAME_ROOTS];
enum vars_scope scope;
unsigned int size;
__decl_thread(HA_RWLOCK_T rwlock);
#ifndef _HAPROXY_VARS_H
#define _HAPROXY_VARS_H
-#include <import/cebu64_tree.h>
+#include <import/ceb64_tree.h>
#include <haproxy/api-t.h>
#include <haproxy/session-t.h>
int i;
for (i = 0; i < VAR_NAME_ROOTS; i++) {
- while ((node = cebu64_first(&vars->name_root[i]))) {
+ while ((node = cebu64_imm_first(&vars->name_root[i]))) {
var = container_of(node, struct var, node);
size += var_clear(vars, var, 1);
}
--- /dev/null
+/*
+ * Compact Elastic Binary Trees - exported functions operating on u32 keys
+ *
+ * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _CEB32_TREE_H
+#define _CEB32_TREE_H
+
+#include "cebtree.h"
+#include <inttypes.h>
+
+/* simpler version */
+struct ceb_node *ceb32_imm_insert(struct ceb_root **root, struct ceb_node *node);
+struct ceb_node *ceb32_imm_first(struct ceb_root *const *root);
+struct ceb_node *ceb32_imm_last(struct ceb_root *const *root);
+struct ceb_node *ceb32_imm_lookup(struct ceb_root *const *root, uint32_t key);
+struct ceb_node *ceb32_imm_lookup_le(struct ceb_root *const *root, uint32_t key);
+struct ceb_node *ceb32_imm_lookup_lt(struct ceb_root *const *root, uint32_t key);
+struct ceb_node *ceb32_imm_lookup_ge(struct ceb_root *const *root, uint32_t key);
+struct ceb_node *ceb32_imm_lookup_gt(struct ceb_root *const *root, uint32_t key);
+struct ceb_node *ceb32_imm_next_unique(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *ceb32_imm_prev_unique(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *ceb32_imm_next_dup(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *ceb32_imm_prev_dup(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *ceb32_imm_next(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *ceb32_imm_prev(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *ceb32_imm_delete(struct ceb_root **root, struct ceb_node *node);
+struct ceb_node *ceb32_imm_pick(struct ceb_root **root, uint32_t key);
+
+struct ceb_node *cebu32_imm_insert(struct ceb_root **root, struct ceb_node *node);
+struct ceb_node *cebu32_imm_first(struct ceb_root *const *root);
+struct ceb_node *cebu32_imm_last(struct ceb_root *const *root);
+struct ceb_node *cebu32_imm_lookup(struct ceb_root *const *root, uint32_t key);
+struct ceb_node *cebu32_imm_lookup_le(struct ceb_root *const *root, uint32_t key);
+struct ceb_node *cebu32_imm_lookup_lt(struct ceb_root *const *root, uint32_t key);
+struct ceb_node *cebu32_imm_lookup_ge(struct ceb_root *const *root, uint32_t key);
+struct ceb_node *cebu32_imm_lookup_gt(struct ceb_root *const *root, uint32_t key);
+struct ceb_node *cebu32_imm_next(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *cebu32_imm_prev(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *cebu32_imm_delete(struct ceb_root **root, struct ceb_node *node);
+struct ceb_node *cebu32_imm_pick(struct ceb_root **root, uint32_t key);
+
+/* generic dump function */
+void ceb32_imm_default_dump(struct ceb_root **ceb_root, const char *label, const void *ctx, int sub);
+
+/* returns the pointer to the uint32_t key */
+static inline uint32_t *ceb32_imm_key(const struct ceb_node *node)
+{
+ return (uint32_t *)ceb_key_ptr(node, sizeof(struct ceb_node));
+}
+
+/* version taking a key offset */
+struct ceb_node *ceb32_ofs_insert(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *ceb32_ofs_first(struct ceb_root *const *root, ptrdiff_t kofs);
+struct ceb_node *ceb32_ofs_last(struct ceb_root *const *root, ptrdiff_t kofs);
+struct ceb_node *ceb32_ofs_lookup(struct ceb_root *const *root, ptrdiff_t kofs, uint32_t key);
+struct ceb_node *ceb32_ofs_lookup_le(struct ceb_root *const *root, ptrdiff_t kofs, uint32_t key);
+struct ceb_node *ceb32_ofs_lookup_lt(struct ceb_root *const *root, ptrdiff_t kofs, uint32_t key);
+struct ceb_node *ceb32_ofs_lookup_ge(struct ceb_root *const *root, ptrdiff_t kofs, uint32_t key);
+struct ceb_node *ceb32_ofs_lookup_gt(struct ceb_root *const *root, ptrdiff_t kofs, uint32_t key);
+struct ceb_node *ceb32_ofs_next_unique(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *ceb32_ofs_prev_unique(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *ceb32_ofs_next_dup(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *ceb32_ofs_prev_dup(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *ceb32_ofs_next(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *ceb32_ofs_prev(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *ceb32_ofs_delete(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *ceb32_ofs_pick(struct ceb_root **root, ptrdiff_t kofs, uint32_t key);
+
+struct ceb_node *cebu32_ofs_insert(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebu32_ofs_first(struct ceb_root *const *root, ptrdiff_t kofs);
+struct ceb_node *cebu32_ofs_last(struct ceb_root *const *root, ptrdiff_t kofs);
+struct ceb_node *cebu32_ofs_lookup(struct ceb_root *const *root, ptrdiff_t kofs, uint32_t key);
+struct ceb_node *cebu32_ofs_lookup_le(struct ceb_root *const *root, ptrdiff_t kofs, uint32_t key);
+struct ceb_node *cebu32_ofs_lookup_lt(struct ceb_root *const *root, ptrdiff_t kofs, uint32_t key);
+struct ceb_node *cebu32_ofs_lookup_ge(struct ceb_root *const *root, ptrdiff_t kofs, uint32_t key);
+struct ceb_node *cebu32_ofs_lookup_gt(struct ceb_root *const *root, ptrdiff_t kofs, uint32_t key);
+struct ceb_node *cebu32_ofs_next(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebu32_ofs_prev(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebu32_ofs_delete(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebu32_ofs_pick(struct ceb_root **root, ptrdiff_t kofs, uint32_t key);
+
+/* generic dump function taking a key offset */
+void ceb32_ofs_default_dump(struct ceb_root *const *root, ptrdiff_t kofs, const char *label, const void *ctx, int sub);
+
+/* returns the pointer to the uint32_t key */
+static inline uint32_t *ceb32_ofs_key(const struct ceb_node *node, ptrdiff_t kofs)
+{
+ return (uint32_t *)ceb_key_ptr(node, kofs);
+}
+
+/* insert at root <root>, the item <item_ptr> using node member <nname> and
+ * key member <kname>, and returns either the inserted item, or the one that
+ * was found there.
+ */
+#define ceb32_item_insert(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = ceb32_ofs_insert(root, _kofs, &(item_ptr)->nname); \
+ (typeof(item_ptr))((char *)(_node) - _nofs); \
+})
+
+/* descend root <root>, and return the first item of type <type>, using node
+ * member <nname> and key member <kname>, or NULL if the tree is empty.
+ */
+#define ceb32_item_first(root, nname, kname, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = ceb32_ofs_first(root, _kofs); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* descend root <root>, and return the last item of type <type>, using node
+ * member <nname> and key member <kname>, or NULL if the tree is empty.
+ */
+#define ceb32_item_last(root, nname, kname, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = ceb32_ofs_last(root, _kofs); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item of type <type> which contains the
+ * key <key>, using node member <nname> and key member <kname>. Returns the
+ * pointer to the item, or NULL if not found.
+ */
+#define ceb32_item_lookup(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = ceb32_ofs_lookup(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the last of
+ * the greatest keys that are lower than or equal to <key>, using node member
+ * <nname> and key member <kname>. Returns the pointer to the item, or NULL if
+ * not found.
+ */
+#define ceb32_item_lookup_le(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = ceb32_ofs_lookup_le(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the last of
+ * the greatest keys that are strictly lower than <key>, using node member
+ * <nname> and key member <kname>. Returns the pointer to the item, or NULL if
+ * not found.
+ */
+#define ceb32_item_lookup_lt(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = ceb32_ofs_lookup_lt(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the first of
+ * the lowest keys key that are greater than or equal to <key>, using node
+ * member <nname> and key member <kname>. Returns the pointer to the item, or
+ * NULL if not found.
+ */
+#define ceb32_item_lookup_ge(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = ceb32_ofs_lookup_ge(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the first of
+ * the lowest keys that are strictly greater than <key>, using node member
+ * <nname> and key member <kname>. Returns the pointer to the item, or NULL if
+ * not found.
+ */
+#define ceb32_item_lookup_gt(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = ceb32_ofs_lookup_gt(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item after item <item_ptr> that has a
+ * strictly greater key, using node member <nname> and key member <kname>, and
+ * returns either the found item, or NULL if none exists.
+ */
+#define ceb32_item_next_unique(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = ceb32_ofs_next_unique(root, _kofs, &(item_ptr)->nname);\
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the last item before item <item_ptr> that has a
+ * strictly lower key, using node member <nname> and key member <kname>, and
+ * returns either the found item, or NULL if none exists.
+ */
+#define ceb32_item_prev_unique(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = ceb32_ofs_prev_unique(root, _kofs, &(item_ptr)->nname);\
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item after item <item_ptr> that has
+ * exactly the same key, using node member <nname> and key member <kname>, and
+ * returns either the found item, or NULL if none exists.
+ */
+#define ceb32_item_next_dup(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = ceb32_ofs_next_dup(root, _kofs, &(item_ptr)->nname); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the last item before item <item_ptr> that has
+ * exactly the same key, using node member <nname> and key member <kname>, and
+ * returns either the found item, or NULL if none exists.
+ */
+#define ceb32_item_prev_dup(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = ceb32_ofs_prev_dup(root, _kofs, &(item_ptr)->nname); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item immediately after item <item_ptr>,
+ * using node member <nname> and key member <kname>, and returns either the
+ * found item, or NULL if none exists.
+ */
+#define ceb32_item_next(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = ceb32_ofs_next(root, _kofs, &(item_ptr)->nname); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the last item immediately before item <item_ptr>,
+ * using node member <nname> and key member <kname>, and returns either the
+ * found item, or NULL if none exists.
+ */
+#define ceb32_item_prev(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = ceb32_ofs_prev(root, _kofs, &(item_ptr)->nname); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item <item_ptr> using node member <nname> and
+ * key member <kname>, deletes it and returns it. If the item is NULL or absent
+ * from the tree, NULL is returned.
+ */
+#define ceb32_item_delete(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ typeof(item_ptr) _item = (item_ptr); \
+ struct ceb_node *_node = ceb32_ofs_delete(root, _kofs, _item ? &_item->nname : NULL); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item of type <type> which contains the
+ * key <key>, using node member <nname> and key member <kname>, deletes it and
+ * returns it. If the key is not found in the tree, NULL is returned.
+ */
+#define ceb32_item_pick(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = ceb32_ofs_pick(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/*** versions using unique keys below ***/
+
+/* insert at root <root>, the item <item_ptr> using node member <nname> and key
+ * member <kname>, and returns either the inserted item, or the one that was
+ * found there.
+ */
+#define cebu32_item_insert(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebu32_ofs_insert(root, _kofs, &(item_ptr)->nname); \
+ (typeof(item_ptr))((char *)(_node) - _nofs); \
+})
+
+/* descend root <root>, and return the first item of type <type>, using node
+ * member <nname> and key member <kname>, or NULL if the tree is empty.
+ */
+#define cebu32_item_first(root, nname, kname, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebu32_ofs_first(root, _kofs); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* descend root <root>, and return the last item of type <type>, using node
+ * member <nname> and key member <kname>, or NULL if the tree is empty.
+ */
+#define cebu32_item_last(root, nname, kname, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebu32_ofs_last(root, _kofs); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the key
+ * <key>, using node member <nname> and key member <kname>. Returns the pointer
+ * to the item, or NULL if not found.
+ */
+#define cebu32_item_lookup(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebu32_ofs_lookup(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the greatest
+ * key that is lower than or equal to <key>, using node member <nname> and key
+ * member <kname>. Returns the pointer to the item, or NULL if not found.
+ */
+#define cebu32_item_lookup_le(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebu32_ofs_lookup_le(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the greatest
+ * key that is strictly lower than <key>, using node member <nname> and key
+ * member <kname>. Returns the pointer to the item, or NULL if not found.
+ */
+#define cebu32_item_lookup_lt(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebu32_ofs_lookup_lt(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the lowest
+ * key key that is greater than or equal to <key>, using node member <nname>
+ * and key member <kname>. Returns the pointer to the item, or NULL if not
+ * found.
+ */
+#define cebu32_item_lookup_ge(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebu32_ofs_lookup_ge(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the lowest
+ * key that is strictly greater than <key>, using node member <nname> and key
+ * member <kname>. Returns the pointer to the item, or NULL if not found.
+ */
+#define cebu32_item_lookup_gt(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebu32_ofs_lookup_gt(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item immediately after item <item_ptr>,
+ * using node member <nname> and key member <kname>, and returns either the
+ * found item, or NULL if none exists.
+ */
+#define cebu32_item_next(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebu32_ofs_next(root, _kofs, &(item_ptr)->nname); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the last item immediately before item <item_ptr>,
+ * using node member <nname> and key member <kname>, and returns either the
+ * found item, or NULL if none exists.
+ */
+#define cebu32_item_prev(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebu32_ofs_prev(root, _kofs, &(item_ptr)->nname); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item <item_ptr> using node member <nname> and
+ * key member <kname>, deletes it and returns it. If the item is NULL or absent
+ * from the tree, NULL is returned.
+ */
+#define cebu32_item_delete(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ typeof(item_ptr) _item = (item_ptr); \
+ struct ceb_node *_node = cebu32_ofs_delete(root, _kofs, _item ? &_item->nname : NULL); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item of type <type> which contains the
+ * key <key>, using node member <nname> and key member <kname>, deletes it and
+ * returns it. If the key is not found in the tree, NULL is returned.
+ */
+#define cebu32_item_pick(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebu32_ofs_pick(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+#endif /* _CEB32_TREE_H */
--- /dev/null
+/*
+ * Compact Elastic Binary Trees - exported functions for operations on u64 keys
+ *
+ * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _CEB64_TREE_H
+#define _CEB64_TREE_H
+
+#include "cebtree.h"
+#include <inttypes.h>
+
+/* simpler version */
+struct ceb_node *ceb64_imm_insert(struct ceb_root **root, struct ceb_node *node);
+struct ceb_node *ceb64_imm_first(struct ceb_root *const *root);
+struct ceb_node *ceb64_imm_last(struct ceb_root *const *root);
+struct ceb_node *ceb64_imm_lookup(struct ceb_root *const *root, uint64_t key);
+struct ceb_node *ceb64_imm_lookup_le(struct ceb_root *const *root, uint64_t key);
+struct ceb_node *ceb64_imm_lookup_lt(struct ceb_root *const *root, uint64_t key);
+struct ceb_node *ceb64_imm_lookup_ge(struct ceb_root *const *root, uint64_t key);
+struct ceb_node *ceb64_imm_lookup_gt(struct ceb_root *const *root, uint64_t key);
+struct ceb_node *ceb64_imm_next_unique(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *ceb64_imm_prev_unique(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *ceb64_imm_next_dup(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *ceb64_imm_prev_dup(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *ceb64_imm_next(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *ceb64_imm_prev(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *ceb64_imm_delete(struct ceb_root **root, struct ceb_node *node);
+struct ceb_node *ceb64_imm_pick(struct ceb_root **root, uint64_t key);
+
+struct ceb_node *cebu64_imm_insert(struct ceb_root **root, struct ceb_node *node);
+struct ceb_node *cebu64_imm_first(struct ceb_root *const *root);
+struct ceb_node *cebu64_imm_last(struct ceb_root *const *root);
+struct ceb_node *cebu64_imm_lookup(struct ceb_root *const *root, uint64_t key);
+struct ceb_node *cebu64_imm_lookup_le(struct ceb_root *const *root, uint64_t key);
+struct ceb_node *cebu64_imm_lookup_lt(struct ceb_root *const *root, uint64_t key);
+struct ceb_node *cebu64_imm_lookup_ge(struct ceb_root *const *root, uint64_t key);
+struct ceb_node *cebu64_imm_lookup_gt(struct ceb_root *const *root, uint64_t key);
+struct ceb_node *cebu64_imm_next(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *cebu64_imm_prev(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *cebu64_imm_delete(struct ceb_root **root, struct ceb_node *node);
+struct ceb_node *cebu64_imm_pick(struct ceb_root **root, uint64_t key);
+
+/* generic dump function */
+void ceb64_imm_default_dump(struct ceb_root **ceb_root, const char *label, const void *ctx, int sub);
+
+/* returns the pointer to the uint64_t key */
+static inline uint64_t *ceb64_imm_key(const struct ceb_node *node)
+{
+ return (uint64_t *)ceb_key_ptr(node, sizeof(struct ceb_node));
+}
+
+/* version taking a key offset */
+struct ceb_node *ceb64_ofs_insert(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *ceb64_ofs_first(struct ceb_root *const *root, ptrdiff_t kofs);
+struct ceb_node *ceb64_ofs_last(struct ceb_root *const *root, ptrdiff_t kofs);
+struct ceb_node *ceb64_ofs_lookup(struct ceb_root *const *root, ptrdiff_t kofs, uint64_t key);
+struct ceb_node *ceb64_ofs_lookup_le(struct ceb_root *const *root, ptrdiff_t kofs, uint64_t key);
+struct ceb_node *ceb64_ofs_lookup_lt(struct ceb_root *const *root, ptrdiff_t kofs, uint64_t key);
+struct ceb_node *ceb64_ofs_lookup_ge(struct ceb_root *const *root, ptrdiff_t kofs, uint64_t key);
+struct ceb_node *ceb64_ofs_lookup_gt(struct ceb_root *const *root, ptrdiff_t kofs, uint64_t key);
+struct ceb_node *ceb64_ofs_next_unique(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *ceb64_ofs_prev_unique(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *ceb64_ofs_next_dup(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *ceb64_ofs_prev_dup(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *ceb64_ofs_next(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *ceb64_ofs_prev(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *ceb64_ofs_delete(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *ceb64_ofs_pick(struct ceb_root **root, ptrdiff_t kofs, uint64_t key);
+
+struct ceb_node *cebu64_ofs_insert(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebu64_ofs_first(struct ceb_root *const *root, ptrdiff_t kofs);
+struct ceb_node *cebu64_ofs_last(struct ceb_root *const *root, ptrdiff_t kofs);
+struct ceb_node *cebu64_ofs_lookup(struct ceb_root *const *root, ptrdiff_t kofs, uint64_t key);
+struct ceb_node *cebu64_ofs_lookup_le(struct ceb_root *const *root, ptrdiff_t kofs, uint64_t key);
+struct ceb_node *cebu64_ofs_lookup_lt(struct ceb_root *const *root, ptrdiff_t kofs, uint64_t key);
+struct ceb_node *cebu64_ofs_lookup_ge(struct ceb_root *const *root, ptrdiff_t kofs, uint64_t key);
+struct ceb_node *cebu64_ofs_lookup_gt(struct ceb_root *const *root, ptrdiff_t kofs, uint64_t key);
+struct ceb_node *cebu64_ofs_next(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebu64_ofs_prev(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebu64_ofs_delete(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebu64_ofs_pick(struct ceb_root **root, ptrdiff_t kofs, uint64_t key);
+
+/* generic dump function taking a key offset */
+void ceb64_ofs_default_dump(struct ceb_root *const *root, ptrdiff_t kofs, const char *label, const void *ctx, int sub);
+
+/* returns the pointer to the uint64_t key */
+static inline uint64_t *ceb64_ofs_key(const struct ceb_node *node, ptrdiff_t kofs)
+{
+ return (uint64_t *)ceb_key_ptr(node, kofs);
+}
+
+/* insert at root <root>, the item <item_ptr> using node member <nname> and
+ * key member <kname>, and returns either the inserted item, or the one that
+ * was found there.
+ */
+#define ceb64_item_insert(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = ceb64_ofs_insert(root, _kofs, &(item_ptr)->nname); \
+ (typeof(item_ptr))((char *)(_node) - _nofs); \
+})
+
+/* descend root <root>, and return the first item of type <type>, using node
+ * member <nname> and key member <kname>, or NULL if the tree is empty.
+ */
+#define ceb64_item_first(root, nname, kname, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = ceb64_ofs_first(root, _kofs); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* descend root <root>, and return the last item of type <type>, using node
+ * member <nname> and key member <kname>, or NULL if the tree is empty.
+ */
+#define ceb64_item_last(root, nname, kname, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = ceb64_ofs_last(root, _kofs); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item of type <type> which contains the
+ * key <key>, using node member <nname> and key member <kname>. Returns the
+ * pointer to the item, or NULL if not found.
+ */
+#define ceb64_item_lookup(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = ceb64_ofs_lookup(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the last of
+ * the greatest keys that are lower than or equal to <key>, using node member
+ * <nname> and key member <kname>. Returns the pointer to the item, or NULL if
+ * not found.
+ */
+#define ceb64_item_lookup_le(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = ceb64_ofs_lookup_le(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the last of
+ * the greatest keys that are strictly lower than <key>, using node member
+ * <nname> and key member <kname>. Returns the pointer to the item, or NULL if
+ * not found.
+ */
+#define ceb64_item_lookup_lt(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = ceb64_ofs_lookup_lt(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the first of
+ * the lowest keys key that are greater than or equal to <key>, using node
+ * member <nname> and key member <kname>. Returns the pointer to the item, or
+ * NULL if not found.
+ */
+#define ceb64_item_lookup_ge(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = ceb64_ofs_lookup_ge(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the first of
+ * the lowest keys that are strictly greater than <key>, using node member
+ * <nname> and key member <kname>. Returns the pointer to the item, or NULL if
+ * not found.
+ */
+#define ceb64_item_lookup_gt(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = ceb64_ofs_lookup_gt(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item after item <item_ptr> that has a
+ * strictly greater key, using node member <nname> and key member <kname>, and
+ * returns either the found item, or NULL if none exists.
+ */
+#define ceb64_item_next_unique(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = ceb64_ofs_next_unique(root, _kofs, &(item_ptr)->nname);\
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the last item before item <item_ptr> that has a
+ * strictly lower key, using node member <nname> and key member <kname>, and
+ * returns either the found item, or NULL if none exists.
+ */
+#define ceb64_item_prev_unique(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = ceb64_ofs_prev_unique(root, _kofs, &(item_ptr)->nname);\
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item after item <item_ptr> that has
+ * exactly the same key, using node member <nname> and key member <kname>, and
+ * returns either the found item, or NULL if none exists.
+ */
+#define ceb64_item_next_dup(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = ceb64_ofs_next_dup(root, _kofs, &(item_ptr)->nname); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the last item before item <item_ptr> that has
+ * exactly the same key, using node member <nname> and key member <kname>, and
+ * returns either the found item, or NULL if none exists.
+ */
+#define ceb64_item_prev_dup(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = ceb64_ofs_prev_dup(root, _kofs, &(item_ptr)->nname); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item immediately after item <item_ptr>,
+ * using node member <nname> and key member <kname>, and returns either the
+ * found item, or NULL if none exists.
+ */
+#define ceb64_item_next(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = ceb64_ofs_next(root, _kofs, &(item_ptr)->nname); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the last item immediately before item <item_ptr>,
+ * using node member <nname> and key member <kname>, and returns either the
+ * found item, or NULL if none exists.
+ */
+#define ceb64_item_prev(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = ceb64_ofs_prev(root, _kofs, &(item_ptr)->nname); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item <item_ptr> using node member <nname> and
+ * key member <kname>, deletes it and returns it. If the item is NULL or absent
+ * from the tree, NULL is returned.
+ */
+#define ceb64_item_delete(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ typeof(item_ptr) _item = (item_ptr); \
+ struct ceb_node *_node = ceb64_ofs_delete(root, _kofs, _item ? &_item->nname : NULL); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item of type <type> which contains the
+ * key <key>, using node member <nname> and key member <kname>, deletes it and
+ * returns it. If the key is not found in the tree, NULL is returned.
+ */
+#define ceb64_item_pick(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = ceb64_ofs_pick(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/*** versions using unique keys below ***/
+
+/* insert at root <root>, the item <item_ptr> using node member <nname> and key
+ * member <kname>, and returns either the inserted item, or the one that was
+ * found there.
+ */
+#define cebu64_item_insert(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebu64_ofs_insert(root, _kofs, &(item_ptr)->nname); \
+ (typeof(item_ptr))((char *)(_node) - _nofs); \
+})
+
+/* descend root <root>, and return the first item of type <type>, using node
+ * member <nname> and key member <kname>, or NULL if the tree is empty.
+ */
+#define cebu64_item_first(root, nname, kname, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebu64_ofs_first(root, _kofs); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* descend root <root>, and return the last item of type <type>, using node
+ * member <nname> and key member <kname>, or NULL if the tree is empty.
+ */
+#define cebu64_item_last(root, nname, kname, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebu64_ofs_last(root, _kofs); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the key
+ * <key>, using node member <nname> and key member <kname>. Returns the pointer
+ * to the item, or NULL if not found.
+ */
+#define cebu64_item_lookup(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebu64_ofs_lookup(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the greatest
+ * key that is lower than or equal to <key>, using node member <nname> and key
+ * member <kname>. Returns the pointer to the item, or NULL if not found.
+ */
+#define cebu64_item_lookup_le(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebu64_ofs_lookup_le(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the greatest
+ * key that is strictly lower than <key>, using node member <nname> and key
+ * member <kname>. Returns the pointer to the item, or NULL if not found.
+ */
+#define cebu64_item_lookup_lt(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebu64_ofs_lookup_lt(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the lowest
+ * key key that is greater than or equal to <key>, using node member <nname>
+ * and key member <kname>. Returns the pointer to the item, or NULL if not
+ * found.
+ */
+#define cebu64_item_lookup_ge(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebu64_ofs_lookup_ge(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the lowest
+ * key that is strictly greater than <key>, using node member <nname> and key
+ * member <kname>. Returns the pointer to the item, or NULL if not found.
+ */
+#define cebu64_item_lookup_gt(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebu64_ofs_lookup_gt(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item immediately after item <item_ptr>,
+ * using node member <nname> and key member <kname>, and returns either the
+ * found item, or NULL if none exists.
+ */
+#define cebu64_item_next(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebu64_ofs_next(root, _kofs, &(item_ptr)->nname); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the last item immediately before item <item_ptr>,
+ * using node member <nname> and key member <kname>, and returns either the
+ * found item, or NULL if none exists.
+ */
+#define cebu64_item_prev(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebu64_ofs_prev(root, _kofs, &(item_ptr)->nname); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item <item_ptr> using node member <nname> and
+ * key member <kname>, deletes it and returns it. If the item is NULL or absent
+ * from the tree, NULL is returned.
+ */
+#define cebu64_item_delete(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ typeof(item_ptr) _item = (item_ptr); \
+ struct ceb_node *_node = cebu64_ofs_delete(root, _kofs, _item ? &_item->nname : NULL); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item of type <type> which contains the
+ * key <key>, using node member <nname> and key member <kname>, deletes it and
+ * returns it. If the key is not found in the tree, NULL is returned.
+ */
+#define cebu64_item_pick(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebu64_ofs_pick(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+#endif /* _CEB64_TREE_H */
--- /dev/null
+/*
+ * Compact Elastic Binary Trees - exported functions operating on addr keys
+ *
+ * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _CEBA_TREE_H
+#define _CEBA_TREE_H
+
+#include "cebtree.h"
+
+/* simpler version */
+struct ceb_node *cebua_imm_insert(struct ceb_root **root, struct ceb_node *node);
+struct ceb_node *cebua_imm_first(struct ceb_root *const *root);
+struct ceb_node *cebua_imm_last(struct ceb_root *const *root);
+struct ceb_node *cebua_imm_lookup(struct ceb_root *const *root, const void *key);
+struct ceb_node *cebua_imm_lookup_le(struct ceb_root *const *root, const void *key);
+struct ceb_node *cebua_imm_lookup_lt(struct ceb_root *const *root, const void *key);
+struct ceb_node *cebua_imm_lookup_ge(struct ceb_root *const *root, const void *key);
+struct ceb_node *cebua_imm_lookup_gt(struct ceb_root *const *root, const void *key);
+struct ceb_node *cebua_imm_next(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *cebua_imm_prev(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *cebua_imm_delete(struct ceb_root **root, struct ceb_node *node);
+struct ceb_node *cebua_imm_pick(struct ceb_root **root, const void *key);
+
+/* generic dump function */
+void ceba_imm_default_dump(struct ceb_root *const *root, const char *label, const void *ctx, int sub);
+
+/* returns the pointer to the area that immediately follows the node */
+static inline void *ceba_imm_key(const struct ceb_node *node)
+{
+ return (void *)ceb_key_ptr(node, sizeof(struct ceb_node));
+}
+
+#endif /* _CEBA_TREE_H */
--- /dev/null
+/*
+ * Compact Elastic Binary Trees - exported functions operating on mb keys
+ *
+ * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _CEBB_TREE_H
+#define _CEBB_TREE_H
+
+#include "cebtree.h"
+
+/* simpler version */
+struct ceb_node *cebb_imm_insert(struct ceb_root **root, struct ceb_node *node, size_t len);
+struct ceb_node *cebb_imm_first(struct ceb_root *const *root, size_t len);
+struct ceb_node *cebb_imm_last(struct ceb_root *const *root, size_t len);
+struct ceb_node *cebb_imm_lookup(struct ceb_root *const *root, const void *key, size_t len);
+struct ceb_node *cebb_imm_lookup_le(struct ceb_root *const *root, const void *key, size_t len);
+struct ceb_node *cebb_imm_lookup_lt(struct ceb_root *const *root, const void *key, size_t len);
+struct ceb_node *cebb_imm_lookup_ge(struct ceb_root *const *root, const void *key, size_t len);
+struct ceb_node *cebb_imm_lookup_gt(struct ceb_root *const *root, const void *key, size_t len);
+struct ceb_node *cebb_imm_next_unique(struct ceb_root *const *root, struct ceb_node *node, size_t len);
+struct ceb_node *cebb_imm_prev_unique(struct ceb_root *const *root, struct ceb_node *node, size_t len);
+struct ceb_node *cebb_imm_next_dup(struct ceb_root *const *root, struct ceb_node *node, size_t len);
+struct ceb_node *cebb_imm_prev_dup(struct ceb_root *const *root, struct ceb_node *node, size_t len);
+struct ceb_node *cebb_imm_next(struct ceb_root *const *root, struct ceb_node *node, size_t len);
+struct ceb_node *cebb_imm_prev(struct ceb_root *const *root, struct ceb_node *node, size_t len);
+struct ceb_node *cebb_imm_delete(struct ceb_root **root, struct ceb_node *node, size_t len);
+struct ceb_node *cebb_imm_pick(struct ceb_root **root, const void *key, size_t len);
+
+struct ceb_node *cebub_imm_insert(struct ceb_root **root, struct ceb_node *node, size_t len);
+struct ceb_node *cebub_imm_first(struct ceb_root *const *root, size_t len);
+struct ceb_node *cebub_imm_last(struct ceb_root *const *root, size_t len);
+struct ceb_node *cebub_imm_lookup(struct ceb_root *const *root, const void *key, size_t len);
+struct ceb_node *cebub_imm_lookup_le(struct ceb_root *const *root, const void *key, size_t len);
+struct ceb_node *cebub_imm_lookup_lt(struct ceb_root *const *root, const void *key, size_t len);
+struct ceb_node *cebub_imm_lookup_ge(struct ceb_root *const *root, const void *key, size_t len);
+struct ceb_node *cebub_imm_lookup_gt(struct ceb_root *const *root, const void *key, size_t len);
+struct ceb_node *cebub_imm_next(struct ceb_root *const *root, struct ceb_node *node, size_t len);
+struct ceb_node *cebub_imm_prev(struct ceb_root *const *root, struct ceb_node *node, size_t len);
+struct ceb_node *cebub_imm_delete(struct ceb_root **root, struct ceb_node *node, size_t len);
+struct ceb_node *cebub_imm_pick(struct ceb_root **root, const void *key, size_t len);
+
+/* returns the pointer to the void* key that follows the node */
+static inline void *cebb_imm_key(const struct ceb_node *node)
+{
+ return (void *)ceb_key_ptr(node, sizeof(struct ceb_node));
+}
+
+/* version taking a key offset */
+struct ceb_node *cebb_ofs_insert(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node, size_t len);
+struct ceb_node *cebb_ofs_first(struct ceb_root *const *root, ptrdiff_t kofs, size_t len);
+struct ceb_node *cebb_ofs_last(struct ceb_root *const *root, ptrdiff_t kofs, size_t len);
+struct ceb_node *cebb_ofs_lookup(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len);
+struct ceb_node *cebb_ofs_lookup_le(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len);
+struct ceb_node *cebb_ofs_lookup_lt(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len);
+struct ceb_node *cebb_ofs_lookup_ge(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len);
+struct ceb_node *cebb_ofs_lookup_gt(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len);
+struct ceb_node *cebb_ofs_next_unique(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node, size_t len);
+struct ceb_node *cebb_ofs_prev_unique(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node, size_t len);
+struct ceb_node *cebb_ofs_next_dup(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node, size_t len);
+struct ceb_node *cebb_ofs_prev_dup(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node, size_t len);
+struct ceb_node *cebb_ofs_next(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node, size_t len);
+struct ceb_node *cebb_ofs_prev(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node, size_t len);
+struct ceb_node *cebb_ofs_delete(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node, size_t len);
+struct ceb_node *cebb_ofs_pick(struct ceb_root **root, ptrdiff_t kofs, const void *key, size_t len);
+
+struct ceb_node *cebub_ofs_insert(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node, size_t len);
+struct ceb_node *cebub_ofs_first(struct ceb_root *const *root, ptrdiff_t kofs, size_t len);
+struct ceb_node *cebub_ofs_last(struct ceb_root *const *root, ptrdiff_t kofs, size_t len);
+struct ceb_node *cebub_ofs_lookup(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len);
+struct ceb_node *cebub_ofs_lookup_le(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len);
+struct ceb_node *cebub_ofs_lookup_lt(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len);
+struct ceb_node *cebub_ofs_lookup_ge(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len);
+struct ceb_node *cebub_ofs_lookup_gt(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len);
+struct ceb_node *cebub_ofs_next(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node, size_t len);
+struct ceb_node *cebub_ofs_prev(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node, size_t len);
+struct ceb_node *cebub_ofs_delete(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node, size_t len);
+struct ceb_node *cebub_ofs_pick(struct ceb_root **root, ptrdiff_t kofs, const void *key, size_t len);
+
+/* returns the pointer to the void* key*/
+static inline void *cebb_ofs_key(const struct ceb_node *node, ptrdiff_t kofs)
+{
+ return (void*)ceb_key_ptr(node, kofs);
+}
+
+/* insert at root <root>, the item <item_ptr> using node member <nname> and key
+ * member <kname>, and length <len> and returns either the inserted item, or
+ * the one that was found there.
+ */
+#define cebb_item_insert(root, nname, kname, len, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebb_ofs_insert(root, _kofs, &(item_ptr)->nname, len); \
+ (typeof(item_ptr))((char *)(_node) - _nofs); \
+})
+
+/* descend root <root>, and return the first item of type <type>, using node
+ * member <nname> and key member <kname>, or NULL if the tree is empty.
+ */
+#define cebb_item_first(root, nname, kname, len, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebb_ofs_first(root, _kofs, len); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* descend root <root>, and return the last item of type <type>, using node
+ * member <nname> and key member <kname>, or NULL if the tree is empty.
+ */
+#define cebb_item_last(root, nname, kname, len, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebb_ofs_last(root, _kofs, len); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item of type <type> which contains the
+ * key <key>, using node member <nname> and key member <kname>. Returns the
+ * pointer to the item, or NULL if not found.
+ */
+#define cebb_item_lookup(root, nname, kname, key, len, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebb_ofs_lookup(root, _kofs, key, len); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the last of
+ * the greatest keys that are lower than or equal to <key>, using node member
+ * <nname> and key member <kname>. Returns the pointer to the item, or NULL if
+ * not found.
+ */
+#define cebb_item_lookup_le(root, nname, kname, key, len, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebb_ofs_lookup_le(root, _kofs, key, len); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the last of
+ * the greatest keys that are strictly lower than <key>, using node member
+ * <nname> and key member <kname>. Returns the pointer to the item, or NULL if
+ * not found.
+ */
+#define cebb_item_lookup_lt(root, nname, kname, key, len, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebb_ofs_lookup_lt(root, _kofs, key, len); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the first of
+ * the lowest keys key that are greater than or equal to <key>, using node
+ * member <nname> and key member <kname>. Returns the pointer to the item, or
+ * NULL if not found.
+ */
+#define cebb_item_lookup_ge(root, nname, kname, key, len, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebb_ofs_lookup_ge(root, _kofs, key, len); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the first of
+ * the lowest keys that are strictly greater than <key>, using node member
+ * <nname> and key member <kname>. Returns the pointer to the item, or NULL if
+ * not found.
+ */
+#define cebb_item_lookup_gt(root, nname, kname, key, len, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebb_ofs_lookup_gt(root, _kofs, key, len); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item after item <item_ptr> that has a
+ * strictly greater key, using node member <nname> and key member <kname>, and
+ * returns either the found item, or NULL if none exists.
+ */
+#define cebb_item_next_unique(root, nname, kname, len, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebb_ofs_next_unique(root, _kofs, &(item_ptr)->nname, len); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the last item before item <item_ptr> that has a
+ * strictly lower key, using node member <nname> and key member <kname>, and
+ * returns either the found item, or NULL if none exists.
+ */
+#define cebb_item_prev_unique(root, nname, kname, len, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebb_ofs_prev_unique(root, _kofs, &(item_ptr)->nname, len); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item after item <item_ptr> that has
+ * exactly the same key, using node member <nname> and key member <kname>, and
+ * returns either the found item, or NULL if none exists.
+ */
+#define cebb_item_next_dup(root, nname, kname, len, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebb_ofs_next_dup(root, _kofs, &(item_ptr)->nname, len); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the last item before item <item_ptr> that has
+ * exactly the same key, using node member <nname> and key member <kname>, and
+ * returns either the found item, or NULL if none exists.
+ */
+#define cebb_item_prev_dup(root, nname, kname, len, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebb_ofs_prev_dup(root, _kofs, &(item_ptr)->nname, len); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item immediately after item <item_ptr>,
+ * using node member <nname> and key member <kname>, and returns either the
+ * found item, or NULL if none exists.
+ */
+#define cebb_item_next(root, nname, kname, len, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebb_ofs_next(root, _kofs, &(item_ptr)->nname, len); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the last item immediately before item <item_ptr>,
+ * using node member <nname> and key member <kname>, and returns either the
+ * found item, or NULL if none exists.
+ */
+#define cebb_item_prev(root, nname, kname, len, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebb_ofs_prev(root, _kofs, &(item_ptr)->nname, len); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item <item_ptr> using node member <nname> and
+ * key member <kname>, deletes it and returns it. If the item is NULL or absent
+ * from the tree, NULL is returned.
+ */
+#define cebb_item_delete(root, nname, kname, len, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ typeof(item_ptr) _item = (item_ptr); \
+ struct ceb_node *_node = cebb_ofs_delete(root, _kofs, _item ? &_item->nname : NULL, len); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item of type <type> which contains the
+ * key <key>, using node member <nname> and key member <kname>, deletes it and
+ * returns it. If the key is not found in the tree, NULL is returned.
+ */
+#define cebb_item_pick(root, nname, kname, key, len, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebb_ofs_pick(root, _kofs, key, len); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/*** versions using unique keys below ***/
+
+/* insert at root <root>, the item <item_ptr> using node member <nname> and key
+ * member <kname>, and returns either the inserted item, or the one that was
+ * found there.
+ */
+#define cebub_item_insert(root, nname, kname, len, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebub_ofs_insert(root, _kofs, &(item_ptr)->nname, len); \
+ (typeof(item_ptr))((char *)(_node) - _nofs); \
+})
+
+/* descend root <root>, and return the first item of type <type>, using node
+ * member <nname> and key member <kname>, or NULL if the tree is empty.
+ */
+#define cebub_item_first(root, nname, kname, len, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebub_ofs_first(root, _kofs, len); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* descend root <root>, and return the last item of type <type>, using node
+ * member <nname> and key member <kname>, or NULL if the tree is empty.
+ */
+#define cebub_item_last(root, nname, kname, len, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebub_ofs_last(root, _kofs, len); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the key
+ * <key>, using node member <nname> and key member <kname>. Returns the pointer
+ * to the item, or NULL if not found.
+ */
+#define cebub_item_lookup(root, nname, kname, key, len, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebub_ofs_lookup(root, _kofs, key, len); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the greatest
+ * key that is lower than or equal to <key>, using node member <nname> and key
+ * member <kname>. Returns the pointer to the item, or NULL if not found.
+ */
+#define cebub_item_lookup_le(root, nname, kname, key, len, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebub_ofs_lookup_le(root, _kofs, key, len); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the greatest
+ * key that is strictly lower than <key>, using node member <nname> and key
+ * member <kname>. Returns the pointer to the item, or NULL if not found.
+ */
+#define cebub_item_lookup_lt(root, nname, kname, key, len, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebub_ofs_lookup_lt(root, _kofs, key, len); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the lowest
+ * key key that is greater than or equal to <key>, using node member <nname>
+ * and key member <kname>. Returns the pointer to the item, or NULL if not
+ * found.
+ */
+#define cebub_item_lookup_ge(root, nname, kname, key, len, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebub_ofs_lookup_ge(root, _kofs, key, len); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the lowest
+ * key that is strictly greater than <key>, using node member <nname> and key
+ * member <kname>. Returns the pointer to the item, or NULL if not found.
+ */
+#define cebub_item_lookup_gt(root, nname, kname, key, len, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebub_ofs_lookup_gt(root, _kofs, key, len); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item immediately after item <item_ptr>,
+ * using node member <nname> and key member <kname>, and returns either the
+ * found item, or NULL if none exists.
+ */
+#define cebub_item_next(root, nname, kname, len, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebub_ofs_next(root, _kofs, &(item_ptr)->nname, len); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the last item immediately before item <item_ptr>,
+ * using node member <nname> and key member <kname>, and returns either the
+ * found item, or NULL if none exists.
+ */
+#define cebub_item_prev(root, nname, kname, len, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebub_ofs_prev(root, _kofs, &(item_ptr)->nname, len); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item <item_ptr> using node member <nname> and
+ * key member <kname>, deletes it and returns it. If the item is NULL or absent
+ * from the tree, NULL is returned.
+ */
+#define cebub_item_delete(root, nname, kname, len, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ typeof(item_ptr) _item = (item_ptr); \
+ struct ceb_node *_node = cebub_ofs_delete(root, _kofs, _item ? &_item->nname : NULL, len); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item of type <type> which contains the
+ * key <key>, using node member <nname> and key member <kname>, deletes it and
+ * returns it. If the key is not found in the tree, NULL is returned.
+ */
+#define cebub_item_pick(root, nname, kname, key, len, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebub_ofs_pick(root, _kofs, key, len); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+#endif /* _CEBB_TREE_H */
--- /dev/null
+/*
+ * Compact Elastic Binary Trees - exported functions operating on indirect blocks
+ *
+ * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _CEBIB_TREE_H
+#define _CEBIB_TREE_H
+
+#include "cebtree.h"
+
+/* simpler version */
+struct ceb_node *cebib_imm_insert(struct ceb_root **root, struct ceb_node *node, size_t len);
+struct ceb_node *cebib_imm_first(struct ceb_root *const *root, size_t len);
+struct ceb_node *cebib_imm_last(struct ceb_root *const *root, size_t len);
+struct ceb_node *cebib_imm_lookup(struct ceb_root *const *root, const void *key, size_t len);
+struct ceb_node *cebib_imm_lookup_le(struct ceb_root *const *root, const void *key, size_t len);
+struct ceb_node *cebib_imm_lookup_lt(struct ceb_root *const *root, const void *key, size_t len);
+struct ceb_node *cebib_imm_lookup_ge(struct ceb_root *const *root, const void *key, size_t len);
+struct ceb_node *cebib_imm_lookup_gt(struct ceb_root *const *root, const void *key, size_t len);
+struct ceb_node *cebib_imm_next_unique(struct ceb_root *const *root, struct ceb_node *node, size_t len);
+struct ceb_node *cebib_imm_prev_unique(struct ceb_root *const *root, struct ceb_node *node, size_t len);
+struct ceb_node *cebib_imm_next_dup(struct ceb_root *const *root, struct ceb_node *node, size_t len);
+struct ceb_node *cebib_imm_prev_dup(struct ceb_root *const *root, struct ceb_node *node, size_t len);
+struct ceb_node *cebib_imm_next(struct ceb_root *const *root, struct ceb_node *node, size_t len);
+struct ceb_node *cebib_imm_prev(struct ceb_root *const *root, struct ceb_node *node, size_t len);
+struct ceb_node *cebib_imm_delete(struct ceb_root **root, struct ceb_node *node, size_t len);
+struct ceb_node *cebib_imm_pick(struct ceb_root **root, const void *key, size_t len);
+
+struct ceb_node *cebuib_imm_insert(struct ceb_root **root, struct ceb_node *node, size_t len);
+struct ceb_node *cebuib_imm_first(struct ceb_root *const *root, size_t len);
+struct ceb_node *cebuib_imm_last(struct ceb_root *const *root, size_t len);
+struct ceb_node *cebuib_imm_lookup(struct ceb_root *const *root, const void *key, size_t len);
+struct ceb_node *cebuib_imm_lookup_le(struct ceb_root *const *root, const void *key, size_t len);
+struct ceb_node *cebuib_imm_lookup_lt(struct ceb_root *const *root, const void *key, size_t len);
+struct ceb_node *cebuib_imm_lookup_ge(struct ceb_root *const *root, const void *key, size_t len);
+struct ceb_node *cebuib_imm_lookup_gt(struct ceb_root *const *root, const void *key, size_t len);
+struct ceb_node *cebuib_imm_next(struct ceb_root *const *root, struct ceb_node *node, size_t len);
+struct ceb_node *cebuib_imm_prev(struct ceb_root *const *root, struct ceb_node *node, size_t len);
+struct ceb_node *cebuib_imm_delete(struct ceb_root **root, struct ceb_node *node, size_t len);
+struct ceb_node *cebuib_imm_pick(struct ceb_root **root, const void *key, size_t len);
+
+/* returns the pointer to the indirect void* key, not the pointer itself,
+ * or NULL if node is NULL (note that the indirect pointer cannot be NULL
+ * if node is non-NULL).
+ */
+static inline void *cebib_imm_key(const struct ceb_node *node)
+{
+ return node ? *(void **)_ceb_key_ptr(node, sizeof(struct ceb_node)) : NULL;
+}
+
+/* version taking a key offset */
+struct ceb_node *cebib_ofs_insert(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node, size_t len);
+struct ceb_node *cebib_ofs_first(struct ceb_root *const *root, ptrdiff_t kofs, size_t len);
+struct ceb_node *cebib_ofs_last(struct ceb_root *const *root, ptrdiff_t kofs, size_t len);
+struct ceb_node *cebib_ofs_lookup(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len);
+struct ceb_node *cebib_ofs_lookup_le(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len);
+struct ceb_node *cebib_ofs_lookup_lt(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len);
+struct ceb_node *cebib_ofs_lookup_ge(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len);
+struct ceb_node *cebib_ofs_lookup_gt(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len);
+struct ceb_node *cebib_ofs_next_unique(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node, size_t len);
+struct ceb_node *cebib_ofs_prev_unique(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node, size_t len);
+struct ceb_node *cebib_ofs_next_dup(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node, size_t len);
+struct ceb_node *cebib_ofs_prev_dup(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node, size_t len);
+struct ceb_node *cebib_ofs_next(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node, size_t len);
+struct ceb_node *cebib_ofs_prev(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node, size_t len);
+struct ceb_node *cebib_ofs_delete(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node, size_t len);
+struct ceb_node *cebib_ofs_pick(struct ceb_root **root, ptrdiff_t kofs, const void *key, size_t len);
+
+struct ceb_node *cebuib_ofs_insert(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node, size_t len);
+struct ceb_node *cebuib_ofs_first(struct ceb_root *const *root, ptrdiff_t kofs, size_t len);
+struct ceb_node *cebuib_ofs_last(struct ceb_root *const *root, ptrdiff_t kofs, size_t len);
+struct ceb_node *cebuib_ofs_lookup(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len);
+struct ceb_node *cebuib_ofs_lookup_le(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len);
+struct ceb_node *cebuib_ofs_lookup_lt(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len);
+struct ceb_node *cebuib_ofs_lookup_ge(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len);
+struct ceb_node *cebuib_ofs_lookup_gt(struct ceb_root *const *root, ptrdiff_t kofs, const void *key, size_t len);
+struct ceb_node *cebuib_ofs_next(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node, size_t len);
+struct ceb_node *cebuib_ofs_prev(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node, size_t len);
+struct ceb_node *cebuib_ofs_delete(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node, size_t len);
+struct ceb_node *cebuib_ofs_pick(struct ceb_root **root, ptrdiff_t kofs, const void *key, size_t len);
+
+/* returns the pointer to the indirect void* key, not the pointer itself,
+ * or NULL if node is NULL (note that the indirect pointer cannot be NULL
+ * if node is non-NULL).
+ */
+static inline void *cebib_ofs_key(const struct ceb_node *node, ptrdiff_t kofs)
+{
+ return node ? *(void **)_ceb_key_ptr(node, kofs) : NULL;
+}
+
+/* insert at root <root>, the item <item_ptr> using node member <nname> and key
+ * member <kname>, and length <len> and returns either the inserted item, or
+ * the one that was found there.
+ */
+#define cebib_item_insert(root, nname, kname, len, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebib_ofs_insert(root, _kofs, &(item_ptr)->nname, len);\
+ (typeof(item_ptr))((char *)(_node) - _nofs); \
+})
+
+/* descend root <root>, and return the first item of type <type>, using node
+ * member <nname> and key member <kname>, or NULL if the tree is empty.
+ */
+#define cebib_item_first(root, nname, kname, len, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebib_ofs_first(root, _kofs, len); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* descend root <root>, and return the last item of type <type>, using node
+ * member <nname> and key member <kname>, or NULL if the tree is empty.
+ */
+#define cebib_item_last(root, nname, kname, len, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebib_ofs_last(root, _kofs, len); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item of type <type> which contains the
+ * key <key>, using node member <nname> and key member <kname>. Returns the
+ * pointer to the item, or NULL if not found.
+ */
+#define cebib_item_lookup(root, nname, kname, key, len, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebib_ofs_lookup(root, _kofs, key, len); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the last of
+ * the greatest keys that are lower than or equal to <key>, using node member
+ * <nname> and key member <kname>. Returns the pointer to the item, or NULL if
+ * not found.
+ */
+#define cebib_item_lookup_le(root, nname, kname, key, len, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebib_ofs_lookup_le(root, _kofs, key, len); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the last of
+ * the greatest keys that are strictly lower than <key>, using node member
+ * <nname> and key member <kname>. Returns the pointer to the item, or NULL if
+ * not found.
+ */
+#define cebib_item_lookup_lt(root, nname, kname, key, len, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebib_ofs_lookup_lt(root, _kofs, key, len); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the first of
+ * the lowest keys key that are greater than or equal to <key>, using node
+ * member <nname> and key member <kname>. Returns the pointer to the item, or
+ * NULL if not found.
+ */
+#define cebib_item_lookup_ge(root, nname, kname, key, len, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebib_ofs_lookup_ge(root, _kofs, key, len); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the first of
+ * the lowest keys that are strictly greater than <key>, using node member
+ * <nname> and key member <kname>. Returns the pointer to the item, or NULL if
+ * not found.
+ */
+#define cebib_item_lookup_gt(root, nname, kname, key, len, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebib_ofs_lookup_gt(root, _kofs, key, len); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item of type <type> after item <item_ptr>
+ * that has a strictly greater key, using node member <nname> and key member
+ * <kname>, and returns either the found item, or NULL if none exists.
+ */
+#define cebib_item_next_unique(root, nname, kname, len, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebib_ofs_next_unique(root, _kofs, &(item_ptr)->nname, len); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the last item of type <type> before item <item_ptr>
+ * that has a strictly lower key, using node member <nname> and key member
+ * <kname>, and returns either the found item, or NULL if none exists.
+ */
+#define cebib_item_prev_unique(root, nname, kname, len, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebib_ofs_prev_unique(root, _kofs, &(item_ptr)->nname, len); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item of type <type> after item <item_ptr>
+ * that has exactly the same key, using node member <nname> and key member
+ * <kname>, and returns either the found item, or NULL if none exists.
+ */
+#define cebib_item_next_dup(root, nname, kname, len, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebib_ofs_next_dup(root, _kofs, &(item_ptr)->nname, len); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the last item of type <type> before item <item_ptr>
+ * that has exactly the same key, using node member <nname> and key member
+ * <kname>, and returns either the found item, or NULL if none exists.
+ */
+#define cebib_item_prev_dup(root, nname, kname, len, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebib_ofs_prev_dup(root, _kofs, &(item_ptr)->nname, len); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item of type <type> immediately after
+ * item <item_ptr>, using node member <nname> and key member <kname>, and
+ * returns either the found item, or NULL if none exists.
+ */
+#define cebib_item_next(root, nname, kname, len, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebib_ofs_next(root, _kofs, &(item_ptr)->nname, len); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the last item of type <type> immediately before
+ * item <item_ptr>, using node member <nname> and key member <kname>, and
+ * returns either the found item, or NULL if none exists.
+ */
+#define cebib_item_prev(root, nname, kname, len, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebib_ofs_prev(root, _kofs, &(item_ptr)->nname, len); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item <item_ptr> using node member <nname> and
+ * key member <kname>, deletes it and returns it. If the item is NULL or absent
+ * from the tree, NULL is returned.
+ */
+#define cebib_item_delete(root, nname, kname, len, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ typeof(item_ptr) _item = (item_ptr); \
+ struct ceb_node *_node = cebib_ofs_delete(root, _kofs, _item ? &_item->nname : NULL, len); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item of type <type> which contains the
+ * key <key>, using node member <nname> and key member <kname>, deletes it and
+ * returns it. If the key is not found in the tree, NULL is returned.
+ */
+#define cebib_item_pick(root, nname, kname, key, len, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebib_ofs_pick(root, _kofs, key, len); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/*** versions using unique keys below ***/
+
+/* insert at root <root>, the item <item_ptr> using node member <nname> and key
+ * member <kname>, and returns either the inserted item, or the one that was
+ * found there.
+ */
+#define cebuib_item_insert(root, nname, kname, len, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebuib_ofs_insert(root, _kofs, &(item_ptr)->nname, len); \
+ (typeof(item_ptr))((char *)(_node) - _nofs); \
+})
+
+/* descend root <root>, and return the first item of type <type>, using node
+ * member <nname> and key member <kname>, or NULL if the tree is empty.
+ */
+#define cebuib_item_first(root, nname, kname, len, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebuib_ofs_first(root, _kofs, len); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* descend root <root>, and return the last item of type <type>, using node
+ * member <nname> and key member <kname>, or NULL if the tree is empty.
+ */
+#define cebuib_item_last(root, nname, kname, len, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebuib_ofs_last(root, _kofs, len); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the key
+ * <key>, using node member <nname> and key member <kname>. Returns the pointer
+ * to the item, or NULL if not found.
+ */
+#define cebuib_item_lookup(root, nname, kname, key, len, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebuib_ofs_lookup(root, _kofs, key, len); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the greatest
+ * key that is lower than or equal to <key>, using node member <nname> and key
+ * member <kname>. Returns the pointer to the item, or NULL if not found.
+ */
+#define cebuib_item_lookup_le(root, nname, kname, key, len, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebuib_ofs_lookup_le(root, _kofs, key, len); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the greatest
+ * key that is strictly lower than <key>, using node member <nname> and key
+ * member <kname>. Returns the pointer to the item, or NULL if not found.
+ */
+#define cebuib_item_lookup_lt(root, nname, kname, key, len, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebuib_ofs_lookup_lt(root, _kofs, key, len); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the lowest
+ * key key that is greater than or equal to <key>, using node member <nname>
+ * and key member <kname>. Returns the pointer to the item, or NULL if not
+ * found.
+ */
+#define cebuib_item_lookup_ge(root, nname, kname, key, len, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebuib_ofs_lookup_ge(root, _kofs, key, len); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the lowest
+ * key that is strictly greater than <key>, using node member <nname> and key
+ * member <kname>. Returns the pointer to the item, or NULL if not found.
+ */
+#define cebuib_item_lookup_gt(root, nname, kname, key, len, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebuib_ofs_lookup_gt(root, _kofs, key, len); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item of type <type> immediately after
+ * item <item_ptr>, using node member <nname> and key member <kname>, and
+ * returns either the found item, or NULL if none exists.
+ */
+#define cebuib_item_next(root, nname, kname, len, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebuib_ofs_next(root, _kofs, &(item_ptr)->nname, len); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the last item of type <type> immediately before
+ * item <item_ptr>, using node member <nname> and key member <kname>, and
+ * returns either the found item, or NULL if none exists.
+ */
+#define cebuib_item_prev(root, nname, kname, len, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebuib_ofs_prev(root, _kofs, &(item_ptr)->nname, len); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item <item_ptr> using node member <nname> and
+ * key member <kname>, deletes it and returns it. If the item is NULL or absent
+ * from the tree, NULL is returned.
+ */
+#define cebuib_item_delete(root, nname, kname, len, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ typeof(item_ptr) _item = (item_ptr); \
+ struct ceb_node *_node = cebuib_ofs_delete(root, _kofs, _item ? &_item->nname : NULL, len); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item of type <type> which contains the
+ * key <key>, using node member <nname> and key member <kname>, deletes it and
+ * returns it. If the key is not found in the tree, NULL is returned.
+ */
+#define cebuib_item_pick(root, nname, kname, key, len, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebuib_ofs_pick(root, _kofs, key, len); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+#endif /* _CEBIB_TREE_H */
--- /dev/null
+/*
+ * Compact Elastic Binary Trees - exported functions operating on indirect strings
+ *
+ * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _CEBIS_TREE_H
+#define _CEBIS_TREE_H
+
+#include "cebtree.h"
+
+/* simpler version */
+struct ceb_node *cebis_imm_insert(struct ceb_root **root, struct ceb_node *node);
+struct ceb_node *cebis_imm_first(struct ceb_root *const *root);
+struct ceb_node *cebis_imm_last(struct ceb_root *const *root);
+struct ceb_node *cebis_imm_lookup(struct ceb_root *const *root, const void *key);
+struct ceb_node *cebis_imm_lookup_le(struct ceb_root *const *root, const void *key);
+struct ceb_node *cebis_imm_lookup_lt(struct ceb_root *const *root, const void *key);
+struct ceb_node *cebis_imm_lookup_ge(struct ceb_root *const *root, const void *key);
+struct ceb_node *cebis_imm_lookup_gt(struct ceb_root *const *root, const void *key);
+struct ceb_node *cebis_imm_next_unique(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *cebis_imm_prev_unique(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *cebis_imm_next_dup(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *cebis_imm_prev_dup(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *cebis_imm_next(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *cebis_imm_prev(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *cebis_imm_delete(struct ceb_root **root, struct ceb_node *node);
+struct ceb_node *cebis_imm_pick(struct ceb_root **root, const void *key);
+
+struct ceb_node *cebuis_imm_insert(struct ceb_root **root, struct ceb_node *node);
+struct ceb_node *cebuis_imm_first(struct ceb_root *const *root);
+struct ceb_node *cebuis_imm_last(struct ceb_root *const *root);
+struct ceb_node *cebuis_imm_lookup(struct ceb_root *const *root, const void *key);
+struct ceb_node *cebuis_imm_lookup_le(struct ceb_root *const *root, const void *key);
+struct ceb_node *cebuis_imm_lookup_lt(struct ceb_root *const *root, const void *key);
+struct ceb_node *cebuis_imm_lookup_ge(struct ceb_root *const *root, const void *key);
+struct ceb_node *cebuis_imm_lookup_gt(struct ceb_root *const *root, const void *key);
+struct ceb_node *cebuis_imm_next(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *cebuis_imm_prev(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *cebuis_imm_delete(struct ceb_root **root, struct ceb_node *node);
+struct ceb_node *cebuis_imm_pick(struct ceb_root **root, const void *key);
+
+/* generic dump function */
+void cebis_imm_default_dump(struct ceb_root *const *root, const char *label, const void *ctx, int sub);
+
+/* returns the pointer to the indirect char* key, not the pointer itself,
+ * or NULL if node is NULL (note that the indirect pointer cannot be NULL
+ * if node is non-NULL).
+ */
+static inline char *cebis_imm_key(const struct ceb_node *node)
+{
+ return node ? *(char **)_ceb_key_ptr(node, sizeof(struct ceb_node)) : NULL;
+}
+
+/* version taking a key offset */
+struct ceb_node *cebis_ofs_insert(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebis_ofs_first(struct ceb_root *const *root, ptrdiff_t kofs);
+struct ceb_node *cebis_ofs_last(struct ceb_root *const *root, ptrdiff_t kofs);
+struct ceb_node *cebis_ofs_lookup(struct ceb_root *const *root, ptrdiff_t kofs, const void *key);
+struct ceb_node *cebis_ofs_lookup_le(struct ceb_root *const *root, ptrdiff_t kofs, const void *key);
+struct ceb_node *cebis_ofs_lookup_lt(struct ceb_root *const *root, ptrdiff_t kofs, const void *key);
+struct ceb_node *cebis_ofs_lookup_ge(struct ceb_root *const *root, ptrdiff_t kofs, const void *key);
+struct ceb_node *cebis_ofs_lookup_gt(struct ceb_root *const *root, ptrdiff_t kofs, const void *key);
+struct ceb_node *cebis_ofs_next_unique(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebis_ofs_prev_unique(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebis_ofs_next_dup(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebis_ofs_prev_dup(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebis_ofs_next(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebis_ofs_prev(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebis_ofs_delete(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebis_ofs_pick(struct ceb_root **root, ptrdiff_t kofs, const void *key);
+
+struct ceb_node *cebuis_ofs_insert(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebuis_ofs_first(struct ceb_root *const *root, ptrdiff_t kofs);
+struct ceb_node *cebuis_ofs_last(struct ceb_root *const *root, ptrdiff_t kofs);
+struct ceb_node *cebuis_ofs_lookup(struct ceb_root *const *root, ptrdiff_t kofs, const void *key);
+struct ceb_node *cebuis_ofs_lookup_le(struct ceb_root *const *root, ptrdiff_t kofs, const void *key);
+struct ceb_node *cebuis_ofs_lookup_lt(struct ceb_root *const *root, ptrdiff_t kofs, const void *key);
+struct ceb_node *cebuis_ofs_lookup_ge(struct ceb_root *const *root, ptrdiff_t kofs, const void *key);
+struct ceb_node *cebuis_ofs_lookup_gt(struct ceb_root *const *root, ptrdiff_t kofs, const void *key);
+struct ceb_node *cebuis_ofs_next(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebuis_ofs_prev(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebuis_ofs_delete(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebuis_ofs_pick(struct ceb_root **root, ptrdiff_t kofs, const void *key);
+
+/* generic dump function taking a key offset */
+void cebis_ofs_default_dump(struct ceb_root *const *root, ptrdiff_t kofs, const char *label, const void *ctx, int sub);
+
+/* returns the pointer to the indirect char* key, not the pointer itself,
+ * or NULL if node is NULL (note that the indirect pointer cannot be NULL
+ * if node is non-NULL).
+ */
+static inline char *cebis_ofs_key(const struct ceb_node *node, ptrdiff_t kofs)
+{
+ return node ? *(char **)_ceb_key_ptr(node, kofs) : NULL;
+}
+
+/* insert at root <root>, the item <item_ptr> using node member <nname> and key
+ * member <kname>, and returns either the inserted item, or the one that was
+ * found there.
+ */
+#define cebis_item_insert(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebis_ofs_insert(root, _kofs, &(item_ptr)->nname); \
+ (typeof(item_ptr))((char *)(_node) - _nofs); \
+})
+
+/* descend root <root>, and return the first item of type <type>, using node
+ * member <nname> and key member <kname>, or NULL if the tree is empty.
+ */
+#define cebis_item_first(root, nname, kname, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebis_ofs_first(root, _kofs); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* descend root <root>, and return the last item of type <type>, using node
+ * member <nname> and key member <kname>, or NULL if the tree is empty.
+ */
+#define cebis_item_last(root, nname, kname, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebis_ofs_last(root, _kofs); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item of type <type> which contains the
+ * key <key>, using node member <nname> and key member <kname>. Returns the
+ * pointer to the item, or NULL if not found.
+ */
+#define cebis_item_lookup(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebis_ofs_lookup(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the last of
+ * the greatest keys that are lower than or equal to <key>, using node member
+ * <nname> and key member <kname>. Returns the pointer to the item, or NULL if
+ * not found.
+ */
+#define cebis_item_lookup_le(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebis_ofs_lookup_le(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the last of
+ * the greatest keys that are strictly lower than <key>, using node member
+ * <nname> and key member <kname>. Returns the pointer to the item, or NULL if
+ * not found.
+ */
+#define cebis_item_lookup_lt(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebis_ofs_lookup_lt(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the first of
+ * the lowest keys key that are greater than or equal to <key>, using node
+ * member <nname> and key member <kname>. Returns the pointer to the item, or
+ * NULL if not found.
+ */
+#define cebis_item_lookup_ge(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebis_ofs_lookup_ge(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the first of
+ * the lowest keys that are strictly greater than <key>, using node member
+ * <nname> and key member <kname>. Returns the pointer to the item, or NULL if
+ * not found.
+ */
+#define cebis_item_lookup_gt(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebis_ofs_lookup_gt(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item after item <item_ptr> that has a
+ * strictly greater key, using node member <nname> and key member <kname>, and
+ * returns either the found item, or NULL if none exists.
+ */
+#define cebis_item_next_unique(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebis_ofs_next_unique(root, _kofs, &(item_ptr)->nname);\
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the last item before item <item_ptr> that has a
+ * strictly lower key, using node member <nname> and key member <kname>, and
+ * returns either the found item, or NULL if none exists.
+ */
+#define cebis_item_prev_unique(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebis_ofs_prev_unique(root, _kofs, &(item_ptr)->nname);\
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item after item <item_ptr> that has
+ * exactly the same key, using node member <nname> and key member <kname>, and
+ * returns either the found item, or NULL if none exists.
+ */
+#define cebis_item_next_dup(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebis_ofs_next_dup(root, _kofs, &(item_ptr)->nname); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the last item before item <item_ptr> that has
+ * exactly the same key, using node member <nname> and key member <kname>, and
+ * returns either the found item, or NULL if none exists.
+ */
+#define cebis_item_prev_dup(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebis_ofs_prev_dup(root, _kofs, &(item_ptr)->nname); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item immediately after item <item_ptr>,
+ * using node member <nname> and key member <kname>, and returns either the
+ * found item, or NULL if none exists.
+ */
+#define cebis_item_next(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebis_ofs_next(root, _kofs, &(item_ptr)->nname); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the last item immediately before item <item_ptr>,
+ * using node member <nname> and key member <kname>, and returns either the
+ * found item, or NULL if none exists.
+ */
+#define cebis_item_prev(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebis_ofs_prev(root, _kofs, &(item_ptr)->nname); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item <item_ptr> using node member <nname> and
+ * key member <kname>, deletes it and returns it. If the item is NULL or absent
+ * from the tree, NULL is returned.
+ */
+#define cebis_item_delete(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ typeof(item_ptr) _item = (item_ptr); \
+ struct ceb_node *_node = cebis_ofs_delete(root, _kofs, _item ? &_item->nname : NULL); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item of type <type> which contains the
+ * key <key>, using node member <nname> and key member <kname>, deletes it and
+ * returns it. If the key is not found in the tree, NULL is returned.
+ */
+#define cebis_item_pick(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebis_ofs_pick(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/*** versions using unique keys below ***/
+
+/* insert at root <root>, the item <item_ptr> using node member <nname> and key
+ * member <kname>, and returns either the inserted item, or the one that was
+ * found there.
+ */
+#define cebuis_item_insert(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebuis_ofs_insert(root, _kofs, &(item_ptr)->nname); \
+ (typeof(item_ptr))((char *)(_node) - _nofs); \
+})
+
+/* descend root <root>, and return the first item of type <type>, using node
+ * member <nname> and key member <kname>, or NULL if the tree is empty.
+ */
+#define cebuis_item_first(root, nname, kname, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebuis_ofs_first(root, _kofs); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* descend root <root>, and return the last item of type <type>, using node
+ * member <nname> and key member <kname>, or NULL if the tree is empty.
+ */
+#define cebuis_item_last(root, nname, kname, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebuis_ofs_last(root, _kofs); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the key
+ * <key>, using node member <nname> and key member <kname>. Returns the pointer
+ * to the item, or NULL if not found.
+ */
+#define cebuis_item_lookup(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebuis_ofs_lookup(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the greatest
+ * key that is lower than or equal to <key>, using node member <nname> and key
+ * member <kname>. Returns the pointer to the item, or NULL if not found.
+ */
+#define cebuis_item_lookup_le(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebuis_ofs_lookup_le(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the greatest
+ * key that is strictly lower than <key>, using node member <nname> and key
+ * member <kname>. Returns the pointer to the item, or NULL if not found.
+ */
+#define cebuis_item_lookup_lt(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebuis_ofs_lookup_lt(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the lowest
+ * key key that is greater than or equal to <key>, using node member <nname>
+ * and key member <kname>. Returns the pointer to the item, or NULL if not
+ * found.
+ */
+#define cebuis_item_lookup_ge(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebuis_ofs_lookup_ge(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the lowest
+ * key that is strictly greater than <key>, using node member <nname> and key
+ * member <kname>. Returns the pointer to the item, or NULL if not found.
+ */
+#define cebuis_item_lookup_gt(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebuis_ofs_lookup_gt(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item immediately after item <item_ptr>,
+ * using node member <nname> and key member <kname>, and returns either the
+ * found item, or NULL if none exists.
+ */
+#define cebuis_item_next(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebuis_ofs_next(root, _kofs, &(item_ptr)->nname); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the last item immediately before item <item_ptr>,
+ * using node member <nname> and key member <kname>, and returns either the
+ * found item, or NULL if none exists.
+ */
+#define cebuis_item_prev(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebuis_ofs_prev(root, _kofs, &(item_ptr)->nname); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item <item_ptr> using node member <nname> and
+ * key member <kname>, deletes it and returns it. If the item is NULL or absent
+ * from the tree, NULL is returned.
+ */
+#define cebuis_item_delete(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ typeof(item_ptr) _item = (item_ptr); \
+ struct ceb_node *_node = cebuis_ofs_delete(root, _kofs, _item ? &_item->nname : NULL); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item of type <type> which contains the
+ * key <key>, using node member <nname> and key member <kname>, deletes it and
+ * returns it. If the key is not found in the tree, NULL is returned.
+ */
+#define cebuis_item_pick(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebuis_ofs_pick(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+#endif /* _CEBIS_TREE_H */
--- /dev/null
+/*
+ * Compact Elastic Binary Trees - exported functions operating on ulong keys
+ *
+ * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _CEBL_TREE_H
+#define _CEBL_TREE_H
+
+#include "cebtree.h"
+
+/* simpler version */
+struct ceb_node *cebl_imm_insert(struct ceb_root **root, struct ceb_node *node);
+struct ceb_node *cebl_imm_first(struct ceb_root *const *root);
+struct ceb_node *cebl_imm_last(struct ceb_root *const *root);
+struct ceb_node *cebl_imm_lookup(struct ceb_root *const *root, unsigned long key);
+struct ceb_node *cebl_imm_lookup_le(struct ceb_root *const *root, unsigned long key);
+struct ceb_node *cebl_imm_lookup_lt(struct ceb_root *const *root, unsigned long key);
+struct ceb_node *cebl_imm_lookup_ge(struct ceb_root *const *root, unsigned long key);
+struct ceb_node *cebl_imm_lookup_gt(struct ceb_root *const *root, unsigned long key);
+struct ceb_node *cebl_imm_next_unique(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *cebl_imm_prev_unique(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *cebl_imm_next_dup(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *cebl_imm_prev_dup(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *cebl_imm_next(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *cebl_imm_prev(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *cebl_imm_delete(struct ceb_root **root, struct ceb_node *node);
+struct ceb_node *cebl_imm_pick(struct ceb_root **root, unsigned long key);
+
+struct ceb_node *cebul_imm_insert(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *cebul_imm_first(struct ceb_root *const *root);
+struct ceb_node *cebul_imm_last(struct ceb_root *const *root);
+struct ceb_node *cebul_imm_lookup(struct ceb_root *const *root, unsigned long key);
+struct ceb_node *cebul_imm_lookup_le(struct ceb_root *const *root, unsigned long key);
+struct ceb_node *cebul_imm_lookup_lt(struct ceb_root *const *root, unsigned long key);
+struct ceb_node *cebul_imm_lookup_ge(struct ceb_root *const *root, unsigned long key);
+struct ceb_node *cebul_imm_lookup_gt(struct ceb_root *const *root, unsigned long key);
+struct ceb_node *cebul_imm_next(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *cebul_imm_prev(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *cebul_imm_delete(struct ceb_root **root, struct ceb_node *node);
+struct ceb_node *cebul_imm_pick(struct ceb_root **root, unsigned long key);
+
+/* generic dump function */
+void cebl_imm_default_dump(struct ceb_root **ceb_root, const char *label, const void *ctx, int sub);
+
+/* returns the pointer to the unsigned long key */
+static inline unsigned long *cebl_imm_key(const struct ceb_node *node)
+{
+ return (unsigned long *)ceb_key_ptr(node, sizeof(struct ceb_node));
+}
+
+/* version taking a key offset */
+struct ceb_node *cebl_ofs_insert(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebl_ofs_first(struct ceb_root *const *root, ptrdiff_t kofs);
+struct ceb_node *cebl_ofs_last(struct ceb_root *const *root, ptrdiff_t kofs);
+struct ceb_node *cebl_ofs_lookup(struct ceb_root *const *root, ptrdiff_t kofs, unsigned long key);
+struct ceb_node *cebl_ofs_lookup_le(struct ceb_root *const *root, ptrdiff_t kofs, unsigned long key);
+struct ceb_node *cebl_ofs_lookup_lt(struct ceb_root *const *root, ptrdiff_t kofs, unsigned long key);
+struct ceb_node *cebl_ofs_lookup_ge(struct ceb_root *const *root, ptrdiff_t kofs, unsigned long key);
+struct ceb_node *cebl_ofs_lookup_gt(struct ceb_root *const *root, ptrdiff_t kofs, unsigned long key);
+struct ceb_node *cebl_ofs_next_unique(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebl_ofs_prev_unique(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebl_ofs_next_dup(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebl_ofs_prev_dup(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebl_ofs_next(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebl_ofs_prev(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebl_ofs_delete(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebl_ofs_pick(struct ceb_root *const *root, ptrdiff_t kofs, unsigned long key);
+
+struct ceb_node *cebul_ofs_insert(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebul_ofs_first(struct ceb_root *const *root, ptrdiff_t kofs);
+struct ceb_node *cebul_ofs_last(struct ceb_root *const *root, ptrdiff_t kofs);
+struct ceb_node *cebul_ofs_lookup(struct ceb_root *const *root, ptrdiff_t kofs, unsigned long key);
+struct ceb_node *cebul_ofs_lookup_le(struct ceb_root *const *root, ptrdiff_t kofs, unsigned long key);
+struct ceb_node *cebul_ofs_lookup_lt(struct ceb_root *const *root, ptrdiff_t kofs, unsigned long key);
+struct ceb_node *cebul_ofs_lookup_ge(struct ceb_root *const *root, ptrdiff_t kofs, unsigned long key);
+struct ceb_node *cebul_ofs_lookup_gt(struct ceb_root *const *root, ptrdiff_t kofs, unsigned long key);
+struct ceb_node *cebul_ofs_next(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebul_ofs_prev(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebul_ofs_delete(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebul_ofs_pick(struct ceb_root **root, ptrdiff_t kofs, unsigned long key);
+
+/* generic dump function taking a key offset */
+void cebl_ofs_default_dump(struct ceb_root *const *root, ptrdiff_t kofs, const char *label, const void *ctx, int sub);
+
+/* returns the pointer to the unsigned long key */
+static inline unsigned long *cebl_ofs_key(const struct ceb_node *node, ptrdiff_t kofs)
+{
+ return (unsigned long *)ceb_key_ptr(node, kofs);
+}
+
+/* insert at root <root>, the item <item_ptr> using node member <nname> and key
+ * member <kname>, and returns either the inserted item, or the one that was
+ * found there.
+ */
+#define cebl_item_insert(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebl_ofs_insert(root, _kofs, &(item_ptr)->nname); \
+ (typeof(item_ptr))((char *)(_node) - _nofs); \
+})
+
+/* descend root <root>, and return the first item of type <type>, using node
+ * member <nname> and key member <kname>, or NULL if the tree is empty.
+ */
+#define cebl_item_first(root, nname, kname, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebl_ofs_first(root, _kofs); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* descend root <root>, and return the last item of type <type>, using node
+ * member <nname> and key member <kname>, or NULL if the tree is empty.
+ */
+#define cebl_item_last(root, nname, kname, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebl_ofs_last(root, _kofs); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item of type <type> which contains the
+ * key <key>, using node member <nname> and key member <kname>. Returns the
+ * pointer to the item, or NULL if not found.
+ */
+#define cebl_item_lookup(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebl_ofs_lookup(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the last of
+ * the greatest keys that are lower than or equal to <key>, using node member
+ * <nname> and key member <kname>. Returns the pointer to the item, or NULL if
+ * not found.
+ */
+#define cebl_item_lookup_le(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebl_ofs_lookup_le(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the last of
+ * the greatest keys that are strictly lower than <key>, using node member
+ * <nname> and key member <kname>. Returns the pointer to the item, or NULL if
+ * not found.
+ */
+#define cebl_item_lookup_lt(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebl_ofs_lookup_lt(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the first of
+ * the lowest keys key that are greater than or equal to <key>, using node
+ * member <nname> and key member <kname>. Returns the pointer to the item, or
+ * NULL if not found.
+ */
+#define cebl_item_lookup_ge(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebl_ofs_lookup_ge(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the first of
+ * the lowest keys that are strictly greater than <key>, using node member
+ * <nname> and key member <kname>. Returns the pointer to the item, or NULL if
+ * not found.
+ */
+#define cebl_item_lookup_gt(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebl_ofs_lookup_gt(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item after item <item_ptr> that has a
+ * strictly greater key, using node member <nname> and key member <kname>, and
+ * returns either the found item, or NULL if none exists.
+ */
+#define cebl_item_next_unique(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebl_ofs_next_unique(root, _kofs, &(item_ptr)->nname); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the last item before item <item_ptr> that has a
+ * strictly lower key, using node member <nname> and key member <kname>, and
+ * returns either the found item, or NULL if none exists.
+ */
+#define cebl_item_prev_unique(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebl_ofs_prev_unique(root, _kofs, &(item_ptr)->nname); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item after item <item_ptr> that has
+ * exactly the same key, using node member <nname> and key member <kname>, and
+ * returns either the found item, or NULL if none exists.
+ */
+#define cebl_item_next_dup(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebl_ofs_next_dup(root, _kofs, &(item_ptr)->nname); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the last item before item <item_ptr> that has
+ * exactly the same key, using node member <nname> and key member <kname>, and
+ * returns either the found item, or NULL if none exists.
+ */
+#define cebl_item_prev_dup(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebl_ofs_prev_dup(root, _kofs, &(item_ptr)->nname); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item immediately after item <item_ptr>,
+ * using node member <nname> and key member <kname>, and returns either the
+ * found item, or NULL if none exists.
+ */
+#define cebl_item_next(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebl_ofs_next(root, _kofs, &(item_ptr)->nname); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the last item immediately before item <item_ptr>,
+ * using node member <nname> and key member <kname>, and returns either the
+ * found item, or NULL if none exists.
+ */
+#define cebl_item_prev(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebl_ofs_prev(root, _kofs, &(item_ptr)->nname); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item <item_ptr> using node member <nname> and
+ * key member <kname>, deletes it and returns it. If the item is NULL or absent
+ * from the tree, NULL is returned.
+ */
+#define cebl_item_delete(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ typeof(item_ptr) _item = (item_ptr); \
+ struct ceb_node *_node = cebl_ofs_delete(root, _kofs, _item ? &_item->nname : NULL); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item of type <type> which contains the
+ * key <key>, using node member <nname> and key member <kname>, deletes it and
+ * returns it. If the key is not found in the tree, NULL is returned.
+ */
+#define cebl_item_pick(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebl_ofs_pick(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/*** versions using unique keys below ***/
+
+/* insert at root <root>, the item <item_ptr> using node member <nname> and key
+ * member <kname>, and returns either the inserted item, or the one that was
+ * found there.
+ */
+#define cebul_item_insert(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebul_ofs_insert(root, _kofs, &(item_ptr)->nname); \
+ (typeof(item_ptr))((char *)(_node) - _nofs); \
+})
+
+/* descend root <root>, and return the first item of type <type>, using node
+ * member <nname> and key member <kname>, or NULL if the tree is empty.
+ */
+#define cebul_item_first(root, nname, kname, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebul_ofs_first(root, _kofs); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* descend root <root>, and return the last item of type <type>, using node
+ * member <nname> and key member <kname>, or NULL if the tree is empty.
+ */
+#define cebul_item_last(root, nname, kname, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebul_ofs_last(root, _kofs); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the key
+ * <key>, using node member <nname> and key member <kname>. Returns the pointer
+ * to the item, or NULL if not found.
+ */
+#define cebul_item_lookup(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebul_ofs_lookup(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the greatest
+ * key that is lower than or equal to <key>, using node member <nname> and key
+ * member <kname>. Returns the pointer to the item, or NULL if not found.
+ */
+#define cebul_item_lookup_le(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebul_ofs_lookup_le(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the greatest
+ * key that is strictly lower than <key>, using node member <nname> and key
+ * member <kname>. Returns the pointer to the item, or NULL if not found.
+ */
+#define cebul_item_lookup_lt(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebul_ofs_lookup_lt(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the lowest
+ * key key that is greater than or equal to <key>, using node member <nname>
+ * and key member <kname>. Returns the pointer to the item, or NULL if not
+ * found.
+ */
+#define cebul_item_lookup_ge(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebul_ofs_lookup_ge(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the lowest
+ * key that is strictly greater than <key>, using node member <nname> and key
+ * member <kname>. Returns the pointer to the item, or NULL if not found.
+ */
+#define cebul_item_lookup_gt(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebul_ofs_lookup_gt(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item immediately after item <item_ptr>,
+ * using node member <nname> and key member <kname>, and returns either the
+ * found item, or NULL if none exists.
+ */
+#define cebul_item_next(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebul_ofs_next(root, _kofs, &(item_ptr)->nname); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the last item immediately before item <item_ptr>,
+ * using node member <nname> and key member <kname>, and returns either the
+ * found item, or NULL if none exists.
+ */
+#define cebul_item_prev(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebul_ofs_prev(root, _kofs, &(item_ptr)->nname); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item <item_ptr> using node member <nname> and
+ * key member <kname>, deletes it and returns it. If the item is NULL or absent
+ * from the tree, NULL is returned.
+ */
+#define cebul_item_delete(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ typeof(item_ptr) _item = (item_ptr); \
+ struct ceb_node *_node = cebul_ofs_delete(root, _kofs, _item ? &_item->nname : NULL); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item of type <type> which contains the
+ * key <key>, using node member <nname> and key member <kname>, deletes it and
+ * returns it. If the key is not found in the tree, NULL is returned.
+ */
+#define cebul_item_pick(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebul_ofs_pick(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+#endif /* _CEBL_TREE_H */
--- /dev/null
+/*
+ * Compact Elastic Binary Trees - exported functions operating on string keys
+ *
+ * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _CEBS_TREE_H
+#define _CEBS_TREE_H
+
+#include "cebtree.h"
+
+/* simpler version */
+struct ceb_node *cebs_imm_insert(struct ceb_root **root, struct ceb_node *node);
+struct ceb_node *cebs_imm_first(struct ceb_root *const *root);
+struct ceb_node *cebs_imm_last(struct ceb_root *const *root);
+struct ceb_node *cebs_imm_lookup(struct ceb_root *const *root, const void *key);
+struct ceb_node *cebs_imm_lookup_le(struct ceb_root *const *root, const void *key);
+struct ceb_node *cebs_imm_lookup_lt(struct ceb_root *const *root, const void *key);
+struct ceb_node *cebs_imm_lookup_ge(struct ceb_root *const *root, const void *key);
+struct ceb_node *cebs_imm_lookup_gt(struct ceb_root *const *root, const void *key);
+struct ceb_node *cebs_imm_next_unique(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *cebs_imm_prev_unique(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *cebs_imm_next_dup(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *cebs_imm_prev_dup(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *cebs_imm_next(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *cebs_imm_prev(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *cebs_imm_delete(struct ceb_root **root, struct ceb_node *node);
+struct ceb_node *cebs_imm_pick(struct ceb_root **root, const void *key);
+
+struct ceb_node *cebus_imm_insert(struct ceb_root **root, struct ceb_node *node);
+struct ceb_node *cebus_imm_first(struct ceb_root *const *root);
+struct ceb_node *cebus_imm_last(struct ceb_root *const *root);
+struct ceb_node *cebus_imm_lookup(struct ceb_root *const *root, const void *key);
+struct ceb_node *cebus_imm_lookup_le(struct ceb_root *const *root, const void *key);
+struct ceb_node *cebus_imm_lookup_lt(struct ceb_root *const *root, const void *key);
+struct ceb_node *cebus_imm_lookup_ge(struct ceb_root *const *root, const void *key);
+struct ceb_node *cebus_imm_lookup_gt(struct ceb_root *const *root, const void *key);
+struct ceb_node *cebus_imm_next(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *cebus_imm_prev(struct ceb_root *const *root, struct ceb_node *node);
+struct ceb_node *cebus_imm_delete(struct ceb_root **root, struct ceb_node *node);
+struct ceb_node *cebus_imm_pick(struct ceb_root **root, const void *key);
+
+/* generic dump function */
+void cebs_imm_default_dump(struct ceb_root *const *root, const char *label, const void *ctx, int sub);
+
+/* returns the pointer to the char* key that follows the node */
+static inline char *cebs_imm_key(const struct ceb_node *node)
+{
+ return (char *)ceb_key_ptr(node, sizeof(struct ceb_node));
+}
+
+/* version taking a key offset */
+struct ceb_node *cebs_ofs_insert(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebs_ofs_first(struct ceb_root *const *root, ptrdiff_t kofs);
+struct ceb_node *cebs_ofs_last(struct ceb_root *const *root, ptrdiff_t kofs);
+struct ceb_node *cebs_ofs_lookup(struct ceb_root *const *root, ptrdiff_t kofs, const void *key);
+struct ceb_node *cebs_ofs_lookup_le(struct ceb_root *const *root, ptrdiff_t kofs, const void *key);
+struct ceb_node *cebs_ofs_lookup_lt(struct ceb_root *const *root, ptrdiff_t kofs, const void *key);
+struct ceb_node *cebs_ofs_lookup_ge(struct ceb_root *const *root, ptrdiff_t kofs, const void *key);
+struct ceb_node *cebs_ofs_lookup_gt(struct ceb_root *const *root, ptrdiff_t kofs, const void *key);
+struct ceb_node *cebs_ofs_next_unique(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebs_ofs_prev_unique(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebs_ofs_next_dup(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebs_ofs_prev_dup(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebs_ofs_next(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebs_ofs_prev(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebs_ofs_delete(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebs_ofs_pick(struct ceb_root **root, ptrdiff_t kofs, const void *key);
+
+struct ceb_node *cebus_ofs_insert(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebus_ofs_first(struct ceb_root *const *root, ptrdiff_t kofs);
+struct ceb_node *cebus_ofs_last(struct ceb_root *const *root, ptrdiff_t kofs);
+struct ceb_node *cebus_ofs_lookup(struct ceb_root *const *root, ptrdiff_t kofs, const void *key);
+struct ceb_node *cebus_ofs_lookup_le(struct ceb_root *const *root, ptrdiff_t kofs, const void *key);
+struct ceb_node *cebus_ofs_lookup_lt(struct ceb_root *const *root, ptrdiff_t kofs, const void *key);
+struct ceb_node *cebus_ofs_lookup_ge(struct ceb_root *const *root, ptrdiff_t kofs, const void *key);
+struct ceb_node *cebus_ofs_lookup_gt(struct ceb_root *const *root, ptrdiff_t kofs, const void *key);
+struct ceb_node *cebus_ofs_next(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebus_ofs_prev(struct ceb_root *const *root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebus_ofs_delete(struct ceb_root **root, ptrdiff_t kofs, struct ceb_node *node);
+struct ceb_node *cebus_ofs_pick(struct ceb_root **root, ptrdiff_t kofs, const void *key);
+
+/* generic dump function taking a key offset */
+void cebs_ofs_default_dump(struct ceb_root *const *root, ptrdiff_t kofs, const char *label, const void *ctx, int sub);
+
+/* returns the pointer to the char* key*/
+static inline char *cebs_ofs_key(const struct ceb_node *node, ptrdiff_t kofs)
+{
+ return (char *)ceb_key_ptr(node, kofs);
+}
+
+/* insert at root <root>, the item <item_ptr> using node member <nname> and key
+ * member <kname>, and returns either the inserted item, or the one that was
+ * found there.
+ */
+#define cebs_item_insert(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebs_ofs_insert(root, _kofs, &(item_ptr)->nname); \
+ (typeof(item_ptr))((char *)(_node) - _nofs); \
+})
+
+/* descend root <root>, and return the first item of type <type>, using node
+ * member <nname> and key member <kname>, or NULL if the tree is empty.
+ */
+#define cebs_item_first(root, nname, kname, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebs_ofs_first(root, _kofs); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* descend root <root>, and return the last item of type <type>, using node
+ * member <nname> and key member <kname>, or NULL if the tree is empty.
+ */
+#define cebs_item_last(root, nname, kname, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebs_ofs_last(root, _kofs); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item of type <type> which contains the
+ * key <key>, using node member <nname> and key member <kname>. Returns the
+ * pointer to the item, or NULL if not found.
+ */
+#define cebs_item_lookup(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebs_ofs_lookup(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the last of
+ * the greatest keys that are lower than or equal to <key>, using node member
+ * <nname> and key member <kname>. Returns the pointer to the item, or NULL if
+ * not found.
+ */
+#define cebs_item_lookup_le(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebs_ofs_lookup_le(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the last of
+ * the greatest keys that are strictly lower than <key>, using node member
+ * <nname> and key member <kname>. Returns the pointer to the item, or NULL if
+ * not found.
+ */
+#define cebs_item_lookup_lt(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebs_ofs_lookup_lt(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the first of
+ * the lowest keys key that are greater than or equal to <key>, using node
+ * member <nname> and key member <kname>. Returns the pointer to the item, or
+ * NULL if not found.
+ */
+#define cebs_item_lookup_ge(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebs_ofs_lookup_ge(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the first of
+ * the lowest keys that are strictly greater than <key>, using node member
+ * <nname> and key member <kname>. Returns the pointer to the item, or NULL if
+ * not found.
+ */
+#define cebs_item_lookup_gt(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebs_ofs_lookup_gt(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item after item <item_ptr> that has a
+ * strictly greater key, using node member <nname> and key member <kname>, and
+ * returns either the found item, or NULL if none exists.
+ */
+#define cebs_item_next_unique(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebs_ofs_next_unique(root, _kofs, &(item_ptr)->nname); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the last item before item <item_ptr> that has a
+ * strictly lower key, using node member <nname> and key member <kname>, and
+ * returns either the found item, or NULL if none exists.
+ */
+#define cebs_item_prev_unique(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebs_ofs_prev_unique(root, _kofs, &(item_ptr)->nname); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item after item <item_ptr> that has
+ * exactly the same key, using node member <nname> and key member <kname>, and
+ * returns either the found item, or NULL if none exists.
+ */
+#define cebs_item_next_dup(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebs_ofs_next_dup(root, _kofs, &(item_ptr)->nname); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the last item before item <item_ptr> that has
+ * exactly the same key, using node member <nname> and key member <kname>, and
+ * returns either the found item, or NULL if none exists.
+ */
+#define cebs_item_prev_dup(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebs_ofs_prev_dup(root, _kofs, &(item_ptr)->nname); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item immediately after item <item_ptr>,
+ * using node member <nname> and key member <kname>, and returns either the
+ * found item, or NULL if none exists.
+ */
+#define cebs_item_next(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebs_ofs_next(root, _kofs, &(item_ptr)->nname); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the last item immediately before item <item_ptr>,
+ * using node member <nname> and key member <kname>, and returns either the
+ * found item, or NULL if none exists.
+ */
+#define cebs_item_prev(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebs_ofs_prev(root, _kofs, &(item_ptr)->nname); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item <item_ptr> using node member <nname> and
+ * key member <kname>, deletes it and returns it. If the item is NULL or absent
+ * from the tree, NULL is returned.
+ */
+#define cebs_item_delete(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ typeof(item_ptr) _item = (item_ptr); \
+ struct ceb_node *_node = cebs_ofs_delete(root, _kofs, _item ? &_item->nname : NULL); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item of type <type> which contains the
+ * key <key>, using node member <nname> and key member <kname>, deletes it and
+ * returns it. If the key is not found in the tree, NULL is returned.
+ */
+#define cebs_item_pick(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebs_ofs_pick(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/*** versions using unique keys below ***/
+
+/* insert at root <root>, the item <item_ptr> using node member <nname> and key
+ * member <kname>, and returns either the inserted item, or the one that was
+ * found there.
+ */
+#define cebus_item_insert(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebus_ofs_insert(root, _kofs, &(item_ptr)->nname); \
+ (typeof(item_ptr))((char *)(_node) - _nofs); \
+})
+
+/* descend root <root>, and return the first item of type <type>, using node
+ * member <nname> and key member <kname>, or NULL if the tree is empty.
+ */
+#define cebus_item_first(root, nname, kname, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebus_ofs_first(root, _kofs); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* descend root <root>, and return the last item of type <type>, using node
+ * member <nname> and key member <kname>, or NULL if the tree is empty.
+ */
+#define cebus_item_last(root, nname, kname, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebus_ofs_last(root, _kofs); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the key
+ * <key>, using node member <nname> and key member <kname>. Returns the pointer
+ * to the item, or NULL if not found.
+ */
+#define cebus_item_lookup(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebus_ofs_lookup(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the greatest
+ * key that is lower than or equal to <key>, using node member <nname> and key
+ * member <kname>. Returns the pointer to the item, or NULL if not found.
+ */
+#define cebus_item_lookup_le(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebus_ofs_lookup_le(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the greatest
+ * key that is strictly lower than <key>, using node member <nname> and key
+ * member <kname>. Returns the pointer to the item, or NULL if not found.
+ */
+#define cebus_item_lookup_lt(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebus_ofs_lookup_lt(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the lowest
+ * key key that is greater than or equal to <key>, using node member <nname>
+ * and key member <kname>. Returns the pointer to the item, or NULL if not
+ * found.
+ */
+#define cebus_item_lookup_ge(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebus_ofs_lookup_ge(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item of type <type> which contains the lowest
+ * key that is strictly greater than <key>, using node member <nname> and key
+ * member <kname>. Returns the pointer to the item, or NULL if not found.
+ */
+#define cebus_item_lookup_gt(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebus_ofs_lookup_gt(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item immediately after item <item_ptr>,
+ * using node member <nname> and key member <kname>, and returns either the
+ * found item, or NULL if none exists.
+ */
+#define cebus_item_next(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebus_ofs_next(root, _kofs, &(item_ptr)->nname); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the last item immediately before item <item_ptr>,
+ * using node member <nname> and key member <kname>, and returns either the
+ * found item, or NULL if none exists.
+ */
+#define cebus_item_prev(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ struct ceb_node *_node = cebus_ofs_prev(root, _kofs, &(item_ptr)->nname); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the item <item_ptr> using node member <nname> and
+ * key member <kname>, deletes it and returns it. If the item is NULL or absent
+ * from the tree, NULL is returned.
+ */
+#define cebus_item_delete(root, nname, kname, item_ptr) ({ \
+ ptrdiff_t _nofs = offsetof(typeof(*(item_ptr)), nname); \
+ ptrdiff_t _kofs = offsetof(typeof(*(item_ptr)), kname) - _nofs; \
+ typeof(item_ptr) _item = (item_ptr); \
+ struct ceb_node *_node = cebus_ofs_delete(root, _kofs, _item ? &_item->nname : NULL); \
+ _node ? (typeof(item_ptr))((char *)(_node) - _nofs) : NULL; \
+})
+
+/* lookup from root <root>, the first item of type <type> which contains the
+ * key <key>, using node member <nname> and key member <kname>, deletes it and
+ * returns it. If the key is not found in the tree, NULL is returned.
+ */
+#define cebus_item_pick(root, nname, kname, key, type) ({ \
+ ptrdiff_t _nofs = offsetof(type, nname); \
+ ptrdiff_t _kofs = offsetof(type, kname) - _nofs; \
+ struct ceb_node *_node = cebus_ofs_pick(root, _kofs, key); \
+ _node ? (type *)((char *)(_node) - _nofs) : NULL; \
+})
+
+#endif /* _CEBS_TREE_H */
/*
* Compact Elastic Binary Trees - internal functions and types
*
- * Copyright (C) 2014-2024 Willy Tarreau - w@1wt.eu
+ * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* the same split bit as its parent node, it is necessary its associated leaf
*
* When descending along the tree, it is possible to know that a search key is
- * not present, because its XOR with both of the branches is strictly higher
+ * not present, because its XOR with both of the branches is stricly higher
* than the inter-branch XOR. The reason is simple : the inter-branch XOR will
* have its highest bit set indicating the split bit. Since it's the bit that
* differs between the two branches, the key cannot have it both set and
#ifndef _CEBTREE_PRV_H
#define _CEBTREE_PRV_H
+#include <sys/types.h>
#include <inttypes.h>
+#include <stddef.h>
#include <string.h>
#include "cebtree.h"
-/* If DEBUG is set, we'll print additional debugging info during the descent */
-#ifdef DEBUG
-#define CEBDBG(x, ...) fprintf(stderr, x, ##__VA_ARGS__)
+/* A few utility functions and macros that we need below */
+
+/* This is used to test if a macro is defined and equals 1. The principle is
+ * that the macro is passed as a value and its value concatenated to the word
+ * "comma_for_one" to form a new macro name. The macro "comma_for_one1" equals
+ * one comma, which, once used in an argument, will shift all of them by one,
+ * so that we can use this to concatenate both a 1 and a 0 and always pick the
+ * second one.
+ */
+#define comma_for_one1 ,
+#define _____equals_1(x, y, ...) (y)
+#define ____equals_1(x, ...) _____equals_1(x, 0)
+#define ___equals_1(x) ____equals_1(comma_for_one ## x 1)
+#define __equals_1(x) ___equals_1(x)
+
+/* gcc 5 and clang 3 brought __has_attribute(), which is not well documented in
+ * the case of gcc, but is convenient since handled at the preprocessor level.
+ * In both cases it's possible to test for __has_attribute() using ifdef. When
+ * not defined we remap this to the __has_attribute_<name> macro so that we'll
+ * later be able to implement on a per-compiler basis those which are missing,
+ * by defining __has_attribute_<name> to 1.
+ */
+#ifndef __has_attribute
+#define __has_attribute(x) __equals_1(__has_attribute_ ## x)
+#endif
+
+/* gcc 10 and clang 3 brought __has_builtin() to test if a builtin exists.
+ * Just like above, if it doesn't exist, we remap it to a macro allowing us
+ * to define these ourselves by defining __has_builtin_<name> to 1.
+ */
+#ifndef __has_builtin
+#define __has_builtin(x) __equals_1(__has_builtin_ ## x)
+#endif
+
+#if !defined(__GNUC__)
+/* Some versions of glibc irresponsibly redefine __attribute__() to empty for
+ * non-gcc compilers, and as such, silently break all constructors with other
+ * other compilers. Let's make sure such incompatibilities are detected if any,
+ * or that the attribute is properly enforced.
+ */
+#undef __attribute__
+#define __attribute__(x) __attribute__(x)
+#endif
+
+/* Define the missing __builtin_prefetch() for tcc. */
+#if defined(__TINYC__) && !defined(__builtin_prefetch)
+#define __builtin_prefetch(addr, ...) do { } while (0)
+#endif
+
+/* __builtin_unreachable() was added in gcc 4.5 */
+#if defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
+#define __has_builtin___builtin_unreachable 1 /* make __builtin_unreachable() return 1 */
+#elif !__has_builtin(__builtin_unreachable)
+#define __builtin_unreachable() do { } while (1)
+#endif
+
+/* FLSNZ: find last set bit for non-zero value. "Last" here means the highest
+ * one. It returns a value from 1 to 32 for 1<<0 to 1<<31.
+ */
+
+#if defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 2)))
+/* gcc >= 4.2 brings __builtin_clz() and __builtin_clzl(), also usable for
+ * non-x86. However on x86 gcc does bad stuff if not properly handled. It xors
+ * the bsr return with 31 and since it doesn't know how to deal with a xor
+ * followed by a negation, it adds two instructions when using 32-clz(). Thus
+ * instead we first cancel the xor using another one then add one. Even on ARM
+ * that provides a clz instruction, it saves one register to proceed like this.
+ */
+
+#define flsnz8(x) flsnz32((unsigned char)x)
+
+static inline __attribute__((always_inline)) unsigned int flsnz32(unsigned int x)
+{
+ return (__builtin_clz(x) ^ 31) + 1;
+}
+
+static inline __attribute__((always_inline)) unsigned int flsnz64(unsigned long long x)
+{
+ return (__builtin_clzll(x) ^ 63) + 1;
+}
+
+#elif (defined(__i386__) || defined(__x86_64__)) && !defined(__atom__) /* Not gcc >= 4.2 */
+/* DO NOT USE ON ATOM! The instruction is emulated and is several times slower
+ * than doing the math by hand.
+ */
+#define flsnz8(x) flsnz32((unsigned char)x)
+
+static inline __attribute__((always_inline)) unsigned int flsnz32(unsigned int x)
+{
+ unsigned int r;
+ __asm__("bsrl %1,%0\n"
+ : "=r" (r) : "rm" (x));
+ return r + 1;
+}
+
+#if defined(__x86_64__)
+static inline __attribute__((always_inline)) unsigned int flsnz64(unsigned long long x)
+{
+ unsigned long long r;
+ __asm__("bsrq %1,%0\n"
+ : "=r" (r) : "rm" (x));
+ return r + 1;
+}
+#else
+static inline __attribute__((always_inline)) unsigned int flsnz64(unsigned long long x)
+{
+ unsigned int h;
+ unsigned int bits = 32;
+
+ h = x >> 32;
+ if (!h) {
+ h = x;
+ bits = 0;
+ }
+ return flsnz32(h) + bits;
+}
+#endif
+
+#else /* Neither gcc >= 4.2 nor x86, use generic code */
+
+static inline __attribute__((always_inline)) unsigned int flsnz8(unsigned int x)
+{
+ unsigned int ret = 0;
+ if (x >> 4) { x >>= 4; ret += 4; }
+ return ret + ((0xFFFFAA50U >> (x << 1)) & 3) + 1;
+}
+
+#define flsnz32(___a) ({ \
+ register unsigned int ___x, ___bits = 0; \
+ ___x = (___a); \
+ if (___x & 0xffff0000) { ___x &= 0xffff0000; ___bits += 16;} \
+ if (___x & 0xff00ff00) { ___x &= 0xff00ff00; ___bits += 8;} \
+ if (___x & 0xf0f0f0f0) { ___x &= 0xf0f0f0f0; ___bits += 4;} \
+ if (___x & 0xcccccccc) { ___x &= 0xcccccccc; ___bits += 2;} \
+ if (___x & 0xaaaaaaaa) { ___x &= 0xaaaaaaaa; ___bits += 1;} \
+ ___bits + 1; \
+ })
+
+static inline __attribute__((always_inline)) unsigned int flsnz64(unsigned long long x)
+{
+ unsigned int h;
+ unsigned int bits = 32;
+
+ h = x >> 32;
+ if (!h) {
+ h = x;
+ bits = 0;
+ }
+ return flsnz32(h) + bits;
+}
+
+#endif
+
+#define flsnz_long(x) ((sizeof(long) > 4) ? flsnz64(x) : flsnz32(x))
+#define flsnz(x) ((sizeof(x) > 4) ? flsnz64(x) : (sizeof(x) > 1) ? flsnz32(x) : flsnz8(x))
+
+/* Compare blocks <a> and <b> byte-to-byte, from bit <ignore> to bit <len-1>.
+ * Return the number of equal bits between strings, assuming that the first
+ * <ignore> bits are already identical. It is possible to return slightly more
+ * than <len> bits if <len> does not stop on a byte boundary and we find exact
+ * bytes. Note that parts or all of <ignore> bits may be rechecked. It is only
+ * passed here as a hint to speed up the check.
+ */
+static
+#if defined(__OPTIMIZE_SIZE__)
+__attribute__((noinline))
+#else
+inline __attribute__((always_inline))
+#endif
+size_t equal_bits(const unsigned char *a,
+ const unsigned char *b,
+ size_t ignore, size_t len)
+{
+ for (ignore >>= 3, a += ignore, b += ignore, ignore <<= 3;
+ ignore < len; ) {
+ unsigned char c;
+
+ a++; b++;
+ ignore += 8;
+ c = b[-1] ^ a[-1];
+
+ if (c) {
+ /* OK now we know that old and new differ at byte <ptr> and that <c> holds
+ * the bit differences. We have to find what bit is differing and report
+ * it as the number of identical bits. Note that low bit numbers are
+ * assigned to high positions in the byte, as we compare them as strings.
+ */
+ ignore -= flsnz_long(c);
+ break;
+ }
+ }
+ return ignore;
+}
+
+/* Compare strings <a> and <b> byte-to-byte, from bit <ignore> to the last 0.
+ * Return the number of equal bits between strings, assuming that the first
+ * <ignore> bits are already identical. Note that parts or all of <ignore> bits
+ * may be rechecked. It is only passed here as a hint to speed up the check.
+ * The caller is responsible for not passing an <ignore> value larger than any
+ * of the two strings. However, referencing any bit from the trailing zero is
+ * permitted. Equal strings are reported as a negative number of bits, which
+ * indicates the end was reached.
+ */
+static
+#if defined(__OPTIMIZE_SIZE__)
+__attribute__((noinline))
#else
-#define CEBDBG(x, ...) do { } while (0)
+inline __attribute__((always_inline))
#endif
+size_t string_equal_bits(const unsigned char *a,
+ const unsigned char *b,
+ size_t ignore)
+{
+ unsigned char c, d;
+ size_t beg;
+
+ beg = ignore >> 3;
+
+ /* skip known and identical bits. We stop at the first different byte
+ * or at the first zero we encounter on either side.
+ */
+ for (;; beg += 2) {
+ c = a[beg + 0];
+ d = b[beg + 0];
+ c ^= d;
+ if (__builtin_expect(c != 0, 0))
+ goto brk1;
+ if (!d)
+ goto same;
+ c = a[beg + 1];
+ d = b[beg + 1];
+ c ^= d;
+ if (__builtin_expect(c != 0, 0))
+ goto brk2;
+ if (!d)
+ goto same;
+ }
+brk2:
+ beg++;
+brk1:
+
+ /* OK now we know that a and b differ at byte <beg>.
+ * We have to find what bit is differing and report it as the number of
+ * identical bits. Note that low bit numbers are assigned to high positions
+ * in the byte, as we compare them as strings.
+ */
+ return (beg << 3) + ((flsnz(c) - 1) ^ 7);
+same:
+ return (size_t)-1;
+}
+
+/* pointer tagging / untagging, to turn ceb_root to ceb_node and conversely */
+
+/* tag an untagged pointer (node -> root) */
+static inline struct ceb_root *_ceb_dotag(const struct ceb_node *node, const uintptr_t tag)
+{
+ return (struct ceb_root *)((uintptr_t)node + tag);
+}
+
+/* untag a tagged pointer (root -> node) */
+static inline struct ceb_node *_ceb_untag(const struct ceb_root *node, const uintptr_t tag)
+{
+ return (struct ceb_node *)((uintptr_t)node - tag);
+}
+
+/* clear a pointer's tag, regardless of its previous value */
+static inline struct ceb_node *_ceb_clrtag(const struct ceb_root *node)
+{
+ return (struct ceb_node *)((uintptr_t)node & ~(uintptr_t)1);
+}
+
+/* report the pointer's tag */
+static inline uintptr_t _ceb_gettag(const struct ceb_root *node)
+{
+ return (uintptr_t)node & (uintptr_t)1;
+}
/* These macros are used by upper level files to create two variants of their
* exported functions:
* - one which uses sizeof(struct ceb_node) as the key offset, for nodes with
- * adjacent keys ; these ones are named <pfx><sfx>(root, ...)
+ * adjacent keys ; these ones are named <pfx><sfx>(root, ...). This is
+ * defined when CEB_USE_BASE is defined.
* - one with an explicit key offset passed by the caller right after the
- * root.
+ * root. This is defined when CEB_USE_OFST is defined.
* Both rely on a forced inline version with a body that immediately follows
* the declaration, so that the declaration looks like a single decorated
* function while 2 are built in practice. There are variants for the basic one
* first variant, it's always replaced by sizeof(struct ceb_node) in the calls
* to the inline version.
*/
-#define CEB_FDECL2(type, pfx, sfx, type1, arg1, type2, arg2) \
+#if defined(CEB_USE_BASE)
+# define _CEB_DEF_BASE(x) x
+#else
+# define _CEB_DEF_BASE(x)
+#endif
+
+#if defined(CEB_USE_OFST)
+# define _CEB_DEF_OFST(x) x
+#else
+# define _CEB_DEF_OFST(x)
+#endif
+
+#define CEB_FDECL2(type, pfx, sfx, type1, arg1, type2, arg2) \
+ _CEB_FDECL2(type, pfx, sfx, type1, arg1, type2, arg2)
+
+#define _CEB_FDECL2(type, pfx, sfx, type1, arg1, type2, arg2) \
static inline __attribute__((always_inline)) \
type _##pfx##sfx(type1 arg1, type2 arg2); \
- type pfx##sfx(type1 arg1) { \
+ _CEB_DEF_BASE(type pfx##_imm##sfx(type1 arg1) { \
return _##pfx##sfx(arg1, sizeof(struct ceb_node)); \
- } \
- type pfx##_ofs##sfx(type1 arg1, type2 arg2) { \
+ }) \
+ _CEB_DEF_OFST(type pfx##_ofs##sfx(type1 arg1, type2 arg2) { \
return _##pfx##sfx(arg1, arg2); \
- } \
+ }) \
static inline __attribute__((always_inline)) \
type _##pfx##sfx(type1 arg1, type2 arg2)
/* function body follows */
#define CEB_FDECL3(type, pfx, sfx, type1, arg1, type2, arg2, type3, arg3) \
+ _CEB_FDECL3(type, pfx, sfx, type1, arg1, type2, arg2, type3, arg3)
+
+#define _CEB_FDECL3(type, pfx, sfx, type1, arg1, type2, arg2, type3, arg3) \
static inline __attribute__((always_inline)) \
type _##pfx##sfx(type1 arg1, type2 arg2, type3 arg3); \
- type pfx##sfx(type1 arg1, type3 arg3) { \
+ _CEB_DEF_BASE(type pfx##_imm##sfx(type1 arg1, type3 arg3) { \
return _##pfx##sfx(arg1, sizeof(struct ceb_node), arg3); \
- } \
- type pfx##_ofs##sfx(type1 arg1, type2 arg2, type3 arg3) { \
+ }) \
+ _CEB_DEF_OFST(type pfx##_ofs##sfx(type1 arg1, type2 arg2, type3 arg3) { \
return _##pfx##sfx(arg1, arg2, arg3); \
- } \
+ }) \
static inline __attribute__((always_inline)) \
type _##pfx##sfx(type1 arg1, type2 arg2, type3 arg3)
/* function body follows */
#define CEB_FDECL4(type, pfx, sfx, type1, arg1, type2, arg2, type3, arg3, type4, arg4) \
+ _CEB_FDECL4(type, pfx, sfx, type1, arg1, type2, arg2, type3, arg3, type4, arg4)
+
+#define _CEB_FDECL4(type, pfx, sfx, type1, arg1, type2, arg2, type3, arg3, type4, arg4) \
static inline __attribute__((always_inline)) \
type _##pfx##sfx(type1 arg1, type2 arg2, type3 arg3, type4 arg4); \
- type pfx##sfx(type1 arg1, type3 arg3, type4 arg4) { \
+ _CEB_DEF_BASE(type pfx##_imm##sfx(type1 arg1, type3 arg3, type4 arg4) { \
return _##pfx##sfx(arg1, sizeof(struct ceb_node), arg3, arg4); \
- } \
- type pfx##_ofs##sfx(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \
+ }) \
+ _CEB_DEF_OFST(type pfx##_ofs##sfx(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \
return _##pfx##sfx(arg1, arg2, arg3, arg4); \
- } \
+ }) \
static inline __attribute__((always_inline)) \
type _##pfx##sfx(type1 arg1, type2 arg2, type3 arg3, type4 arg4)
/* function body follows */
+#define CEB_FDECL5(type, pfx, sfx, type1, arg1, type2, arg2, type3, arg3, type4, arg4, type5, arg5) \
+ _CEB_FDECL5(type, pfx, sfx, type1, arg1, type2, arg2, type3, arg3, type4, arg4, type5, arg5)
+
+#define _CEB_FDECL5(type, pfx, sfx, type1, arg1, type2, arg2, type3, arg3, type4, arg4, type5, arg5) \
+ static inline __attribute__((always_inline)) \
+ type _##pfx##sfx(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5); \
+ _CEB_DEF_BASE(type pfx##_imm##sfx(type1 arg1, type3 arg3, type4 arg4, type5 arg5) { \
+ return _##pfx##sfx(arg1, sizeof(struct ceb_node), arg3, arg4, arg5); \
+ }) \
+ _CEB_DEF_OFST(type pfx##_ofs##sfx(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) { \
+ return _##pfx##sfx(arg1, arg2, arg3, arg4, arg5); \
+ }) \
+ static inline __attribute__((always_inline)) \
+ type _##pfx##sfx(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5)
+ /* function body follows */
+
/* tree walk method: key, left, right */
enum ceb_walk_meth {
CEB_WM_FST, /* look up "first" (walk left only) */
/* returns the ceb_key_storage pointer for node <n> and offset <o> */
#define NODEK(n, o) ((union ceb_key_storage*)(((char *)(n)) + (o)))
-/* Returns the xor (or common length) between the two sides <l> and <r> if both
- * are non-null, otherwise between the first non-null one and the value in the
- * associate key. As a reminder, memory blocks place their length in key_u64.
- * This is only intended for internal use, essentially for debugging.
- *
- * <kofs> contains the offset between the key and the node's base. When simply
- * adjacent, this would just be sizeof(ceb_node).
- */
-__attribute__((unused))
-static inline uint64_t _xor_branches(ptrdiff_t kofs, enum ceb_key_type key_type, uint32_t key_u32,
- uint64_t key_u64, const void *key_ptr,
- const struct ceb_node *l,
- const struct ceb_node *r)
-{
- if (l && r) {
- if (key_type == CEB_KT_MB)
- return equal_bits(NODEK(l, kofs)->mb, NODEK(r, kofs)->mb, 0, key_u64 << 3);
- else if (key_type == CEB_KT_IM)
- return equal_bits(NODEK(l, kofs)->mb, NODEK(r, kofs)->ptr, 0, key_u64 << 3);
- else if (key_type == CEB_KT_ST)
- return string_equal_bits(NODEK(l, kofs)->str, NODEK(r, kofs)->str, 0);
- else if (key_type == CEB_KT_IS)
- return string_equal_bits(NODEK(l, kofs)->ptr, NODEK(r, kofs)->ptr, 0);
- else if (key_type == CEB_KT_U64)
- return NODEK(l, kofs)->u64 ^ NODEK(r, kofs)->u64;
- else if (key_type == CEB_KT_U32)
- return NODEK(l, kofs)->u32 ^ NODEK(r, kofs)->u32;
- else if (key_type == CEB_KT_ADDR)
- return ((uintptr_t)l ^ (uintptr_t)r);
- else
- return 0;
- }
-
- if (!l)
- l = r;
-
- if (key_type == CEB_KT_MB)
- return equal_bits(key_ptr, NODEK(l, kofs)->mb, 0, key_u64 << 3);
- else if (key_type == CEB_KT_IM)
- return equal_bits(key_ptr, NODEK(l, kofs)->ptr, 0, key_u64 << 3);
- else if (key_type == CEB_KT_ST)
- return string_equal_bits(key_ptr, NODEK(l, kofs)->str, 0);
- else if (key_type == CEB_KT_IS)
- return string_equal_bits(key_ptr, NODEK(l, kofs)->ptr, 0);
- else if (key_type == CEB_KT_U64)
- return key_u64 ^ NODEK(l, kofs)->u64;
- else if (key_type == CEB_KT_U32)
- return key_u32 ^ NODEK(l, kofs)->u32;
- else if (key_type == CEB_KT_ADDR)
- return ((uintptr_t)key_ptr ^ (uintptr_t)r);
- else
- return 0;
-}
-
-#ifdef DEBUG
-__attribute__((unused))
-static void dbg(int line,
- const char *pfx,
- enum ceb_walk_meth meth,
- ptrdiff_t kofs,
- enum ceb_key_type key_type,
- struct ceb_node * const *root,
- const struct ceb_node *p,
- uint32_t key_u32,
- uint64_t key_u64,
- const void *key_ptr,
- uint32_t px32,
- uint64_t px64,
- size_t plen)
-{
- const char *meths[] = {
- [CEB_WM_FST] = "FST",
- [CEB_WM_NXT] = "NXT",
- [CEB_WM_PRV] = "PRV",
- [CEB_WM_LST] = "LST",
- [CEB_WM_KEQ] = "KEQ",
- [CEB_WM_KGE] = "KGE",
- [CEB_WM_KGT] = "KGT",
- [CEB_WM_KLE] = "KLE",
- [CEB_WM_KLT] = "KLT",
- [CEB_WM_KNX] = "KNX",
- [CEB_WM_KPR] = "KPR",
- };
- const char *ktypes[] = {
- [CEB_KT_ADDR] = "ADDR",
- [CEB_KT_U32] = "U32",
- [CEB_KT_U64] = "U64",
- [CEB_KT_MB] = "MB",
- [CEB_KT_IM] = "IM",
- [CEB_KT_ST] = "ST",
- [CEB_KT_IS] = "IS",
- };
- const char *kstr __attribute__((unused)) = ktypes[key_type];
- const char *mstr __attribute__((unused)) = meths[meth];
- long long nlen __attribute__((unused)) = 0;
- long long llen __attribute__((unused)) = 0;
- long long rlen __attribute__((unused)) = 0;
- long long xlen __attribute__((unused)) = 0;
-
- if (p)
- nlen = _xor_branches(kofs, key_type, key_u32, key_u64, key_ptr, p, NULL);
-
- if (p && p->b[0])
- llen = _xor_branches(kofs, key_type, key_u32, key_u64, key_ptr, p->b[0], NULL);
-
- if (p && p->b[1])
- rlen = _xor_branches(kofs, key_type, key_u32, key_u64, key_ptr, NULL, p->b[1]);
-
- if (p && p->b[0] && p->b[1])
- xlen = _xor_branches(kofs, key_type, key_u32, key_u64, key_ptr, p->b[0], p->b[1]);
-
- switch (key_type) {
- case CEB_KT_U32:
- CEBDBG("%04d (%8s) m=%s.%s key=%#x root=%p pxor=%#x p=%p,%#x(^%#llx) l=%p,%#x(^%#llx) r=%p,%#x(^%#llx) l^r=%#llx\n",
- line, pfx, kstr, mstr, key_u32, root, px32,
- p, p ? NODEK(p, kofs)->u32 : 0, nlen,
- p ? p->b[0] : NULL, p ? NODEK(p->b[0], kofs)->u32 : 0, llen,
- p ? p->b[1] : NULL, p ? NODEK(p->b[1], kofs)->u32 : 0, rlen,
- xlen);
- break;
- case CEB_KT_U64:
- CEBDBG("%04d (%8s) m=%s.%s key=%#llx root=%p pxor=%#llx p=%p,%#llx(^%#llx) l=%p,%#llx(^%#llx) r=%p,%#llx(^%#llx) l^r=%#llx\n",
- line, pfx, kstr, mstr, (long long)key_u64, root, (long long)px64,
- p, (long long)(p ? NODEK(p, kofs)->u64 : 0), nlen,
- p ? p->b[0] : NULL, (long long)(p ? NODEK(p->b[0], kofs)->u64 : 0), llen,
- p ? p->b[1] : NULL, (long long)(p ? NODEK(p->b[1], kofs)->u64 : 0), rlen,
- xlen);
- break;
- case CEB_KT_MB:
- CEBDBG("%04d (%8s) m=%s.%s key=%p root=%p plen=%ld p=%p,%p(^%llu) l=%p,%p(^%llu) r=%p,%p(^%llu) l^r=%llu\n",
- line, pfx, kstr, mstr, key_ptr, root, (long)plen,
- p, p ? NODEK(p, kofs)->mb : 0, nlen,
- p ? p->b[0] : NULL, p ? NODEK(p->b[0], kofs)->mb : 0, llen,
- p ? p->b[1] : NULL, p ? NODEK(p->b[1], kofs)->mb : 0, rlen,
- xlen);
- break;
- case CEB_KT_IM:
- CEBDBG("%04d (%8s) m=%s.%s key=%p root=%p plen=%ld p=%p,%p(^%llu) l=%p,%p(^%llu) r=%p,%p(^%llu) l^r=%llu\n",
- line, pfx, kstr, mstr, key_ptr, root, (long)plen,
- p, p ? NODEK(p, kofs)->ptr : 0, nlen,
- p ? p->b[0] : NULL, p ? NODEK(p->b[0], kofs)->ptr : 0, llen,
- p ? p->b[1] : NULL, p ? NODEK(p->b[1], kofs)->ptr : 0, rlen,
- xlen);
- break;
- case CEB_KT_ST:
- CEBDBG("%04d (%8s) m=%s.%s key='%s' root=%p plen=%ld p=%p,%s(^%llu) l=%p,%s(^%llu) r=%p,%s(^%llu) l^r=%llu\n",
- line, pfx, kstr, mstr, key_ptr ? (const char *)key_ptr : "", root, (long)plen,
- p, p ? (const char *)NODEK(p, kofs)->str : "-", nlen,
- p ? p->b[0] : NULL, p ? (const char *)NODEK(p->b[0], kofs)->str : "-", llen,
- p ? p->b[1] : NULL, p ? (const char *)NODEK(p->b[1], kofs)->str : "-", rlen,
- xlen);
- break;
- case CEB_KT_IS:
- CEBDBG("%04d (%8s) m=%s.%s key='%s' root=%p plen=%ld p=%p,%s(^%llu) l=%p,%s(^%llu) r=%p,%s(^%llu) l^r=%llu\n",
- line, pfx, kstr, mstr, key_ptr ? (const char *)key_ptr : "", root, (long)plen,
- p, p ? (const char *)NODEK(p, kofs)->ptr : "-", nlen,
- p ? p->b[0] : NULL, p ? (const char *)NODEK(p->b[0], kofs)->ptr : "-", llen,
- p ? p->b[1] : NULL, p ? (const char *)NODEK(p->b[1], kofs)->ptr : "-", rlen,
- xlen);
- break;
- case CEB_KT_ADDR:
- /* key type is the node's address */
- CEBDBG("%04d (%8s) m=%s.%s key=%#llx root=%p pxor=%#llx p=%p,%#llx(^%#llx) l=%p,%#llx(^%#llx) r=%p,%#llx(^%#llx) l^r=%#llx\n",
- line, pfx, kstr, mstr, (long long)(uintptr_t)key_ptr, root, (long long)px64,
- p, (long long)(uintptr_t)p, nlen,
- p ? p->b[0] : NULL, p ? (long long)(uintptr_t)p->b[0] : 0, llen,
- p ? p->b[1] : NULL, p ? (long long)(uintptr_t)p->b[1] : 0, rlen,
- xlen);
- }
-}
-#else
-#define dbg(...) do { } while (0)
-#endif
-
/* Generic tree descent function. It must absolutely be inlined so that the
* compiler can eliminate the tests related to the various return pointers,
* which must either point to a local variable in the caller, or be NULL.
* size arrays are passed in key_ptr with their length in key_u64. For keyless
* nodes whose address serves as the key, the pointer needs to be passed in
* key_ptr, and pxor64 will be used internally.
+ * The support for duplicates is advertised by ret_is_dup not being null; it
+ * will be filled on return with an indication whether the node belongs to a
+ * duplicate list or not.
*/
static inline __attribute__((always_inline))
-struct ceb_node *_cebu_descend(struct ceb_node **root,
- enum ceb_walk_meth meth,
- ptrdiff_t kofs,
- enum ceb_key_type key_type,
- uint32_t key_u32,
- uint64_t key_u64,
- const void *key_ptr,
- int *ret_nside,
- struct ceb_node ***ret_root,
- struct ceb_node **ret_lparent,
- int *ret_lpside,
- struct ceb_node **ret_nparent,
- int *ret_npside,
- struct ceb_node **ret_gparent,
- int *ret_gpside,
- struct ceb_node **ret_back)
+struct ceb_node *_ceb_descend(struct ceb_root **root,
+ enum ceb_walk_meth meth,
+ ptrdiff_t kofs,
+ enum ceb_key_type key_type,
+ uint32_t key_u32,
+ uint64_t key_u64,
+ const void *key_ptr,
+ int *ret_nside,
+ struct ceb_root ***ret_root,
+ struct ceb_node **ret_lparent,
+ int *ret_lpside,
+ struct ceb_node **ret_nparent,
+ int *ret_npside,
+ struct ceb_node **ret_gparent,
+ int *ret_gpside,
+ struct ceb_root **ret_back,
+ int *ret_is_dup)
{
-#if !defined(__OPTIMIZE__) && __GNUC_PREREQ__(12, 0)
+#if defined(__GNUC__) && (__GNUC__ >= 12) && !defined(__OPTIMIZE__)
/* Avoid a bogus warning with gcc 12 and above: it warns about negative
* memcmp() length in non-existing code paths at -O0, as reported here:
* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114622
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstringop-overread"
#endif
- struct ceb_node *p;
- union ceb_key_storage *l, *r, *k;
+ struct ceb_node *node;
+ union ceb_key_storage *k;
struct ceb_node *gparent = NULL;
- struct ceb_node *nparent = NULL;
struct ceb_node *bnode = NULL;
struct ceb_node *lparent;
- uint32_t pxor32 = ~0U; // previous xor between branches
- uint64_t pxor64 = ~0ULL; // previous xor between branches
+ uint32_t pxor32 __attribute__((unused)) = ~0U; // previous xor between branches
+ uint64_t pxor64 __attribute__((unused)) = ~0ULL; // previous xor between branches
int gpside = 0; // side on the grand parent
- int npside = 0; // side on the node's parent
long lpside = 0; // side on the leaf's parent
long brside = 0; // branch side when descending
size_t llen = 0; // left vs key matching length
size_t rlen = 0; // right vs key matching length
size_t plen = 0; // previous common len between branches
- int found = 0; // key was found (saves an extra strcmp for arrays)
-
- dbg(__LINE__, "_enter__", meth, kofs, key_type, root, NULL, key_u32, key_u64, key_ptr, pxor32, pxor64, plen);
+ int is_leaf = 0; // set if the current node is a leaf
/* the parent will be the (possibly virtual) node so that
- * &lparent->l == root.
+ * &lparent->l == root, i.e. container_of(root, struct ceb_node, b[0]).
*/
- lparent = container_of(root, struct ceb_node, b[0]);
- gparent = nparent = lparent;
+ lparent = (struct ceb_node *)((char *)root - (long)&((struct ceb_node *)0)->b[0]);
+ gparent = lparent;
+ if (ret_nparent)
+ *ret_nparent = NULL;
+ if (ret_npside)
+ *ret_npside = 0;
/* for key-less descents we need to set the initial branch to take */
switch (meth) {
break;
}
+ /* In case of deletion, we need the node's parent and side. It's
+ * normally discovered during the descent while comparing branches,
+ * but there's a case where it's not possible, it's when the root
+ * is the node's parent because the first node is the one we're
+ * looking for. So we have to perform this check here.
+ */
+ if (meth >= CEB_WM_KEQ && ret_nparent && ret_npside) {
+ union ceb_key_storage *k = NODEK(_ceb_clrtag(*root), kofs);
+
+ if (((key_type == CEB_KT_MB || key_type == CEB_KT_IM) &&
+ (memcmp(key_ptr, ((key_type == CEB_KT_MB) ? k->mb : k->ptr), key_u64) == 0)) ||
+ ((key_type == CEB_KT_ST || key_type == CEB_KT_IS) &&
+ (strcmp(key_ptr, (const void *)((key_type == CEB_KT_ST) ? k->str : k->ptr)) == 0))) {
+ *ret_nparent = lparent;
+ *ret_npside = lpside;
+ }
+ }
+
/* the previous xor is initialized to the largest possible inter-branch
* value so that it can never match on the first test as we want to use
* it to detect a leaf vs node. That's achieved with plen==0 for arrays
* and pxorXX==~0 for scalars.
*/
+ node = _ceb_clrtag(*root);
+ is_leaf = _ceb_gettag(*root);
+
+ if (ret_lpside) {
+ /* this is a deletion, benefits from prefetching */
+ __builtin_prefetch(node->b[0], 0);
+ __builtin_prefetch(node->b[1], 0);
+ }
+
while (1) {
- p = *root;
+ union ceb_key_storage *lks, *rks;
+ struct ceb_node *ln, *rn, *next;
+ struct ceb_root *lr, *rr;
+ int next_leaf, lnl, rnl;
- /* Tests have shown that for write-intensive workloads (many
- * insertions/deletion), prefetching for reads is counter
- * productive (-10% perf) but that prefetching only the next
- * nodes for writes when deleting can yield around 3% extra
- * boost.
- */
- if (ret_lpside) {
- /* this is a deletion, prefetch for writes */
- __builtin_prefetch(p->b[0], 1);
- __builtin_prefetch(p->b[1], 1);
- }
+ lr = node->b[0]; // tagged versions
+ rr = node->b[1];
- /* neither pointer is tagged */
- k = NODEK(p, kofs);
- l = NODEK(p->b[0], kofs);
- r = NODEK(p->b[1], kofs);
+ /* get a copy of the corresponding nodes */
+ lnl = _ceb_gettag(lr);
+ ln = _ceb_clrtag(lr);
+ rnl = _ceb_gettag(rr);
+ rn = _ceb_clrtag(rr);
- dbg(__LINE__, "newp", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen);
+ /* neither pointer is tagged */
+ k = NODEK(node, kofs);
- /* two equal pointers identifies the nodeless leaf. */
- if (l == r) {
- dbg(__LINE__, "l==r", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen);
+ if (is_leaf)
break;
- }
+
+ /* Tests show that this is the most optimal location to start
+ * a prefetch for adjacent nodes.
+ */
+ __builtin_prefetch(ln, 0);
+ __builtin_prefetch(rn, 0);
+
+ lks = NODEK(ln, kofs);
+ rks = NODEK(rn, kofs);
/* In the following block, we're dealing with type-specific
* operations which follow the same construct for each type:
* - if we're deleting, it could be the key we were
* looking for so we have to check for it as long as
* it's still possible to keep a copy of the node's
- * parent. <found> is set int this case for expensive
- * types.
+ * parent.
*/
if (key_type == CEB_KT_U32) {
uint32_t xor32; // left vs right branch xor
uint32_t kl, kr;
- kl = l->u32; kr = r->u32;
- xor32 = kl ^ kr;
-
- if (xor32 > pxor32) { // test using 2 4 6 4
- dbg(__LINE__, "xor>", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen);
- break;
- }
-
+ kl = lks->u32; kr = rks->u32;
if (meth >= CEB_WM_KEQ) {
- /* "found" is not used here */
kl ^= key_u32; kr ^= key_u32;
brside = kl >= kr;
+ }
+ xor32 = kl ^ kr;
+ if (meth >= CEB_WM_KEQ) {
/* let's stop if our key is not there */
-
- if (kl > xor32 && kr > xor32) {
- dbg(__LINE__, "mismatch", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen);
+ if (kl > xor32 && kr > xor32)
break;
- }
- if (ret_npside || ret_nparent) {
+ if (ret_nparent && !*ret_nparent && ret_npside) {
if (key_u32 == k->u32) {
- dbg(__LINE__, "equal", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen);
- nparent = lparent;
- npside = lpside;
+ *ret_nparent = lparent;
+ *ret_npside = lpside;
}
}
+
+ /* for pure lookups, no need to go down the leaf
+ * if we've found the key.
+ */
+ if (!ret_root && !ret_lpside && !ret_lparent &&
+ !ret_gpside && !ret_gparent && !ret_back) {
+ if (key_u32 == k->u32)
+ break;
+ }
}
pxor32 = xor32;
}
uint64_t xor64; // left vs right branch xor
uint64_t kl, kr;
- kl = l->u64; kr = r->u64;
- xor64 = kl ^ kr;
-
- if (xor64 > pxor64) { // test using 2 4 6 4
- dbg(__LINE__, "xor>", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen);
- break;
- }
-
+ kl = lks->u64; kr = rks->u64;
if (meth >= CEB_WM_KEQ) {
- /* "found" is not used here */
kl ^= key_u64; kr ^= key_u64;
brside = kl >= kr;
+ }
+ xor64 = kl ^ kr;
+ if (meth >= CEB_WM_KEQ) {
/* let's stop if our key is not there */
-
- if (kl > xor64 && kr > xor64) {
- dbg(__LINE__, "mismatch", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen);
+ if (kl > xor64 && kr > xor64)
break;
- }
- if (ret_npside || ret_nparent) {
+ if (ret_nparent && !*ret_nparent && ret_npside) {
if (key_u64 == k->u64) {
- dbg(__LINE__, "equal", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen);
- nparent = lparent;
- npside = lpside;
+ *ret_nparent = lparent;
+ *ret_npside = lpside;
}
}
+
+ /* for pure lookups, no need to go down the leaf
+ * if we've found the key.
+ */
+ if (!ret_root && !ret_lpside && !ret_lparent &&
+ !ret_gpside && !ret_gparent && !ret_back) {
+ if (key_u64 == k->u64)
+ break;
+ }
}
pxor64 = xor64;
}
- else if (key_type == CEB_KT_MB) {
- size_t xlen = 0; // left vs right matching length
+ else if (key_type == CEB_KT_ADDR) {
+ uintptr_t xoraddr; // left vs right branch xor
+ uintptr_t kl, kr;
+ kl = (uintptr_t)lks; kr = (uintptr_t)rks;
if (meth >= CEB_WM_KEQ) {
- /* measure identical lengths */
- llen = equal_bits(key_ptr, l->mb, 0, key_u64 << 3);
- rlen = equal_bits(key_ptr, r->mb, 0, key_u64 << 3);
- brside = llen <= rlen;
- if (llen == rlen && (uint64_t)llen == key_u64 << 3)
- found = 1;
- }
-
- xlen = equal_bits(l->mb, r->mb, 0, key_u64 << 3);
- if (xlen < plen) {
- /* this is a leaf. E.g. triggered using 2 4 6 4 */
- dbg(__LINE__, "xor>", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen);
- break;
+ kl ^= (uintptr_t)key_ptr; kr ^= (uintptr_t)key_ptr;
+ brside = kl >= kr;
}
+ xoraddr = kl ^ kr;
if (meth >= CEB_WM_KEQ) {
/* let's stop if our key is not there */
-
- if (llen < xlen && rlen < xlen) {
- dbg(__LINE__, "mismatch", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen);
+ if (kl > xoraddr && kr > xoraddr)
break;
- }
- if (ret_npside || ret_nparent) { // delete ?
- size_t mlen = llen > rlen ? llen : rlen;
-
- if (mlen > xlen)
- mlen = xlen;
-
- if ((uint64_t)xlen / 8 == key_u64 || memcmp(key_ptr + mlen / 8, k->mb + mlen / 8, key_u64 - mlen / 8) == 0) {
- dbg(__LINE__, "equal", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen);
- nparent = lparent;
- npside = lpside;
- found = 1;
+ if (ret_nparent && !*ret_nparent && ret_npside) {
+ if ((uintptr_t)key_ptr == (uintptr_t)node) {
+ *ret_nparent = lparent;
+ *ret_npside = lpside;
}
}
+
+ /* for pure lookups, no need to go down the leaf
+ * if we've found the key.
+ */
+ if (!ret_root && !ret_lpside && !ret_lparent &&
+ !ret_gpside && !ret_gparent && !ret_back) {
+ if ((uintptr_t)key_ptr == (uintptr_t)node)
+ break;
+ }
}
- plen = xlen;
+ pxor64 = xoraddr;
}
- else if (key_type == CEB_KT_IM) {
+ else if (key_type == CEB_KT_MB || key_type == CEB_KT_IM) {
size_t xlen = 0; // left vs right matching length
if (meth >= CEB_WM_KEQ) {
/* measure identical lengths */
- llen = equal_bits(key_ptr, l->ptr, 0, key_u64 << 3);
- rlen = equal_bits(key_ptr, r->ptr, 0, key_u64 << 3);
+ llen = equal_bits(key_ptr, (key_type == CEB_KT_MB) ? lks->mb : lks->ptr, plen, key_u64 << 3);
+ rlen = equal_bits(key_ptr, (key_type == CEB_KT_MB) ? rks->mb : rks->ptr, plen, key_u64 << 3);
brside = llen <= rlen;
- if (llen == rlen && (uint64_t)llen == key_u64 << 3)
- found = 1;
}
- xlen = equal_bits(l->ptr, r->ptr, 0, key_u64 << 3);
- if (xlen < plen) {
- /* this is a leaf. E.g. triggered using 2 4 6 4 */
- dbg(__LINE__, "xor>", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen);
- break;
- }
+ xlen = equal_bits((key_type == CEB_KT_MB) ? lks->mb : lks->ptr,
+ (key_type == CEB_KT_MB) ? rks->mb : rks->ptr, plen, key_u64 << 3);
if (meth >= CEB_WM_KEQ) {
/* let's stop if our key is not there */
-
- if (llen < xlen && rlen < xlen) {
- dbg(__LINE__, "mismatch", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen);
+ if (llen < xlen && rlen < xlen)
break;
- }
-
- if (ret_npside || ret_nparent) { // delete ?
- size_t mlen = llen > rlen ? llen : rlen;
- if (mlen > xlen)
- mlen = xlen;
+ if (ret_nparent && ret_npside && !*ret_nparent &&
+ ((llen == key_u64 << 3) || (rlen == key_u64 << 3))) {
+ *ret_nparent = node;
+ *ret_npside = brside;
+ }
- if ((uint64_t)xlen / 8 == key_u64 || memcmp(key_ptr + mlen / 8, k->ptr + mlen / 8, key_u64 - mlen / 8) == 0) {
- dbg(__LINE__, "equal", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen);
- nparent = lparent;
- npside = lpside;
- found = 1;
+ /* for pure lookups, no need to go down the leaf
+ * if we've found the key.
+ */
+ if (!ret_root && !ret_lpside && !ret_lparent &&
+ !ret_gpside && !ret_gparent && !ret_back) {
+ if (llen == key_u64 << 3) {
+ node = ln;
+ plen = llen;
+ break;
+ }
+ if (rlen == key_u64 << 3) {
+ node = rn;
+ plen = rlen;
+ break;
}
}
}
plen = xlen;
}
- else if (key_type == CEB_KT_ST) {
+ else if (key_type == CEB_KT_ST || key_type == CEB_KT_IS) {
size_t xlen = 0; // left vs right matching length
if (meth >= CEB_WM_KEQ) {
* leaf. We take that negative length for an
* infinite one, hence the uint cast.
*/
- llen = string_equal_bits(key_ptr, l->str, 0);
- rlen = string_equal_bits(key_ptr, r->str, 0);
+ llen = string_equal_bits(key_ptr, (key_type == CEB_KT_ST) ? lks->str : lks->ptr, plen);
+ rlen = string_equal_bits(key_ptr, (key_type == CEB_KT_ST) ? rks->str : rks->ptr, plen);
brside = (size_t)llen <= (size_t)rlen;
- if ((ssize_t)llen < 0 || (ssize_t)rlen < 0)
- found = 1;
- }
+ if (ret_nparent && ret_npside && !*ret_nparent &&
+ ((ssize_t)llen < 0 || (ssize_t)rlen < 0)) {
+ *ret_nparent = node;
+ *ret_npside = brside;
+ }
- xlen = string_equal_bits(l->str, r->str, 0);
- if (xlen < plen) {
- /* this is a leaf. E.g. triggered using 2 4 6 4 */
- dbg(__LINE__, "xor>", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen);
- break;
+ /* for pure lookups, no need to go down the leaf
+ * if we've found the key.
+ */
+ if (!ret_root && !ret_lpside && !ret_lparent &&
+ !ret_gpside && !ret_gparent && !ret_back) {
+ if ((ssize_t)llen < 0) {
+ node = ln;
+ plen = llen;
+ break;
+ }
+ if ((ssize_t)rlen < 0) {
+ node = rn;
+ plen = rlen;
+ break;
+ }
+ }
}
- if (meth >= CEB_WM_KEQ) {
- /* let's stop if our key is not there */
-
- if ((unsigned)llen < (unsigned)xlen && (unsigned)rlen < (unsigned)xlen) {
- dbg(__LINE__, "mismatch", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen);
- break;
- }
+ /* the compiler cannot know this never happens and this helps it optimize the code */
+ if ((ssize_t)plen < 0)
+ __builtin_unreachable();
- if (ret_npside || ret_nparent) { // delete ?
- size_t mlen = llen > rlen ? llen : rlen;
+ xlen = string_equal_bits((key_type == CEB_KT_ST) ? lks->str : lks->ptr,
+ (key_type == CEB_KT_ST) ? rks->str : rks->ptr, plen);
- if (mlen > xlen)
- mlen = xlen;
+ /* let's stop if our key is not there */
+ if (meth >= CEB_WM_KEQ && llen < xlen && rlen < xlen)
+ break;
- if (strcmp(key_ptr + mlen / 8, (const void *)k->str + mlen / 8) == 0) {
- /* strcmp() still needed. E.g. 1 2 3 4 10 11 4 3 2 1 10 11 fails otherwise */
- dbg(__LINE__, "equal", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen);
- nparent = lparent;
- npside = lpside;
- found = 1;
- }
- }
- }
plen = xlen;
}
- else if (key_type == CEB_KT_IS) {
- size_t xlen = 0; // left vs right matching length
- if (meth >= CEB_WM_KEQ) {
- /* Note that a negative length indicates an
- * equal value with the final zero reached, but
- * it is still needed to descend to find the
- * leaf. We take that negative length for an
- * infinite one, hence the uint cast.
- */
- llen = string_equal_bits(key_ptr, l->ptr, 0);
- rlen = string_equal_bits(key_ptr, r->ptr, 0);
- brside = (size_t)llen <= (size_t)rlen;
- if ((ssize_t)llen < 0 || (ssize_t)rlen < 0)
- found = 1;
- }
+ /* shift all copies by one */
+ gparent = lparent;
+ gpside = lpside;
+ lparent = node;
+ lpside = brside;
+ if (brside) {
+ if (meth == CEB_WM_KPR || meth == CEB_WM_KLE || meth == CEB_WM_KLT)
+ bnode = node;
+ next = rn;
+ next_leaf = rnl;
+ root = &node->b[1];
- xlen = string_equal_bits(l->ptr, r->ptr, 0);
- if (xlen < plen) {
- /* this is a leaf. E.g. triggered using 2 4 6 4 */
- dbg(__LINE__, "xor>", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen);
- break;
- }
+ /* change branch for key-less walks */
+ if (meth == CEB_WM_NXT)
+ brside = 0;
+ }
+ else {
+ if (meth == CEB_WM_KNX || meth == CEB_WM_KGE || meth == CEB_WM_KGT)
+ bnode = node;
+ next = ln;
+ next_leaf = lnl;
+ root = &node->b[0];
- if (meth >= CEB_WM_KEQ) {
- /* let's stop if our key is not there */
+ /* change branch for key-less walks */
+ if (meth == CEB_WM_PRV)
+ brside = 1;
+ }
- if ((unsigned)llen < (unsigned)xlen && (unsigned)rlen < (unsigned)xlen) {
- dbg(__LINE__, "mismatch", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen);
- break;
- }
+ if (next == node) {
+ /* loops over itself, it's either a leaf or the single and last list element of a dup sub-tree */
+ break;
+ }
- if (ret_npside || ret_nparent) { // delete ?
- size_t mlen = llen > rlen ? llen : rlen;
+ /* let the compiler know there's no NULL in the tree */
+ if (!next)
+ __builtin_unreachable();
- if (mlen > xlen)
- mlen = xlen;
+ node = next;
+ is_leaf = next_leaf;
+ }
- if (strcmp(key_ptr + mlen / 8, (const void *)k->ptr + mlen / 8) == 0) {
- /* strcmp() still needed. E.g. 1 2 3 4 10 11 4 3 2 1 10 11 fails otherwise */
- dbg(__LINE__, "equal", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen);
- nparent = lparent;
- npside = lpside;
- found = 1;
- }
- }
- }
- plen = xlen;
- }
- else if (key_type == CEB_KT_ADDR) {
- uintptr_t xoraddr; // left vs right branch xor
- uintptr_t kl, kr;
-
- kl = (uintptr_t)l; kr = (uintptr_t)r;
- xoraddr = kl ^ kr;
-
- if (xoraddr > (uintptr_t)pxor64) { // test using 2 4 6 4
- dbg(__LINE__, "xor>", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen);
- break;
- }
-
- if (meth >= CEB_WM_KEQ) {
- /* "found" is not used here */
- kl ^= (uintptr_t)key_ptr; kr ^= (uintptr_t)key_ptr;
- brside = kl >= kr;
-
- /* let's stop if our key is not there */
-
- if (kl > xoraddr && kr > xoraddr) {
- dbg(__LINE__, "mismatch", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen);
- break;
- }
-
- if (ret_npside || ret_nparent) {
- if ((uintptr_t)key_ptr == (uintptr_t)p) {
- dbg(__LINE__, "equal", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen);
- nparent = lparent;
- npside = lpside;
- }
- }
- }
- pxor64 = xoraddr;
- }
-
- /* shift all copies by one */
- gparent = lparent;
- gpside = lpside;
- lparent = p;
- lpside = brside;
- if (brside) {
- if (meth == CEB_WM_KPR || meth == CEB_WM_KLE || meth == CEB_WM_KLT)
- bnode = p;
- root = &p->b[1];
-
- /* change branch for key-less walks */
- if (meth == CEB_WM_NXT)
- brside = 0;
-
- dbg(__LINE__, "side1", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen);
- }
- else {
- if (meth == CEB_WM_KNX || meth == CEB_WM_KGE || meth == CEB_WM_KGT)
- bnode = p;
- root = &p->b[0];
-
- /* change branch for key-less walks */
- if (meth == CEB_WM_PRV)
- brside = 1;
-
- dbg(__LINE__, "side0", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen);
- }
-
- if (p == *root) {
- /* loops over itself, it's a leaf */
- dbg(__LINE__, "loop", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen);
- break;
+ if (ret_is_dup) {
+ if (is_leaf && _ceb_gettag(node->b[0]) && _ceb_gettag(node->b[1]) &&
+ (_ceb_clrtag(node->b[0]) != node || _ceb_clrtag(node->b[1]) != node)) {
+ /* This leaf has two tagged pointers, with at least one not pointing
+ * to itself, it's not the nodeless leaf, it's a duplicate.
+ */
+ *ret_is_dup = 1;
+ } else {
+ *ret_is_dup = 0;
}
}
* still deserved, depending on the matching method.
*/
- /* if we've exited on an exact match after visiting a regular node
- * (i.e. not the nodeless leaf), we'll avoid checking the string again.
- * However if it doesn't match, we must make sure to compare from
- * within the key (which can be shorter than the ones already there),
- * so we restart the check from the longest of the two lengths, which
- * guarantees these bits exist. Test with "100", "10", "1" to see where
- * this is needed.
- */
- if ((key_type == CEB_KT_ST || key_type == CEB_KT_IS) && meth >= CEB_WM_KEQ && !found)
- plen = (llen > rlen) ? llen : rlen;
-
/* update the pointers needed for modifications (insert, delete) */
if (ret_nside && meth >= CEB_WM_KEQ) {
switch (key_type) {
case CEB_KT_U64:
*ret_nside = key_u64 >= k->u64;
break;
- case CEB_KT_MB:
- *ret_nside = (uint64_t)plen / 8 == key_u64 || memcmp(key_ptr + plen / 8, k->mb + plen / 8, key_u64 - plen / 8) >= 0;
+ case CEB_KT_ADDR:
+ *ret_nside = (uintptr_t)key_ptr >= (uintptr_t)node;
break;
+ case CEB_KT_MB:
case CEB_KT_IM:
- *ret_nside = (uint64_t)plen / 8 == key_u64 || memcmp(key_ptr + plen / 8, k->ptr + plen / 8, key_u64 - plen / 8) >= 0;
+ *ret_nside = (uint64_t)plen / 8 == key_u64 ||
+ memcmp(key_ptr + plen / 8, ((key_type == CEB_KT_MB) ? k->mb : k->ptr) + plen / 8, key_u64 - plen / 8) >= 0;
break;
+
case CEB_KT_ST:
- *ret_nside = found || strcmp(key_ptr + plen / 8, (const void *)k->str + plen / 8) >= 0;
- break;
case CEB_KT_IS:
- *ret_nside = found || strcmp(key_ptr + plen / 8, (const void *)k->ptr + plen / 8) >= 0;
- break;
- case CEB_KT_ADDR:
- *ret_nside = (uintptr_t)key_ptr >= (uintptr_t)p;
+ *ret_nside = (ssize_t)plen < 0 ||
+ strcmp(key_ptr + plen / 8, (const void *)((key_type == CEB_KT_ST) ? k->str : k->ptr) + plen / 8) >= 0;
break;
}
}
- if (ret_root)
+ if (ret_root) {
+ /* this node is going to be changed */
*ret_root = root;
+ __builtin_prefetch(root, 1);
+ }
/* info needed by delete */
if (ret_lpside)
*ret_lpside = lpside;
- if (ret_lparent)
+ if (ret_lparent) {
+ /* this node is going to be changed */
*ret_lparent = lparent;
-
- if (ret_npside)
- *ret_npside = npside;
-
- if (ret_nparent)
- *ret_nparent = nparent;
+ __builtin_prefetch(lparent, 1);
+ }
if (ret_gpside)
*ret_gpside = gpside;
*ret_gparent = gparent;
if (ret_back)
- *ret_back = bnode;
-
- dbg(__LINE__, "_ret____", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen);
+ *ret_back = _ceb_dotag(bnode, 0);
if (meth >= CEB_WM_KEQ) {
/* For lookups, an equal value means an instant return. For insertions,
(meth == CEB_WM_KGT && k->u32 > key_u32) ||
(meth == CEB_WM_KLE && k->u32 <= key_u32) ||
(meth == CEB_WM_KLT && k->u32 < key_u32))
- return p;
+ return node;
}
else if (key_type == CEB_KT_U64) {
if ((meth == CEB_WM_KEQ && k->u64 == key_u64) ||
(meth == CEB_WM_KGT && k->u64 > key_u64) ||
(meth == CEB_WM_KLE && k->u64 <= key_u64) ||
(meth == CEB_WM_KLT && k->u64 < key_u64))
- return p;
+ return node;
}
- else if (key_type == CEB_KT_MB) {
- int diff;
-
- if ((uint64_t)plen / 8 == key_u64)
- diff = 0;
- else
- diff = memcmp(k->mb + plen / 8, key_ptr + plen / 8, key_u64 - plen / 8);
-
- if ((meth == CEB_WM_KEQ && diff == 0) ||
- (meth == CEB_WM_KNX && diff == 0) ||
- (meth == CEB_WM_KPR && diff == 0) ||
- (meth == CEB_WM_KGE && diff >= 0) ||
- (meth == CEB_WM_KGT && diff > 0) ||
- (meth == CEB_WM_KLE && diff <= 0) ||
- (meth == CEB_WM_KLT && diff < 0))
- return p;
+ else if (key_type == CEB_KT_ADDR) {
+ if ((meth == CEB_WM_KEQ && (uintptr_t)node == (uintptr_t)key_ptr) ||
+ (meth == CEB_WM_KNX && (uintptr_t)node == (uintptr_t)key_ptr) ||
+ (meth == CEB_WM_KPR && (uintptr_t)node == (uintptr_t)key_ptr) ||
+ (meth == CEB_WM_KGE && (uintptr_t)node >= (uintptr_t)key_ptr) ||
+ (meth == CEB_WM_KGT && (uintptr_t)node > (uintptr_t)key_ptr) ||
+ (meth == CEB_WM_KLE && (uintptr_t)node <= (uintptr_t)key_ptr) ||
+ (meth == CEB_WM_KLT && (uintptr_t)node < (uintptr_t)key_ptr))
+ return node;
}
- else if (key_type == CEB_KT_IM) {
+ else if (key_type == CEB_KT_MB || key_type == CEB_KT_IM) {
int diff;
if ((uint64_t)plen / 8 == key_u64)
diff = 0;
else
- diff = memcmp(k->ptr + plen / 8, key_ptr + plen / 8, key_u64 - plen / 8);
+ diff = memcmp(((key_type == CEB_KT_MB) ? k->mb : k->ptr) + plen / 8, key_ptr + plen / 8, key_u64 - plen / 8);
if ((meth == CEB_WM_KEQ && diff == 0) ||
(meth == CEB_WM_KNX && diff == 0) ||
(meth == CEB_WM_KGT && diff > 0) ||
(meth == CEB_WM_KLE && diff <= 0) ||
(meth == CEB_WM_KLT && diff < 0))
- return p;
+ return node;
}
- else if (key_type == CEB_KT_ST) {
+ else if (key_type == CEB_KT_ST || key_type == CEB_KT_IS) {
int diff;
- if (found)
+ if ((ssize_t)plen < 0)
diff = 0;
else
- diff = strcmp((const void *)k->str + plen / 8, key_ptr + plen / 8);
+ diff = strcmp((const void *)((key_type == CEB_KT_ST) ? k->str : k->ptr) + plen / 8, key_ptr + plen / 8);
if ((meth == CEB_WM_KEQ && diff == 0) ||
(meth == CEB_WM_KNX && diff == 0) ||
(meth == CEB_WM_KGT && diff > 0) ||
(meth == CEB_WM_KLE && diff <= 0) ||
(meth == CEB_WM_KLT && diff < 0))
- return p;
- }
- else if (key_type == CEB_KT_IS) {
- int diff;
-
- if (found)
- diff = 0;
- else
- diff = strcmp((const void *)k->ptr + plen / 8, key_ptr + plen / 8);
-
- if ((meth == CEB_WM_KEQ && diff == 0) ||
- (meth == CEB_WM_KNX && diff == 0) ||
- (meth == CEB_WM_KPR && diff == 0) ||
- (meth == CEB_WM_KGE && diff >= 0) ||
- (meth == CEB_WM_KGT && diff > 0) ||
- (meth == CEB_WM_KLE && diff <= 0) ||
- (meth == CEB_WM_KLT && diff < 0))
- return p;
- }
- else if (key_type == CEB_KT_ADDR) {
- if ((meth == CEB_WM_KEQ && (uintptr_t)p == (uintptr_t)key_ptr) ||
- (meth == CEB_WM_KNX && (uintptr_t)p == (uintptr_t)key_ptr) ||
- (meth == CEB_WM_KPR && (uintptr_t)p == (uintptr_t)key_ptr) ||
- (meth == CEB_WM_KGE && (uintptr_t)p >= (uintptr_t)key_ptr) ||
- (meth == CEB_WM_KGT && (uintptr_t)p > (uintptr_t)key_ptr) ||
- (meth == CEB_WM_KLE && (uintptr_t)p <= (uintptr_t)key_ptr) ||
- (meth == CEB_WM_KLT && (uintptr_t)p < (uintptr_t)key_ptr))
- return p;
+ return node;
}
} else if (meth == CEB_WM_FST || meth == CEB_WM_LST) {
- return p;
+ return node;
} else if (meth == CEB_WM_PRV || meth == CEB_WM_NXT) {
- return p;
+ return node;
}
/* lookups and deletes fail here */
* caller to proceed since the element is not there.
*/
return NULL;
-#if __GNUC_PREREQ__(12, 0)
+#if defined(__GNUC__) && (__GNUC__ >= 12) && !defined(__OPTIMIZE__)
#pragma GCC diagnostic pop
#endif
}
+/*
+ * Below are the functions that support duplicate keys (_ceb_*)
+ */
-/* Generic tree insertion function for trees with unique keys. Inserts node
+/* Generic tree insertion function for trees with duplicate keys. Inserts node
* <node> into tree <tree>, with key type <key_type> and key <key_*>.
* Returns the inserted node or the one that already contains the same key.
+ * If <is_dup_ptr> is non-null, then duplicates are permitted and this variable
+ * is used to temporarily carry an internal state.
*/
static inline __attribute__((always_inline))
-struct ceb_node *_cebu_insert(struct ceb_node **root,
- struct ceb_node *node,
- ptrdiff_t kofs,
- enum ceb_key_type key_type,
- uint32_t key_u32,
- uint64_t key_u64,
- const void *key_ptr)
+struct ceb_node *_ceb_insert(struct ceb_root **root,
+ struct ceb_node *node,
+ ptrdiff_t kofs,
+ enum ceb_key_type key_type,
+ uint32_t key_u32,
+ uint64_t key_u64,
+ const void *key_ptr,
+ int *is_dup_ptr)
{
- struct ceb_node **parent;
+ struct ceb_root **parent;
struct ceb_node *ret;
int nside;
if (!*root) {
/* empty tree, insert a leaf only */
- node->b[0] = node->b[1] = node;
- *root = node;
+ node->b[0] = node->b[1] = _ceb_dotag(node, 1);
+ *root = _ceb_dotag(node, 1);
return node;
}
- ret = _cebu_descend(root, CEB_WM_KEQ, kofs, key_type, key_u32, key_u64, key_ptr, &nside, &parent, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+ ret = _ceb_descend(root, CEB_WM_KEQ, kofs, key_type, key_u32, key_u64, key_ptr, &nside, &parent, NULL, NULL, NULL, NULL, NULL, NULL, NULL, is_dup_ptr);
if (!ret) {
/* The key was not in the tree, we can insert it. Better use an
* optimizes it a bit.
*/
if (nside) {
- node->b[1] = node;
+ node->b[1] = _ceb_dotag(node, 1);
node->b[0] = *parent;
} else {
- node->b[0] = node;
+ node->b[0] = _ceb_dotag(node, 1);
node->b[1] = *parent;
}
- *parent = node;
+ *parent = _ceb_dotag(node, 0);
+ ret = node;
+ } else if (is_dup_ptr) {
+ /* The key was found. We must insert after it as the last
+ * element of the dups list, which means that our left branch
+ * will point to the key, the right one to the first dup
+ * (i.e. previous dup's right if it exists, otherwise ourself)
+ * and the parent must point to us.
+ */
+ node->b[0] = *parent;
+
+ if (*is_dup_ptr) {
+ node->b[1] = _ceb_untag(*parent, 1)->b[1];
+ _ceb_untag(*parent, 1)->b[1] = _ceb_dotag(node, 1);
+ } else {
+ node->b[1] = _ceb_dotag(node, 1);
+ }
+ *parent = _ceb_dotag(node, 1);
ret = node;
}
return ret;
}
/* Returns the first node or NULL if not found, assuming a tree made of keys of
- * type <key_type>.
+ * type <key_type>, and optionally <key_len> for fixed-size arrays (otherwise 0).
+ * If the tree starts with duplicates, the first of them is returned.
*/
static inline __attribute__((always_inline))
-struct ceb_node *_cebu_first(struct ceb_node **root,
- ptrdiff_t kofs,
- enum ceb_key_type key_type)
+struct ceb_node *_ceb_first(struct ceb_root *const *root,
+ ptrdiff_t kofs,
+ enum ceb_key_type key_type,
+ uint64_t key_len,
+ int *is_dup_ptr)
{
+ struct ceb_node *node;
+
if (!*root)
return NULL;
- return _cebu_descend(root, CEB_WM_FST, kofs, key_type, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+ node = _ceb_descend((struct ceb_root **)root, CEB_WM_FST, kofs, key_type, 0, key_len, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, is_dup_ptr);
+ if (node && is_dup_ptr && *is_dup_ptr) {
+ /* on a duplicate, the first node is right->left and it's a leaf */
+ node = _ceb_untag(_ceb_untag(node->b[1], 1)->b[0], 1);
+ }
+ return node;
}
/* Returns the last node or NULL if not found, assuming a tree made of keys of
- * type <key_type>.
+ * type <key_type>, and optionally <key_len> for fixed-size arrays (otherwise 0).
+ * If the tree ends with duplicates, the last of them is returned.
*/
static inline __attribute__((always_inline))
-struct ceb_node *_cebu_last(struct ceb_node **root,
- ptrdiff_t kofs,
- enum ceb_key_type key_type)
+struct ceb_node *_ceb_last(struct ceb_root *const *root,
+ ptrdiff_t kofs,
+ enum ceb_key_type key_type,
+ uint64_t key_len,
+ int *is_dup_ptr)
{
if (!*root)
return NULL;
- return _cebu_descend(root, CEB_WM_LST, kofs, key_type, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+ /* note for duplicates: the current scheme always returns the last one by default */
+ return _ceb_descend((struct ceb_root **)root, CEB_WM_LST, kofs, key_type, 0, key_len, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, is_dup_ptr);
}
/* Searches in the tree <root> made of keys of type <key_type>, for the next
* that fork.
*/
static inline __attribute__((always_inline))
-struct ceb_node *_cebu_next(struct ceb_node **root,
- ptrdiff_t kofs,
- enum ceb_key_type key_type,
- uint32_t key_u32,
- uint64_t key_u64,
- const void *key_ptr)
+struct ceb_node *_ceb_next_unique(struct ceb_root *const *root,
+ ptrdiff_t kofs,
+ enum ceb_key_type key_type,
+ uint32_t key_u32,
+ uint64_t key_u64,
+ const void *key_ptr,
+ int *is_dup_ptr)
{
- struct ceb_node *restart;
+ struct ceb_root *restart;
if (!*root)
return NULL;
- if (!_cebu_descend(root, CEB_WM_KNX, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &restart))
+ if (!_ceb_descend((struct ceb_root **)root, CEB_WM_KNX, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &restart, is_dup_ptr))
return NULL;
if (!restart)
return NULL;
- return _cebu_descend(&restart, CEB_WM_NXT, kofs, key_type, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+ return _ceb_descend(&restart, CEB_WM_NXT, kofs, key_type, 0, key_u64, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, is_dup_ptr);
}
/* Searches in the tree <root> made of keys of type <key_type>, for the prev
* that fork.
*/
static inline __attribute__((always_inline))
-struct ceb_node *_cebu_prev(struct ceb_node **root,
- ptrdiff_t kofs,
- enum ceb_key_type key_type,
- uint32_t key_u32,
- uint64_t key_u64,
- const void *key_ptr)
+struct ceb_node *_ceb_prev_unique(struct ceb_root *const *root,
+ ptrdiff_t kofs,
+ enum ceb_key_type key_type,
+ uint32_t key_u32,
+ uint64_t key_u64,
+ const void *key_ptr,
+ int *is_dup_ptr)
{
- struct ceb_node *restart;
+ struct ceb_root *restart;
if (!*root)
return NULL;
- if (!_cebu_descend(root, CEB_WM_KPR, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &restart))
+ if (!_ceb_descend((struct ceb_root **)root, CEB_WM_KPR, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &restart, is_dup_ptr))
return NULL;
if (!restart)
return NULL;
- return _cebu_descend(&restart, CEB_WM_PRV, kofs, key_type, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+ return _ceb_descend(&restart, CEB_WM_PRV, kofs, key_type, 0, key_u64, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, is_dup_ptr);
}
-/* Searches in the tree <root> made of keys of type <key_type>, for the node
- * containing the key <key_*>. Returns NULL if not found.
+/* Searches in the tree <root> made of keys of type <key_type>, for the next
+ * node after <from> also containing key <key_*>. Returns NULL if not found.
+ * It's up to the caller to pass the current node's key in <key_*>.
*/
static inline __attribute__((always_inline))
-struct ceb_node *_cebu_lookup(struct ceb_node **root,
- ptrdiff_t kofs,
- enum ceb_key_type key_type,
- uint32_t key_u32,
- uint64_t key_u64,
- const void *key_ptr)
+struct ceb_node *_ceb_next_dup(struct ceb_root *const *root,
+ ptrdiff_t kofs,
+ enum ceb_key_type key_type,
+ uint32_t key_u32,
+ uint64_t key_u64,
+ const void *key_ptr,
+ const struct ceb_node *from)
{
+ struct ceb_node *node;
+ int is_dup;
+
if (!*root)
return NULL;
- return _cebu_descend(root, CEB_WM_KEQ, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+ node = _ceb_descend((struct ceb_root **)root, CEB_WM_KNX, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &is_dup);
+ if (!node)
+ return NULL;
+
+ /* Normally at this point, if node != from, we've found a node that
+ * differs from the one we're starting from, which indicates that
+ * the starting point belongs to a dup list and is not the last one.
+ * We must then visit the other members. We cannot navigate from the
+ * regular leaf node (the first one) but we can easily verify if we're
+ * on that one by checking if it's node->b[1]->b[0], in which case we
+ * jump to node->b[1]. Otherwise we take from->b[1].
+ */
+ if (node != from) {
+ if (_ceb_untag(node->b[1], 1)->b[0] == _ceb_dotag(from, 1))
+ return _ceb_untag(node->b[1], 1);
+ else
+ return _ceb_untag(from->b[1], 1);
+ }
+
+ /* there's no other dup here */
+ return NULL;
}
-/* Searches in the tree <root> made of keys of type <key_type>, for the node
- * containing the key <key_*> or the highest one that's lower than it. Returns
- * NULL if not found.
+/* Searches in the tree <root> made of keys of type <key_type>, for the prev
+ * node before <from> also containing key <key_*>. Returns NULL if not found.
+ * It's up to the caller to pass the current node's key in <key_*>.
*/
static inline __attribute__((always_inline))
-struct ceb_node *_cebu_lookup_le(struct ceb_node **root,
- ptrdiff_t kofs,
- enum ceb_key_type key_type,
- uint32_t key_u32,
- uint64_t key_u64,
- const void *key_ptr)
+struct ceb_node *_ceb_prev_dup(struct ceb_root *const *root,
+ ptrdiff_t kofs,
+ enum ceb_key_type key_type,
+ uint32_t key_u32,
+ uint64_t key_u64,
+ const void *key_ptr,
+ const struct ceb_node *from)
{
- struct ceb_node *ret = NULL;
- struct ceb_node *restart;
+ struct ceb_node *node;
+ int is_dup;
if (!*root)
return NULL;
- ret = _cebu_descend(root, CEB_WM_KLE, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &restart);
- if (ret)
- return ret;
+ node = _ceb_descend((struct ceb_root **)root, CEB_WM_KPR, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &is_dup);
+ if (!node)
+ return NULL;
+
+ /* Here we have several possibilities:
+ * - from == node => we've found our node. It may be a unique node,
+ * or the last one of a dup series. We'll sort that out thanks to
+ * is_dup, and if it's a dup, we'll use node->b[0].
+ * - from is not the first dup, so we haven't visited them all yet,
+ * hence we visit node->b[0] to switch to the previous dup.
+ * - from is the first dup so we've visited them all.
+ */
+ if (is_dup && (node == from || _ceb_untag(node->b[1], 1)->b[0] != _ceb_dotag(from, 1)))
+ return _ceb_untag(from->b[0], 1);
+
+ /* there's no other dup here */
+ return NULL;
+}
+
+/* Searches in the tree <root> made of keys of type <key_type>, for the next
+ * node after <from> which contains key <key_*>. Returns NULL if not found.
+ * It's up to the caller to pass the current node's key in <key_*>. The
+ * approach consists in looking up that node first, recalling the last time a
+ * left turn was made, and returning the first node along the right branch at
+ * that fork. In case the current node belongs to a duplicate list, all dups
+ * will be visited in insertion order prior to jumping to different keys.
+ */
+static inline __attribute__((always_inline))
+struct ceb_node *_ceb_next(struct ceb_root *const *root,
+ ptrdiff_t kofs,
+ enum ceb_key_type key_type,
+ uint32_t key_u32,
+ uint64_t key_u64,
+ const void *key_ptr,
+ const struct ceb_node *from,
+ int *is_dup_ptr)
+{
+ struct ceb_root *restart;
+ struct ceb_node *node;
+
+ if (!*root)
+ return NULL;
+
+ node = _ceb_descend((struct ceb_root **)root, CEB_WM_KNX, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &restart, is_dup_ptr);
+ if (!node)
+ return NULL;
+ /* Normally at this point, if node != from, we've found a node that
+ * differs from the one we're starting from, which indicates that
+ * the starting point belongs to a dup list and is not the last one.
+ * We must then visit the other members. We cannot navigate from the
+ * regular leaf node (the first one) but we can easily verify if we're
+ * on that one by checking if it's _ceb_untag(node->b[1], 0)->b[0], in which case we
+ * jump to node->b[1]. Otherwise we take from->b[1].
+ */
+ if (node != from) {
+ if (_ceb_untag(node->b[1], 1)->b[0] == _ceb_dotag(from, 1))
+ return _ceb_untag(node->b[1], 1);
+ else
+ return _ceb_untag(from->b[1], 1);
+ }
+
+ /* Here the looked up node was found (node == from) and we can look up
+ * the next unique one if any.
+ */
if (!restart)
return NULL;
- return _cebu_descend(&restart, CEB_WM_PRV, kofs, key_type, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+ /* this look up will stop on the topmost dup in a sub-tree which is
+ * also the last one. Thanks to restart we know that this entry exists.
+ */
+ node = _ceb_descend(&restart, CEB_WM_NXT, kofs, key_type, 0, key_u64, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, is_dup_ptr);
+ if (node && is_dup_ptr && *is_dup_ptr) {
+ /* on a duplicate, the first node is right->left and it's a leaf */
+ node = _ceb_untag(_ceb_untag(node->b[1], 1)->b[0], 1);
+ }
+ return node;
}
-/* Searches in the tree <root> made of keys of type <key_type>, for the node
- * containing the greatest key that is strictly lower than <key_*>. Returns
- * NULL if not found. It's very similar to next() except that the looked up
- * value doesn't need to exist.
+/* Searches in the tree <root> made of keys of type <key_type>, for the prev
+ * node before the one containing the key <key_*>. Returns NULL if not found.
+ * It's up to the caller to pass the current node's key in <key_*>. The
+ * approach consists in looking up that node first, recalling the last time a
+ * right turn was made, and returning the last node along the left branch at
+ * that fork. In case the current node belongs to a duplicate list, all dups
+ * will be visited in reverse insertion order prior to jumping to different
+ * keys.
*/
static inline __attribute__((always_inline))
-struct ceb_node *_cebu_lookup_lt(struct ceb_node **root,
- ptrdiff_t kofs,
- enum ceb_key_type key_type,
- uint32_t key_u32,
- uint64_t key_u64,
- const void *key_ptr)
+struct ceb_node *_ceb_prev(struct ceb_root *const *root,
+ ptrdiff_t kofs,
+ enum ceb_key_type key_type,
+ uint32_t key_u32,
+ uint64_t key_u64,
+ const void *key_ptr,
+ const struct ceb_node *from,
+ int *is_dup_ptr)
{
- struct ceb_node *ret = NULL;
- struct ceb_node *restart;
+ struct ceb_root *restart;
+ struct ceb_node *node;
if (!*root)
return NULL;
- ret = _cebu_descend(root, CEB_WM_KLT, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &restart);
- if (ret)
- return ret;
+ node = _ceb_descend((struct ceb_root **)root, CEB_WM_KPR, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &restart, is_dup_ptr);
+ if (!node)
+ return NULL;
+ /* Here we have several possibilities:
+ * - from == node => we've found our node. It may be a unique node,
+ * or the last one of a dup series. We'll sort that out thanks to
+ * is_dup, and if it's a dup, we'll use node->b[0].
+ * - from is not the first dup, so we haven't visited them all yet,
+ * hence we visit node->b[0] to switch to the previous dup.
+ * - from is the first dup so we've visited them all, we now need
+ * to jump to the previous unique value.
+ */
+ if (is_dup_ptr && *is_dup_ptr && (node == from || _ceb_untag(node->b[1], 1)->b[0] != _ceb_dotag(from, 1)))
+ return _ceb_untag(from->b[0], 1);
+
+ /* look up the previous unique entry */
if (!restart)
return NULL;
- return _cebu_descend(&restart, CEB_WM_PRV, kofs, key_type, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+ /* Note that the descent stops on the last dup which is the one we want */
+ return _ceb_descend(&restart, CEB_WM_PRV, kofs, key_type, 0, key_u64, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, is_dup_ptr);
}
-/* Searches in the tree <root> made of keys of type <key_type>, for the node
- * containing the key <key_*> or the smallest one that's greater than it.
+/* Searches in the tree <root> made of keys of type <key_type>, for the first
+ * node containing the key <key_*>. Returns NULL if not found.
+ */
+static inline __attribute__((always_inline))
+struct ceb_node *_ceb_lookup(struct ceb_root *const *root,
+ ptrdiff_t kofs,
+ enum ceb_key_type key_type,
+ uint32_t key_u32,
+ uint64_t key_u64,
+ const void *key_ptr,
+ int *is_dup_ptr)
+{
+ struct ceb_node *ret;
+
+ if (!*root)
+ return NULL;
+
+ ret = _ceb_descend((struct ceb_root **)root, CEB_WM_KEQ, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, is_dup_ptr);
+ if (ret && is_dup_ptr && *is_dup_ptr) {
+ /* on a duplicate, the first node is right->left and it's a leaf */
+ ret = _ceb_untag(_ceb_untag(ret->b[1], 1)->b[0], 1);
+ }
+ return ret;
+}
+
+/* Searches in the tree <root> made of keys of type <key_type>, for the last
+ * node containing the key <key_*> or the highest one that's lower than it.
* Returns NULL if not found.
*/
static inline __attribute__((always_inline))
-struct ceb_node *_cebu_lookup_ge(struct ceb_node **root,
- ptrdiff_t kofs,
- enum ceb_key_type key_type,
- uint32_t key_u32,
- uint64_t key_u64,
- const void *key_ptr)
+struct ceb_node *_ceb_lookup_le(struct ceb_root *const *root,
+ ptrdiff_t kofs,
+ enum ceb_key_type key_type,
+ uint32_t key_u32,
+ uint64_t key_u64,
+ const void *key_ptr,
+ int *is_dup_ptr)
{
struct ceb_node *ret = NULL;
- struct ceb_node *restart;
+ struct ceb_root *restart;
if (!*root)
return NULL;
- ret = _cebu_descend(root, CEB_WM_KGE, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &restart);
+ /* note that for duplicates, we already find the last one */
+ ret = _ceb_descend((struct ceb_root **)root, CEB_WM_KLE, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &restart, is_dup_ptr);
if (ret)
return ret;
if (!restart)
return NULL;
- return _cebu_descend(&restart, CEB_WM_NXT, kofs, key_type, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+ return _ceb_descend(&restart, CEB_WM_PRV, kofs, key_type, 0, key_u64, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, is_dup_ptr);
}
-/* Searches in the tree <root> made of keys of type <key_type>, for the node
- * containing the lowest key that is strictly greater than <key_*>. Returns
- * NULL if not found. It's very similar to prev() except that the looked up
- * value doesn't need to exist.
+/* Searches in the tree <root> made of keys of type <key_type>, for the last
+ * node containing the greatest key that is strictly lower than <key_*>.
+ * Returns NULL if not found. It's very similar to next() except that the
+ * looked up value doesn't need to exist.
*/
static inline __attribute__((always_inline))
-struct ceb_node *_cebu_lookup_gt(struct ceb_node **root,
- ptrdiff_t kofs,
- enum ceb_key_type key_type,
- uint32_t key_u32,
- uint64_t key_u64,
- const void *key_ptr)
+struct ceb_node *_ceb_lookup_lt(struct ceb_root *const *root,
+ ptrdiff_t kofs,
+ enum ceb_key_type key_type,
+ uint32_t key_u32,
+ uint64_t key_u64,
+ const void *key_ptr,
+ int *is_dup_ptr)
{
struct ceb_node *ret = NULL;
- struct ceb_node *restart;
+ struct ceb_root *restart;
if (!*root)
return NULL;
- ret = _cebu_descend(root, CEB_WM_KGT, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &restart);
+ /* note that for duplicates, we already find the last one */
+ ret = _ceb_descend((struct ceb_root **)root, CEB_WM_KLT, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &restart, is_dup_ptr);
if (ret)
return ret;
if (!restart)
return NULL;
- return _cebu_descend(&restart, CEB_WM_NXT, kofs, key_type, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+ return _ceb_descend(&restart, CEB_WM_PRV, kofs, key_type, 0, key_u64, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, is_dup_ptr);
+}
+
+/* Searches in the tree <root> made of keys of type <key_type>, for the first
+ * node containing the key <key_*> or the smallest one that's greater than it.
+ * Returns NULL if not found. If <is_dup_ptr> is non-null, then duplicates are
+ * permitted and this variable is used to temporarily carry an internal state.
+
+ */
+static inline __attribute__((always_inline))
+struct ceb_node *_ceb_lookup_ge(struct ceb_root *const *root,
+ ptrdiff_t kofs,
+ enum ceb_key_type key_type,
+ uint32_t key_u32,
+ uint64_t key_u64,
+ const void *key_ptr,
+ int *is_dup_ptr)
+{
+ struct ceb_node *ret = NULL;
+ struct ceb_root *restart;
+
+ if (!*root)
+ return NULL;
+
+ ret = _ceb_descend((struct ceb_root **)root, CEB_WM_KGE, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &restart, is_dup_ptr);
+ if (!ret) {
+ if (!restart)
+ return NULL;
+
+ ret = _ceb_descend(&restart, CEB_WM_NXT, kofs, key_type, 0, key_u64, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, is_dup_ptr);
+ }
+
+ if (ret && is_dup_ptr && *is_dup_ptr) {
+ /* on a duplicate, the first node is right->left and it's a leaf */
+ ret = _ceb_untag(_ceb_untag(ret->b[1], 1)->b[0], 1);
+ }
+ return ret;
+}
+
+/* Searches in the tree <root> made of keys of type <key_type>, for the first
+ * node containing the lowest key that is strictly greater than <key_*>. Returns
+ * NULL if not found. It's very similar to prev() except that the looked up
+ * value doesn't need to exist. If <is_dup_ptr> is non-null, then duplicates are
+ * permitted and this variable is used to temporarily carry an internal state.
+ */
+static inline __attribute__((always_inline))
+struct ceb_node *_ceb_lookup_gt(struct ceb_root *const *root,
+ ptrdiff_t kofs,
+ enum ceb_key_type key_type,
+ uint32_t key_u32,
+ uint64_t key_u64,
+ const void *key_ptr,
+ int *is_dup_ptr)
+{
+ struct ceb_node *ret = NULL;
+ struct ceb_root *restart;
+
+ if (!*root)
+ return NULL;
+
+ ret = _ceb_descend((struct ceb_root **)root, CEB_WM_KGT, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &restart, is_dup_ptr);
+ if (!ret) {
+ if (!restart)
+ return NULL;
+
+ ret = _ceb_descend(&restart, CEB_WM_NXT, kofs, key_type, 0, key_u64, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, is_dup_ptr);
+ }
+
+ if (ret && is_dup_ptr && *is_dup_ptr) {
+ /* on a duplicate, the first node is right->left and it's a leaf */
+ ret = _ceb_untag(_ceb_untag(ret->b[1], 1)->b[0], 1);
+ }
+ return ret;
}
/* Searches in the tree <root> made of keys of type <key_type>, for the node
* node is detected since it has b[0]==NULL, which this functions also clears
* after operation. The function is idempotent, so it's safe to attempt to
* delete an already deleted node (NULL is returned in this case since the node
- * was not in the tree).
+ * was not in the tree). If <is_dup_ptr> is non-null, then duplicates are
+ * permitted and this variable is used to temporarily carry an internal state.
*/
static inline __attribute__((always_inline))
-struct ceb_node *_cebu_delete(struct ceb_node **root,
- struct ceb_node *node,
- ptrdiff_t kofs,
- enum ceb_key_type key_type,
- uint32_t key_u32,
- uint64_t key_u64,
- const void *key_ptr)
+struct ceb_node *_ceb_delete(struct ceb_root **root,
+ struct ceb_node *node,
+ ptrdiff_t kofs,
+ enum ceb_key_type key_type,
+ uint32_t key_u32,
+ uint64_t key_u64,
+ const void *key_ptr,
+ int *is_dup_ptr)
{
struct ceb_node *lparent, *nparent, *gparent;
int lpside, npside, gpside;
goto done;
}
- ret = _cebu_descend(root, CEB_WM_KEQ, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL,
- &lparent, &lpside, &nparent, &npside, &gparent, &gpside, NULL);
+ ret = _ceb_descend(root, CEB_WM_KEQ, kofs, key_type, key_u32, key_u64, key_ptr, NULL, NULL,
+ &lparent, &lpside, &nparent, &npside, &gparent, &gpside, NULL, is_dup_ptr);
if (!ret) {
/* key not found */
goto done;
}
+ if (is_dup_ptr && *is_dup_ptr) {
+ /* the node to be deleted belongs to a dup sub-tree whose ret
+ * is the last. The possibilities here are:
+ * 1) node==NULL => unspecified, we delete the first one,
+ * which is the tree leaf. The tree node (if it exists)
+ * is replaced by the first dup. There's nothing else to
+ * change.
+ * 2) node is the tree leaf. The tree node (if it exists)
+ * is replaced by the first dup.
+ * 3) node is a dup. We just delete the dup.
+ * In order to delete a dup, there are 4 cases:
+ * a) node==last and there's a single dup, it's this one
+ * -> *parent = node->b[0];
+ * b) node==last and there's another dup:
+ * -> *parent = node->b[0];
+ * node->b[0]->b[1] = node->b[1];
+ * (or (*parent)->b[1] = node->b[1] covers a and b)
+ * c) node==first != last:
+ * -> node->b[1]->b[0] = node->b[0];
+ * last->b[1] = node->b[1];
+ * (or (*parent)->b[1] = node->b[1] covers a,b,c)
+ * d) node!=first && !=last:
+ * -> node->b[1]->b[0] = node->b[0];
+ * node->b[0]->b[1] = node->b[1];
+ * a,b,c,d can be simplified as:
+ * ((node == first) ? last : node->b[0])->b[1] = node->b[1];
+ * *((node == last) ? parent : &node->b[1]->b[0]) = node->b[0];
+ */
+ struct ceb_node *first, *last;
+
+ last = ret;
+ first = _ceb_untag(last->b[1], 1);
+
+ /* cases 1 and 2 below */
+ if (!node || node == _ceb_untag(first->b[0], 1)) {
+ /* node unspecified or the first, remove the leaf and
+ * convert the first entry to it.
+ */
+ ret = _ceb_untag(first->b[0], 1); // update return node
+ last->b[1] = first->b[1]; // new first (remains OK if last==first)
+
+ if (ret->b[0] != _ceb_dotag(ret, 1) || ret->b[1] != _ceb_dotag(ret, 1)) {
+ /* not the nodeless leaf, a node exists, put it
+ * on the first and update its parent.
+ */
+ first->b[0] = ret->b[0];
+ first->b[1] = ret->b[1];
+ nparent->b[npside] = _ceb_dotag(first, 0);
+ }
+ else {
+ /* first becomes the nodeless leaf since we only keep its leaf */
+ first->b[0] = first->b[1] = _ceb_dotag(first, 1);
+ }
+ /* first becomes a leaf, it must be tagged */
+ if (last != first)
+ _ceb_untag(last->b[1], 1)->b[0] = _ceb_dotag(first, 1);
+ /* done */
+ } else {
+ /* case 3: the node to delete is a dup, we only have to
+ * manipulate the list.
+ */
+ ret = node;
+ ((node == first) ? last : _ceb_untag(node->b[0], 1))->b[1] = node->b[1];
+ *((node == last) ? &lparent->b[lpside] : &_ceb_untag(node->b[1], 1)->b[0]) = node->b[0];
+ /* done */
+ }
+ goto mark_and_leave;
+ }
+
+ /* ok below the returned value is a real leaf, we have to adjust the tree */
+
if (ret == node || !node) {
if (&lparent->b[0] == root) {
/* there was a single entry, this one, so we're just
/* we're removing the node-less item, the parent will
* take this role.
*/
- lparent->b[0] = lparent->b[1] = lparent;
+ lparent->b[0] = lparent->b[1] = _ceb_dotag(lparent, 1);
goto mark_and_leave;
}
*/
lparent->b[0] = ret->b[0];
lparent->b[1] = ret->b[1];
- nparent->b[npside] = lparent;
+ nparent->b[npside] = _ceb_dotag(lparent, 0);
mark_and_leave:
/* now mark the node as deleted */
return ret;
}
-/*
- * Functions used to dump trees in Dot format.
- */
-
-/* dump the root and its link to the first node or leaf */
-__attribute__((unused))
-static void cebu_default_dump_root(ptrdiff_t kofs, enum ceb_key_type key_type, struct ceb_node *const *root, const void *ctx)
-{
- const struct ceb_node *node;
-
- printf(" \"%lx_n\" [label=\"root\\n%lx\"]\n", (long)root, (long)root);
-
- node = *root;
- if (node) {
- /* under the root we've either a node or the first leaf */
- printf(" \"%lx_n\" -> \"%lx_%c\" [label=\"B\" arrowsize=0.66];\n",
- (long)root, (long)node,
- (node->b[0] == node->b[1]) ? 'l' : 'n');
- }
-}
-
-/* dump a node */
-__attribute__((unused))
-static void cebu_default_dump_node(ptrdiff_t kofs, enum ceb_key_type key_type, const struct ceb_node *node, int level, const void *ctx)
-{
- unsigned long long int_key = 0;
- uint64_t pxor, lxor, rxor;
-
- switch (key_type) {
- case CEB_KT_ADDR:
- int_key = (uintptr_t)node;
- break;
- case CEB_KT_U32:
- int_key = NODEK(node, kofs)->u32;
- break;
- case CEB_KT_U64:
- int_key = NODEK(node, kofs)->u64;
- break;
- default:
- break;
- }
-
- /* xor of the keys of the two lower branches */
- pxor = _xor_branches(kofs, key_type, 0, 0, NULL,
- node->b[0], node->b[1]);
-
- /* xor of the keys of the left branch's lower branches */
- lxor = _xor_branches(kofs, key_type, 0, 0, NULL,
- (((struct ceb_node*)node->b[0])->b[0]),
- (((struct ceb_node*)node->b[0])->b[1]));
-
- /* xor of the keys of the right branch's lower branches */
- rxor = _xor_branches(kofs, key_type, 0, 0, NULL,
- (((struct ceb_node*)node->b[1])->b[0]),
- (((struct ceb_node*)node->b[1])->b[1]));
-
- switch (key_type) {
- case CEB_KT_ADDR:
- case CEB_KT_U32:
- case CEB_KT_U64:
- printf(" \"%lx_n\" [label=\"%lx\\nlev=%d bit=%d\\nkey=%llu\" fillcolor=\"lightskyblue1\"%s];\n",
- (long)node, (long)node, level, flsnz(pxor) - 1, int_key, (ctx == node) ? " color=red" : "");
-
- printf(" \"%lx_n\" -> \"%lx_%c\" [label=\"L\" arrowsize=0.66 %s];\n",
- (long)node, (long)node->b[0],
- (lxor < pxor && ((struct ceb_node*)node->b[0])->b[0] != ((struct ceb_node*)node->b[0])->b[1]) ? 'n' : 'l',
- (node == node->b[0]) ? " dir=both" : "");
-
- printf(" \"%lx_n\" -> \"%lx_%c\" [label=\"R\" arrowsize=0.66 %s];\n",
- (long)node, (long)node->b[1],
- (rxor < pxor && ((struct ceb_node*)node->b[1])->b[0] != ((struct ceb_node*)node->b[1])->b[1]) ? 'n' : 'l',
- (node == node->b[1]) ? " dir=both" : "");
- break;
- case CEB_KT_MB:
- break;
- case CEB_KT_IM:
- break;
- case CEB_KT_ST:
- printf(" \"%lx_n\" [label=\"%lx\\nlev=%d bit=%ld\\nkey=\\\"%s\\\"\" fillcolor=\"lightskyblue1\"%s];\n",
- (long)node, (long)node, level, (long)pxor, NODEK(node, kofs)->str, (ctx == node) ? " color=red" : "");
-
- printf(" \"%lx_n\" -> \"%lx_%c\" [label=\"L\" arrowsize=0.66 %s];\n",
- (long)node, (long)node->b[0],
- (lxor > pxor && ((struct ceb_node*)node->b[0])->b[0] != ((struct ceb_node*)node->b[0])->b[1]) ? 'n' : 'l',
- (node == node->b[0]) ? " dir=both" : "");
-
- printf(" \"%lx_n\" -> \"%lx_%c\" [label=\"R\" arrowsize=0.66 %s];\n",
- (long)node, (long)node->b[1],
- (rxor > pxor && ((struct ceb_node*)node->b[1])->b[0] != ((struct ceb_node*)node->b[1])->b[1]) ? 'n' : 'l',
- (node == node->b[1]) ? " dir=both" : "");
- break;
- case CEB_KT_IS:
- printf(" \"%lx_n\" [label=\"%lx\\nlev=%d bit=%ld\\nkey=\\\"%s\\\"\" fillcolor=\"lightskyblue1\"%s];\n",
- (long)node, (long)node, level, (long)pxor, NODEK(node, kofs)->ptr, (ctx == node) ? " color=red" : "");
-
- printf(" \"%lx_n\" -> \"%lx_%c\" [label=\"L\" arrowsize=0.66 %s];\n",
- (long)node, (long)node->b[0],
- (lxor > pxor && ((struct ceb_node*)node->b[0])->b[0] != ((struct ceb_node*)node->b[0])->b[1]) ? 'n' : 'l',
- (node == node->b[0]) ? " dir=both" : "");
-
- printf(" \"%lx_n\" -> \"%lx_%c\" [label=\"R\" arrowsize=0.66 %s];\n",
- (long)node, (long)node->b[1],
- (rxor > pxor && ((struct ceb_node*)node->b[1])->b[0] != ((struct ceb_node*)node->b[1])->b[1]) ? 'n' : 'l',
- (node == node->b[1]) ? " dir=both" : "");
- break;
- }
-}
-
-/* dump a leaf */
-__attribute__((unused))
-static void cebu_default_dump_leaf(ptrdiff_t kofs, enum ceb_key_type key_type, const struct ceb_node *node, int level, const void *ctx)
-{
- unsigned long long int_key = 0;
- uint64_t pxor;
-
- switch (key_type) {
- case CEB_KT_ADDR:
- int_key = (uintptr_t)node;
- break;
- case CEB_KT_U32:
- int_key = NODEK(node, kofs)->u32;
- break;
- case CEB_KT_U64:
- int_key = NODEK(node, kofs)->u64;
- break;
- default:
- break;
- }
-
- /* xor of the keys of the two lower branches */
- pxor = _xor_branches(kofs, key_type, 0, 0, NULL,
- node->b[0], node->b[1]);
-
- switch (key_type) {
- case CEB_KT_ADDR:
- case CEB_KT_U32:
- case CEB_KT_U64:
- if (node->b[0] == node->b[1])
- printf(" \"%lx_l\" [label=\"%lx\\nlev=%d\\nkey=%llu\\n\" fillcolor=\"green\"%s];\n",
- (long)node, (long)node, level, int_key, (ctx == node) ? " color=red" : "");
- else
- printf(" \"%lx_l\" [label=\"%lx\\nlev=%d bit=%d\\nkey=%llu\\n\" fillcolor=\"yellow\"%s];\n",
- (long)node, (long)node, level, flsnz(pxor) - 1, int_key, (ctx == node) ? " color=red" : "");
- break;
- case CEB_KT_MB:
- break;
- case CEB_KT_IM:
- break;
- case CEB_KT_ST:
- if (node->b[0] == node->b[1])
- printf(" \"%lx_l\" [label=\"%lx\\nlev=%d\\nkey=\\\"%s\\\"\\n\" fillcolor=\"green\"%s];\n",
- (long)node, (long)node, level, NODEK(node, kofs)->str, (ctx == node) ? " color=red" : "");
- else
- printf(" \"%lx_l\" [label=\"%lx\\nlev=%d bit=%ld\\nkey=\\\"%s\\\"\\n\" fillcolor=\"yellow\"%s];\n",
- (long)node, (long)node, level, (long)pxor, NODEK(node, kofs)->str, (ctx == node) ? " color=red" : "");
- break;
- case CEB_KT_IS:
- if (node->b[0] == node->b[1])
- printf(" \"%lx_l\" [label=\"%lx\\nlev=%d\\nkey=\\\"%s\\\"\\n\" fillcolor=\"green\"%s];\n",
- (long)node, (long)node, level, NODEK(node, kofs)->ptr, (ctx == node) ? " color=red" : "");
- else
- printf(" \"%lx_l\" [label=\"%lx\\nlev=%d bit=%ld\\nkey=\\\"%s\\\"\\n\" fillcolor=\"yellow\"%s];\n",
- (long)node, (long)node, level, (long)pxor, NODEK(node, kofs)->ptr, (ctx == node) ? " color=red" : "");
- break;
- }
-}
-
-/* Dumps a tree through the specified callbacks, falling back to the default
- * callbacks above if left NULL.
- */
-__attribute__((unused))
-static const struct ceb_node *cebu_default_dump_tree(ptrdiff_t kofs, enum ceb_key_type key_type, struct ceb_node *const *root,
- uint64_t pxor, const void *last, int level, const void *ctx,
- void (*root_dump)(ptrdiff_t kofs, enum ceb_key_type key_type, struct ceb_node *const *root, const void *ctx),
- void (*node_dump)(ptrdiff_t kofs, enum ceb_key_type key_type, const struct ceb_node *node, int level, const void *ctx),
- void (*leaf_dump)(ptrdiff_t kofs, enum ceb_key_type key_type, const struct ceb_node *node, int level, const void *ctx))
-{
- const struct ceb_node *node = *root;
- uint64_t xor;
-
- if (!node) /* empty tree */
- return node;
-
- if (!root_dump)
- root_dump = cebu_default_dump_root;
-
- if (!node_dump)
- node_dump = cebu_default_dump_node;
-
- if (!leaf_dump)
- leaf_dump = cebu_default_dump_leaf;
-
- if (!level) {
- /* dump the first arrow */
- root_dump(kofs, key_type, root, ctx);
- }
-
- /* regular nodes, all branches are canonical */
-
- if (node->b[0] == node->b[1]) {
- /* first inserted leaf */
- leaf_dump(kofs, key_type, node, level, ctx);
- return node;
- }
-
- xor = _xor_branches(kofs, key_type, 0, 0, NULL,
- node->b[0], node->b[1]);
-
- switch (key_type) {
- case CEB_KT_ADDR:
- case CEB_KT_U32:
- case CEB_KT_U64:
- if (pxor && xor >= pxor) {
- /* that's a leaf for a scalar type */
- leaf_dump(kofs, key_type, node, level, ctx);
- return node;
- }
- break;
- default:
- if (pxor && xor <= pxor) {
- /* that's a leaf for a non-scalar type */
- leaf_dump(kofs, key_type, node, level, ctx);
- return node;
- }
- break;
- }
-
- /* that's a regular node */
- node_dump(kofs, key_type, node, level, ctx);
-
- last = cebu_default_dump_tree(kofs, key_type, &node->b[0], xor, last, level + 1, ctx, root_dump, node_dump, leaf_dump);
- return cebu_default_dump_tree(kofs, key_type, &node->b[1], xor, last, level + 1, ctx, root_dump, node_dump, leaf_dump);
-}
-
+//#if defined(CEB_ENABLE_DUMP)
+/* The dump functions are in cebtree-dbg.c */
+
+void ceb_imm_default_dump_root(ptrdiff_t kofs, enum ceb_key_type key_type, struct ceb_root *const *root, const void *ctx, int sub);
+void ceb_imm_default_dump_node(ptrdiff_t kofs, enum ceb_key_type key_type, const struct ceb_node *node, int level, const void *ctx, int sub);
+void ceb_imm_default_dump_dups(ptrdiff_t kofs, enum ceb_key_type key_type, const struct ceb_node *node, int level, const void *ctx, int sub);
+void ceb_imm_default_dump_leaf(ptrdiff_t kofs, enum ceb_key_type key_type, const struct ceb_node *node, int level, const void *ctx, int sub);
+const struct ceb_node *ceb_imm_default_dump_tree(ptrdiff_t kofs, enum ceb_key_type key_type, struct ceb_root *const *root,
+ uint64_t pxor, const void *last, int level, const void *ctx, int sub,
+ void (*root_dump)(ptrdiff_t kofs, enum ceb_key_type key_type, struct ceb_root *const *root, const void *ctx, int sub),
+ void (*node_dump)(ptrdiff_t kofs, enum ceb_key_type key_type, const struct ceb_node *node, int level, const void *ctx, int sub),
+ void (*dups_dump)(ptrdiff_t kofs, enum ceb_key_type key_type, const struct ceb_node *node, int level, const void *ctx, int sub),
+ void (*leaf_dump)(ptrdiff_t kofs, enum ceb_key_type key_type, const struct ceb_node *node, int level, const void *ctx, int sub));
+//#endif /* CEB_ENABLE_DUMP */
#endif /* _CEBTREE_PRV_H */
#define _CEBTREE_H
#include <stddef.h>
-#include "ebtree.h"
+
+/* offsetof() is provided as a builtin starting with gcc-4.1 */
+#ifndef offsetof
+# if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1))
+# define offsetof(type, field) __builtin_offsetof(type, field)
+# else
+# define offsetof(type, field) ((__size_t)(__uintptr_t)((const volatile void *)&((type *)0)->field))
+# endif
+#endif
+
+/* Linux-like "container_of". It returns a pointer to the structure of type
+ * <type> which has its member <name> stored at address <ptr>.
+ */
+#ifndef container_of
+#define container_of(ptr, type, name) ((type *)(((char *)(ptr)) - offsetof(type, name)))
+#endif
+
+/* returns a pointer to the structure of type <type> which has its member <name>
+ * stored at address <ptr>, unless <ptr> is 0, in which case 0 is returned.
+ */
+#ifndef container_of_safe
+#define container_of_safe(ptr, type, name) \
+ ({ void *__p = (ptr); \
+ __p ? container_of(__p, type, name) : (type *)0; \
+ })
+#endif
+
+/* This is what a tagged pointer points to, as found on the root or any branch.
+ * It's only a forward declaration so that it is never directly dereferenced.
+ */
+struct ceb_root;
/* Standard node when using absolute pointers */
struct ceb_node {
- struct ceb_node *b[2]; /* branches: 0=left, 1=right */
+ struct ceb_root *b[2]; /* branches: 0=left, 1=right */
};
-/* indicates whether a valid node is in a tree or not */
-static inline int ceb_intree(const struct ceb_node *node)
+/* initializes a root as an empty tree */
+static inline void ceb_init_root(struct ceb_root **root)
{
- return !!node->b[0];
+ *root = NULL;
}
-/* tag an untagged pointer */
-static inline struct ceb_node *__ceb_dotag(const struct ceb_node *node)
+/* initializes a node as not belonging to a tree */
+static inline void ceb_init_node(struct ceb_node *node)
{
- return (struct ceb_node *)((size_t)node + 1);
+ node->b[0] = NULL;
}
-/* untag a tagged pointer */
-static inline struct ceb_node *__ceb_untag(const struct ceb_node *node)
+/* indicates whether a valid node is in a tree or not */
+static inline int ceb_intree(const struct ceb_node *node)
{
- return (struct ceb_node *)((size_t)node - 1);
+ return !!node->b[0];
}
-/* clear a pointer's tag */
-static inline struct ceb_node *__ceb_clrtag(const struct ceb_node *node)
+/* indicates whether a root is empty or not */
+static inline int ceb_isempty(struct ceb_root * const*root)
{
- return (struct ceb_node *)((size_t)node & ~((size_t)1));
+ return !*root;
}
-/* returns whether a pointer is tagged */
-static inline int __ceb_tagged(const struct ceb_node *node)
+/* returns a pointer to the key from the node and offset, where node is
+ * assumed to be non-null.
+ */
+static inline void *_ceb_key_ptr(const struct ceb_node *node, ptrdiff_t kofs)
{
- return !!((size_t)node & 1);
+ return (void*)((char *)node + kofs);
}
-/* returns an integer equivalent of the pointer */
-static inline size_t __ceb_intptr(struct ceb_node *tree)
+/* returns a pointer to the key from the node and offset if node is non-null,
+ * otherwise null. I.e. this is made to safely return a pointer to the key
+ * location from the return of a lookup operation.
+ */
+static inline void *ceb_key_ptr(const struct ceb_node *node, ptrdiff_t kofs)
{
- return (size_t)tree;
+ return node ? _ceb_key_ptr(node, kofs) : NULL;
}
-///* returns true if at least one of the branches is a subtree node, indicating
-// * that the current node is at the top of a duplicate sub-tree and that all
-// * values below it are the same.
-// */
-//static inline int __ceb_is_dup(const struct ceb_node *node)
-//{
-// return __ceb_tagged((struct ceb_node *)(__ceb_intptr(node->l) | __ceb_intptr(node->r)));
-//}
#endif /* _CEBTREE_H */
+++ /dev/null
-/*
- * Compact Elastic Binary Trees - exported functions operating on u32 keys
- *
- * Copyright (C) 2014-2024 Willy Tarreau - w@1wt.eu
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#include "cebtree.h"
-#include <inttypes.h>
-
-/* simpler version */
-struct ceb_node *cebu32_insert(struct ceb_node **root, struct ceb_node *node);
-struct ceb_node *cebu32_first(struct ceb_node **root);
-struct ceb_node *cebu32_last(struct ceb_node **root);
-struct ceb_node *cebu32_lookup(struct ceb_node **root, uint32_t key);
-struct ceb_node *cebu32_lookup_le(struct ceb_node **root, uint32_t key);
-struct ceb_node *cebu32_lookup_lt(struct ceb_node **root, uint32_t key);
-struct ceb_node *cebu32_lookup_ge(struct ceb_node **root, uint32_t key);
-struct ceb_node *cebu32_lookup_gt(struct ceb_node **root, uint32_t key);
-struct ceb_node *cebu32_next(struct ceb_node **root, struct ceb_node *node);
-struct ceb_node *cebu32_prev(struct ceb_node **root, struct ceb_node *node);
-struct ceb_node *cebu32_delete(struct ceb_node **root, struct ceb_node *node);
-struct ceb_node *cebu32_pick(struct ceb_node **root, uint32_t key);
-void cebu32_default_dump(struct ceb_node **ceb_root, const char *label, const void *ctx);
-
-/* version taking a key offset */
-struct ceb_node *cebu32_ofs_insert(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node);
-struct ceb_node *cebu32_ofs_first(struct ceb_node **root, ptrdiff_t kofs);
-struct ceb_node *cebu32_ofs_last(struct ceb_node **root, ptrdiff_t kofs);
-struct ceb_node *cebu32_ofs_lookup(struct ceb_node **root, ptrdiff_t kofs, uint32_t key);
-struct ceb_node *cebu32_ofs_lookup_le(struct ceb_node **root, ptrdiff_t kofs, uint32_t key);
-struct ceb_node *cebu32_ofs_lookup_lt(struct ceb_node **root, ptrdiff_t kofs, uint32_t key);
-struct ceb_node *cebu32_ofs_lookup_ge(struct ceb_node **root, ptrdiff_t kofs, uint32_t key);
-struct ceb_node *cebu32_ofs_lookup_gt(struct ceb_node **root, ptrdiff_t kofs, uint32_t key);
-struct ceb_node *cebu32_ofs_next(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node);
-struct ceb_node *cebu32_ofs_prev(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node);
-struct ceb_node *cebu32_ofs_delete(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node);
-struct ceb_node *cebu32_ofs_pick(struct ceb_node **root, ptrdiff_t kofs, uint32_t key);
-void cebu32_ofs_default_dump(struct ceb_node **root, ptrdiff_t kofs, const char *label, const void *ctx);
+++ /dev/null
-/*
- * Compact Elastic Binary Trees - exported functions for operations on u64 keys
- *
- * Copyright (C) 2014-2024 Willy Tarreau - w@1wt.eu
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#include "cebtree.h"
-#include <inttypes.h>
-
-/* simpler version */
-struct ceb_node *cebu64_insert(struct ceb_node **root, struct ceb_node *node);
-struct ceb_node *cebu64_first(struct ceb_node **root);
-struct ceb_node *cebu64_last(struct ceb_node **root);
-struct ceb_node *cebu64_lookup(struct ceb_node **root, uint64_t key);
-struct ceb_node *cebu64_lookup_le(struct ceb_node **root, uint64_t key);
-struct ceb_node *cebu64_lookup_lt(struct ceb_node **root, uint64_t key);
-struct ceb_node *cebu64_lookup_ge(struct ceb_node **root, uint64_t key);
-struct ceb_node *cebu64_lookup_gt(struct ceb_node **root, uint64_t key);
-struct ceb_node *cebu64_next(struct ceb_node **root, struct ceb_node *node);
-struct ceb_node *cebu64_prev(struct ceb_node **root, struct ceb_node *node);
-struct ceb_node *cebu64_delete(struct ceb_node **root, struct ceb_node *node);
-struct ceb_node *cebu64_pick(struct ceb_node **root, uint64_t key);
-void cebu64_default_dump(struct ceb_node **ceb_root, const char *label, const void *ctx);
-
-/* version taking a key offset */
-struct ceb_node *cebu64_ofs_insert(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node);
-struct ceb_node *cebu64_ofs_first(struct ceb_node **root, ptrdiff_t kofs);
-struct ceb_node *cebu64_ofs_last(struct ceb_node **root, ptrdiff_t kofs);
-struct ceb_node *cebu64_ofs_lookup(struct ceb_node **root, ptrdiff_t kofs, uint64_t key);
-struct ceb_node *cebu64_ofs_lookup_le(struct ceb_node **root, ptrdiff_t kofs, uint64_t key);
-struct ceb_node *cebu64_ofs_lookup_lt(struct ceb_node **root, ptrdiff_t kofs, uint64_t key);
-struct ceb_node *cebu64_ofs_lookup_ge(struct ceb_node **root, ptrdiff_t kofs, uint64_t key);
-struct ceb_node *cebu64_ofs_lookup_gt(struct ceb_node **root, ptrdiff_t kofs, uint64_t key);
-struct ceb_node *cebu64_ofs_next(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node);
-struct ceb_node *cebu64_ofs_prev(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node);
-struct ceb_node *cebu64_ofs_delete(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node);
-struct ceb_node *cebu64_ofs_pick(struct ceb_node **root, ptrdiff_t kofs, uint64_t key);
-void cebu64_ofs_default_dump(struct ceb_node **root, ptrdiff_t kofs, const char *label, const void *ctx);
+++ /dev/null
-/*
- * Compact Elastic Binary Trees - exported functions operating on addr keys
- *
- * Copyright (C) 2014-2024 Willy Tarreau - w@1wt.eu
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#include "cebtree.h"
-
-/* simpler version */
-struct ceb_node *cebua_insert(struct ceb_node **root, struct ceb_node *node);
-struct ceb_node *cebua_first(struct ceb_node **root);
-struct ceb_node *cebua_last(struct ceb_node **root);
-struct ceb_node *cebua_lookup(struct ceb_node **root, const void *key);
-struct ceb_node *cebua_lookup_le(struct ceb_node **root, const void *key);
-struct ceb_node *cebua_lookup_lt(struct ceb_node **root, const void *key);
-struct ceb_node *cebua_lookup_ge(struct ceb_node **root, const void *key);
-struct ceb_node *cebua_lookup_gt(struct ceb_node **root, const void *key);
-struct ceb_node *cebua_next(struct ceb_node **root, struct ceb_node *node);
-struct ceb_node *cebua_prev(struct ceb_node **root, struct ceb_node *node);
-struct ceb_node *cebua_delete(struct ceb_node **root, struct ceb_node *node);
-struct ceb_node *cebua_pick(struct ceb_node **root, const void *key);
-void cebua_default_dump(struct ceb_node **root, const char *label, const void *ctx);
-
-/* version taking a key offset */
-struct ceb_node *cebua_ofs_insert(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node);
-struct ceb_node *cebua_ofs_first(struct ceb_node **root, ptrdiff_t kofs);
-struct ceb_node *cebua_ofs_last(struct ceb_node **root, ptrdiff_t kofs);
-struct ceb_node *cebua_ofs_lookup(struct ceb_node **root, ptrdiff_t kofs, const void *key);
-struct ceb_node *cebua_ofs_lookup_le(struct ceb_node **root, ptrdiff_t kofs, const void *key);
-struct ceb_node *cebua_ofs_lookup_lt(struct ceb_node **root, ptrdiff_t kofs, const void *key);
-struct ceb_node *cebua_ofs_lookup_ge(struct ceb_node **root, ptrdiff_t kofs, const void *key);
-struct ceb_node *cebua_ofs_lookup_gt(struct ceb_node **root, ptrdiff_t kofs, const void *key);
-struct ceb_node *cebua_ofs_next(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node);
-struct ceb_node *cebua_ofs_prev(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node);
-struct ceb_node *cebua_ofs_delete(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node);
-struct ceb_node *cebua_ofs_pick(struct ceb_node **root, ptrdiff_t kofs, const void *key);
-void cebua_ofs_default_dump(struct ceb_node **root, ptrdiff_t kofs, const char *label, const void *ctx);
+++ /dev/null
-/*
- * Compact Elastic Binary Trees - exported functions operating on mb keys
- *
- * Copyright (C) 2014-2024 Willy Tarreau - w@1wt.eu
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#include "cebtree.h"
-
-/* simpler version */
-struct ceb_node *cebub_insert(struct ceb_node **root, struct ceb_node *node, size_t len);
-struct ceb_node *cebub_first(struct ceb_node **root);
-struct ceb_node *cebub_last(struct ceb_node **root);
-struct ceb_node *cebub_lookup(struct ceb_node **root, const void *key, size_t len);
-struct ceb_node *cebub_lookup_le(struct ceb_node **root, const void *key, size_t len);
-struct ceb_node *cebub_lookup_lt(struct ceb_node **root, const void *key, size_t len);
-struct ceb_node *cebub_lookup_ge(struct ceb_node **root, const void *key, size_t len);
-struct ceb_node *cebub_lookup_gt(struct ceb_node **root, const void *key, size_t len);
-struct ceb_node *cebub_next(struct ceb_node **root, struct ceb_node *node, size_t len);
-struct ceb_node *cebub_prev(struct ceb_node **root, struct ceb_node *node, size_t len);
-struct ceb_node *cebub_delete(struct ceb_node **root, struct ceb_node *node, size_t len);
-struct ceb_node *cebub_pick(struct ceb_node **root, const void *key, size_t len);
-
-/* version taking a key offset */
-struct ceb_node *cebub_ofs_insert(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node, size_t len);
-struct ceb_node *cebub_ofs_first(struct ceb_node **root, ptrdiff_t kofs, size_t len);
-struct ceb_node *cebub_ofs_last(struct ceb_node **root, ptrdiff_t kofs, size_t len);
-struct ceb_node *cebub_ofs_lookup(struct ceb_node **root, ptrdiff_t kofs, const void *key, size_t len);
-struct ceb_node *cebub_ofs_lookup_le(struct ceb_node **root, ptrdiff_t kofs, const void *key, size_t len);
-struct ceb_node *cebub_ofs_lookup_lt(struct ceb_node **root, ptrdiff_t kofs, const void *key, size_t len);
-struct ceb_node *cebub_ofs_lookup_ge(struct ceb_node **root, ptrdiff_t kofs, const void *key, size_t len);
-struct ceb_node *cebub_ofs_lookup_gt(struct ceb_node **root, ptrdiff_t kofs, const void *key, size_t len);
-struct ceb_node *cebub_ofs_next(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node, size_t len);
-struct ceb_node *cebub_ofs_prev(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node, size_t len);
-struct ceb_node *cebub_ofs_delete(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node, size_t len);
-struct ceb_node *cebub_ofs_pick(struct ceb_node **root, ptrdiff_t kofs, const void *key, size_t len);
+++ /dev/null
-/*
- * Compact Elastic Binary Trees - exported functions operating on indirect blocks
- *
- * Copyright (C) 2014-2024 Willy Tarreau - w@1wt.eu
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#include "cebtree.h"
-
-/* simpler version */
-struct ceb_node *cebuib_insert(struct ceb_node **root, struct ceb_node *node, size_t len);
-struct ceb_node *cebuib_first(struct ceb_node **root);
-struct ceb_node *cebuib_last(struct ceb_node **root);
-struct ceb_node *cebuib_lookup(struct ceb_node **root, const void *key, size_t len);
-struct ceb_node *cebuib_lookup_le(struct ceb_node **root, const void *key, size_t len);
-struct ceb_node *cebuib_lookup_lt(struct ceb_node **root, const void *key, size_t len);
-struct ceb_node *cebuib_lookup_ge(struct ceb_node **root, const void *key, size_t len);
-struct ceb_node *cebuib_lookup_gt(struct ceb_node **root, const void *key, size_t len);
-struct ceb_node *cebuib_next(struct ceb_node **root, struct ceb_node *node, size_t len);
-struct ceb_node *cebuib_prev(struct ceb_node **root, struct ceb_node *node, size_t len);
-struct ceb_node *cebuib_delete(struct ceb_node **root, struct ceb_node *node, size_t len);
-struct ceb_node *cebuib_pick(struct ceb_node **root, const void *key, size_t len);
-
-/* version taking a key offset */
-struct ceb_node *cebuib_ofs_insert(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node, size_t len);
-struct ceb_node *cebuib_ofs_first(struct ceb_node **root, ptrdiff_t kofs, size_t len);
-struct ceb_node *cebuib_ofs_last(struct ceb_node **root, ptrdiff_t kofs, size_t len);
-struct ceb_node *cebuib_ofs_lookup(struct ceb_node **root, ptrdiff_t kofs, const void *key, size_t len);
-struct ceb_node *cebuib_ofs_lookup_le(struct ceb_node **root, ptrdiff_t kofs, const void *key, size_t len);
-struct ceb_node *cebuib_ofs_lookup_lt(struct ceb_node **root, ptrdiff_t kofs, const void *key, size_t len);
-struct ceb_node *cebuib_ofs_lookup_ge(struct ceb_node **root, ptrdiff_t kofs, const void *key, size_t len);
-struct ceb_node *cebuib_ofs_lookup_gt(struct ceb_node **root, ptrdiff_t kofs, const void *key, size_t len);
-struct ceb_node *cebuib_ofs_next(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node, size_t len);
-struct ceb_node *cebuib_ofs_prev(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node, size_t len);
-struct ceb_node *cebuib_ofs_delete(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node, size_t len);
-struct ceb_node *cebuib_ofs_pick(struct ceb_node **root, ptrdiff_t kofs, const void *key, size_t len);
+++ /dev/null
-/*
- * Compact Elastic Binary Trees - exported functions operating on indirect strings
- *
- * Copyright (C) 2014-2024 Willy Tarreau - w@1wt.eu
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#include "cebtree.h"
-
-/* simpler version */
-struct ceb_node *cebuis_insert(struct ceb_node **root, struct ceb_node *node);
-struct ceb_node *cebuis_first(struct ceb_node **root);
-struct ceb_node *cebuis_last(struct ceb_node **root);
-struct ceb_node *cebuis_lookup(struct ceb_node **root, const void *key);
-struct ceb_node *cebuis_lookup_le(struct ceb_node **root, const void *key);
-struct ceb_node *cebuis_lookup_lt(struct ceb_node **root, const void *key);
-struct ceb_node *cebuis_lookup_ge(struct ceb_node **root, const void *key);
-struct ceb_node *cebuis_lookup_gt(struct ceb_node **root, const void *key);
-struct ceb_node *cebuis_next(struct ceb_node **root, struct ceb_node *node);
-struct ceb_node *cebuis_prev(struct ceb_node **root, struct ceb_node *node);
-struct ceb_node *cebuis_delete(struct ceb_node **root, struct ceb_node *node);
-struct ceb_node *cebuis_pick(struct ceb_node **root, const void *key);
-void cebuis_default_dump(struct ceb_node **root, const char *label, const void *ctx);
-
-/* version taking a key offset */
-struct ceb_node *cebuis_ofs_insert(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node);
-struct ceb_node *cebuis_ofs_first(struct ceb_node **root, ptrdiff_t kofs);
-struct ceb_node *cebuis_ofs_last(struct ceb_node **root, ptrdiff_t kofs);
-struct ceb_node *cebuis_ofs_lookup(struct ceb_node **root, ptrdiff_t kofs, const void *key);
-struct ceb_node *cebuis_ofs_lookup_le(struct ceb_node **root, ptrdiff_t kofs, const void *key);
-struct ceb_node *cebuis_ofs_lookup_lt(struct ceb_node **root, ptrdiff_t kofs, const void *key);
-struct ceb_node *cebuis_ofs_lookup_ge(struct ceb_node **root, ptrdiff_t kofs, const void *key);
-struct ceb_node *cebuis_ofs_lookup_gt(struct ceb_node **root, ptrdiff_t kofs, const void *key);
-struct ceb_node *cebuis_ofs_next(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node);
-struct ceb_node *cebuis_ofs_prev(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node);
-struct ceb_node *cebuis_ofs_delete(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node);
-struct ceb_node *cebuis_ofs_pick(struct ceb_node **root, ptrdiff_t kofs, const void *key);
-void cebuis_ofs_default_dump(struct ceb_node **root, ptrdiff_t kofs, const char *label, const void *ctx);
+++ /dev/null
-/*
- * Compact Elastic Binary Trees - exported functions operating on ulong keys
- *
- * Copyright (C) 2014-2024 Willy Tarreau - w@1wt.eu
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#include "cebtree.h"
-
-/* simpler version */
-struct ceb_node *cebul_insert(struct ceb_node **root, struct ceb_node *node);
-struct ceb_node *cebul_first(struct ceb_node **root);
-struct ceb_node *cebul_last(struct ceb_node **root);
-struct ceb_node *cebul_lookup(struct ceb_node **root, unsigned long key);
-struct ceb_node *cebul_lookup_le(struct ceb_node **root, unsigned long key);
-struct ceb_node *cebul_lookup_lt(struct ceb_node **root, unsigned long key);
-struct ceb_node *cebul_lookup_ge(struct ceb_node **root, unsigned long key);
-struct ceb_node *cebul_lookup_gt(struct ceb_node **root, unsigned long key);
-struct ceb_node *cebul_next(struct ceb_node **root, struct ceb_node *node);
-struct ceb_node *cebul_prev(struct ceb_node **root, struct ceb_node *node);
-struct ceb_node *cebul_delete(struct ceb_node **root, struct ceb_node *node);
-struct ceb_node *cebul_pick(struct ceb_node **root, unsigned long key);
-void cebul_default_dump(struct ceb_node **ceb_root, const char *label, const void *ctx);
-
-/* version taking a key offset */
-struct ceb_node *cebul_ofs_insert(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node);
-struct ceb_node *cebul_ofs_first(struct ceb_node **root, ptrdiff_t kofs);
-struct ceb_node *cebul_ofs_last(struct ceb_node **root, ptrdiff_t kofs);
-struct ceb_node *cebul_ofs_lookup(struct ceb_node **root, ptrdiff_t kofs, unsigned long key);
-struct ceb_node *cebul_ofs_lookup_le(struct ceb_node **root, ptrdiff_t kofs, unsigned long key);
-struct ceb_node *cebul_ofs_lookup_lt(struct ceb_node **root, ptrdiff_t kofs, unsigned long key);
-struct ceb_node *cebul_ofs_lookup_ge(struct ceb_node **root, ptrdiff_t kofs, unsigned long key);
-struct ceb_node *cebul_ofs_lookup_gt(struct ceb_node **root, ptrdiff_t kofs, unsigned long key);
-struct ceb_node *cebul_ofs_next(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node);
-struct ceb_node *cebul_ofs_prev(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node);
-struct ceb_node *cebul_ofs_delete(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node);
-struct ceb_node *cebul_ofs_pick(struct ceb_node **root, ptrdiff_t kofs, unsigned long key);
-void cebul_ofs_default_dump(struct ceb_node **root, ptrdiff_t kofs, const char *label, const void *ctx);
+++ /dev/null
-/*
- * Compact Elastic Binary Trees - exported functions operating on string keys
- *
- * Copyright (C) 2014-2024 Willy Tarreau - w@1wt.eu
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#include "cebtree.h"
-
-/* simpler version */
-struct ceb_node *cebus_insert(struct ceb_node **root, struct ceb_node *node);
-struct ceb_node *cebus_first(struct ceb_node **root);
-struct ceb_node *cebus_last(struct ceb_node **root);
-struct ceb_node *cebus_lookup(struct ceb_node **root, const void *key);
-struct ceb_node *cebus_lookup_le(struct ceb_node **root, const void *key);
-struct ceb_node *cebus_lookup_lt(struct ceb_node **root, const void *key);
-struct ceb_node *cebus_lookup_ge(struct ceb_node **root, const void *key);
-struct ceb_node *cebus_lookup_gt(struct ceb_node **root, const void *key);
-struct ceb_node *cebus_next(struct ceb_node **root, struct ceb_node *node);
-struct ceb_node *cebus_prev(struct ceb_node **root, struct ceb_node *node);
-struct ceb_node *cebus_delete(struct ceb_node **root, struct ceb_node *node);
-struct ceb_node *cebus_pick(struct ceb_node **root, const void *key);
-void cebus_default_dump(struct ceb_node **root, const char *label, const void *ctx);
-
-/* version taking a key offset */
-struct ceb_node *cebus_ofs_insert(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node);
-struct ceb_node *cebus_ofs_first(struct ceb_node **root, ptrdiff_t kofs);
-struct ceb_node *cebus_ofs_last(struct ceb_node **root, ptrdiff_t kofs);
-struct ceb_node *cebus_ofs_lookup(struct ceb_node **root, ptrdiff_t kofs, const void *key);
-struct ceb_node *cebus_ofs_lookup_le(struct ceb_node **root, ptrdiff_t kofs, const void *key);
-struct ceb_node *cebus_ofs_lookup_lt(struct ceb_node **root, ptrdiff_t kofs, const void *key);
-struct ceb_node *cebus_ofs_lookup_ge(struct ceb_node **root, ptrdiff_t kofs, const void *key);
-struct ceb_node *cebus_ofs_lookup_gt(struct ceb_node **root, ptrdiff_t kofs, const void *key);
-struct ceb_node *cebus_ofs_next(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node);
-struct ceb_node *cebus_ofs_prev(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node);
-struct ceb_node *cebus_ofs_delete(struct ceb_node **root, ptrdiff_t kofs, struct ceb_node *node);
-struct ceb_node *cebus_ofs_pick(struct ceb_node **root, ptrdiff_t kofs, const void *key);
-void cebus_ofs_default_dump(struct ceb_node **root, ptrdiff_t kofs, const char *label, const void *ctx);
--- /dev/null
+/*
+ * Compact Elastic Binary Trees - exported functions operating on addr keys
+ *
+ * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/* NOTE: this file is only meant to be included from other C files. It will
+ * use the following private macros that must be defined by the caller:
+ * - CEB_KEY_TYPE: CEB_KT_ADDR
+ * - CEB_MKEY_PFX: function name prefix for multi-key (ceba)
+ * - CEB_UKEY_PFX: function name prefix for unique keys (cebua)
+ *
+ * There's no support for duplicates (we're indexing the node's address), nor
+ * for offsets (it makes no sense to apply an offset to the node's address).
+ */
+#include "cebtree-prv.h"
+
+/*
+ * Below are the functions that only support unique keys (_cebu_*)
+ */
+
+/*****************************************************************************\
+ * The declarations below always cause two functions to be declared, one *
+ * starting with "cebua_*" and one with "cebua_ofs_*" which takes a key *
+ * offset just after the root. The one without kofs just has this argument *
+ * omitted from its declaration and replaced with sizeof(struct ceb_node) in *
+ * the call to the underlying functions. *
+\*****************************************************************************/
+
+/* Inserts node <node> into unique tree <tree> based on its own address
+ * Returns the inserted node or the one that has the same address.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _insert, struct ceb_root **, root, ptrdiff_t, kofs, struct ceb_node *, node)
+{
+ return _ceb_insert(root, node, kofs, CEB_KEY_TYPE, 0, 0, node, NULL);
+}
+
+/* return the first node or NULL if not found. */
+CEB_FDECL2(struct ceb_node *, CEB_UKEY_PFX, _first, struct ceb_root *const *, root, ptrdiff_t, kofs)
+{
+ return _ceb_first(root, kofs, CEB_KEY_TYPE, 0, NULL);
+}
+
+/* return the last node or NULL if not found. */
+CEB_FDECL2(struct ceb_node *, CEB_UKEY_PFX, _last, struct ceb_root *const *, root, ptrdiff_t, kofs)
+{
+ return _ceb_last(root, kofs, CEB_KEY_TYPE, 0, NULL);
+}
+
+/* look up the specified key, and returns either the node containing it, or
+ * NULL if not found.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _lookup, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key)
+{
+ return _ceb_lookup(root, kofs, CEB_KEY_TYPE, 0, 0, key, NULL);
+}
+
+/* look up the specified key or the highest below it, and returns either the
+ * node containing it, or NULL if not found.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _lookup_le, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key)
+{
+ return _ceb_lookup_le(root, kofs, CEB_KEY_TYPE, 0, 0, key, NULL);
+}
+
+/* look up highest key below the specified one, and returns either the
+ * node containing it, or NULL if not found.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _lookup_lt, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key)
+{
+ return _ceb_lookup_lt(root, kofs, CEB_KEY_TYPE, 0, 0, key, NULL);
+}
+
+/* look up the specified key or the smallest above it, and returns either the
+ * node containing it, or NULL if not found.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _lookup_ge, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key)
+{
+ return _ceb_lookup_ge(root, kofs, CEB_KEY_TYPE, 0, 0, key, NULL);
+}
+
+/* look up the smallest key above the specified one, and returns either the
+ * node containing it, or NULL if not found.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _lookup_gt, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key)
+{
+ return _ceb_lookup_gt(root, kofs, CEB_KEY_TYPE, 0, 0, key, NULL);
+}
+
+/* search for the next node after the specified one, and return it, or NULL if
+ * not found. The approach consists in looking up that node, recalling the last
+ * time a left turn was made, and returning the first node along the right
+ * branch at that fork.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _next, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node)
+{
+ return _ceb_next_unique(root, kofs, CEB_KEY_TYPE, 0, 0, node, NULL);
+}
+
+/* search for the prev node before the specified one, and return it, or NULL if
+ * not found. The approach consists in looking up that node, recalling the last
+ * time a right turn was made, and returning the last node along the left
+ * branch at that fork.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _prev, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node)
+{
+ return _ceb_prev_unique(root, kofs, CEB_KEY_TYPE, 0, 0, node, NULL);
+}
+
+/* look up the specified node with its key and deletes it if found, and in any
+ * case, returns the node.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _delete, struct ceb_root **, root, ptrdiff_t, kofs, struct ceb_node *, node)
+{
+ return _ceb_delete(root, node, kofs, CEB_KEY_TYPE, 0, 0, node, NULL);
+}
+
+/* look up the specified key, and detaches it and returns it if found, or NULL
+ * if not found.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _pick, struct ceb_root **, root, ptrdiff_t, kofs, const void *, key)
+{
+ return _ceb_delete(root, NULL, kofs, CEB_KEY_TYPE, 0, 0, key, NULL);
+}
+
+/*
+ * Functions used to dump trees in Dot format. These are only enabled if
+ * CEB_ENABLE_DUMP is defined.
+ */
+
+#if defined(CEB_ENABLE_DUMP)
+
+#include <stdio.h>
+#define TO_STR(x) _TO_STR(x)
+#define _TO_STR(x) #x
+
+/* dumps a ceb_node tree using the default functions above. If a node matches
+ * <ctx>, this one will be highlighted in red. If the <sub> value is non-null,
+ * only a subgraph will be printed. If it's null, and root is non-null, then
+ * the tree is dumped at once, otherwise if root is NULL, then a prologue is
+ * dumped when label is not NULL, or the epilogue when label is NULL. As a
+ * summary:
+ * sub root label
+ * 0 NULL NULL epilogue only (closing brace and LF)
+ * 0 NULL text prologue with <text> as label
+ * 0 tree * prologue+tree+epilogue at once
+ * N>0 tree * only the tree, after a prologue and before an epilogue
+ */
+CEB_FDECL5(void, CEB_MKEY_PFX, _default_dump, struct ceb_root *const *, root, ptrdiff_t, kofs, const char *, label, const void *, ctx, int, sub)
+{
+ if (!sub && label) {
+ printf("\ndigraph " TO_STR(CEB_MKEY_PFX) "_tree {\n"
+ " fontname=\"fixed\";\n"
+ " fontsize=8\n"
+ " label=\"%s\"\n"
+ "", label);
+
+ printf(" node [fontname=\"fixed\" fontsize=8 shape=\"box\" style=\"filled\" color=\"black\" fillcolor=\"white\"];\n"
+ " edge [fontname=\"fixed\" fontsize=8 style=\"solid\" color=\"magenta\" dir=\"forward\"];\n");
+ } else
+ printf("\n### sub %d ###\n\n", sub);
+
+ if (root)
+ ceb_imm_default_dump_tree(kofs, CEB_KEY_TYPE, root, 0, NULL, 0, ctx, sub, NULL, NULL, NULL, NULL);
+
+ if (!sub && (root || !label))
+ printf("}\n");
+}
+
+#endif /* CEB_ENABLE_DUMP */
--- /dev/null
+/*
+ * Compact Elastic Binary Trees - exported functions operating on mb keys
+ *
+ * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/* NOTE: this file is only meant to be included from other C files. It will
+ * use the following private macros that must be defined by the caller:
+ * - CEB_KEY_TYPE: CEB_KT_IM, CEB_KT_MB
+ * - CEB_KEY_MEMBER: member of the struct ceb_node holding the key
+ * - CEB_MKEY_PFX: function name prefix for multi-key
+ * - CEB_UKEY_PFX: function name prefix for unique keys
+ */
+#include "cebtree-prv.h"
+
+/*
+ * Below are the functions that support duplicate keys (_ceb_*)
+ */
+
+/*****************************************************************************\
+ * The declarations below always cause two functions to be declared, one *
+ * starting with "cebs_*" and one with "cebs_ofs_*" which takes a key offset *
+ * just after the root. The one without kofs just has this argument omitted *
+ * from its declaration and replaced with sizeof(struct ceb_node) in the *
+ * call to the underlying functions. *
+\*****************************************************************************/
+
+/* Inserts node <node> into tree <tree> based on its key that immediately
+ * follows the node and for <len> bytes. Returns the inserted node or the one
+ * that already contains the same key.
+ */
+CEB_FDECL4(struct ceb_node *, CEB_MKEY_PFX, _insert, struct ceb_root **, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len)
+{
+ const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER;
+ int is_dup;
+
+ return _ceb_insert(root, node, kofs, CEB_KEY_TYPE, 0, len, key, &is_dup);
+}
+
+/* return the first node or NULL if not found. */
+CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _first, struct ceb_root *const *, root, ptrdiff_t, kofs, size_t, len)
+{
+ int is_dup;
+
+ return _ceb_first(root, kofs, CEB_KEY_TYPE, len, &is_dup);
+}
+
+/* return the last node or NULL if not found. */
+CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _last, struct ceb_root *const *, root, ptrdiff_t, kofs, size_t, len)
+{
+ int is_dup;
+
+ return _ceb_last(root, kofs, CEB_KEY_TYPE, len, &is_dup);
+}
+
+/* look up the specified key <key> of length <len>, and returns either the node
+ * containing it, or NULL if not found.
+ */
+CEB_FDECL4(struct ceb_node *, CEB_MKEY_PFX, _lookup, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key, size_t, len)
+{
+ int is_dup;
+
+ return _ceb_lookup(root, kofs, CEB_KEY_TYPE, 0, len, key, &is_dup);
+}
+
+/* look up the specified key or the highest below it, and returns either the
+ * node containing it, or NULL if not found.
+ */
+CEB_FDECL4(struct ceb_node *, CEB_MKEY_PFX, _lookup_le, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key, size_t, len)
+{
+ int is_dup;
+
+ return _ceb_lookup_le(root, kofs, CEB_KEY_TYPE, 0, len, key, &is_dup);
+}
+
+/* look up highest key below the specified one, and returns either the
+ * node containing it, or NULL if not found.
+ */
+CEB_FDECL4(struct ceb_node *, CEB_MKEY_PFX, _lookup_lt, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key, size_t, len)
+{
+ int is_dup;
+
+ return _ceb_lookup_lt(root, kofs, CEB_KEY_TYPE, 0, len, key, &is_dup);
+}
+
+/* look up the specified key or the smallest above it, and returns either the
+ * node containing it, or NULL if not found.
+ */
+CEB_FDECL4(struct ceb_node *, CEB_MKEY_PFX, _lookup_ge, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key, size_t, len)
+{
+ int is_dup;
+
+ return _ceb_lookup_ge(root, kofs, CEB_KEY_TYPE, 0, len, key, &is_dup);
+}
+
+/* look up the smallest key above the specified one, and returns either the
+ * node containing it, or NULL if not found.
+ */
+CEB_FDECL4(struct ceb_node *, CEB_MKEY_PFX, _lookup_gt, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key, size_t, len)
+{
+ int is_dup;
+
+ return _ceb_lookup_gt(root, kofs, CEB_KEY_TYPE, 0, len, key, &is_dup);
+}
+
+/* search for the next node after the specified one, and return it, or NULL if
+ * not found. The approach consists in looking up that node, recalling the last
+ * time a left turn was made, and returning the first node along the right
+ * branch at that fork. The <len> field must correspond to the key length in
+ * bytes.
+ */
+CEB_FDECL4(struct ceb_node *, CEB_MKEY_PFX, _next_unique, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len)
+{
+ const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER;
+ int is_dup;
+
+ return _ceb_next_unique(root, kofs, CEB_KEY_TYPE, 0, len, key, &is_dup);
+}
+
+/* search for the prev node before the specified one, and return it, or NULL if
+ * not found. The approach consists in looking up that node, recalling the last
+ * time a right turn was made, and returning the last node along the left
+ * branch at that fork. The <len> field must correspond to the key length in
+ * bytes.
+ */
+CEB_FDECL4(struct ceb_node *, CEB_MKEY_PFX, _prev_unique, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len)
+{
+ const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER;
+ int is_dup;
+
+ return _ceb_prev_unique(root, kofs, CEB_KEY_TYPE, 0, len, key, &is_dup);
+}
+
+/* search for the next node after the specified one containing the same value,
+ * and return it, or NULL if not found.
+ */
+CEB_FDECL4(struct ceb_node *, CEB_MKEY_PFX, _next_dup, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len)
+{
+ const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER;
+
+ return _ceb_next_dup(root, kofs, CEB_KEY_TYPE, 0, len, key, node);
+}
+
+/* search for the prev node before the specified one containing the same value,
+ * and return it, or NULL if not found.
+ */
+CEB_FDECL4(struct ceb_node *, CEB_MKEY_PFX, _prev_dup, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len)
+{
+ const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER;
+
+ return _ceb_prev_dup(root, kofs, CEB_KEY_TYPE, 0, len, key, node);
+}
+
+/* search for the next node after the specified one, and return it, or NULL if
+ * not found. The approach consists in looking up that node, recalling the last
+ * time a left turn was made, and returning the first node along the right
+ * branch at that fork. The <len> field must correspond to the key length in
+ * bytes.
+ */
+CEB_FDECL4(struct ceb_node *, CEB_MKEY_PFX, _next, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len)
+{
+ const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER;
+ int is_dup;
+
+ return _ceb_next(root, kofs, CEB_KEY_TYPE, 0, len, key, node, &is_dup);
+}
+
+/* search for the prev node before the specified one, and return it, or NULL if
+ * not found. The approach consists in looking up that node, recalling the last
+ * time a right turn was made, and returning the last node along the left
+ * branch at that fork. The <len> field must correspond to the key length in
+ * bytes.
+ */
+CEB_FDECL4(struct ceb_node *, CEB_MKEY_PFX, _prev, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len)
+{
+ const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER;
+ int is_dup;
+
+ return _ceb_prev(root, kofs, CEB_KEY_TYPE, 0, len, key, node, &is_dup);
+}
+
+/* look up the specified node with its key and deletes it if found, and in any
+ * case, returns the node. The <len> field must correspond to the key length in
+ * bytes.
+ */
+CEB_FDECL4(struct ceb_node *, CEB_MKEY_PFX, _delete, struct ceb_root **, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len)
+{
+ const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER;
+ int is_dup;
+
+ return _ceb_delete(root, node, kofs, CEB_KEY_TYPE, 0, len, key, &is_dup);
+}
+
+/* look up the specified key, and detaches it and returns it if found, or NULL
+ * if not found. The <len> field must correspond to the key length in bytes.
+ */
+CEB_FDECL4(struct ceb_node *, CEB_MKEY_PFX, _pick, struct ceb_root **, root, ptrdiff_t, kofs, const void *, key, size_t, len)
+{
+ int is_dup;
+
+ return _ceb_delete(root, NULL, kofs, CEB_KEY_TYPE, 0, len, key, &is_dup);
+}
+
+/*
+ * Below are the functions that only support unique keys (_cebu_*)
+ */
+
+/*****************************************************************************\
+ * The declarations below always cause two functions to be declared, one *
+ * starting with "cebub_*" and one with "cebub_ofs_*" which takes a key *
+ * offset just after the root. The one without kofs just has this argument *
+ * omitted from its declaration and replaced with sizeof(struct ceb_node) in *
+ * the call to the underlying functions. *
+\*****************************************************************************/
+
+/* Inserts node <node> into unique tree <tree> based on its key that
+ * immediately follows the node and for <len> bytes. Returns the
+ * inserted node or the one that already contains the same key.
+ */
+CEB_FDECL4(struct ceb_node *, CEB_UKEY_PFX, _insert, struct ceb_root **, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len)
+{
+ const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER;
+
+ return _ceb_insert(root, node, kofs, CEB_KEY_TYPE, 0, len, key, NULL);
+}
+
+/* return the first node or NULL if not found. */
+CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _first, struct ceb_root *const *, root, ptrdiff_t, kofs, size_t, len)
+{
+ return _ceb_first(root, kofs, CEB_KEY_TYPE, len, NULL);
+}
+
+/* return the last node or NULL if not found. */
+CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _last, struct ceb_root *const *, root, ptrdiff_t, kofs, size_t, len)
+{
+ return _ceb_last(root, kofs, CEB_KEY_TYPE, len, NULL);
+}
+
+/* look up the specified key <key> of length <len>, and returns either the node
+ * containing it, or NULL if not found.
+ */
+CEB_FDECL4(struct ceb_node *, CEB_UKEY_PFX, _lookup, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key, size_t, len)
+{
+ return _ceb_lookup(root, kofs, CEB_KEY_TYPE, 0, len, key, NULL);
+}
+
+/* look up the specified key or the highest below it, and returns either the
+ * node containing it, or NULL if not found.
+ */
+CEB_FDECL4(struct ceb_node *, CEB_UKEY_PFX, _lookup_le, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key, size_t, len)
+{
+ return _ceb_lookup_le(root, kofs, CEB_KEY_TYPE, 0, len, key, NULL);
+}
+
+/* look up highest key below the specified one, and returns either the
+ * node containing it, or NULL if not found.
+ */
+CEB_FDECL4(struct ceb_node *, CEB_UKEY_PFX, _lookup_lt, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key, size_t, len)
+{
+ return _ceb_lookup_lt(root, kofs, CEB_KEY_TYPE, 0, len, key, NULL);
+}
+
+/* look up the specified key or the smallest above it, and returns either the
+ * node containing it, or NULL if not found.
+ */
+CEB_FDECL4(struct ceb_node *, CEB_UKEY_PFX, _lookup_ge, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key, size_t, len)
+{
+ return _ceb_lookup_ge(root, kofs, CEB_KEY_TYPE, 0, len, key, NULL);
+}
+
+/* look up the smallest key above the specified one, and returns either the
+ * node containing it, or NULL if not found.
+ */
+CEB_FDECL4(struct ceb_node *, CEB_UKEY_PFX, _lookup_gt, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key, size_t, len)
+{
+ return _ceb_lookup_gt(root, kofs, CEB_KEY_TYPE, 0, len, key, NULL);
+}
+
+/* search for the next node after the specified one, and return it, or NULL if
+ * not found. The approach consists in looking up that node, recalling the last
+ * time a left turn was made, and returning the first node along the right
+ * branch at that fork. The <len> field must correspond to the key length in
+ * bytes.
+ */
+CEB_FDECL4(struct ceb_node *, CEB_UKEY_PFX, _next, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len)
+{
+ const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER;
+
+ return _ceb_next_unique(root, kofs, CEB_KEY_TYPE, 0, len, key, NULL);
+}
+
+/* search for the prev node before the specified one, and return it, or NULL if
+ * not found. The approach consists in looking up that node, recalling the last
+ * time a right turn was made, and returning the last node along the left
+ * branch at that fork. The <len> field must correspond to the key length in
+ * bytes.
+ */
+CEB_FDECL4(struct ceb_node *, CEB_UKEY_PFX, _prev, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len)
+{
+ const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER;
+
+ return _ceb_prev_unique(root, kofs, CEB_KEY_TYPE, 0, len, key, NULL);
+}
+
+/* look up the specified node with its key and deletes it if found, and in any
+ * case, returns the node. The <len> field must correspond to the key length in
+ * bytes.
+ */
+CEB_FDECL4(struct ceb_node *, CEB_UKEY_PFX, _delete, struct ceb_root **, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len)
+{
+ const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER;
+
+ return _ceb_delete(root, node, kofs, CEB_KEY_TYPE, 0, len, key, NULL);
+}
+
+/* look up the specified key, and detaches it and returns it if found, or NULL
+ * if not found. The <len> field must correspond to the key length in bytes.
+ */
+CEB_FDECL4(struct ceb_node *, CEB_UKEY_PFX, _pick, struct ceb_root **, root, ptrdiff_t, kofs, const void *, key, size_t, len)
+{
+ return _ceb_delete(root, NULL, kofs, CEB_KEY_TYPE, 0, len, key, NULL);
+}
--- /dev/null
+/*
+ * Compact Elastic Binary Trees - exported functions operating on integer keys
+ *
+ * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/* NOTE: this file is only meant to be included from other C files. It will
+ * use the following private macros that must be defined by the caller:
+ * - CEB_KEY_TYPE: uint32_t, uint64_t, unsigned long
+ * - CEB_KEY_MEMBER: member of the struct ceb_node holding the key
+ * - CEB_MKEY_PFX: function name prefix for multi-key
+ * - CEB_UKEY_PFX: function name prefix for unique keys
+ *
+ * The dump functions will only be build if CEB_ENABLE_DUMP is defined.
+ */
+#include "cebtree-prv.h"
+
+/*
+ * Below are the functions that support duplicate keys (_ceb_*)
+ */
+
+/*****************************************************************************\
+ * The declarations below always cause two functions to be declared, one *
+ * starting with "cebs_*" and one with "cebs_ofs_*" which takes a key offset *
+ * just after the root. The one without kofs just has this argument omitted *
+ * from its declaration and replaced with sizeof(struct ceb_node) in the *
+ * call to the underlying functions. *
+\*****************************************************************************/
+
+/* Inserts node <node> into tree <tree> based on its key that immediately
+ * follows the node. Returns the inserted node or the one that already contains
+ * the same key.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _insert, struct ceb_root **, root, ptrdiff_t, kofs, struct ceb_node *, node)
+{
+ CEB_KEY_TYPE key = NODEK(node, kofs)->CEB_KEY_MEMBER;
+ int is_dup;
+
+ if (sizeof(CEB_KEY_TYPE) <= 4)
+ return _ceb_insert(root, node, kofs, CEB_KT_U32, key, 0, NULL, &is_dup);
+ else
+ return _ceb_insert(root, node, kofs, CEB_KT_U64, 0, key, NULL, &is_dup);
+}
+
+/* return the first node or NULL if not found. */
+CEB_FDECL2(struct ceb_node *, CEB_MKEY_PFX, _first, struct ceb_root *const *, root, ptrdiff_t, kofs)
+{
+ int is_dup;
+
+ if (sizeof(CEB_KEY_TYPE) <= 4)
+ return _ceb_first(root, kofs, CEB_KT_U32, 0, &is_dup);
+ else
+ return _ceb_first(root, kofs, CEB_KT_U64, 0, &is_dup);
+}
+
+/* return the last node or NULL if not found. */
+CEB_FDECL2(struct ceb_node *, CEB_MKEY_PFX, _last, struct ceb_root *const *, root, ptrdiff_t, kofs)
+{
+ int is_dup;
+
+ if (sizeof(CEB_KEY_TYPE) <= 4)
+ return _ceb_last(root, kofs, CEB_KT_U32, 0, &is_dup);
+ else
+ return _ceb_last(root, kofs, CEB_KT_U64, 0, &is_dup);
+}
+
+/* look up the specified key, and returns either the node containing it, or
+ * NULL if not found.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _lookup, struct ceb_root *const *, root, ptrdiff_t, kofs, CEB_KEY_TYPE, key)
+{
+ int is_dup;
+
+ if (sizeof(CEB_KEY_TYPE) <= 4)
+ return _ceb_lookup(root, kofs, CEB_KT_U32, key, 0, NULL, &is_dup);
+ else
+ return _ceb_lookup(root, kofs, CEB_KT_U64, 0, key, NULL, &is_dup);
+}
+
+/* look up the specified key or the highest below it, and returns either the
+ * node containing it, or NULL if not found.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _lookup_le, struct ceb_root *const *, root, ptrdiff_t, kofs, CEB_KEY_TYPE, key)
+{
+ int is_dup;
+
+ if (sizeof(CEB_KEY_TYPE) <= 4)
+ return _ceb_lookup_le(root, kofs, CEB_KT_U32, key, 0, NULL, &is_dup);
+ else
+ return _ceb_lookup_le(root, kofs, CEB_KT_U64, 0, key, NULL, &is_dup);
+}
+
+/* look up highest key below the specified one, and returns either the
+ * node containing it, or NULL if not found.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _lookup_lt, struct ceb_root *const *, root, ptrdiff_t, kofs, CEB_KEY_TYPE, key)
+{
+ int is_dup;
+
+ if (sizeof(CEB_KEY_TYPE) <= 4)
+ return _ceb_lookup_lt(root, kofs, CEB_KT_U32, key, 0, NULL, &is_dup);
+ else
+ return _ceb_lookup_lt(root, kofs, CEB_KT_U64, 0, key, NULL, &is_dup);
+}
+
+/* look up the specified key or the smallest above it, and returns either the
+ * node containing it, or NULL if not found.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _lookup_ge, struct ceb_root *const *, root, ptrdiff_t, kofs, CEB_KEY_TYPE, key)
+{
+ int is_dup;
+
+ if (sizeof(CEB_KEY_TYPE) <= 4)
+ return _ceb_lookup_ge(root, kofs, CEB_KT_U32, key, 0, NULL, &is_dup);
+ else
+ return _ceb_lookup_ge(root, kofs, CEB_KT_U64, 0, key, NULL, &is_dup);
+}
+
+/* look up the smallest key above the specified one, and returns either the
+ * node containing it, or NULL if not found.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _lookup_gt, struct ceb_root *const *, root, ptrdiff_t, kofs, CEB_KEY_TYPE, key)
+{
+ int is_dup;
+
+ if (sizeof(CEB_KEY_TYPE) <= 4)
+ return _ceb_lookup_gt(root, kofs, CEB_KT_U32, key, 0, NULL, &is_dup);
+ else
+ return _ceb_lookup_gt(root, kofs, CEB_KT_U64, 0, key, NULL, &is_dup);
+}
+
+/* search for the next node after the specified one, and return it, or NULL if
+ * not found. The approach consists in looking up that node, recalling the last
+ * time a left turn was made, and returning the first node along the right
+ * branch at that fork.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _next_unique, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node)
+{
+ CEB_KEY_TYPE key = NODEK(node, kofs)->CEB_KEY_MEMBER;
+ int is_dup;
+
+ if (sizeof(CEB_KEY_TYPE) <= 4)
+ return _ceb_next_unique(root, kofs, CEB_KT_U32, key, 0, NULL, &is_dup);
+ else
+ return _ceb_next_unique(root, kofs, CEB_KT_U64, 0, key, NULL, &is_dup);
+}
+
+/* search for the prev node before the specified one, and return it, or NULL if
+ * not found. The approach consists in looking up that node, recalling the last
+ * time a right turn was made, and returning the last node along the left
+ * branch at that fork.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _prev_unique, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node)
+{
+ CEB_KEY_TYPE key = NODEK(node, kofs)->CEB_KEY_MEMBER;
+ int is_dup;
+
+ if (sizeof(CEB_KEY_TYPE) <= 4)
+ return _ceb_prev_unique(root, kofs, CEB_KT_U32, key, 0, NULL, &is_dup);
+ else
+ return _ceb_prev_unique(root, kofs, CEB_KT_U64, 0, key, NULL, &is_dup);
+}
+
+/* search for the next node after the specified one containing the same value,
+ * and return it, or NULL if not found.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _next_dup, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node)
+{
+ CEB_KEY_TYPE key = NODEK(node, kofs)->CEB_KEY_MEMBER;
+
+ if (sizeof(CEB_KEY_TYPE) <= 4)
+ return _ceb_next_dup(root, kofs, CEB_KT_U32, key, 0, NULL, node);
+ else
+ return _ceb_next_dup(root, kofs, CEB_KT_U64, 0, key, NULL, node);
+}
+
+/* search for the prev node before the specified one containing the same value,
+ * and return it, or NULL if not found.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _prev_dup, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node)
+{
+ CEB_KEY_TYPE key = NODEK(node, kofs)->CEB_KEY_MEMBER;
+
+ if (sizeof(CEB_KEY_TYPE) <= 4)
+ return _ceb_prev_dup(root, kofs, CEB_KT_U32, key, 0, NULL, node);
+ else
+ return _ceb_prev_dup(root, kofs, CEB_KT_U64, 0, key, NULL, node);
+}
+
+/* search for the next node after the specified one, and return it, or NULL if
+ * not found. The approach consists in looking up that node, recalling the last
+ * time a left turn was made, and returning the first node along the right
+ * branch at that fork.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _next, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node)
+{
+ CEB_KEY_TYPE key = NODEK(node, kofs)->CEB_KEY_MEMBER;
+ int is_dup;
+
+ if (sizeof(CEB_KEY_TYPE) <= 4)
+ return _ceb_next(root, kofs, CEB_KT_U32, key, 0, NULL, node, &is_dup);
+ else
+ return _ceb_next(root, kofs, CEB_KT_U64, 0, key, NULL, node, &is_dup);
+}
+
+/* search for the prev node before the specified one, and return it, or NULL if
+ * not found. The approach consists in looking up that node, recalling the last
+ * time a right turn was made, and returning the last node along the left
+ * branch at that fork.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _prev, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node)
+{
+ CEB_KEY_TYPE key = NODEK(node, kofs)->CEB_KEY_MEMBER;
+ int is_dup;
+
+ if (sizeof(CEB_KEY_TYPE) <= 4)
+ return _ceb_prev(root, kofs, CEB_KT_U32, key, 0, NULL, node, &is_dup);
+ else
+ return _ceb_prev(root, kofs, CEB_KT_U64, 0, key, NULL, node, &is_dup);
+}
+
+/* look up the specified node with its key and deletes it if found, and in any
+ * case, returns the node.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _delete, struct ceb_root **, root, ptrdiff_t, kofs, struct ceb_node *, node)
+{
+ CEB_KEY_TYPE key = NODEK(node, kofs)->CEB_KEY_MEMBER;
+ int is_dup;
+
+ if (sizeof(CEB_KEY_TYPE) <= 4)
+ return _ceb_delete(root, node, kofs, CEB_KT_U32, key, 0, NULL, &is_dup);
+ else
+ return _ceb_delete(root, node, kofs, CEB_KT_U64, 0, key, NULL, &is_dup);
+}
+
+/* look up the specified key, and detaches it and returns it if found, or NULL
+ * if not found.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _pick, struct ceb_root **, root, ptrdiff_t, kofs, CEB_KEY_TYPE, key)
+{
+ int is_dup;
+
+ if (sizeof(CEB_KEY_TYPE) <= 4)
+ return _ceb_delete(root, NULL, kofs, CEB_KT_U32, key, 0, NULL, &is_dup);
+ else
+ return _ceb_delete(root, NULL, kofs, CEB_KT_U64, 0, key, NULL, &is_dup);
+}
+
+/*
+ * Below are the functions that only support unique keys (_cebu_*)
+ */
+
+/*****************************************************************************\
+ * The declarations below always cause two functions to be declared, one *
+ * starting with "cebu32_*" and one with "cebu32_ofs_*" which takes a key *
+ * offset just after the root. The one without kofs just has this argument *
+ * omitted from its declaration and replaced with sizeof(struct ceb_node) in *
+ * the call to the underlying functions. *
+\*****************************************************************************/
+
+/* Inserts node <node> into unique tree <tree> based on its key that
+ * immediately follows the node. Returns the inserted node or the one
+ * that already contains the same key.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _insert, struct ceb_root **, root, ptrdiff_t, kofs, struct ceb_node *, node)
+{
+ CEB_KEY_TYPE key = NODEK(node, kofs)->CEB_KEY_MEMBER;
+
+ if (sizeof(CEB_KEY_TYPE) <= 4)
+ return _ceb_insert(root, node, kofs, CEB_KT_U32, key, 0, NULL, NULL);
+ else
+ return _ceb_insert(root, node, kofs, CEB_KT_U64, 0, key, NULL, NULL);
+}
+
+/* return the first node or NULL if not found. */
+CEB_FDECL2(struct ceb_node *, CEB_UKEY_PFX, _first, struct ceb_root *const *, root, ptrdiff_t, kofs)
+{
+ if (sizeof(CEB_KEY_TYPE) <= 4)
+ return _ceb_first(root, kofs, CEB_KT_U32, 0, NULL);
+ else
+ return _ceb_first(root, kofs, CEB_KT_U64, 0, NULL);
+}
+
+/* return the last node or NULL if not found. */
+CEB_FDECL2(struct ceb_node *, CEB_UKEY_PFX, _last, struct ceb_root *const *, root, ptrdiff_t, kofs)
+{
+ if (sizeof(CEB_KEY_TYPE) <= 4)
+ return _ceb_last(root, kofs, CEB_KT_U32, 0, NULL);
+ else
+ return _ceb_last(root, kofs, CEB_KT_U64, 0, NULL);
+}
+
+/* look up the specified key, and returns either the node containing it, or
+ * NULL if not found.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _lookup, struct ceb_root *const *, root, ptrdiff_t, kofs, CEB_KEY_TYPE, key)
+{
+ if (sizeof(CEB_KEY_TYPE) <= 4)
+ return _ceb_lookup(root, kofs, CEB_KT_U32, key, 0, NULL, NULL);
+ else
+ return _ceb_lookup(root, kofs, CEB_KT_U64, 0, key, NULL, NULL);
+}
+
+/* look up the specified key or the highest below it, and returns either the
+ * node containing it, or NULL if not found.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _lookup_le, struct ceb_root *const *, root, ptrdiff_t, kofs, CEB_KEY_TYPE, key)
+{
+ if (sizeof(CEB_KEY_TYPE) <= 4)
+ return _ceb_lookup_le(root, kofs, CEB_KT_U32, key, 0, NULL, NULL);
+ else
+ return _ceb_lookup_le(root, kofs, CEB_KT_U64, 0, key, NULL, NULL);
+}
+
+/* look up highest key below the specified one, and returns either the
+ * node containing it, or NULL if not found.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _lookup_lt, struct ceb_root *const *, root, ptrdiff_t, kofs, CEB_KEY_TYPE, key)
+{
+ if (sizeof(CEB_KEY_TYPE) <= 4)
+ return _ceb_lookup_lt(root, kofs, CEB_KT_U32, key, 0, NULL, NULL);
+ else
+ return _ceb_lookup_lt(root, kofs, CEB_KT_U64, 0, key, NULL, NULL);
+}
+
+/* look up the specified key or the smallest above it, and returns either the
+ * node containing it, or NULL if not found.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _lookup_ge, struct ceb_root *const *, root, ptrdiff_t, kofs, CEB_KEY_TYPE, key)
+{
+ if (sizeof(CEB_KEY_TYPE) <= 4)
+ return _ceb_lookup_ge(root, kofs, CEB_KT_U32, key, 0, NULL, NULL);
+ else
+ return _ceb_lookup_ge(root, kofs, CEB_KT_U64, 0, key, NULL, NULL);
+}
+
+/* look up the smallest key above the specified one, and returns either the
+ * node containing it, or NULL if not found.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _lookup_gt, struct ceb_root *const *, root, ptrdiff_t, kofs, CEB_KEY_TYPE, key)
+{
+ if (sizeof(CEB_KEY_TYPE) <= 4)
+ return _ceb_lookup_gt(root, kofs, CEB_KT_U32, key, 0, NULL, NULL);
+ else
+ return _ceb_lookup_gt(root, kofs, CEB_KT_U64, 0, key, NULL, NULL);
+}
+
+/* search for the next node after the specified one, and return it, or NULL if
+ * not found. The approach consists in looking up that node, recalling the last
+ * time a left turn was made, and returning the first node along the right
+ * branch at that fork.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _next, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node)
+{
+ CEB_KEY_TYPE key = NODEK(node, kofs)->CEB_KEY_MEMBER;
+
+ if (sizeof(CEB_KEY_TYPE) <= 4)
+ return _ceb_next_unique(root, kofs, CEB_KT_U32, key, 0, NULL, NULL);
+ else
+ return _ceb_next_unique(root, kofs, CEB_KT_U64, 0, key, NULL, NULL);
+}
+
+/* search for the prev node before the specified one, and return it, or NULL if
+ * not found. The approach consists in looking up that node, recalling the last
+ * time a right turn was made, and returning the last node along the left
+ * branch at that fork.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _prev, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node)
+{
+ CEB_KEY_TYPE key = NODEK(node, kofs)->CEB_KEY_MEMBER;
+
+ if (sizeof(CEB_KEY_TYPE) <= 4)
+ return _ceb_prev_unique(root, kofs, CEB_KT_U32, key, 0, NULL, NULL);
+ else
+ return _ceb_prev_unique(root, kofs, CEB_KT_U64, 0, key, NULL, NULL);
+}
+
+/* look up the specified node with its key and deletes it if found, and in any
+ * case, returns the node.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _delete, struct ceb_root **, root, ptrdiff_t, kofs, struct ceb_node *, node)
+{
+ CEB_KEY_TYPE key = NODEK(node, kofs)->CEB_KEY_MEMBER;
+
+ if (sizeof(CEB_KEY_TYPE) <= 4)
+ return _ceb_delete(root, node, kofs, CEB_KT_U32, key, 0, NULL, NULL);
+ else
+ return _ceb_delete(root, node, kofs, CEB_KT_U64, 0, key, NULL, NULL);
+}
+
+/* look up the specified key, and detaches it and returns it if found, or NULL
+ * if not found.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _pick, struct ceb_root **, root, ptrdiff_t, kofs, CEB_KEY_TYPE, key)
+{
+ if (sizeof(CEB_KEY_TYPE) <= 4)
+ return _ceb_delete(root, NULL, kofs, CEB_KT_U32, key, 0, NULL, NULL);
+ else
+ return _ceb_delete(root, NULL, kofs, CEB_KT_U64, 0, key, NULL, NULL);
+}
+
+/*
+ * Functions used to dump trees in Dot format. These are only enabled if
+ * CEB_ENABLE_DUMP is defined.
+ */
+
+#if defined(CEB_ENABLE_DUMP)
+
+#include <stdio.h>
+#define TO_STR(x) _TO_STR(x)
+#define _TO_STR(x) #x
+
+/* dumps a ceb_node tree using the default functions above. If a node matches
+ * <ctx>, this one will be highlighted in red. If the <sub> value is non-null,
+ * only a subgraph will be printed. If it's null, and root is non-null, then
+ * the tree is dumped at once, otherwise if root is NULL, then a prologue is
+ * dumped when label is not NULL, or the epilogue when label is NULL. As a
+ * summary:
+ * sub root label
+ * 0 NULL NULL epilogue only (closing brace and LF)
+ * 0 NULL text prologue with <text> as label
+ * 0 tree * prologue+tree+epilogue at once
+ * N>0 tree * only the tree, after a prologue and before an epilogue
+ */
+CEB_FDECL5(void, CEB_MKEY_PFX, _default_dump, struct ceb_root *const *, root, ptrdiff_t, kofs, const char *, label, const void *, ctx, int, sub)
+{
+ if (!sub && label) {
+ printf("\ndigraph " TO_STR(CEB_MKEY_PFX) "_tree {\n"
+ " fontname=\"fixed\";\n"
+ " fontsize=8\n"
+ " label=\"%s\"\n"
+ "", label);
+
+ printf(" node [fontname=\"fixed\" fontsize=8 shape=\"box\" style=\"filled\" color=\"black\" fillcolor=\"white\"];\n"
+ " edge [fontname=\"fixed\" fontsize=8 style=\"solid\" color=\"magenta\" dir=\"forward\"];\n");
+ } else
+ printf("\n### sub %d ###\n\n", sub);
+
+ if (root)
+ ceb_imm_default_dump_tree(kofs, sizeof(CEB_KEY_TYPE) <= 4 ? CEB_KT_U32 : CEB_KT_U64, root, 0, NULL, 0, ctx, sub, NULL, NULL, NULL, NULL);
+
+ if (!sub && (root || !label))
+ printf("}\n");
+}
+
+#endif /* CEB_ENABLE_DUMP */
--- /dev/null
+/*
+ * Compact Elastic Binary Trees - exported functions operating on string keys
+ *
+ * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/* NOTE: this file is only meant to be included from other C files. It will
+ * use the following private macros that must be defined by the caller:
+ * - CEB_KEY_TYPE: CEB_KT_ST, CEB_KT_IS
+ * - CEB_KEY_MEMBER: str, ptr
+ * - CEB_MKEY_PFX: function name prefix for multi-key (cebs, cebis)
+ * - CEB_UKEY_PFX: function name prefix for unique keys (cebus, cebuis)
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "cebtree-prv.h"
+
+#define TO_STR(x) _TO_STR(x)
+#define _TO_STR(x) #x
+
+/*
+ * Below are the functions that support duplicate keys (_ceb_*)
+ */
+
+/*****************************************************************************\
+ * The declarations below always cause two functions to be declared, one *
+ * starting with "cebs_*" and one with "cebs_ofs_*" which takes a key offset *
+ * just after the root. The one without kofs just has this argument omitted *
+ * from its declaration and replaced with sizeof(struct ceb_node) in the *
+ * call to the underlying functions. *
+\*****************************************************************************/
+
+/* Inserts node <node> into tree <tree> based on its key that immediately
+ * follows the node. Returns the inserted node or the one that already contains
+ * the same key.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _insert, struct ceb_root **, root, ptrdiff_t, kofs, struct ceb_node *, node)
+{
+ const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER;
+ int is_dup;
+
+ return _ceb_insert(root, node, kofs, CEB_KEY_TYPE, 0, 0, key, &is_dup);
+}
+
+/* return the first node or NULL if not found. */
+CEB_FDECL2(struct ceb_node *, CEB_MKEY_PFX, _first, struct ceb_root *const *, root, ptrdiff_t, kofs)
+{
+ int is_dup;
+
+ return _ceb_first(root, kofs, CEB_KEY_TYPE, 0, &is_dup);
+}
+
+/* return the last node or NULL if not found. */
+CEB_FDECL2(struct ceb_node *, CEB_MKEY_PFX, _last, struct ceb_root *const *, root, ptrdiff_t, kofs)
+{
+ int is_dup;
+
+ return _ceb_last(root, kofs, CEB_KEY_TYPE, 0, &is_dup);
+}
+
+/* look up the specified key, and returns either the node containing it, or
+ * NULL if not found.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _lookup, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key)
+{
+ int is_dup;
+
+ return _ceb_lookup(root, kofs, CEB_KEY_TYPE, 0, 0, key, &is_dup);
+}
+
+/* look up the specified key or the highest below it, and returns either the
+ * node containing it, or NULL if not found.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _lookup_le, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key)
+{
+ int is_dup;
+
+ return _ceb_lookup_le(root, kofs, CEB_KEY_TYPE, 0, 0, key, &is_dup);
+}
+
+/* look up highest key below the specified one, and returns either the
+ * node containing it, or NULL if not found.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _lookup_lt, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key)
+{
+ int is_dup;
+
+ return _ceb_lookup_lt(root, kofs, CEB_KEY_TYPE, 0, 0, key, &is_dup);
+}
+
+/* look up the specified key or the smallest above it, and returns either the
+ * node containing it, or NULL if not found.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _lookup_ge, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key)
+{
+ int is_dup;
+
+ return _ceb_lookup_ge(root, kofs, CEB_KEY_TYPE, 0, 0, key, &is_dup);
+}
+
+/* look up the smallest key above the specified one, and returns either the
+ * node containing it, or NULL if not found.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _lookup_gt, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key)
+{
+ int is_dup;
+
+ return _ceb_lookup_gt(root, kofs, CEB_KEY_TYPE, 0, 0, key, &is_dup);
+}
+
+/* search for the next node after the specified one, and return it, or NULL if
+ * not found. The approach consists in looking up that node, recalling the last
+ * time a left turn was made, and returning the first node along the right
+ * branch at that fork.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _next_unique, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node)
+{
+ const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER;
+ int is_dup;
+
+ return _ceb_next_unique(root, kofs, CEB_KEY_TYPE, 0, 0, key, &is_dup);
+}
+
+/* search for the prev node before the specified one, and return it, or NULL if
+ * not found. The approach consists in looking up that node, recalling the last
+ * time a right turn was made, and returning the last node along the left
+ * branch at that fork.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _prev_unique, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node)
+{
+ const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER;
+ int is_dup;
+
+ return _ceb_prev_unique(root, kofs, CEB_KEY_TYPE, 0, 0, key, &is_dup);
+}
+
+/* search for the next node after the specified one containing the same value,
+ * and return it, or NULL if not found.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _next_dup, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node)
+{
+ const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER;
+
+ return _ceb_next_dup(root, kofs, CEB_KEY_TYPE, 0, 0, key, node);
+}
+
+/* search for the prev node before the specified one containing the same value,
+ * and return it, or NULL if not found.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _prev_dup, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node)
+{
+ const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER;
+
+ return _ceb_prev_dup(root, kofs, CEB_KEY_TYPE, 0, 0, key, node);
+}
+
+/* search for the next node after the specified one, and return it, or NULL if
+ * not found. The approach consists in looking up that node, recalling the last
+ * time a left turn was made, and returning the first node along the right
+ * branch at that fork.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _next, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node)
+{
+ const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER;
+ int is_dup;
+
+ return _ceb_next(root, kofs, CEB_KEY_TYPE, 0, 0, key, node, &is_dup);
+}
+
+/* search for the prev node before the specified one, and return it, or NULL if
+ * not found. The approach consists in looking up that node, recalling the last
+ * time a right turn was made, and returning the last node along the left
+ * branch at that fork.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _prev, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node)
+{
+ const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER;
+ int is_dup;
+
+ return _ceb_prev(root, kofs, CEB_KEY_TYPE, 0, 0, key, node, &is_dup);
+}
+
+/* look up the specified node with its key and deletes it if found, and in any
+ * case, returns the node.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _delete, struct ceb_root **, root, ptrdiff_t, kofs, struct ceb_node *, node)
+{
+ const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER;
+ int is_dup;
+
+ return _ceb_delete(root, node, kofs, CEB_KEY_TYPE, 0, 0, key, &is_dup);
+}
+
+/* look up the specified key, and detaches it and returns it if found, or NULL
+ * if not found.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_MKEY_PFX, _pick, struct ceb_root **, root, ptrdiff_t, kofs, const void *, key)
+{
+ int is_dup;
+
+ return _ceb_delete(root, NULL, kofs, CEB_KEY_TYPE, 0, 0, key, &is_dup);
+}
+
+/*
+ * Below are the functions that only support unique keys (_cebu_*)
+ */
+
+/*****************************************************************************\
+ * The declarations below always cause two functions to be declared, one *
+ * starting with "cebus_*" and one with "cebus_ofs_*" which takes a key *
+ * offset just after the root. The one without kofs just has this argument *
+ * omitted from its declaration and replaced with sizeof(struct ceb_node) in *
+ * the call to the underlying functions. *
+\*****************************************************************************/
+
+/* Inserts node <node> into unique tree <tree> based on its key that
+ * immediately follows the node. Returns the inserted node or the one
+ * that already contains the same key.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _insert, struct ceb_root **, root, ptrdiff_t, kofs, struct ceb_node *, node)
+{
+ const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER;
+
+ return _ceb_insert(root, node, kofs, CEB_KEY_TYPE, 0, 0, key, NULL);
+}
+
+/* return the first node or NULL if not found. */
+CEB_FDECL2(struct ceb_node *, CEB_UKEY_PFX, _first, struct ceb_root *const *, root, ptrdiff_t, kofs)
+{
+ return _ceb_first(root, kofs, CEB_KEY_TYPE, 0, NULL);
+}
+
+/* return the last node or NULL if not found. */
+CEB_FDECL2(struct ceb_node *, CEB_UKEY_PFX, _last, struct ceb_root *const *, root, ptrdiff_t, kofs)
+{
+ return _ceb_last(root, kofs, CEB_KEY_TYPE, 0, NULL);
+}
+
+/* look up the specified key, and returns either the node containing it, or
+ * NULL if not found.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _lookup, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key)
+{
+ return _ceb_lookup(root, kofs, CEB_KEY_TYPE, 0, 0, key, NULL);
+}
+
+/* look up the specified key or the highest below it, and returns either the
+ * node containing it, or NULL if not found.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _lookup_le, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key)
+{
+ return _ceb_lookup_le(root, kofs, CEB_KEY_TYPE, 0, 0, key, NULL);
+}
+
+/* look up highest key below the specified one, and returns either the
+ * node containing it, or NULL if not found.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _lookup_lt, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key)
+{
+ return _ceb_lookup_lt(root, kofs, CEB_KEY_TYPE, 0, 0, key, NULL);
+}
+
+/* look up the specified key or the smallest above it, and returns either the
+ * node containing it, or NULL if not found.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _lookup_ge, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key)
+{
+ return _ceb_lookup_ge(root, kofs, CEB_KEY_TYPE, 0, 0, key, NULL);
+}
+
+/* look up the smallest key above the specified one, and returns either the
+ * node containing it, or NULL if not found.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _lookup_gt, struct ceb_root *const *, root, ptrdiff_t, kofs, const void *, key)
+{
+ return _ceb_lookup_gt(root, kofs, CEB_KEY_TYPE, 0, 0, key, NULL);
+}
+
+/* search for the next node after the specified one, and return it, or NULL if
+ * not found. The approach consists in looking up that node, recalling the last
+ * time a left turn was made, and returning the first node along the right
+ * branch at that fork.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _next, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node)
+{
+ const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER;
+
+ return _ceb_next_unique(root, kofs, CEB_KEY_TYPE, 0, 0, key, NULL);
+}
+
+/* search for the prev node before the specified one, and return it, or NULL if
+ * not found. The approach consists in looking up that node, recalling the last
+ * time a right turn was made, and returning the last node along the left
+ * branch at that fork.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _prev, struct ceb_root *const *, root, ptrdiff_t, kofs, struct ceb_node *, node)
+{
+ const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER;
+
+ return _ceb_prev_unique(root, kofs, CEB_KEY_TYPE, 0, 0, key, NULL);
+}
+
+/* look up the specified node with its key and deletes it if found, and in any
+ * case, returns the node.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _delete, struct ceb_root **, root, ptrdiff_t, kofs, struct ceb_node *, node)
+{
+ const void *key = NODEK(node, kofs)->CEB_KEY_MEMBER;
+
+ return _ceb_delete(root, node, kofs, CEB_KEY_TYPE, 0, 0, key, NULL);
+}
+
+/* look up the specified key, and detaches it and returns it if found, or NULL
+ * if not found.
+ */
+CEB_FDECL3(struct ceb_node *, CEB_UKEY_PFX, _pick, struct ceb_root **, root, ptrdiff_t, kofs, const void *, key)
+{
+ return _ceb_delete(root, NULL, kofs, CEB_KEY_TYPE, 0, 0, key, NULL);
+}
+
+/*
+ * Functions used to dump trees in Dot format. These are only enabled if
+ * CEB_ENABLE_DUMP is defined.
+ */
+
+#if defined(CEB_ENABLE_DUMP)
+
+#include <stdio.h>
+#define TO_STR(x) _TO_STR(x)
+#define _TO_STR(x) #x
+
+/* dumps a ceb_node tree using the default functions above. If a node matches
+ * <ctx>, this one will be highlighted in red. If the <sub> value is non-null,
+ * only a subgraph will be printed. If it's null, and root is non-null, then
+ * the tree is dumped at once, otherwise if root is NULL, then a prologue is
+ * dumped when label is not NULL, or the epilogue when label is NULL. As a
+ * summary:
+ * sub root label
+ * 0 NULL NULL epilogue only (closing brace and LF)
+ * 0 NULL text prologue with <text> as label
+ * 0 tree * prologue+tree+epilogue at once
+ * N>0 tree * only the tree, after a prologue and before an epilogue
+ */
+CEB_FDECL5(void, CEB_MKEY_PFX, _default_dump, struct ceb_root *const *, root, ptrdiff_t, kofs, const char *, label, const void *, ctx, int, sub)
+{
+ if (!sub && label) {
+ printf("\ndigraph " TO_STR(CEB_MKEY_PFX) "_tree {\n"
+ " fontname=\"fixed\";\n"
+ " fontsize=8\n"
+ " label=\"%s\"\n"
+ "", label);
+
+ printf(" node [fontname=\"fixed\" fontsize=8 shape=\"box\" style=\"filled\" color=\"black\" fillcolor=\"white\"];\n"
+ " edge [fontname=\"fixed\" fontsize=8 style=\"solid\" color=\"magenta\" dir=\"forward\"];\n");
+ } else
+ printf("\n### sub %d ###\n\n", sub);
+
+ if (root)
+ ceb_imm_default_dump_tree(kofs, CEB_KEY_TYPE, root, 0, NULL, 0, ctx, sub, NULL, NULL, NULL, NULL);
+
+ if (!sub && (root || !label))
+ printf("}\n");
+}
+
+#endif /* CEB_ENABLE_DUMP */
--- /dev/null
+/*
+ * Compact Elastic Binary Trees - exported functions operating on u32 keys
+ *
+ * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifdef CEBTREE_DEV
+/* just to verify API compatibility */
+#include "ceb32_tree.h"
+#endif
+
+#define CEB_USE_BASE
+#define CEB_USE_OFST
+#define CEB_KEY_TYPE uint32_t
+#define CEB_KEY_MEMBER u32
+#define CEB_MKEY_PFX ceb32
+#define CEB_UKEY_PFX cebu32
+
+#include "_ceb_int.c"
--- /dev/null
+/*
+ * Compact Elastic Binary Trees - exported functions operating on u64 keys
+ *
+ * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifdef CEBTREE_DEV
+/* just to verify API compatibility */
+#include "ceb64_tree.h"
+#endif
+
+#define CEB_USE_BASE
+#define CEB_USE_OFST
+#define CEB_KEY_TYPE uint64_t
+#define CEB_KEY_MEMBER u64
+#define CEB_MKEY_PFX ceb64
+#define CEB_UKEY_PFX cebu64
+
+#include "_ceb_int.c"
--- /dev/null
+/*
+ * Compact Elastic Binary Trees - exported functions operating on addr keys
+ *
+ * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifdef CEBTREE_DEV
+/* just to verify API compatibility */
+#include "ceba_tree.h"
+#endif
+
+#define CEB_USE_BASE
+#define CEB_KEY_TYPE CEB_KT_ADDR
+#define CEB_MKEY_PFX ceba
+#define CEB_UKEY_PFX cebua
+
+#include "_ceb_addr.c"
--- /dev/null
+/*
+ * Compact Elastic Binary Trees - exported functions operating on mb keys
+ *
+ * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifdef CEBTREE_DEV
+/* just to verify API compatibility */
+#include "cebb_tree.h"
+#endif
+
+#define CEB_USE_BASE
+#define CEB_USE_OFST
+#define CEB_KEY_TYPE CEB_KT_MB
+#define CEB_KEY_MEMBER mb
+#define CEB_MKEY_PFX cebb
+#define CEB_UKEY_PFX cebub
+
+#include "_ceb_blk.c"
--- /dev/null
+/*
+ * Compact Elastic Binary Trees - exported functions operating on indirect blocks
+ *
+ * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifdef CEBTREE_DEV
+/* just to verify API compatibility */
+#include "cebib_tree.h"
+#endif
+
+#define CEB_USE_BASE
+#define CEB_USE_OFST
+#define CEB_KEY_TYPE CEB_KT_IM
+#define CEB_KEY_MEMBER ptr
+#define CEB_MKEY_PFX cebib
+#define CEB_UKEY_PFX cebuib
+
+#include "_ceb_blk.c"
--- /dev/null
+/*
+ * Compact Elastic Binary Trees - exported functions operating on indirect strings
+ *
+ * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifdef CEBTREE_DEV
+/* just to verify API compatibility */
+#include "cebis_tree.h"
+#endif
+
+#define CEB_USE_BASE
+#define CEB_USE_OFST
+#define CEB_KEY_TYPE CEB_KT_IS
+#define CEB_KEY_MEMBER ptr
+#define CEB_MKEY_PFX cebis
+#define CEB_UKEY_PFX cebuis
+
+#include "_ceb_str.c"
--- /dev/null
+/*
+ * Compact Elastic Binary Trees - exported functions operating on ulong keys
+ *
+ * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifdef CEBTREE_DEV
+/* just to verify API compatibility */
+#include "cebl_tree.h"
+#endif
+
+#define CEB_USE_BASE
+#define CEB_USE_OFST
+#define CEB_KEY_TYPE unsigned long
+#define CEB_KEY_MEMBER ul
+#define CEB_MKEY_PFX cebl
+#define CEB_UKEY_PFX cebul
+
+#include "_ceb_int.c"
--- /dev/null
+/*
+ * Compact Elastic Binary Trees - exported functions operating on string keys
+ *
+ * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifdef CEBTREE_DEV
+/* just to verify API compatibility */
+#include "cebs_tree.h"
+#endif
+
+#define CEB_USE_BASE
+#define CEB_USE_OFST
+#define CEB_KEY_TYPE CEB_KT_ST
+#define CEB_KEY_MEMBER str
+#define CEB_MKEY_PFX cebs
+#define CEB_UKEY_PFX cebus
+
+#include "_ceb_str.c"
--- /dev/null
+/*
+ * Compact Elastic Binary Trees - debugging functions
+ *
+ * Copyright (C) 2014-2025 Willy Tarreau - w@1wt.eu
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+/*
+ * Functions used to dump trees in Dot format.
+ */
+#include <stdio.h>
+#include "cebtree-prv.h"
+
+#if defined(CEB_ENABLE_DUMP)
+
+/* Returns the xor (or the complement of the common length for strings) between
+ * the two sides <l> and <r> if both are non-null, otherwise between the first
+ * non-null one and the value in the associate key. As a reminder, memory
+ * blocks place their length in key_u64. This is only intended for internal
+ * use, essentially for debugging. It only returns zero when the keys are
+ * identical, and returns a greater value for keys that are more distant.
+ *
+ * <kofs> contains the offset between the key and the node's base. When simply
+ * adjacent, this would just be sizeof(ceb_node).
+ */
+static uint64_t _xor_branches(ptrdiff_t kofs, enum ceb_key_type key_type, uint32_t key_u32,
+ uint64_t key_u64, const void *key_ptr,
+ const struct ceb_root *_l,
+ const struct ceb_root *_r)
+{
+ const struct ceb_node *l, *r;
+
+ l = _ceb_clrtag(_l);
+ r = _ceb_clrtag(_r);
+
+ if (l && r) {
+ if (key_type == CEB_KT_MB)
+ return (key_u64 << 3) - equal_bits(NODEK(l, kofs)->mb, NODEK(r, kofs)->mb, 0, key_u64 << 3);
+ else if (key_type == CEB_KT_IM)
+ return (key_u64 << 3) - equal_bits(NODEK(l, kofs)->mb, NODEK(r, kofs)->ptr, 0, key_u64 << 3);
+ else if (key_type == CEB_KT_ST)
+ return ~string_equal_bits(NODEK(l, kofs)->str, NODEK(r, kofs)->str, 0);
+ else if (key_type == CEB_KT_IS)
+ return ~string_equal_bits(NODEK(l, kofs)->ptr, NODEK(r, kofs)->ptr, 0);
+ else if (key_type == CEB_KT_U64)
+ return NODEK(l, kofs)->u64 ^ NODEK(r, kofs)->u64;
+ else if (key_type == CEB_KT_U32)
+ return NODEK(l, kofs)->u32 ^ NODEK(r, kofs)->u32;
+ else if (key_type == CEB_KT_ADDR)
+ return ((uintptr_t)l ^ (uintptr_t)r);
+ else
+ return 0;
+ }
+
+ if (!l)
+ l = r;
+
+ if (key_type == CEB_KT_MB)
+ return (key_u64 << 3) - equal_bits(key_ptr, NODEK(l, kofs)->mb, 0, key_u64 << 3);
+ else if (key_type == CEB_KT_IM)
+ return (key_u64 << 3) - equal_bits(key_ptr, NODEK(l, kofs)->ptr, 0, key_u64 << 3);
+ else if (key_type == CEB_KT_ST)
+ return ~string_equal_bits(key_ptr, NODEK(l, kofs)->str, 0);
+ else if (key_type == CEB_KT_IS)
+ return ~string_equal_bits(key_ptr, NODEK(l, kofs)->ptr, 0);
+ else if (key_type == CEB_KT_U64)
+ return key_u64 ^ NODEK(l, kofs)->u64;
+ else if (key_type == CEB_KT_U32)
+ return key_u32 ^ NODEK(l, kofs)->u32;
+ else if (key_type == CEB_KT_ADDR)
+ return ((uintptr_t)key_ptr ^ (uintptr_t)r);
+ else
+ return 0;
+}
+
+/* dump the root and its link to the first node or leaf */
+void ceb_imm_default_dump_root(ptrdiff_t kofs, enum ceb_key_type key_type, struct ceb_root *const *root, const void *ctx, int sub)
+{
+ const struct ceb_node *node;
+ uint64_t pxor;
+
+ if (!sub)
+ printf(" \"%lx_n_%d\" [label=\"root\\n%lx\"]\n", (long)root, sub, (long)root);
+ else
+ printf(" \"%lx_n_%d\" [label=\"root\\n%lx\\ntree #%d\"]\n", (long)root, sub, (long)root, sub);
+
+ node = _ceb_clrtag(*root);
+ if (node) {
+ /* under the root we've either a node or the first leaf */
+
+ /* xor of the keys of the two lower branches */
+ pxor = _xor_branches(kofs, key_type, 0, 0, NULL,
+ node->b[0], node->b[1]);
+
+ printf(" \"%lx_n_%d\" -> \"%lx_%c_%d\" [label=\"B\" arrowsize=0.66%s];\n",
+ (long)root, sub, (long)node,
+ (node->b[0] == node->b[1] || !pxor) ? 'l' : 'n', sub,
+ (ctx == node) ? " color=red" : "");
+ }
+}
+
+/* dump a node */
+void ceb_imm_default_dump_node(ptrdiff_t kofs, enum ceb_key_type key_type, const struct ceb_node *node, int level, const void *ctx, int sub)
+{
+ unsigned long long int_key = 0;
+ uint64_t pxor, lxor, rxor;
+ const char *str_key = NULL;
+
+ switch (key_type) {
+ case CEB_KT_ADDR:
+ int_key = (uintptr_t)node;
+ break;
+ case CEB_KT_U32:
+ int_key = NODEK(node, kofs)->u32;
+ break;
+ case CEB_KT_U64:
+ int_key = NODEK(node, kofs)->u64;
+ break;
+ case CEB_KT_ST:
+ str_key = (char*)NODEK(node, kofs)->str;
+ break;
+ case CEB_KT_IS:
+ str_key = (char*)NODEK(node, kofs)->ptr;
+ break;
+ default:
+ break;
+ }
+
+ /* xor of the keys of the two lower branches */
+ pxor = _xor_branches(kofs, key_type, 0, 0, NULL,
+ node->b[0], node->b[1]);
+
+ /* xor of the keys of the left branch's lower branches */
+ lxor = _xor_branches(kofs, key_type, 0, 0, NULL,
+ (_ceb_clrtag(node->b[0])->b[0]),
+ (_ceb_clrtag(node->b[0])->b[1]));
+
+ /* xor of the keys of the right branch's lower branches */
+ rxor = _xor_branches(kofs, key_type, 0, 0, NULL,
+ (_ceb_clrtag(node->b[1])->b[0]),
+ (_ceb_clrtag(node->b[1])->b[1]));
+
+ switch (key_type) {
+ case CEB_KT_ADDR:
+ case CEB_KT_U32:
+ case CEB_KT_U64:
+ printf(" \"%lx_n_%d\" [label=\"%lx\\nlev=%d bit=%d\\nkey=%llu\" fillcolor=\"lightskyblue1\"%s];\n",
+ (long)node, sub, (long)node, level, flsnz(pxor) - 1, int_key, (ctx == node) ? " color=red" : "");
+
+ printf(" \"%lx_n_%d\" -> \"%lx_%c_%d\" [label=\"L\" arrowsize=0.66%s%s];\n",
+ (long)node, sub, (long)_ceb_clrtag(node->b[0]),
+ (lxor < pxor && _ceb_clrtag(node->b[0])->b[0] != _ceb_clrtag(node->b[0])->b[1] && lxor) ? 'n' : 'l',
+ sub, (node == _ceb_clrtag(node->b[0])) ? " dir=both" : "", (ctx == _ceb_clrtag(node->b[0])) ? " color=red" : "");
+
+ printf(" \"%lx_n_%d\" -> \"%lx_%c_%d\" [label=\"R\" arrowsize=0.66%s%s];\n",
+ (long)node, sub, (long)_ceb_clrtag(node->b[1]),
+ (rxor < pxor && _ceb_clrtag(node->b[1])->b[0] != _ceb_clrtag(node->b[1])->b[1] && rxor) ? 'n' : 'l',
+ sub, (node == _ceb_clrtag(node->b[1])) ? " dir=both" : "", (ctx == _ceb_clrtag(node->b[1])) ? " color=red" : "");
+ break;
+ case CEB_KT_MB:
+ break;
+ case CEB_KT_IM:
+ break;
+ case CEB_KT_ST:
+ case CEB_KT_IS:
+ printf(" \"%lx_n_%d\" [label=\"%lx\\nlev=%d bit=%ld\\nkey=\\\"%s\\\"\" fillcolor=\"lightskyblue1\"%s];\n",
+ (long)node, sub, (long)node, level, (long)~pxor, str_key, (ctx == node) ? " color=red" : "");
+
+ printf(" \"%lx_n_%d\" -> \"%lx_%c_%d\" [label=\"L\" arrowsize=0.66%s%s];\n",
+ (long)node, sub, (long)_ceb_clrtag(node->b[0]),
+ (lxor < pxor && _ceb_clrtag(node->b[0])->b[0] != _ceb_clrtag(node->b[0])->b[1] && lxor) ? 'n' : 'l',
+ sub, (node == _ceb_clrtag(node->b[0])) ? " dir=both" : "", (ctx == _ceb_clrtag(node->b[0])) ? " color=red" : "");
+
+ printf(" \"%lx_n_%d\" -> \"%lx_%c_%d\" [label=\"R\" arrowsize=0.66%s%s];\n",
+ (long)node, sub, (long)_ceb_clrtag(node->b[1]),
+ (rxor < pxor && _ceb_clrtag(node->b[1])->b[0] != _ceb_clrtag(node->b[1])->b[1] && rxor) ? 'n' : 'l',
+ sub, (node == _ceb_clrtag(node->b[1])) ? " dir=both" : "", (ctx == _ceb_clrtag(node->b[1])) ? " color=red" : "");
+ break;
+ }
+}
+
+/* dump a duplicate entry */
+void ceb_imm_default_dump_dups(ptrdiff_t kofs, enum ceb_key_type key_type, const struct ceb_node *node, int level, const void *ctx, int sub)
+{
+ unsigned long long int_key = 0;
+ const struct ceb_node *leaf;
+ const char *str_key = NULL;
+ int is_last;
+
+ switch (key_type) {
+ case CEB_KT_ADDR:
+ int_key = (uintptr_t)node;
+ break;
+ case CEB_KT_U32:
+ int_key = NODEK(node, kofs)->u32;
+ break;
+ case CEB_KT_U64:
+ int_key = NODEK(node, kofs)->u64;
+ break;
+ case CEB_KT_ST:
+ str_key = (char*)NODEK(node, kofs)->str;
+ break;
+ case CEB_KT_IS:
+ str_key = (char*)NODEK(node, kofs)->ptr;
+ break;
+ default:
+ break;
+ }
+
+ /* Let's try to determine which one is the last of the series. The
+ * right node's left node is a tree leaf in this only case. This means
+ * that node either has both sides equal to itself, or a distinct
+ * neighbours.
+ */
+ leaf = _ceb_clrtag(_ceb_untag(node->b[1], 1)->b[0]);
+
+ is_last = 1;
+ if (leaf->b[0] != _ceb_dotag(leaf, 1) || leaf->b[1] != _ceb_dotag(leaf, 1))
+ is_last = _xor_branches(kofs, key_type, 0, 0, NULL,
+ leaf->b[0], leaf->b[1]) != 0;
+
+ switch (key_type) {
+ case CEB_KT_ADDR:
+ case CEB_KT_U32:
+ case CEB_KT_U64:
+ printf(" \"%lx_l_%d\" [label=\"%lx\\nlev=%d\\nkey=%llu\" fillcolor=\"wheat1\"%s];\n",
+ (long)node, sub, (long)node, level, int_key, (ctx == node) ? " color=red" : "");
+
+ printf(" \"%lx_l_%d\":sw -> \"%lx_l_%d\":n [taillabel=\"L\" arrowsize=0.66%s];\n",
+ (long)node, sub, (long)_ceb_clrtag(node->b[0]), sub, (ctx == _ceb_clrtag(node->b[0])) ? " color=red" : "");
+
+ printf(" \"%lx_l_%d\":%s -> \"%lx_l_%d\":%s [taillabel=\"R\" arrowsize=0.66%s];\n",
+ (long)node, sub, is_last ? "se" : "ne",
+ (long)_ceb_clrtag(node->b[1]), sub, is_last ? "e" : "s", (ctx == _ceb_clrtag(node->b[1])) ? " color=red" : "");
+ break;
+ case CEB_KT_MB:
+ break;
+ case CEB_KT_IM:
+ break;
+ case CEB_KT_ST:
+ case CEB_KT_IS:
+ printf(" \"%lx_l_%d\" [label=\"%lx\\nlev=%d\\nkey=\\\"%s\\\"\" fillcolor=\"wheat1\"%s];\n",
+ (long)node, sub, (long)node, level, str_key, (ctx == node) ? " color=red" : "");
+
+ printf(" \"%lx_l_%d\":sw -> \"%lx_l_%d\":n [taillabel=\"L\" arrowsize=0.66%s];\n",
+ (long)node, sub, (long)_ceb_clrtag(node->b[0]), sub, (ctx == _ceb_clrtag(node->b[0])) ? " color=red" : "");
+
+ printf(" \"%lx_l_%d\":%s -> \"%lx_l_%d\":%s [taillabel=\"R\" arrowsize=0.66%s];\n",
+ (long)node, sub, is_last ? "se" : "ne",
+ (long)_ceb_clrtag(node->b[1]), sub, is_last ? "e" : "s", (ctx == _ceb_clrtag(node->b[1])) ? " color=red" : "");
+ break;
+ }
+}
+
+/* dump a leaf */
+void ceb_imm_default_dump_leaf(ptrdiff_t kofs, enum ceb_key_type key_type, const struct ceb_node *node, int level, const void *ctx, int sub)
+{
+ unsigned long long int_key = 0;
+ const char *str_key = NULL;
+ uint64_t pxor;
+
+ switch (key_type) {
+ case CEB_KT_ADDR:
+ int_key = (uintptr_t)node;
+ break;
+ case CEB_KT_U32:
+ int_key = NODEK(node, kofs)->u32;
+ break;
+ case CEB_KT_U64:
+ int_key = NODEK(node, kofs)->u64;
+ break;
+ case CEB_KT_ST:
+ str_key = (char*)NODEK(node, kofs)->str;
+ break;
+ case CEB_KT_IS:
+ str_key = (char*)NODEK(node, kofs)->ptr;
+ break;
+ default:
+ break;
+ }
+
+ /* xor of the keys of the two lower branches */
+ pxor = _xor_branches(kofs, key_type, 0, 0, NULL,
+ node->b[0], node->b[1]);
+
+ switch (key_type) {
+ case CEB_KT_ADDR:
+ case CEB_KT_U32:
+ case CEB_KT_U64:
+ if (node->b[0] == node->b[1])
+ printf(" \"%lx_l_%d\" [label=\"%lx\\nlev=%d\\nkey=%llu\\n\" fillcolor=\"green\"%s];\n",
+ (long)node, sub, (long)node, level, int_key, (ctx == node) ? " color=red" : "");
+ else
+ printf(" \"%lx_l_%d\" [label=\"%lx\\nlev=%d bit=%d\\nkey=%llu\\n\" fillcolor=\"yellow\"%s];\n",
+ (long)node, sub, (long)node, level, flsnz(pxor) - 1, int_key, (ctx == node) ? " color=red" : "");
+ break;
+ case CEB_KT_MB:
+ break;
+ case CEB_KT_IM:
+ break;
+ case CEB_KT_ST:
+ case CEB_KT_IS:
+ if (node->b[0] == node->b[1])
+ printf(" \"%lx_l_%d\" [label=\"%lx\\nlev=%d\\nkey=\\\"%s\\\"\\n\" fillcolor=\"green\"%s];\n",
+ (long)node, sub, (long)node, level, str_key, (ctx == node) ? " color=red" : "");
+ else
+ printf(" \"%lx_l_%d\" [label=\"%lx\\nlev=%d bit=%ld\\nkey=\\\"%s\\\"\\n\" fillcolor=\"yellow\"%s];\n",
+ (long)node, sub, (long)node, level, (long)~pxor, str_key, (ctx == node) ? " color=red" : "");
+ break;
+ }
+}
+
+/* Dumps a tree through the specified callbacks, falling back to the default
+ * callbacks above if left NULL.
+ */
+
+const struct ceb_node *ceb_imm_default_dump_tree(ptrdiff_t kofs, enum ceb_key_type key_type, struct ceb_root *const *root,
+ uint64_t pxor, const void *last, int level, const void *ctx, int sub,
+ void (*root_dump)(ptrdiff_t kofs, enum ceb_key_type key_type, struct ceb_root *const *root, const void *ctx, int sub),
+ void (*node_dump)(ptrdiff_t kofs, enum ceb_key_type key_type, const struct ceb_node *node, int level, const void *ctx, int sub),
+ void (*dups_dump)(ptrdiff_t kofs, enum ceb_key_type key_type, const struct ceb_node *node, int level, const void *ctx, int sub),
+ void (*leaf_dump)(ptrdiff_t kofs, enum ceb_key_type key_type, const struct ceb_node *node, int level, const void *ctx, int sub))
+{
+ const struct ceb_node *node = _ceb_clrtag(*root);
+ uint64_t xor;
+
+ if (!node) /* empty tree */
+ return node;
+
+ if (!root_dump)
+ root_dump = ceb_imm_default_dump_root;
+
+ if (!node_dump)
+ node_dump = ceb_imm_default_dump_node;
+
+ if (!dups_dump)
+ dups_dump = ceb_imm_default_dump_dups;
+
+ if (!leaf_dump)
+ leaf_dump = ceb_imm_default_dump_leaf;
+
+ if (!level) {
+ /* dump the first arrow */
+ root_dump(kofs, key_type, root, ctx, sub);
+ }
+
+ /* regular nodes, all branches are canonical */
+
+ while (1) {
+ if (node->b[0] == _ceb_dotag(node, 1) && node->b[1] == _ceb_dotag(node, 1)) {
+ /* first inserted leaf */
+ leaf_dump(kofs, key_type, node, level, ctx, sub);
+ return node;
+ }
+
+ xor = _xor_branches(kofs, key_type, 0, 0, NULL,
+ node->b[0], node->b[1]);
+ if (xor)
+ break;
+
+ /* a zero XOR with different branches indicates a list element,
+ * we dump it and walk to the left until we find the node.
+ */
+ dups_dump(kofs, key_type, node, level, ctx, sub);
+ node = _ceb_clrtag(node->b[0]);
+ }
+
+ if (pxor && xor >= pxor) {
+ /* that's a leaf */
+ leaf_dump(kofs, key_type, node, level, ctx, sub);
+ return node;
+ }
+
+ /* that's a regular node */
+ node_dump(kofs, key_type, node, level, ctx, sub);
+
+ last = ceb_imm_default_dump_tree(kofs, key_type, &node->b[0], xor, last, level + 1, ctx, sub, root_dump, node_dump, dups_dump, leaf_dump);
+ return ceb_imm_default_dump_tree(kofs, key_type, &node->b[1], xor, last, level + 1, ctx, sub, root_dump, node_dump, dups_dump, leaf_dump);
+}
+
+#endif /* CEB_ENABLE_DUMP */
+++ /dev/null
-/*
- * Compact Elastic Binary Trees - exported functions operating on u32 keys
- *
- * Copyright (C) 2014-2024 Willy Tarreau - w@1wt.eu
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include "cebtree-prv.h"
-
-/*****************************************************************************\
- * The declarations below always cause two functions to be declared, one *
- * starting with "cebu32_*" and one with "cebu32_ofs_*" which takes a key *
- * offset just after the root. The one without kofs just has this argument *
- * omitted from its declaration and replaced with sizeof(struct ceb_node) in *
- * the call to the underlying functions. *
-\*****************************************************************************/
-
-/* Inserts node <node> into unique tree <tree> based on its key that
- * immediately follows the node. Returns the inserted node or the one
- * that already contains the same key.
- */
-CEB_FDECL3(struct ceb_node *, cebu32, _insert, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node)
-{
- uint32_t key = NODEK(node, kofs)->u32;
-
- return _cebu_insert(root, node, kofs, CEB_KT_U32, key, 0, NULL);
-}
-
-/* return the first node or NULL if not found. */
-CEB_FDECL2(struct ceb_node *, cebu32, _first, struct ceb_node **, root, ptrdiff_t, kofs)
-{
- return _cebu_first(root, kofs, CEB_KT_U32);
-}
-
-/* return the last node or NULL if not found. */
-CEB_FDECL2(struct ceb_node *, cebu32, _last, struct ceb_node **, root, ptrdiff_t, kofs)
-{
- return _cebu_last(root, kofs, CEB_KT_U32);
-}
-
-/* look up the specified key, and returns either the node containing it, or
- * NULL if not found.
- */
-CEB_FDECL3(struct ceb_node *, cebu32, _lookup, struct ceb_node **, root, ptrdiff_t, kofs, uint32_t, key)
-{
- return _cebu_lookup(root, kofs, CEB_KT_U32, key, 0, NULL);
-}
-
-/* look up the specified key or the highest below it, and returns either the
- * node containing it, or NULL if not found.
- */
-CEB_FDECL3(struct ceb_node *, cebu32, _lookup_le, struct ceb_node **, root, ptrdiff_t, kofs, uint32_t, key)
-{
- return _cebu_lookup_le(root, kofs, CEB_KT_U32, key, 0, NULL);
-}
-
-/* look up highest key below the specified one, and returns either the
- * node containing it, or NULL if not found.
- */
-CEB_FDECL3(struct ceb_node *, cebu32, _lookup_lt, struct ceb_node **, root, ptrdiff_t, kofs, uint32_t, key)
-{
- return _cebu_lookup_lt(root, kofs, CEB_KT_U32, key, 0, NULL);
-}
-
-/* look up the specified key or the smallest above it, and returns either the
- * node containing it, or NULL if not found.
- */
-CEB_FDECL3(struct ceb_node *, cebu32, _lookup_ge, struct ceb_node **, root, ptrdiff_t, kofs, uint32_t, key)
-{
- return _cebu_lookup_ge(root, kofs, CEB_KT_U32, key, 0, NULL);
-}
-
-/* look up the smallest key above the specified one, and returns either the
- * node containing it, or NULL if not found.
- */
-CEB_FDECL3(struct ceb_node *, cebu32, _lookup_gt, struct ceb_node **, root, ptrdiff_t, kofs, uint32_t, key)
-{
- return _cebu_lookup_gt(root, kofs, CEB_KT_U32, key, 0, NULL);
-}
-
-/* search for the next node after the specified one, and return it, or NULL if
- * not found. The approach consists in looking up that node, recalling the last
- * time a left turn was made, and returning the first node along the right
- * branch at that fork.
- */
-CEB_FDECL3(struct ceb_node *, cebu32, _next, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node)
-{
- uint32_t key = NODEK(node, kofs)->u32;
-
- return _cebu_next(root, kofs, CEB_KT_U32, key, 0, NULL);
-}
-
-/* search for the prev node before the specified one, and return it, or NULL if
- * not found. The approach consists in looking up that node, recalling the last
- * time a right turn was made, and returning the last node along the left
- * branch at that fork.
- */
-CEB_FDECL3(struct ceb_node *, cebu32, _prev, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node)
-{
- uint32_t key = NODEK(node, kofs)->u32;
-
- return _cebu_prev(root, kofs, CEB_KT_U32, key, 0, NULL);
-}
-
-/* look up the specified node with its key and deletes it if found, and in any
- * case, returns the node.
- */
-CEB_FDECL3(struct ceb_node *, cebu32, _delete, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node)
-{
- uint32_t key = NODEK(node, kofs)->u32;
-
- return _cebu_delete(root, node, kofs, CEB_KT_U32, key, 0, NULL);
-}
-
-/* look up the specified key, and detaches it and returns it if found, or NULL
- * if not found.
- */
-CEB_FDECL3(struct ceb_node *, cebu32, _pick, struct ceb_node **, root, ptrdiff_t, kofs, uint32_t, key)
-{
- return _cebu_delete(root, NULL, kofs, CEB_KT_U32, key, 0, NULL);
-}
-
-/* dumps a ceb_node tree using the default functions above. If a node matches
- * <ctx>, this one will be highlighted in red.
- */
-CEB_FDECL4(void, cebu32, _default_dump, struct ceb_node **, root, ptrdiff_t, kofs, const char *, label, const void *, ctx)
-{
- printf("\ndigraph cebu32_tree {\n"
- " fontname=\"fixed\";\n"
- " fontsize=8\n"
- " label=\"%s\"\n"
- "", label);
-
- printf(" node [fontname=\"fixed\" fontsize=8 shape=\"box\" style=\"filled\" color=\"black\" fillcolor=\"white\"];\n"
- " edge [fontname=\"fixed\" fontsize=8 style=\"solid\" color=\"magenta\" dir=\"forward\"];\n");
-
- cebu_default_dump_tree(kofs, CEB_KT_U32, root, 0, NULL, 0, ctx, NULL, NULL, NULL);
-
- printf("}\n");
-}
+++ /dev/null
-/*
- * Compact Elastic Binary Trees - exported functions operating on u64 keys
- *
- * Copyright (C) 2014-2024 Willy Tarreau - w@1wt.eu
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include "cebtree-prv.h"
-
-/*****************************************************************************\
- * The declarations below always cause two functions to be declared, one *
- * starting with "cebu64_*" and one with "cebu64_ofs_*" which takes a key *
- * offset just after the root. The one without kofs just has this argument *
- * omitted from its declaration and replaced with sizeof(struct ceb_node) in *
- * the call to the underlying functions. *
-\*****************************************************************************/
-
-/* Inserts node <node> into unique tree <tree> based on its key that
- * immediately follows the node. Returns the inserted node or the one
- * that already contains the same key.
- */
-CEB_FDECL3(struct ceb_node *, cebu64, _insert, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node)
-{
- uint64_t key = NODEK(node, kofs)->u64;
-
- return _cebu_insert(root, node, kofs, CEB_KT_U64, 0, key, NULL);
-}
-
-/* return the first node or NULL if not found. */
-CEB_FDECL2(struct ceb_node *, cebu64, _first, struct ceb_node **, root, ptrdiff_t, kofs)
-{
- return _cebu_first(root, kofs, CEB_KT_U64);
-}
-
-/* return the last node or NULL if not found. */
-CEB_FDECL2(struct ceb_node *, cebu64, _last, struct ceb_node **, root, ptrdiff_t, kofs)
-{
- return _cebu_last(root, kofs, CEB_KT_U64);
-}
-
-/* look up the specified key, and returns either the node containing it, or
- * NULL if not found.
- */
-CEB_FDECL3(struct ceb_node *, cebu64, _lookup, struct ceb_node **, root, ptrdiff_t, kofs, uint64_t, key)
-{
- return _cebu_lookup(root, kofs, CEB_KT_U64, 0, key, NULL);
-}
-
-/* look up the specified key or the highest below it, and returns either the
- * node containing it, or NULL if not found.
- */
-CEB_FDECL3(struct ceb_node *, cebu64, _lookup_le, struct ceb_node **, root, ptrdiff_t, kofs, uint64_t, key)
-{
- return _cebu_lookup_le(root, kofs, CEB_KT_U64, 0, key, NULL);
-}
-
-/* look up highest key below the specified one, and returns either the
- * node containing it, or NULL if not found.
- */
-CEB_FDECL3(struct ceb_node *, cebu64, _lookup_lt, struct ceb_node **, root, ptrdiff_t, kofs, uint64_t, key)
-{
- return _cebu_lookup_lt(root, kofs, CEB_KT_U64, 0, key, NULL);
-}
-
-/* look up the specified key or the smallest above it, and returns either the
- * node containing it, or NULL if not found.
- */
-CEB_FDECL3(struct ceb_node *, cebu64, _lookup_ge, struct ceb_node **, root, ptrdiff_t, kofs, uint64_t, key)
-{
- return _cebu_lookup_ge(root, kofs, CEB_KT_U64, 0, key, NULL);
-}
-
-/* look up the smallest key above the specified one, and returns either the
- * node containing it, or NULL if not found.
- */
-CEB_FDECL3(struct ceb_node *, cebu64, _lookup_gt, struct ceb_node **, root, ptrdiff_t, kofs, uint64_t, key)
-{
- return _cebu_lookup_gt(root, kofs, CEB_KT_U64, 0, key, NULL);
-}
-
-/* search for the next node after the specified one, and return it, or NULL if
- * not found. The approach consists in looking up that node, recalling the last
- * time a left turn was made, and returning the first node along the right
- * branch at that fork.
- */
-CEB_FDECL3(struct ceb_node *, cebu64, _next, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node)
-{
- uint64_t key = NODEK(node, kofs)->u64;
-
- return _cebu_next(root, kofs, CEB_KT_U64, 0, key, NULL);
-}
-
-/* search for the prev node before the specified one, and return it, or NULL if
- * not found. The approach consists in looking up that node, recalling the last
- * time a right turn was made, and returning the last node along the left
- * branch at that fork.
- */
-CEB_FDECL3(struct ceb_node *, cebu64, _prev, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node)
-{
- uint64_t key = NODEK(node, kofs)->u64;
-
- return _cebu_prev(root, kofs, CEB_KT_U64, 0, key, NULL);
-}
-
-/* look up the specified node with its key and deletes it if found, and in any
- * case, returns the node.
- */
-CEB_FDECL3(struct ceb_node *, cebu64, _delete, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node)
-{
- uint64_t key = NODEK(node, kofs)->u64;
-
- return _cebu_delete(root, node, kofs, CEB_KT_U64, 0, key, NULL);
-}
-
-/* look up the specified key, and detaches it and returns it if found, or NULL
- * if not found.
- */
-CEB_FDECL3(struct ceb_node *, cebu64, _pick, struct ceb_node **, root, ptrdiff_t, kofs, uint64_t, key)
-{
- return _cebu_delete(root, NULL, kofs, CEB_KT_U64, 0, key, NULL);
-}
-
-/* dumps a ceb_node tree using the default functions above. If a node matches
- * <ctx>, this one will be highlighted in red.
- */
-CEB_FDECL4(void, cebu64, _default_dump, struct ceb_node **, root, ptrdiff_t, kofs, const char *, label, const void *, ctx)
-{
- printf("\ndigraph cebu64_tree {\n"
- " fontname=\"fixed\";\n"
- " fontsize=8\n"
- " label=\"%s\"\n"
- "", label);
-
- printf(" node [fontname=\"fixed\" fontsize=8 shape=\"box\" style=\"filled\" color=\"black\" fillcolor=\"white\"];\n"
- " edge [fontname=\"fixed\" fontsize=8 style=\"solid\" color=\"magenta\" dir=\"forward\"];\n");
-
- cebu_default_dump_tree(kofs, CEB_KT_U64, root, 0, NULL, 0, ctx, NULL, NULL, NULL);
-
- printf("}\n");
-}
+++ /dev/null
-/*
- * Compact Elastic Binary Trees - exported functions operating on addr keys
- *
- * Copyright (C) 2014-2024 Willy Tarreau - w@1wt.eu
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include "cebtree-prv.h"
-
-/*****************************************************************************\
- * The declarations below always cause two functions to be declared, one *
- * starting with "cebua_*" and one with "cebua_ofs_*" which takes a key *
- * offset just after the root. The one without kofs just has this argument *
- * omitted from its declaration and replaced with sizeof(struct ceb_node) in *
- * the call to the underlying functions. *
-\*****************************************************************************/
-
-/* Inserts node <node> into unique tree <tree> based on its own address
- * Returns the inserted node or the one that has the same address.
- */
-CEB_FDECL3(struct ceb_node *, cebua, _insert, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node)
-{
- return _cebu_insert(root, node, kofs, CEB_KT_ADDR, 0, 0, node);
-}
-
-/* return the first node or NULL if not found. */
-CEB_FDECL2(struct ceb_node *, cebua, _first, struct ceb_node **, root, ptrdiff_t, kofs)
-{
- return _cebu_first(root, kofs, CEB_KT_ADDR);
-}
-
-/* return the last node or NULL if not found. */
-CEB_FDECL2(struct ceb_node *, cebua, _last, struct ceb_node **, root, ptrdiff_t, kofs)
-{
- return _cebu_last(root, kofs, CEB_KT_ADDR);
-}
-
-/* look up the specified key, and returns either the node containing it, or
- * NULL if not found.
- */
-CEB_FDECL3(struct ceb_node *, cebua, _lookup, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key)
-{
- return _cebu_lookup(root, kofs, CEB_KT_ADDR, 0, 0, key);
-}
-
-/* look up the specified key or the highest below it, and returns either the
- * node containing it, or NULL if not found.
- */
-CEB_FDECL3(struct ceb_node *, cebua, _lookup_le, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key)
-{
- return _cebu_lookup_le(root, kofs, CEB_KT_ADDR, 0, 0, key);
-}
-
-/* look up highest key below the specified one, and returns either the
- * node containing it, or NULL if not found.
- */
-CEB_FDECL3(struct ceb_node *, cebua, _lookup_lt, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key)
-{
- return _cebu_lookup_lt(root, kofs, CEB_KT_ADDR, 0, 0, key);
-}
-
-/* look up the specified key or the smallest above it, and returns either the
- * node containing it, or NULL if not found.
- */
-CEB_FDECL3(struct ceb_node *, cebua, _lookup_ge, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key)
-{
- return _cebu_lookup_ge(root, kofs, CEB_KT_ADDR, 0, 0, key);
-}
-
-/* look up the smallest key above the specified one, and returns either the
- * node containing it, or NULL if not found.
- */
-CEB_FDECL3(struct ceb_node *, cebua, _lookup_gt, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key)
-{
- return _cebu_lookup_gt(root, kofs, CEB_KT_ADDR, 0, 0, key);
-}
-
-/* search for the next node after the specified one, and return it, or NULL if
- * not found. The approach consists in looking up that node, recalling the last
- * time a left turn was made, and returning the first node along the right
- * branch at that fork.
- */
-CEB_FDECL3(struct ceb_node *, cebua, _next, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node)
-{
- return _cebu_next(root, kofs, CEB_KT_ADDR, 0, 0, node);
-}
-
-/* search for the prev node before the specified one, and return it, or NULL if
- * not found. The approach consists in looking up that node, recalling the last
- * time a right turn was made, and returning the last node along the left
- * branch at that fork.
- */
-CEB_FDECL3(struct ceb_node *, cebua, _prev, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node)
-{
- return _cebu_prev(root, kofs, CEB_KT_ADDR, 0, 0, node);
-}
-
-/* look up the specified node with its key and deletes it if found, and in any
- * case, returns the node.
- */
-CEB_FDECL3(struct ceb_node *, cebua, _delete, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node)
-{
- return _cebu_delete(root, node, kofs, CEB_KT_ADDR, 0, 0, node);
-}
-
-/* look up the specified key, and detaches it and returns it if found, or NULL
- * if not found.
- */
-CEB_FDECL3(struct ceb_node *, cebua, _pick, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key)
-{
- return _cebu_delete(root, NULL, kofs, CEB_KT_ADDR, 0, 0, key);
-}
-
-/* dumps a ceb_node tree using the default functions above. If a node matches
- * <ctx>, this one will be highlighted in red.
- */
-CEB_FDECL4(void, cebua, _default_dump, struct ceb_node **, root, ptrdiff_t, kofs, const char *, label, const void *, ctx)
-{
- printf("\ndigraph cebua_tree {\n"
- " fontname=\"fixed\";\n"
- " fontsize=8\n"
- " label=\"%s\"\n"
- "", label);
-
- printf(" node [fontname=\"fixed\" fontsize=8 shape=\"box\" style=\"filled\" color=\"black\" fillcolor=\"white\"];\n"
- " edge [fontname=\"fixed\" fontsize=8 style=\"solid\" color=\"magenta\" dir=\"forward\"];\n");
-
- cebu_default_dump_tree(kofs, CEB_KT_ADDR, root, 0, NULL, 0, ctx, NULL, NULL, NULL);
-
- printf("}\n");
-}
+++ /dev/null
-/*
- * Compact Elastic Binary Trees - exported functions operating on mb keys
- *
- * Copyright (C) 2014-2024 Willy Tarreau - w@1wt.eu
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "cebtree-prv.h"
-
-/*****************************************************************************\
- * The declarations below always cause two functions to be declared, one *
- * starting with "cebub_*" and one with "cebub_ofs_*" which takes a key *
- * offset just after the root. The one without kofs just has this argument *
- * omitted from its declaration and replaced with sizeof(struct ceb_node) in *
- * the call to the underlying functions. *
-\*****************************************************************************/
-
-/* Inserts node <node> into unique tree <tree> based on its key that
- * immediately follows the node and for <len> bytes. Returns the
- * inserted node or the one that already contains the same key.
- */
-CEB_FDECL4(struct ceb_node *, cebub, _insert, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len)
-{
- const void *key = NODEK(node, kofs)->mb;
-
- return _cebu_insert(root, node, kofs, CEB_KT_MB, 0, len, key);
-}
-
-/* return the first node or NULL if not found. */
-CEB_FDECL2(struct ceb_node *, cebub, _first, struct ceb_node **, root, ptrdiff_t, kofs)
-{
- return _cebu_first(root, kofs, CEB_KT_MB);
-}
-
-/* return the last node or NULL if not found. */
-CEB_FDECL2(struct ceb_node *, cebub, _last, struct ceb_node **, root, ptrdiff_t, kofs)
-{
- return _cebu_last(root, kofs, CEB_KT_MB);
-}
-
-/* look up the specified key <key> of length <len>, and returns either the node
- * containing it, or NULL if not found.
- */
-CEB_FDECL4(struct ceb_node *, cebub, _lookup, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key, size_t, len)
-{
- return _cebu_lookup(root, kofs, CEB_KT_MB, 0, len, key);
-}
-
-/* look up the specified key or the highest below it, and returns either the
- * node containing it, or NULL if not found.
- */
-CEB_FDECL4(struct ceb_node *, cebub, _lookup_le, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key, size_t, len)
-{
- return _cebu_lookup_le(root, kofs, CEB_KT_MB, 0, len, key);
-}
-
-/* look up highest key below the specified one, and returns either the
- * node containing it, or NULL if not found.
- */
-CEB_FDECL4(struct ceb_node *, cebub, _lookup_lt, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key, size_t, len)
-{
- return _cebu_lookup_lt(root, kofs, CEB_KT_MB, 0, len, key);
-}
-
-/* look up the specified key or the smallest above it, and returns either the
- * node containing it, or NULL if not found.
- */
-CEB_FDECL4(struct ceb_node *, cebub, _lookup_ge, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key, size_t, len)
-{
- return _cebu_lookup_ge(root, kofs, CEB_KT_MB, 0, len, key);
-}
-
-/* look up the smallest key above the specified one, and returns either the
- * node containing it, or NULL if not found.
- */
-CEB_FDECL4(struct ceb_node *, cebub, _lookup_gt, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key, size_t, len)
-{
- return _cebu_lookup_gt(root, kofs, CEB_KT_MB, 0, len, key);
-}
-
-/* search for the next node after the specified one, and return it, or NULL if
- * not found. The approach consists in looking up that node, recalling the last
- * time a left turn was made, and returning the first node along the right
- * branch at that fork. The <len> field must correspond to the key length in
- * bytes.
- */
-CEB_FDECL4(struct ceb_node *, cebub, _next, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len)
-{
- const void *key = NODEK(node, kofs)->mb;
-
- return _cebu_next(root, kofs, CEB_KT_MB, 0, len, key);
-}
-
-/* search for the prev node before the specified one, and return it, or NULL if
- * not found. The approach consists in looking up that node, recalling the last
- * time a right turn was made, and returning the last node along the left
- * branch at that fork. The <len> field must correspond to the key length in
- * bytes.
- */
-CEB_FDECL4(struct ceb_node *, cebub, _prev, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len)
-{
- const void *key = NODEK(node, kofs)->mb;
-
- return _cebu_prev(root, kofs, CEB_KT_MB, 0, len, key);
-}
-
-/* look up the specified node with its key and deletes it if found, and in any
- * case, returns the node. The <len> field must correspond to the key length in
- * bytes.
- */
-CEB_FDECL4(struct ceb_node *, cebub, _delete, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len)
-{
- const void *key = NODEK(node, kofs)->mb;
-
- return _cebu_delete(root, node, kofs, CEB_KT_MB, 0, len, key);
-}
-
-/* look up the specified key, and detaches it and returns it if found, or NULL
- * if not found. The <len> field must correspond to the key length in bytes.
- */
-CEB_FDECL4(struct ceb_node *, cebub, _pick, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key, size_t, len)
-{
- return _cebu_delete(root, NULL, kofs, CEB_KT_MB, 0, len, key);
-}
+++ /dev/null
-/*
- * Compact Elastic Binary Trees - exported functions operating on indirect blocks
- *
- * Copyright (C) 2014-2024 Willy Tarreau - w@1wt.eu
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "cebtree-prv.h"
-
-/*****************************************************************************\
- * The declarations below always cause two functions to be declared, one *
- * starting with "cebuib_*" and one with "cebuib_ofs_*" which takes a key *
- * offset just after the root. The one without kofs just has this argument *
- * omitted from its declaration and replaced with sizeof(struct ceb_node) in *
- * the call to the underlying functions. *
-\*****************************************************************************/
-
-/* Inserts node <node> into unique tree <tree> based on its key whose pointer
- * immediately follows the node and for <len> bytes. Returns the inserted node
- * or the one that already contains the same key.
- */
-CEB_FDECL4(struct ceb_node *, cebuib, _insert, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len)
-{
- const void *key = NODEK(node, kofs)->ptr;
-
- return _cebu_insert(root, node, kofs, CEB_KT_IM, 0, len, key);
-}
-
-/* return the first node or NULL if not found. */
-CEB_FDECL2(struct ceb_node *, cebuib, _first, struct ceb_node **, root, ptrdiff_t, kofs)
-{
- return _cebu_first(root, kofs, CEB_KT_IM);
-}
-
-/* return the last node or NULL if not found. */
-CEB_FDECL2(struct ceb_node *, cebuib, _last, struct ceb_node **, root, ptrdiff_t, kofs)
-{
- return _cebu_last(root, kofs, CEB_KT_IM);
-}
-
-/* look up the specified key <key> of length <len>, and returns either the node
- * containing it, or NULL if not found.
- */
-CEB_FDECL4(struct ceb_node *, cebuib, _lookup, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key, size_t, len)
-{
- return _cebu_lookup(root, kofs, CEB_KT_IM, 0, len, key);
-}
-
-/* look up the specified key or the highest below it, and returns either the
- * node containing it, or NULL if not found.
- */
-CEB_FDECL4(struct ceb_node *, cebuib, _lookup_le, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key, size_t, len)
-{
- return _cebu_lookup_le(root, kofs, CEB_KT_IM, 0, len, key);
-}
-
-/* look up highest key below the specified one, and returns either the
- * node containing it, or NULL if not found.
- */
-CEB_FDECL4(struct ceb_node *, cebuib, _lookup_lt, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key, size_t, len)
-{
- return _cebu_lookup_lt(root, kofs, CEB_KT_IM, 0, len, key);
-}
-
-/* look up the specified key or the smallest above it, and returns either the
- * node containing it, or NULL if not found.
- */
-CEB_FDECL4(struct ceb_node *, cebuib, _lookup_ge, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key, size_t, len)
-{
- return _cebu_lookup_ge(root, kofs, CEB_KT_IM, 0, len, key);
-}
-
-/* look up the smallest key above the specified one, and returns either the
- * node containing it, or NULL if not found.
- */
-CEB_FDECL4(struct ceb_node *, cebuib, _lookup_gt, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key, size_t, len)
-{
- return _cebu_lookup_gt(root, kofs, CEB_KT_IM, 0, len, key);
-}
-
-/* search for the next node after the specified one, and return it, or NULL if
- * not found. The approach consists in looking up that node, recalling the last
- * time a left turn was made, and returning the first node along the right
- * branch at that fork. The <len> field must correspond to the key length in
- * bytes.
- */
-CEB_FDECL4(struct ceb_node *, cebuib, _next, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len)
-{
- const void *key = NODEK(node, kofs)->ptr;
-
- return _cebu_next(root, kofs, CEB_KT_IM, 0, len, key);
-}
-
-/* search for the prev node before the specified one, and return it, or NULL if
- * not found. The approach consists in looking up that node, recalling the last
- * time a right turn was made, and returning the last node along the left
- * branch at that fork. The <len> field must correspond to the key length in
- * bytes.
- */
-CEB_FDECL4(struct ceb_node *, cebuib, _prev, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len)
-{
- const void *key = NODEK(node, kofs)->ptr;
-
- return _cebu_prev(root, kofs, CEB_KT_IM, 0, len, key);
-}
-
-/* look up the specified node with its key and deletes it if found, and in any
- * case, returns the node. The <len> field must correspond to the key length in
- * bytes.
- */
-CEB_FDECL4(struct ceb_node *, cebuib, _delete, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node, size_t, len)
-{
- const void *key = NODEK(node, kofs)->ptr;
-
- return _cebu_delete(root, node, kofs, CEB_KT_IM, 0, len, key);
-}
-
-/* look up the specified key, and detaches it and returns it if found, or NULL
- * if not found. The <len> field must correspond to the key length in bytes.
- */
-CEB_FDECL4(struct ceb_node *, cebuib, _pick, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key, size_t, len)
-{
- return _cebu_delete(root, NULL, kofs, CEB_KT_IM, 0, len, key);
-}
+++ /dev/null
-/*
- * Compact Elastic Binary Trees - exported functions operating on indirect strings
- *
- * Copyright (C) 2014-2024 Willy Tarreau - w@1wt.eu
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "cebtree-prv.h"
-
-/*****************************************************************************\
- * The declarations below always cause two functions to be declared, one *
- * starting with "cebuis_*" and one with "cebuis_ofs_*" which takes a key *
- * offset just after the root. The one without kofs just has this argument *
- * omitted from its declaration and replaced with sizeof(struct ceb_node) in *
- * the call to the underlying functions. *
-\*****************************************************************************/
-
-/* Inserts node <node> into unique tree <tree> based on its key whose pointer
- * immediately follows the node. Returns the inserted node or the one that
- * already contains the same key.
- */
-CEB_FDECL3(struct ceb_node *, cebuis, _insert, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node)
-{
- const void *key = NODEK(node, kofs)->ptr;
-
- return _cebu_insert(root, node, kofs, CEB_KT_IS, 0, 0, key);
-}
-
-/* return the first node or NULL if not found. */
-CEB_FDECL2(struct ceb_node *, cebuis, _first, struct ceb_node **, root, ptrdiff_t, kofs)
-{
- return _cebu_first(root, kofs, CEB_KT_IS);
-}
-
-/* return the last node or NULL if not found. */
-CEB_FDECL2(struct ceb_node *, cebuis, _last, struct ceb_node **, root, ptrdiff_t, kofs)
-{
- return _cebu_last(root, kofs, CEB_KT_IS);
-}
-
-/* look up the specified key, and returns either the node containing it, or
- * NULL if not found.
- */
-CEB_FDECL3(struct ceb_node *, cebuis, _lookup, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key)
-{
- return _cebu_lookup(root, kofs, CEB_KT_IS, 0, 0, key);
-}
-
-/* look up the specified key or the highest below it, and returns either the
- * node containing it, or NULL if not found.
- */
-CEB_FDECL3(struct ceb_node *, cebuis, _lookup_le, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key)
-{
- return _cebu_lookup_le(root, kofs, CEB_KT_IS, 0, 0, key);
-}
-
-/* look up highest key below the specified one, and returns either the
- * node containing it, or NULL if not found.
- */
-CEB_FDECL3(struct ceb_node *, cebuis, _lookup_lt, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key)
-{
- return _cebu_lookup_lt(root, kofs, CEB_KT_IS, 0, 0, key);
-}
-
-/* look up the specified key or the smallest above it, and returns either the
- * node containing it, or NULL if not found.
- */
-CEB_FDECL3(struct ceb_node *, cebuis, _lookup_ge, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key)
-{
- return _cebu_lookup_ge(root, kofs, CEB_KT_IS, 0, 0, key);
-}
-
-/* look up the smallest key above the specified one, and returns either the
- * node containing it, or NULL if not found.
- */
-CEB_FDECL3(struct ceb_node *, cebuis, _lookup_gt, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key)
-{
- return _cebu_lookup_gt(root, kofs, CEB_KT_IS, 0, 0, key);
-}
-
-/* search for the next node after the specified one, and return it, or NULL if
- * not found. The approach consists in looking up that node, recalling the last
- * time a left turn was made, and returning the first node along the right
- * branch at that fork.
- */
-CEB_FDECL3(struct ceb_node *, cebuis, _next, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node)
-{
- const void *key = NODEK(node, kofs)->ptr;
-
- return _cebu_next(root, kofs, CEB_KT_IS, 0, 0, key);
-}
-
-/* search for the prev node before the specified one, and return it, or NULL if
- * not found. The approach consists in looking up that node, recalling the last
- * time a right turn was made, and returning the last node along the left
- * branch at that fork.
- */
-CEB_FDECL3(struct ceb_node *, cebuis, _prev, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node)
-{
- const void *key = NODEK(node, kofs)->ptr;
-
- return _cebu_prev(root, kofs, CEB_KT_IS, 0, 0, key);
-}
-
-/* look up the specified node with its key and deletes it if found, and in any
- * case, returns the node.
- */
-CEB_FDECL3(struct ceb_node *, cebuis, _delete, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node)
-{
- const void *key = NODEK(node, kofs)->ptr;
-
- return _cebu_delete(root, node, kofs, CEB_KT_IS, 0, 0, key);
-}
-
-/* look up the specified key, and detaches it and returns it if found, or NULL
- * if not found.
- */
-CEB_FDECL3(struct ceb_node *, cebuis, _pick, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key)
-{
- return _cebu_delete(root, NULL, kofs, CEB_KT_IS, 0, 0, key);
-}
+++ /dev/null
-/*
- * Compact Elastic Binary Trees - exported functions operating on ulong keys
- *
- * Copyright (C) 2014-2024 Willy Tarreau - w@1wt.eu
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include "cebtree-prv.h"
-
-/*****************************************************************************\
- * The declarations below always cause two functions to be declared, one *
- * starting with "cebu32_*" and one with "cebu32_ofs_*" which takes a key *
- * offset just after the root. The one without kofs just has this argument *
- * omitted from its declaration and replaced with sizeof(struct ceb_node) in *
- * the call to the underlying functions. *
-\*****************************************************************************/
-
-/* Inserts node <node> into unique tree <tree> based on its key that
- * immediately follows the node. Returns the inserted node or the one
- * that already contains the same key.
- */
-CEB_FDECL3(struct ceb_node *, cebul, _insert, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node)
-{
- unsigned long key = NODEK(node, kofs)->ul;
-
- if (sizeof(long) <= 4)
- return _cebu_insert(root, node, kofs, CEB_KT_U32, key, 0, NULL);
- else
- return _cebu_insert(root, node, kofs, CEB_KT_U64, 0, key, NULL);
-}
-
-/* return the first node or NULL if not found. */
-CEB_FDECL2(struct ceb_node *, cebul, _first, struct ceb_node **, root, ptrdiff_t, kofs)
-{
- if (sizeof(long) <= 4)
- return _cebu_first(root, kofs, CEB_KT_U32);
- else
- return _cebu_first(root, kofs, CEB_KT_U64);
-}
-
-/* return the last node or NULL if not found. */
-CEB_FDECL2(struct ceb_node *, cebul, _last, struct ceb_node **, root, ptrdiff_t, kofs)
-{
- if (sizeof(long) <= 4)
- return _cebu_last(root, kofs, CEB_KT_U32);
- else
- return _cebu_last(root, kofs, CEB_KT_U64);
-}
-
-/* look up the specified key, and returns either the node containing it, or
- * NULL if not found.
- */
-CEB_FDECL3(struct ceb_node *, cebul, _lookup, struct ceb_node **, root, ptrdiff_t, kofs, unsigned long, key)
-{
- if (sizeof(long) <= 4)
- return _cebu_lookup(root, kofs, CEB_KT_U32, key, 0, NULL);
- else
- return _cebu_lookup(root, kofs, CEB_KT_U64, 0, key, NULL);
-}
-
-/* look up the specified key or the highest below it, and returns either the
- * node containing it, or NULL if not found.
- */
-CEB_FDECL3(struct ceb_node *, cebul, _lookup_le, struct ceb_node **, root, ptrdiff_t, kofs, unsigned long, key)
-{
- if (sizeof(long) <= 4)
- return _cebu_lookup_le(root, kofs, CEB_KT_U32, key, 0, NULL);
- else
- return _cebu_lookup_le(root, kofs, CEB_KT_U64, 0, key, NULL);
-}
-
-/* look up highest key below the specified one, and returns either the
- * node containing it, or NULL if not found.
- */
-CEB_FDECL3(struct ceb_node *, cebul, _lookup_lt, struct ceb_node **, root, ptrdiff_t, kofs, unsigned long, key)
-{
- if (sizeof(long) <= 4)
- return _cebu_lookup_lt(root, kofs, CEB_KT_U32, key, 0, NULL);
- else
- return _cebu_lookup_lt(root, kofs, CEB_KT_U64, 0, key, NULL);
-}
-
-/* look up the specified key or the smallest above it, and returns either the
- * node containing it, or NULL if not found.
- */
-CEB_FDECL3(struct ceb_node *, cebul, _lookup_ge, struct ceb_node **, root, ptrdiff_t, kofs, unsigned long, key)
-{
- if (sizeof(long) <= 4)
- return _cebu_lookup_ge(root, kofs, CEB_KT_U32, key, 0, NULL);
- else
- return _cebu_lookup_ge(root, kofs, CEB_KT_U64, 0, key, NULL);
-}
-
-/* look up the smallest key above the specified one, and returns either the
- * node containing it, or NULL if not found.
- */
-CEB_FDECL3(struct ceb_node *, cebul, _lookup_gt, struct ceb_node **, root, ptrdiff_t, kofs, unsigned long, key)
-{
- if (sizeof(long) <= 4)
- return _cebu_lookup_gt(root, kofs, CEB_KT_U32, key, 0, NULL);
- else
- return _cebu_lookup_gt(root, kofs, CEB_KT_U64, 0, key, NULL);
-}
-
-/* search for the next node after the specified one, and return it, or NULL if
- * not found. The approach consists in looking up that node, recalling the last
- * time a left turn was made, and returning the first node along the right
- * branch at that fork.
- */
-CEB_FDECL3(struct ceb_node *, cebul, _next, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node)
-{
- unsigned long key = NODEK(node, kofs)->ul;
-
- if (sizeof(long) <= 4)
- return _cebu_next(root, kofs, CEB_KT_U32, key, 0, NULL);
- else
- return _cebu_next(root, kofs, CEB_KT_U64, 0, key, NULL);
-}
-
-/* search for the prev node before the specified one, and return it, or NULL if
- * not found. The approach consists in looking up that node, recalling the last
- * time a right turn was made, and returning the last node along the left
- * branch at that fork.
- */
-CEB_FDECL3(struct ceb_node *, cebul, _prev, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node)
-{
- unsigned long key = NODEK(node, kofs)->ul;
-
- if (sizeof(long) <= 4)
- return _cebu_prev(root, kofs, CEB_KT_U32, key, 0, NULL);
- else
- return _cebu_prev(root, kofs, CEB_KT_U64, 0, key, NULL);
-}
-
-/* look up the specified node with its key and deletes it if found, and in any
- * case, returns the node.
- */
-CEB_FDECL3(struct ceb_node *, cebul, _delete, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node)
-{
- unsigned long key = NODEK(node, kofs)->ul;
-
- if (sizeof(long) <= 4)
- return _cebu_delete(root, node, kofs, CEB_KT_U32, key, 0, NULL);
- else
- return _cebu_delete(root, node, kofs, CEB_KT_U64, 0, key, NULL);
-}
-
-/* look up the specified key, and detaches it and returns it if found, or NULL
- * if not found.
- */
-CEB_FDECL3(struct ceb_node *, cebul, _pick, struct ceb_node **, root, ptrdiff_t, kofs, unsigned long, key)
-{
- if (sizeof(long) <= 4)
- return _cebu_delete(root, NULL, kofs, CEB_KT_U32, key, 0, NULL);
- else
- return _cebu_delete(root, NULL, kofs, CEB_KT_U64, 0, key, NULL);
-}
-
-/* dumps a ceb_node tree using the default functions above. If a node matches
- * <ctx>, this one will be highlighted in red.
- */
-CEB_FDECL4(void, cebul, _default_dump, struct ceb_node **, root, ptrdiff_t, kofs, const char *, label, const void *, ctx)
-{
- printf("\ndigraph cebul_tree {\n"
- " fontname=\"fixed\";\n"
- " fontsize=8\n"
- " label=\"%s\"\n"
- "", label);
-
- printf(" node [fontname=\"fixed\" fontsize=8 shape=\"box\" style=\"filled\" color=\"black\" fillcolor=\"white\"];\n"
- " edge [fontname=\"fixed\" fontsize=8 style=\"solid\" color=\"magenta\" dir=\"forward\"];\n");
-
- cebu_default_dump_tree(kofs, sizeof(long) <= 4 ? CEB_KT_U32 : CEB_KT_U64, root, 0, NULL, 0, ctx, NULL, NULL, NULL);
-
- printf("}\n");
-}
+++ /dev/null
-/*
- * Compact Elastic Binary Trees - exported functions operating on string keys
- *
- * Copyright (C) 2014-2024 Willy Tarreau - w@1wt.eu
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "cebtree-prv.h"
-
-/*****************************************************************************\
- * The declarations below always cause two functions to be declared, one *
- * starting with "cebus_*" and one with "cebus_ofs_*" which takes a key *
- * offset just after the root. The one without kofs just has this argument *
- * omitted from its declaration and replaced with sizeof(struct ceb_node) in *
- * the call to the underlying functions. *
-\*****************************************************************************/
-
-/* Inserts node <node> into unique tree <tree> based on its key that
- * immediately follows the node. Returns the inserted node or the one
- * that already contains the same key.
- */
-CEB_FDECL3(struct ceb_node *, cebus, _insert, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node)
-{
- const void *key = NODEK(node, kofs)->str;
-
- return _cebu_insert(root, node, kofs, CEB_KT_ST, 0, 0, key);
-}
-
-/* return the first node or NULL if not found. */
-CEB_FDECL2(struct ceb_node *, cebus, _first, struct ceb_node **, root, ptrdiff_t, kofs)
-{
- return _cebu_first(root, kofs, CEB_KT_ST);
-}
-
-/* return the last node or NULL if not found. */
-CEB_FDECL2(struct ceb_node *, cebus, _last, struct ceb_node **, root, ptrdiff_t, kofs)
-{
- return _cebu_last(root, kofs, CEB_KT_ST);
-}
-
-/* look up the specified key, and returns either the node containing it, or
- * NULL if not found.
- */
-CEB_FDECL3(struct ceb_node *, cebus, _lookup, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key)
-{
- return _cebu_lookup(root, kofs, CEB_KT_ST, 0, 0, key);
-}
-
-/* look up the specified key or the highest below it, and returns either the
- * node containing it, or NULL if not found.
- */
-CEB_FDECL3(struct ceb_node *, cebus, _lookup_le, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key)
-{
- return _cebu_lookup_le(root, kofs, CEB_KT_ST, 0, 0, key);
-}
-
-/* look up highest key below the specified one, and returns either the
- * node containing it, or NULL if not found.
- */
-CEB_FDECL3(struct ceb_node *, cebus, _lookup_lt, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key)
-{
- return _cebu_lookup_lt(root, kofs, CEB_KT_ST, 0, 0, key);
-}
-
-/* look up the specified key or the smallest above it, and returns either the
- * node containing it, or NULL if not found.
- */
-CEB_FDECL3(struct ceb_node *, cebus, _lookup_ge, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key)
-{
- return _cebu_lookup_ge(root, kofs, CEB_KT_ST, 0, 0, key);
-}
-
-/* look up the smallest key above the specified one, and returns either the
- * node containing it, or NULL if not found.
- */
-CEB_FDECL3(struct ceb_node *, cebus, _lookup_gt, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key)
-{
- return _cebu_lookup_gt(root, kofs, CEB_KT_ST, 0, 0, key);
-}
-
-/* search for the next node after the specified one, and return it, or NULL if
- * not found. The approach consists in looking up that node, recalling the last
- * time a left turn was made, and returning the first node along the right
- * branch at that fork.
- */
-CEB_FDECL3(struct ceb_node *, cebus, _next, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node)
-{
- const void *key = NODEK(node, kofs)->str;
-
- return _cebu_next(root, kofs, CEB_KT_ST, 0, 0, key);
-}
-
-/* search for the prev node before the specified one, and return it, or NULL if
- * not found. The approach consists in looking up that node, recalling the last
- * time a right turn was made, and returning the last node along the left
- * branch at that fork.
- */
-CEB_FDECL3(struct ceb_node *, cebus, _prev, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node)
-{
- const void *key = NODEK(node, kofs)->str;
-
- return _cebu_prev(root, kofs, CEB_KT_ST, 0, 0, key);
-}
-
-/* look up the specified node with its key and deletes it if found, and in any
- * case, returns the node.
- */
-CEB_FDECL3(struct ceb_node *, cebus, _delete, struct ceb_node **, root, ptrdiff_t, kofs, struct ceb_node *, node)
-{
- const void *key = NODEK(node, kofs)->str;
-
- return _cebu_delete(root, node, kofs, CEB_KT_ST, 0, 0, key);
-}
-
-/* look up the specified key, and detaches it and returns it if found, or NULL
- * if not found.
- */
-CEB_FDECL3(struct ceb_node *, cebus, _pick, struct ceb_node **, root, ptrdiff_t, kofs, const void *, key)
-{
- return _cebu_delete(root, NULL, kofs, CEB_KT_ST, 0, 0, key);
-}
-
-/* dumps a ceb_node tree using the default functions above. If a node matches
- * <ctx>, this one will be highlighted in red.
- */
-CEB_FDECL4(void, cebus, _default_dump, struct ceb_node **, root, ptrdiff_t, kofs, const char *, label, const void *, ctx)
-{
- printf("\ndigraph cebus_tree {\n"
- " fontname=\"fixed\";\n"
- " fontsize=8\n"
- " label=\"%s\"\n"
- "", label);
-
- printf(" node [fontname=\"fixed\" fontsize=8 shape=\"box\" style=\"filled\" color=\"black\" fillcolor=\"white\"];\n"
- " edge [fontname=\"fixed\" fontsize=8 style=\"solid\" color=\"magenta\" dir=\"forward\"];\n");
-
- cebu_default_dump_tree(kofs, CEB_KT_ST, root, 0, NULL, 0, ctx, NULL, NULL, NULL);
-
- printf("}\n");
-}
#include <sys/prctl.h>
#endif
-#include <import/cebus_tree.h>
+#include <import/cebs_tree.h>
#include <import/eb32sctree.h>
#include <import/eb32tree.h>
#include <import/ebmbtree.h>
/* known file names, made of file_name_node, to be used with file_name_*() */
struct {
- struct ceb_node *root; // file names tree, used with cebus_*()
+ struct ceb_root *root; // file names tree, used with cebus_*()
__decl_thread(HA_RWLOCK_T lock);
} file_names = { 0 };
return NULL;
HA_RWLOCK_RDLOCK(OTHER_LOCK, &file_names.lock);
- node = cebus_lookup(&file_names.root, name);
+ node = cebus_imm_lookup(&file_names.root, name);
HA_RWLOCK_RDUNLOCK(OTHER_LOCK, &file_names.lock);
if (node) {
memcpy(file->name, name, len + 1);
HA_RWLOCK_WRLOCK(OTHER_LOCK, &file_names.lock);
- node = cebus_insert(&file_names.root, &file->node);
+ node = cebus_imm_insert(&file_names.root, &file->node);
HA_RWLOCK_WRUNLOCK(OTHER_LOCK, &file_names.lock);
if (node != &file->node) {
HA_RWLOCK_WRLOCK(OTHER_LOCK, &file_names.lock);
- while ((node = cebus_first(&file_names.root))) {
+ while ((node = cebus_imm_first(&file_names.root))) {
file = container_of(node, struct file_name_node, node);
- cebus_delete(&file_names.root, node);
+ cebus_imm_delete(&file_names.root, node);
free(file);
}
#include <haproxy/vars.h>
#include <haproxy/xxhash.h>
-#include <import/cebu64_tree.h>
+#include <import/ceb64_tree.h>
/* This contains a pool of struct vars */
var->data.type = SMP_T_ANY;
if (!(var->flags & VF_PERMANENT) || force) {
- cebu64_delete(&vars->name_root[var->name_hash % VAR_NAME_ROOTS], &var->node);
+ cebu64_imm_delete(&vars->name_root[var->name_hash % VAR_NAME_ROOTS], &var->node);
pool_free(var_pool, var);
size += sizeof(struct var);
}
int i;
for (i = 0; i < VAR_NAME_ROOTS; i++) {
- while ((node = cebu64_first(&vars->name_root[i]))) {
+ while ((node = cebu64_imm_first(&vars->name_root[i]))) {
var = container_of(node, struct var, node);
size += var_clear(vars, var, 1);
}
{
struct ceb_node *node;
- node = cebu64_lookup(&vars->name_root[name_hash % VAR_NAME_ROOTS], name_hash);
+ node = cebu64_imm_lookup(&vars->name_root[name_hash % VAR_NAME_ROOTS], name_hash);
if (node)
return container_of(node, struct var, node);
var->name_hash = desc->name_hash;
var->flags = flags & VF_PERMANENT;
var->data.type = SMP_T_ANY;
- cebu64_insert(&vars->name_root[var->name_hash % VAR_NAME_ROOTS], &var->node);
+ cebu64_imm_insert(&vars->name_root[var->name_hash % VAR_NAME_ROOTS], &var->node);
}
/* A variable of type SMP_T_ANY is considered as unset (either created