From ddf900a0ced9d90e4211f2e5ad599b46ddf5f7b0 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Mon, 7 Jul 2025 10:58:21 +0200 Subject: [PATCH] IMPORT: cebtree: import version 0.5.0 to support duplicates The support for duplicates is necessary for various use cases related to config names, so let's upgrade to the latest version which brings this support. This updates the cebtree code to commit 808ed67 (tag 0.5.0). A few tiny adaptations were needed: - replace a few ceb_node** with ceb_root** since pointers are now tagged ; - replace cebu*.h with ceb*.h since both are now merged in the same include file. This way we can drop the unused cebu*.h files from cebtree that are provided only for compatibility. - rename immediate storage functions to cebXX_imm_XXX() as per the API change in 0.5 that makes immediate explicit rather than implicit. This only affects vars and tools.c:copy_file_name(). The tests continue to work. --- Makefile | 12 +- include/haproxy/vars-t.h | 2 +- include/haproxy/vars.h | 4 +- include/import/ceb32_tree.h | 426 +++++++ include/import/ceb64_tree.h | 426 +++++++ include/import/ceba_tree.h | 55 + include/import/cebb_tree.h | 419 +++++++ include/import/cebib_tree.h | 425 +++++++ include/import/cebis_tree.h | 431 +++++++ include/import/cebl_tree.h | 425 +++++++ include/import/cebs_tree.h | 425 +++++++ include/import/cebtree-prv.h | 2105 ++++++++++++++++++---------------- include/import/cebtree.h | 89 +- include/import/cebu32_tree.h | 58 - include/import/cebu64_tree.h | 58 - include/import/cebua_tree.h | 57 - include/import/cebub_tree.h | 55 - include/import/cebuib_tree.h | 55 - include/import/cebuis_tree.h | 57 - include/import/cebul_tree.h | 57 - include/import/cebus_tree.h | 57 - src/_ceb_addr.c | 190 +++ src/_ceb_blk.c | 342 ++++++ src/_ceb_int.c | 466 ++++++++ src/_ceb_str.c | 386 +++++++ src/ceb32_tree.c | 39 + src/ceb64_tree.c | 39 + src/ceba_tree.c | 37 + src/cebb_tree.c | 39 + src/cebib_tree.c | 39 + src/cebis_tree.c | 39 + src/cebl_tree.c | 39 + src/cebs_tree.c | 39 + src/cebtree-dbg.c | 401 +++++++ src/cebu32_tree.c | 162 --- src/cebu64_tree.c | 162 --- src/cebua_tree.c | 153 --- src/cebub_tree.c | 147 --- src/cebuib_tree.c | 147 --- src/cebuis_tree.c | 144 --- src/cebul_tree.c | 198 ---- src/cebus_tree.c | 163 --- src/tools.c | 12 +- src/vars.c | 10 +- 44 files changed, 6335 insertions(+), 2756 deletions(-) create mode 100644 include/import/ceb32_tree.h create mode 100644 include/import/ceb64_tree.h create mode 100644 include/import/ceba_tree.h create mode 100644 include/import/cebb_tree.h create mode 100644 include/import/cebib_tree.h create mode 100644 include/import/cebis_tree.h create mode 100644 include/import/cebl_tree.h create mode 100644 include/import/cebs_tree.h delete mode 100644 include/import/cebu32_tree.h delete mode 100644 include/import/cebu64_tree.h delete mode 100644 include/import/cebua_tree.h delete mode 100644 include/import/cebub_tree.h delete mode 100644 include/import/cebuib_tree.h delete mode 100644 include/import/cebuis_tree.h delete mode 100644 include/import/cebul_tree.h delete mode 100644 include/import/cebus_tree.h create mode 100644 src/_ceb_addr.c create mode 100644 src/_ceb_blk.c create mode 100644 src/_ceb_int.c create mode 100644 src/_ceb_str.c create mode 100644 src/ceb32_tree.c create mode 100644 src/ceb64_tree.c create mode 100644 src/ceba_tree.c create mode 100644 src/cebb_tree.c create mode 100644 src/cebib_tree.c create mode 100644 src/cebis_tree.c create mode 100644 src/cebl_tree.c create mode 100644 src/cebs_tree.c create mode 100644 src/cebtree-dbg.c delete mode 100644 src/cebu32_tree.c delete mode 100644 src/cebu64_tree.c delete mode 100644 src/cebua_tree.c delete mode 100644 src/cebub_tree.c delete mode 100644 src/cebuib_tree.c delete mode 100644 src/cebuis_tree.c delete mode 100644 src/cebul_tree.c delete mode 100644 src/cebus_tree.c diff --git a/Makefile b/Makefile index c710f3123..ba217f2bc 100644 --- a/Makefile +++ b/Makefile @@ -962,15 +962,15 @@ OBJS += src/mux_h2.o src/mux_h1.o src/mux_fcgi.o src/log.o \ 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 \ diff --git a/include/haproxy/vars-t.h b/include/haproxy/vars-t.h index 7a709bda2..157576585 100644 --- a/include/haproxy/vars-t.h +++ b/include/haproxy/vars-t.h @@ -50,7 +50,7 @@ enum vars_scope { #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); diff --git a/include/haproxy/vars.h b/include/haproxy/vars.h index bfadee5ed..bffeb2214 100644 --- a/include/haproxy/vars.h +++ b/include/haproxy/vars.h @@ -22,7 +22,7 @@ #ifndef _HAPROXY_VARS_H #define _HAPROXY_VARS_H -#include +#include #include #include @@ -86,7 +86,7 @@ static inline void vars_prune(struct vars *vars, struct session *sess, struct st 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); } diff --git a/include/import/ceb32_tree.h b/include/import/ceb32_tree.h new file mode 100644 index 000000000..c8522453e --- /dev/null +++ b/include/import/ceb32_tree.h @@ -0,0 +1,426 @@ +/* + * 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 + +/* 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 , the item using node member and + * key member , 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 , and return the first item of type , using node + * member and key member , 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 , and return the last item of type , using node + * member and key member , 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 , the first item of type which contains the + * key , using node member and key member . 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 , the item of type which contains the last of + * the greatest keys that are lower than or equal to , using node member + * and key member . 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 , the item of type which contains the last of + * the greatest keys that are strictly lower than , using node member + * and key member . 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 , the item of type which contains the first of + * the lowest keys key that are greater than or equal to , using node + * member and key member . 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 , the item of type which contains the first of + * the lowest keys that are strictly greater than , using node member + * and key member . 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 , the first item after item that has a + * strictly greater key, using node member and key member , 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 , the last item before item that has a + * strictly lower key, using node member and key member , 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 , the first item after item that has + * exactly the same key, using node member and key member , 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 , the last item before item that has + * exactly the same key, using node member and key member , 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 , the first item immediately after item , + * using node member and key member , 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 , the last item immediately before item , + * using node member and key member , 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 , the item using node member and + * key member , 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 , the first item of type which contains the + * key , using node member and key member , 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 , the item using node member and key + * member , 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 , and return the first item of type , using node + * member and key member , 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 , and return the last item of type , using node + * member and key member , 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 , the item of type which contains the key + * , using node member and key member . 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 , the item of type which contains the greatest + * key that is lower than or equal to , using node member and key + * member . 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 , the item of type which contains the greatest + * key that is strictly lower than , using node member and key + * member . 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 , the item of type which contains the lowest + * key key that is greater than or equal to , using node member + * and key member . 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 , the item of type which contains the lowest + * key that is strictly greater than , using node member and key + * member . 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 , the first item immediately after item , + * using node member and key member , 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 , the last item immediately before item , + * using node member and key member , 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 , the item using node member and + * key member , 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 , the first item of type which contains the + * key , using node member and key member , 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 */ diff --git a/include/import/ceb64_tree.h b/include/import/ceb64_tree.h new file mode 100644 index 000000000..94cbb046c --- /dev/null +++ b/include/import/ceb64_tree.h @@ -0,0 +1,426 @@ +/* + * 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 + +/* 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 , the item using node member and + * key member , 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 , and return the first item of type , using node + * member and key member , 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 , and return the last item of type , using node + * member and key member , 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 , the first item of type which contains the + * key , using node member and key member . 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 , the item of type which contains the last of + * the greatest keys that are lower than or equal to , using node member + * and key member . 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 , the item of type which contains the last of + * the greatest keys that are strictly lower than , using node member + * and key member . 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 , the item of type which contains the first of + * the lowest keys key that are greater than or equal to , using node + * member and key member . 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 , the item of type which contains the first of + * the lowest keys that are strictly greater than , using node member + * and key member . 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 , the first item after item that has a + * strictly greater key, using node member and key member , 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 , the last item before item that has a + * strictly lower key, using node member and key member , 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 , the first item after item that has + * exactly the same key, using node member and key member , 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 , the last item before item that has + * exactly the same key, using node member and key member , 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 , the first item immediately after item , + * using node member and key member , 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 , the last item immediately before item , + * using node member and key member , 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 , the item using node member and + * key member , 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 , the first item of type which contains the + * key , using node member and key member , 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 , the item using node member and key + * member , 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 , and return the first item of type , using node + * member and key member , 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 , and return the last item of type , using node + * member and key member , 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 , the item of type which contains the key + * , using node member and key member . 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 , the item of type which contains the greatest + * key that is lower than or equal to , using node member and key + * member . 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 , the item of type which contains the greatest + * key that is strictly lower than , using node member and key + * member . 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 , the item of type which contains the lowest + * key key that is greater than or equal to , using node member + * and key member . 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 , the item of type which contains the lowest + * key that is strictly greater than , using node member and key + * member . 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 , the first item immediately after item , + * using node member and key member , 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 , the last item immediately before item , + * using node member and key member , 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 , the item using node member and + * key member , 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 , the first item of type which contains the + * key , using node member and key member , 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 */ diff --git a/include/import/ceba_tree.h b/include/import/ceba_tree.h new file mode 100644 index 000000000..99d7d7015 --- /dev/null +++ b/include/import/ceba_tree.h @@ -0,0 +1,55 @@ +/* + * 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 */ diff --git a/include/import/cebb_tree.h b/include/import/cebb_tree.h new file mode 100644 index 000000000..6474c49e6 --- /dev/null +++ b/include/import/cebb_tree.h @@ -0,0 +1,419 @@ +/* + * 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 , the item using node member and key + * member , and length 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 , and return the first item of type , using node + * member and key member , 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 , and return the last item of type , using node + * member and key member , 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 , the first item of type which contains the + * key , using node member and key member . 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 , the item of type which contains the last of + * the greatest keys that are lower than or equal to , using node member + * and key member . 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 , the item of type which contains the last of + * the greatest keys that are strictly lower than , using node member + * and key member . 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 , the item of type which contains the first of + * the lowest keys key that are greater than or equal to , using node + * member and key member . 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 , the item of type which contains the first of + * the lowest keys that are strictly greater than , using node member + * and key member . 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 , the first item after item that has a + * strictly greater key, using node member and key member , 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 , the last item before item that has a + * strictly lower key, using node member and key member , 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 , the first item after item that has + * exactly the same key, using node member and key member , 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 , the last item before item that has + * exactly the same key, using node member and key member , 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 , the first item immediately after item , + * using node member and key member , 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 , the last item immediately before item , + * using node member and key member , 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 , the item using node member and + * key member , 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 , the first item of type which contains the + * key , using node member and key member , 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 , the item using node member and key + * member , 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 , and return the first item of type , using node + * member and key member , 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 , and return the last item of type , using node + * member and key member , 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 , the item of type which contains the key + * , using node member and key member . 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 , the item of type which contains the greatest + * key that is lower than or equal to , using node member and key + * member . 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 , the item of type which contains the greatest + * key that is strictly lower than , using node member and key + * member . 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 , the item of type which contains the lowest + * key key that is greater than or equal to , using node member + * and key member . 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 , the item of type which contains the lowest + * key that is strictly greater than , using node member and key + * member . 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 , the first item immediately after item , + * using node member and key member , 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 , the last item immediately before item , + * using node member and key member , 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 , the item using node member and + * key member , 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 , the first item of type which contains the + * key , using node member and key member , 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 */ diff --git a/include/import/cebib_tree.h b/include/import/cebib_tree.h new file mode 100644 index 000000000..75ee5b91d --- /dev/null +++ b/include/import/cebib_tree.h @@ -0,0 +1,425 @@ +/* + * 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 , the item using node member and key + * member , and length 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 , and return the first item of type , using node + * member and key member , 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 , and return the last item of type , using node + * member and key member , 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 , the first item of type which contains the + * key , using node member and key member . 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 , the item of type which contains the last of + * the greatest keys that are lower than or equal to , using node member + * and key member . 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 , the item of type which contains the last of + * the greatest keys that are strictly lower than , using node member + * and key member . 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 , the item of type which contains the first of + * the lowest keys key that are greater than or equal to , using node + * member and key member . 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 , the item of type which contains the first of + * the lowest keys that are strictly greater than , using node member + * and key member . 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 , the first item of type after item + * that has a strictly greater key, using node member and key member + * , 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 , the last item of type before item + * that has a strictly lower key, using node member and key member + * , 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 , the first item of type after item + * that has exactly the same key, using node member and key member + * , 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 , the last item of type before item + * that has exactly the same key, using node member and key member + * , 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 , the first item of type immediately after + * item , using node member and key member , 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 , the last item of type immediately before + * item , using node member and key member , 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 , the item using node member and + * key member , 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 , the first item of type which contains the + * key , using node member and key member , 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 , the item using node member and key + * member , 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 , and return the first item of type , using node + * member and key member , 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 , and return the last item of type , using node + * member and key member , 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 , the item of type which contains the key + * , using node member and key member . 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 , the item of type which contains the greatest + * key that is lower than or equal to , using node member and key + * member . 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 , the item of type which contains the greatest + * key that is strictly lower than , using node member and key + * member . 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 , the item of type which contains the lowest + * key key that is greater than or equal to , using node member + * and key member . 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 , the item of type which contains the lowest + * key that is strictly greater than , using node member and key + * member . 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 , the first item of type immediately after + * item , using node member and key member , 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 , the last item of type immediately before + * item , using node member and key member , 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 , the item using node member and + * key member , 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 , the first item of type which contains the + * key , using node member and key member , 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 */ diff --git a/include/import/cebis_tree.h b/include/import/cebis_tree.h new file mode 100644 index 000000000..5d6fc1193 --- /dev/null +++ b/include/import/cebis_tree.h @@ -0,0 +1,431 @@ +/* + * 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 , the item using node member and key + * member , 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 , and return the first item of type , using node + * member and key member , 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 , and return the last item of type , using node + * member and key member , 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 , the first item of type which contains the + * key , using node member and key member . 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 , the item of type which contains the last of + * the greatest keys that are lower than or equal to , using node member + * and key member . 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 , the item of type which contains the last of + * the greatest keys that are strictly lower than , using node member + * and key member . 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 , the item of type which contains the first of + * the lowest keys key that are greater than or equal to , using node + * member and key member . 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 , the item of type which contains the first of + * the lowest keys that are strictly greater than , using node member + * and key member . 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 , the first item after item that has a + * strictly greater key, using node member and key member , 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 , the last item before item that has a + * strictly lower key, using node member and key member , 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 , the first item after item that has + * exactly the same key, using node member and key member , 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 , the last item before item that has + * exactly the same key, using node member and key member , 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 , the first item immediately after item , + * using node member and key member , 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 , the last item immediately before item , + * using node member and key member , 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 , the item using node member and + * key member , 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 , the first item of type which contains the + * key , using node member and key member , 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 , the item using node member and key + * member , 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 , and return the first item of type , using node + * member and key member , 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 , and return the last item of type , using node + * member and key member , 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 , the item of type which contains the key + * , using node member and key member . 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 , the item of type which contains the greatest + * key that is lower than or equal to , using node member and key + * member . 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 , the item of type which contains the greatest + * key that is strictly lower than , using node member and key + * member . 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 , the item of type which contains the lowest + * key key that is greater than or equal to , using node member + * and key member . 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 , the item of type which contains the lowest + * key that is strictly greater than , using node member and key + * member . 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 , the first item immediately after item , + * using node member and key member , 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 , the last item immediately before item , + * using node member and key member , 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 , the item using node member and + * key member , 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 , the first item of type which contains the + * key , using node member and key member , 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 */ diff --git a/include/import/cebl_tree.h b/include/import/cebl_tree.h new file mode 100644 index 000000000..1ba4102e7 --- /dev/null +++ b/include/import/cebl_tree.h @@ -0,0 +1,425 @@ +/* + * 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 , the item using node member and key + * member , 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 , and return the first item of type , using node + * member and key member , 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 , and return the last item of type , using node + * member and key member , 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 , the first item of type which contains the + * key , using node member and key member . 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 , the item of type which contains the last of + * the greatest keys that are lower than or equal to , using node member + * and key member . 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 , the item of type which contains the last of + * the greatest keys that are strictly lower than , using node member + * and key member . 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 , the item of type which contains the first of + * the lowest keys key that are greater than or equal to , using node + * member and key member . 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 , the item of type which contains the first of + * the lowest keys that are strictly greater than , using node member + * and key member . 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 , the first item after item that has a + * strictly greater key, using node member and key member , 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 , the last item before item that has a + * strictly lower key, using node member and key member , 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 , the first item after item that has + * exactly the same key, using node member and key member , 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 , the last item before item that has + * exactly the same key, using node member and key member , 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 , the first item immediately after item , + * using node member and key member , 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 , the last item immediately before item , + * using node member and key member , 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 , the item using node member and + * key member , 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 , the first item of type which contains the + * key , using node member and key member , 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 , the item using node member and key + * member , 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 , and return the first item of type , using node + * member and key member , 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 , and return the last item of type , using node + * member and key member , 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 , the item of type which contains the key + * , using node member and key member . 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 , the item of type which contains the greatest + * key that is lower than or equal to , using node member and key + * member . 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 , the item of type which contains the greatest + * key that is strictly lower than , using node member and key + * member . 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 , the item of type which contains the lowest + * key key that is greater than or equal to , using node member + * and key member . 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 , the item of type which contains the lowest + * key that is strictly greater than , using node member and key + * member . 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 , the first item immediately after item , + * using node member and key member , 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 , the last item immediately before item , + * using node member and key member , 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 , the item using node member and + * key member , 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 , the first item of type which contains the + * key , using node member and key member , 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 */ diff --git a/include/import/cebs_tree.h b/include/import/cebs_tree.h new file mode 100644 index 000000000..0c1f0b657 --- /dev/null +++ b/include/import/cebs_tree.h @@ -0,0 +1,425 @@ +/* + * 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 , the item using node member and key + * member , 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 , and return the first item of type , using node + * member and key member , 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 , and return the last item of type , using node + * member and key member , 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 , the first item of type which contains the + * key , using node member and key member . 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 , the item of type which contains the last of + * the greatest keys that are lower than or equal to , using node member + * and key member . 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 , the item of type which contains the last of + * the greatest keys that are strictly lower than , using node member + * and key member . 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 , the item of type which contains the first of + * the lowest keys key that are greater than or equal to , using node + * member and key member . 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 , the item of type which contains the first of + * the lowest keys that are strictly greater than , using node member + * and key member . 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 , the first item after item that has a + * strictly greater key, using node member and key member , 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 , the last item before item that has a + * strictly lower key, using node member and key member , 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 , the first item after item that has + * exactly the same key, using node member and key member , 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 , the last item before item that has + * exactly the same key, using node member and key member , 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 , the first item immediately after item , + * using node member and key member , 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 , the last item immediately before item , + * using node member and key member , 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 , the item using node member and + * key member , 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 , the first item of type which contains the + * key , using node member and key member , 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 , the item using node member and key + * member , 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 , and return the first item of type , using node + * member and key member , 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 , and return the last item of type , using node + * member and key member , 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 , the item of type which contains the key + * , using node member and key member . 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 , the item of type which contains the greatest + * key that is lower than or equal to , using node member and key + * member . 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 , the item of type which contains the greatest + * key that is strictly lower than , using node member and key + * member . 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 , the item of type which contains the lowest + * key key that is greater than or equal to , using node member + * and key member . 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 , the item of type which contains the lowest + * key that is strictly greater than , using node member and key + * member . 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 , the first item immediately after item , + * using node member and key member , 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 , the last item immediately before item , + * using node member and key member , 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 , the item using node member and + * key member , 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 , the first item of type which contains the + * key , using node member and key member , 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 */ diff --git a/include/import/cebtree-prv.h b/include/import/cebtree-prv.h index 0309e7891..221968af5 100644 --- a/include/import/cebtree-prv.h +++ b/include/import/cebtree-prv.h @@ -1,7 +1,7 @@ /* * 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 @@ -63,7 +63,7 @@ * 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 @@ -83,23 +83,296 @@ #ifndef _CEBTREE_PRV_H #define _CEBTREE_PRV_H +#include #include +#include #include #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__) -#else -#define CEBDBG(x, ...) do { } while (0) +/* 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_ macro so that we'll + * later be able to implement on a per-compiler basis those which are missing, + * by defining __has_attribute_ 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_ 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 and byte-to-byte, from bit to bit . + * Return the number of equal bits between strings, assuming that the first + * bits are already identical. It is possible to return slightly more + * than bits if does not stop on a byte boundary and we find exact + * bytes. Note that parts or all of 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 and that 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 and byte-to-byte, from bit to the last 0. + * Return the number of equal bits between strings, assuming that the first + * bits are already identical. Note that parts or all of 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 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 +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 . + * 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 (root, ...) + * adjacent keys ; these ones are named (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 @@ -108,45 +381,82 @@ * 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) */ @@ -185,180 +495,6 @@ union ceb_key_storage { /* returns the ceb_key_storage pointer for node and offset */ #define NODEK(n, o) ((union ceb_key_storage*)(((char *)(n)) + (o))) -/* Returns the xor (or common length) between the two sides and 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. - * - * 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. @@ -370,26 +506,30 @@ static void dbg(int line, * 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 @@ -397,30 +537,30 @@ struct ceb_node *_cebu_descend(struct ceb_node **root, #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) { @@ -435,38 +575,67 @@ struct ceb_node *_cebu_descend(struct ceb_node **root, 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. */ - while (1) { - p = *root; + node = _ceb_clrtag(*root); + is_leaf = _ceb_gettag(*root); - /* 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); - } + if (ret_lpside) { + /* this is a deletion, benefits from prefetching */ + __builtin_prefetch(node->b[0], 0); + __builtin_prefetch(node->b[1], 0); + } + + while (1) { + union ceb_key_storage *lks, *rks; + struct ceb_node *ln, *rn, *next; + struct ceb_root *lr, *rr; + int next_leaf, lnl, rnl; + + lr = node->b[0]; // tagged versions + rr = node->b[1]; + + /* get a copy of the corresponding nodes */ + lnl = _ceb_gettag(lr); + ln = _ceb_clrtag(lr); + rnl = _ceb_gettag(rr); + rn = _ceb_clrtag(rr); /* neither pointer is tagged */ - k = NODEK(p, kofs); - l = NODEK(p->b[0], kofs); - r = NODEK(p->b[1], kofs); + k = NODEK(node, kofs); - dbg(__LINE__, "newp", meth, kofs, key_type, root, p, key_u32, key_u64, key_ptr, pxor32, pxor64, plen); - - /* 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: @@ -509,40 +678,39 @@ struct ceb_node *_cebu_descend(struct ceb_node **root, * - 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. 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_nparent && !*ret_nparent && ret_npside) { + if (key_u32 == k->u32) { + *ret_nparent = lparent; + *ret_npside = lpside; + } } - if (ret_npside || ret_nparent) { - 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; - } + /* 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; @@ -551,288 +719,215 @@ struct ceb_node *_cebu_descend(struct ceb_node **root, 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_nparent && !*ret_nparent && ret_npside) { + if (key_u64 == k->u64) { + *ret_nparent = lparent; + *ret_npside = lpside; + } } - if (ret_npside || ret_nparent) { - 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; - } + /* 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 - - 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; - } - - 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); - 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; - } - } - } - plen = xlen; - } - else if (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); - 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; - } - - 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); - 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->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; - } - } - } - plen = xlen; - } - else if (key_type == CEB_KT_ST) { - 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->str, 0); - rlen = string_equal_bits(key_ptr, r->str, 0); - brside = (size_t)llen <= (size_t)rlen; - if ((ssize_t)llen < 0 || (ssize_t)rlen < 0) - found = 1; - } - - 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; - } - - 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; - } - - if (ret_npside || ret_nparent) { // delete ? - size_t mlen = llen > rlen ? llen : rlen; - - if (mlen > xlen) - mlen = xlen; - - 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; - } - - 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; - } - - 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; - } - - if (ret_npside || ret_nparent) { // delete ? - size_t mlen = llen > rlen ? llen : rlen; - - if (mlen > xlen) - mlen = xlen; - - 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; - } - + kl = (uintptr_t)lks; kr = (uintptr_t)rks; if (meth >= CEB_WM_KEQ) { - /* "found" is not used here */ 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 (kl > xoraddr && kr > xoraddr) { - 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_nparent && !*ret_nparent && ret_npside) { + if ((uintptr_t)key_ptr == (uintptr_t)node) { + *ret_nparent = lparent; + *ret_npside = lpside; + } } - 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; - } + /* 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; } } pxor64 = xoraddr; } + 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, (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; + } + + 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) + break; + + if (ret_nparent && ret_npside && !*ret_nparent && + ((llen == key_u64 << 3) || (rlen == key_u64 << 3))) { + *ret_nparent = node; + *ret_npside = brside; + } + + /* 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 || 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, (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 (ret_nparent && ret_npside && !*ret_nparent && + ((ssize_t)llen < 0 || (ssize_t)rlen < 0)) { + *ret_nparent = node; + *ret_npside = brside; + } + + /* 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; + } + } + } + + /* the compiler cannot know this never happens and this helps it optimize the code */ + if ((ssize_t)plen < 0) + __builtin_unreachable(); + + xlen = string_equal_bits((key_type == CEB_KT_ST) ? lks->str : lks->ptr, + (key_type == CEB_KT_ST) ? rks->str : rks->ptr, plen); + + /* let's stop if our key is not there */ + if (meth >= CEB_WM_KEQ && llen < xlen && rlen < xlen) + break; + + plen = xlen; + } /* shift all copies by one */ gparent = lparent; gpside = lpside; - lparent = p; + lparent = node; lpside = brside; if (brside) { if (meth == CEB_WM_KPR || meth == CEB_WM_KLE || meth == CEB_WM_KLT) - bnode = p; - root = &p->b[1]; + bnode = node; + next = rn; + next_leaf = rnl; + root = &node->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]; + bnode = node; + next = ln; + next_leaf = lnl; + root = &node->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); + if (next == node) { + /* loops over itself, it's either a leaf or the single and last list element of a dup sub-tree */ break; } + + /* let the compiler know there's no NULL in the tree */ + if (!next) + __builtin_unreachable(); + + node = next; + is_leaf = next_leaf; + } + + 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; + } } /* here we're on the closest node from the requested value. It may be @@ -841,17 +936,6 @@ struct ceb_node *_cebu_descend(struct ceb_node **root, * 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) { @@ -861,39 +945,38 @@ struct ceb_node *_cebu_descend(struct ceb_node **root, 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; - break; - 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; - 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 = (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, ((key_type == CEB_KT_MB) ? k->mb : k->ptr) + plen / 8, key_u64 - plen / 8) >= 0; + break; + + case CEB_KT_ST: + case CEB_KT_IS: + *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; @@ -902,9 +985,7 @@ struct ceb_node *_cebu_descend(struct ceb_node **root, *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, @@ -920,7 +1001,7 @@ struct ceb_node *_cebu_descend(struct ceb_node **root, (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) || @@ -930,90 +1011,56 @@ struct ceb_node *_cebu_descend(struct ceb_node **root, (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; - } - 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_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); - - 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_ST) { - int diff; - - if (found) - diff = 0; - else - diff = strcmp((const void *)k->str + 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_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; + return node; } 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; + 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_MB || key_type == CEB_KT_IM) { + int diff; + + if ((uint64_t)plen / 8 == key_u64) + diff = 0; + else + 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_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 node; + } + else if (key_type == CEB_KT_ST || key_type == CEB_KT_IS) { + int diff; + + if ((ssize_t)plen < 0) + diff = 0; + else + 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_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 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 */ @@ -1023,37 +1070,43 @@ struct ceb_node *_cebu_descend(struct ceb_node **root, * 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 * into tree , with key type and key . * Returns the inserted node or the one that already contains the same key. + * If 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 @@ -1062,44 +1115,75 @@ struct ceb_node *_cebu_insert(struct ceb_node **root, * 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 . + * type , and optionally 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 . + * type , and optionally 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 made of keys of type , for the next @@ -1110,25 +1194,26 @@ struct ceb_node *_cebu_last(struct ceb_node **root, * 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 made of keys of type , for the prev @@ -1139,156 +1224,373 @@ struct ceb_node *_cebu_next(struct ceb_node **root, * 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 made of keys of type , for the node - * containing the key . Returns NULL if not found. +/* Searches in the tree made of keys of type , for the next + * node after also containing key . Returns NULL if not found. + * It's up to the caller to pass the current node's key in . */ 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) { - 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); -} - -/* Searches in the tree made of keys of type , for the node - * containing the 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_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 *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_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 made of keys of type , for the prev + * node before also containing key . Returns NULL if not found. + * It's up to the caller to pass the current node's key in . + */ +static inline __attribute__((always_inline)) +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 *node; + int is_dup; + + if (!*root) + return NULL; + + 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 made of keys of type , for the next + * node after which contains key . Returns NULL if not found. + * It's up to the caller to pass the current node's key in . 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 made of keys of type , for the node - * containing the greatest key that is strictly lower than . 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 made of keys of type , for the prev + * node before the one containing the key . Returns NULL if not found. + * It's up to the caller to pass the current node's key in . 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 made of keys of type , for the node - * containing the key or the smallest one that's greater than it. +/* Searches in the tree made of keys of type , for the first + * node containing the 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 made of keys of type , for the last + * node containing the 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 made of keys of type , for the node - * containing the lowest key that is strictly greater than . 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 made of keys of type , for the last + * node containing the greatest key that is strictly lower than . + * 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 made of keys of type , for the first + * node containing the key or the smallest one that's greater than it. + * Returns NULL if not found. If 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 made of keys of type , for the first + * node containing the lowest key that is strictly greater than . Returns + * NULL if not found. It's very similar to prev() except that the looked up + * value doesn't need to exist. If 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 made of keys of type , for the node @@ -1298,16 +1600,18 @@ struct ceb_node *_cebu_lookup_gt(struct ceb_node **root, * 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 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; @@ -1323,14 +1627,85 @@ struct ceb_node *_cebu_delete(struct ceb_node **root, 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 @@ -1354,7 +1729,7 @@ struct ceb_node *_cebu_delete(struct ceb_node **root, /* 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; } @@ -1364,7 +1739,7 @@ struct ceb_node *_cebu_delete(struct ceb_node **root, */ 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 */ @@ -1374,239 +1749,19 @@ done: 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 */ diff --git a/include/import/cebtree.h b/include/import/cebtree.h index 25d3853dd..5ebb9abae 100644 --- a/include/import/cebtree.h +++ b/include/import/cebtree.h @@ -28,56 +28,83 @@ #define _CEBTREE_H #include -#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 + * which has its member stored at address . + */ +#ifndef container_of +#define container_of(ptr, type, name) ((type *)(((char *)(ptr)) - offsetof(type, name))) +#endif + +/* returns a pointer to the structure of type which has its member + * stored at address , unless 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 */ }; +/* initializes a root as an empty tree */ +static inline void ceb_init_root(struct ceb_root **root) +{ + *root = NULL; +} + +/* initializes a node as not belonging to a tree */ +static inline void ceb_init_node(struct ceb_node *node) +{ + node->b[0] = NULL; +} + /* indicates whether a valid node is in a tree or not */ static inline int ceb_intree(const struct ceb_node *node) { return !!node->b[0]; } -/* tag an untagged pointer */ -static inline struct ceb_node *__ceb_dotag(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 + 1); + return !*root; } -/* untag a tagged pointer */ -static inline struct ceb_node *__ceb_untag(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 (struct ceb_node *)((size_t)node - 1); + return (void*)((char *)node + kofs); } -/* clear a pointer's tag */ -static inline struct ceb_node *__ceb_clrtag(const struct ceb_node *node) +/* 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 (struct ceb_node *)((size_t)node & ~((size_t)1)); + return node ? _ceb_key_ptr(node, kofs) : NULL; } -/* returns whether a pointer is tagged */ -static inline int __ceb_tagged(const struct ceb_node *node) -{ - return !!((size_t)node & 1); -} - -/* returns an integer equivalent of the pointer */ -static inline size_t __ceb_intptr(struct ceb_node *tree) -{ - return (size_t)tree; -} - -///* 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 */ diff --git a/include/import/cebu32_tree.h b/include/import/cebu32_tree.h deleted file mode 100644 index 5972a0a26..000000000 --- a/include/import/cebu32_tree.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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 - -/* 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); diff --git a/include/import/cebu64_tree.h b/include/import/cebu64_tree.h deleted file mode 100644 index 15f8c0f8a..000000000 --- a/include/import/cebu64_tree.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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 - -/* 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); diff --git a/include/import/cebua_tree.h b/include/import/cebua_tree.h deleted file mode 100644 index 86758b599..000000000 --- a/include/import/cebua_tree.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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); diff --git a/include/import/cebub_tree.h b/include/import/cebub_tree.h deleted file mode 100644 index 55a0be044..000000000 --- a/include/import/cebub_tree.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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); diff --git a/include/import/cebuib_tree.h b/include/import/cebuib_tree.h deleted file mode 100644 index 6d6c2c1ea..000000000 --- a/include/import/cebuib_tree.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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); diff --git a/include/import/cebuis_tree.h b/include/import/cebuis_tree.h deleted file mode 100644 index 377bb85a9..000000000 --- a/include/import/cebuis_tree.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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); diff --git a/include/import/cebul_tree.h b/include/import/cebul_tree.h deleted file mode 100644 index 2080b6e19..000000000 --- a/include/import/cebul_tree.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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); diff --git a/include/import/cebus_tree.h b/include/import/cebus_tree.h deleted file mode 100644 index f88f1d6da..000000000 --- a/include/import/cebus_tree.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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); diff --git a/src/_ceb_addr.c b/src/_ceb_addr.c new file mode 100644 index 000000000..cced39f39 --- /dev/null +++ b/src/_ceb_addr.c @@ -0,0 +1,190 @@ +/* + * 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 into unique 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 +#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 + * , this one will be highlighted in red. If the 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 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 */ diff --git a/src/_ceb_blk.c b/src/_ceb_blk.c new file mode 100644 index 000000000..d3683c929 --- /dev/null +++ b/src/_ceb_blk.c @@ -0,0 +1,342 @@ +/* + * 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 into tree based on its key that immediately + * follows the node and for 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 of length , 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 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 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 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 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 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 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 into unique tree based on its key that + * immediately follows the node and for 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 of length , 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 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 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 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 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); +} diff --git a/src/_ceb_int.c b/src/_ceb_int.c new file mode 100644 index 000000000..086313f6a --- /dev/null +++ b/src/_ceb_int.c @@ -0,0 +1,466 @@ +/* + * 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 into 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 into unique 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 +#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 + * , this one will be highlighted in red. If the 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 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 */ diff --git a/src/_ceb_str.c b/src/_ceb_str.c new file mode 100644 index 000000000..f188abd28 --- /dev/null +++ b/src/_ceb_str.c @@ -0,0 +1,386 @@ +/* + * 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 +#include +#include +#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 into 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 into unique 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 +#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 + * , this one will be highlighted in red. If the 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 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 */ diff --git a/src/ceb32_tree.c b/src/ceb32_tree.c new file mode 100644 index 000000000..5be8ac4df --- /dev/null +++ b/src/ceb32_tree.c @@ -0,0 +1,39 @@ +/* + * 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" diff --git a/src/ceb64_tree.c b/src/ceb64_tree.c new file mode 100644 index 000000000..17eeb4413 --- /dev/null +++ b/src/ceb64_tree.c @@ -0,0 +1,39 @@ +/* + * 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" diff --git a/src/ceba_tree.c b/src/ceba_tree.c new file mode 100644 index 000000000..30c34cf76 --- /dev/null +++ b/src/ceba_tree.c @@ -0,0 +1,37 @@ +/* + * 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" diff --git a/src/cebb_tree.c b/src/cebb_tree.c new file mode 100644 index 000000000..57b78f755 --- /dev/null +++ b/src/cebb_tree.c @@ -0,0 +1,39 @@ +/* + * 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" diff --git a/src/cebib_tree.c b/src/cebib_tree.c new file mode 100644 index 000000000..da06d2cf3 --- /dev/null +++ b/src/cebib_tree.c @@ -0,0 +1,39 @@ +/* + * 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" diff --git a/src/cebis_tree.c b/src/cebis_tree.c new file mode 100644 index 000000000..97d73fa8f --- /dev/null +++ b/src/cebis_tree.c @@ -0,0 +1,39 @@ +/* + * 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" diff --git a/src/cebl_tree.c b/src/cebl_tree.c new file mode 100644 index 000000000..ba4128a45 --- /dev/null +++ b/src/cebl_tree.c @@ -0,0 +1,39 @@ +/* + * 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" diff --git a/src/cebs_tree.c b/src/cebs_tree.c new file mode 100644 index 000000000..e4e84d9e6 --- /dev/null +++ b/src/cebs_tree.c @@ -0,0 +1,39 @@ +/* + * 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" diff --git a/src/cebtree-dbg.c b/src/cebtree-dbg.c new file mode 100644 index 000000000..b82baa009 --- /dev/null +++ b/src/cebtree-dbg.c @@ -0,0 +1,401 @@ +/* + * 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 +#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 and 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. + * + * 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 */ diff --git a/src/cebu32_tree.c b/src/cebu32_tree.c deleted file mode 100644 index b1598a566..000000000 --- a/src/cebu32_tree.c +++ /dev/null @@ -1,162 +0,0 @@ -/* - * 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 -#include -#include -#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 into unique 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 - * , 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"); -} diff --git a/src/cebu64_tree.c b/src/cebu64_tree.c deleted file mode 100644 index 0b6729ad3..000000000 --- a/src/cebu64_tree.c +++ /dev/null @@ -1,162 +0,0 @@ -/* - * 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 -#include -#include -#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 into unique 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 - * , 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"); -} diff --git a/src/cebua_tree.c b/src/cebua_tree.c deleted file mode 100644 index ca2665e71..000000000 --- a/src/cebua_tree.c +++ /dev/null @@ -1,153 +0,0 @@ -/* - * 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 -#include -#include -#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 into unique 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 - * , 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"); -} diff --git a/src/cebub_tree.c b/src/cebub_tree.c deleted file mode 100644 index f11a507ff..000000000 --- a/src/cebub_tree.c +++ /dev/null @@ -1,147 +0,0 @@ -/* - * 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 -#include -#include -#include -#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 into unique tree based on its key that - * immediately follows the node and for 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 of length , 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 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 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 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 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); -} diff --git a/src/cebuib_tree.c b/src/cebuib_tree.c deleted file mode 100644 index 2aa6f8ba3..000000000 --- a/src/cebuib_tree.c +++ /dev/null @@ -1,147 +0,0 @@ -/* - * 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 -#include -#include -#include -#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 into unique tree based on its key whose pointer - * immediately follows the node and for 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 of length , 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 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 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 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 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); -} diff --git a/src/cebuis_tree.c b/src/cebuis_tree.c deleted file mode 100644 index 300218da6..000000000 --- a/src/cebuis_tree.c +++ /dev/null @@ -1,144 +0,0 @@ -/* - * 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 -#include -#include -#include -#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 into unique 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); -} diff --git a/src/cebul_tree.c b/src/cebul_tree.c deleted file mode 100644 index d1c2ad08b..000000000 --- a/src/cebul_tree.c +++ /dev/null @@ -1,198 +0,0 @@ -/* - * 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 -#include -#include -#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 into unique 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 - * , 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"); -} diff --git a/src/cebus_tree.c b/src/cebus_tree.c deleted file mode 100644 index a981d3afc..000000000 --- a/src/cebus_tree.c +++ /dev/null @@ -1,163 +0,0 @@ -/* - * 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 -#include -#include -#include -#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 into unique 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 - * , 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"); -} diff --git a/src/tools.c b/src/tools.c index e6a076770..929d93512 100644 --- a/src/tools.c +++ b/src/tools.c @@ -58,7 +58,7 @@ extern void *__elf_aux_vector; #include #endif -#include +#include #include #include #include @@ -127,7 +127,7 @@ int build_is_static = 0; /* 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 }; @@ -7254,7 +7254,7 @@ const char *copy_file_name(const char *name) 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) { @@ -7269,7 +7269,7 @@ const char *copy_file_name(const char *name) 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) { @@ -7288,9 +7288,9 @@ void free_all_file_names() 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); } diff --git a/src/vars.c b/src/vars.c index 5cef93586..8e7ac8239 100644 --- a/src/vars.c +++ b/src/vars.c @@ -20,7 +20,7 @@ #include #include -#include +#include /* This contains a pool of struct vars */ @@ -190,7 +190,7 @@ unsigned int var_clear(struct vars *vars, struct var *var, int force) 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); } @@ -208,7 +208,7 @@ void vars_prune_per_sess(struct vars *vars) 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); } @@ -336,7 +336,7 @@ static struct var *var_get(struct vars *vars, uint64_t name_hash) { 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); @@ -439,7 +439,7 @@ int var_set(const struct var_desc *desc, struct sample *smp, uint flags) 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