Commit ab264d5f authored by Gilles Chanteperdrix's avatar Gilles Chanteperdrix Committed by Philippe Gerum
Browse files

boilerplate/avl: merge pshared support for AVL trees

Make the AVL tree usable in shared memory when AVL_SHARED is defined
at build time, switching to offset-based memory references.

Gilles published this code in July 2016 as part of his personal
toolkit for hobby projects aka 'libchutils'.
parent fa5a45c4
......@@ -23,276 +23,444 @@
#ifndef _BOILERPLATE_AVL_H
#define _BOILERPLATE_AVL_H
#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
struct avlh {
unsigned int thr: 3;
#define AVLH_APP_BITS 28
unsigned int flags: AVLH_APP_BITS;
int type: 2;
int balance :2;
unsigned int flags :25; /* Application-specific */
struct avlh *link[3];
int balance: 2;
union {
ptrdiff_t offset;
struct avlh *ptr;
} link[3];
};
/* Using -1 and 1 for left and right is slightly faster than 0 and 1, using 0
for "up" is just here for orthogonality... and avoid wasting 4 bytes or
having to use a union in struct avlh. */
#define AVL_LEFT -1
#define AVL_UP 0
#define AVL_RIGHT 1
/* maps AVL_LEFT to AVL_RIGHT and reciprocally. */
#define avl_opposite(type) (-(type))
/* maps AVL_LEFT to -1 and AVL_RIGHT to 1. */
#define avl_type2sign(type) (type)
/* maps AVL_LEFT and AVL_RIGHT to arrays index (or bit positions). */
#define avl_type2index(type) ((type)+1)
/* maps <0 to AVL_LEFT and >0 to AVL_RIGHT. */
#define avl_sign2type(sign) (sign)
#define AVL_THR_LEFT (1<<avl_type2index(AVL_LEFT))
#define AVL_THR_RIGHT (1<<avl_type2index(AVL_RIGHT))
#define avlh_thr_set(holder, side) ((holder)->thr |= 1 << avl_type2index(side))
#define avlh_thr_clr(holder, side) ((holder)->thr &= ~(1 << avl_type2index(side)))
#define avlh_thr_tst(holder, side) ((holder)->thr & (1 << avl_type2index(side)))
#define avlh_link(holder, dir) ((holder)->link[avl_type2index(dir)])
#define avlh_up(holder) avlh_link((holder), AVL_UP)
#define avlh_left(holder) avlh_link((holder), AVL_LEFT)
#define avlh_right(holder) avlh_link((holder), AVL_RIGHT)
#define avlh_parent_link(holder) (avlh_link(avlh_up(holder), (holder)->type))
struct avl;
typedef struct avlh *avl_search_t(const struct avl *, const struct avlh *, int *);
/*
* Comparison function: should return -1 if left is less than right, 0
* if they are equal and 1 if left is greather than right. You can use
* the avl_sign function which will convert a difference to -1, 0,
* 1. Beware of overflow however. You can also use avl_cmp_sign()
* which should not have any such problems.
*/
typedef int avlh_cmp_t(const struct avlh *const, const struct avlh *const);
typedef struct avlh *
avl_search_t(const struct avl *, const struct avlh *, int *, int);
typedef int avlh_prn_t(char *, size_t, const struct avlh *const);
struct avl {
struct avlh anchor;
avl_search_t *search;
avlh_cmp_t *cmp;
struct avlh *end[3];
union {
ptrdiff_t offset;
struct avlh *ptr;
} end[3];
unsigned int count;
unsigned int height;
};
#define avl_searchfn(avl) ((avl)->search)
#define avl_cmp(avl) ((avl)->cmp)
#define avl_count(avl) ((avl)->count)
#define avl_height(avl) ((avl)->height)
#define avl_anchor(avl) (&(avl)->anchor)
#define avl_end(avl, dir) ((avl)->end[avl_type2index(dir)])
#define avl_top(avl) (avlh_right(avl_anchor(avl)))
#define avl_head(avl) (avl_end((avl), AVL_LEFT))
#define avl_tail(avl) (avl_end((avl), AVL_RIGHT))
#define AVL_LEFT -1
#define AVL_UP 0
#define AVL_RIGHT 1
/* maps AVL_LEFT to AVL_RIGHT and reciprocally. */
#define avl_opposite(type) (-(type))
/* maps AVL_LEFT and AVL_RIGHT to arrays index (or bit positions). */
#define avl_type2index(type) ((type)+1)
#ifdef __cplusplus
extern "C" {
#endif
#define AVL_THR_LEFT (1 << avl_type2index(AVL_LEFT))
#define AVL_THR_RIGHT (1 << avl_type2index(AVL_RIGHT))
void avl_init(struct avl *avl, avl_search_t *search, avlh_cmp_t *cmp);
#define avlh_up(avl, holder) avlh_link((avl), (holder), AVL_UP)
#define avlh_left(avl, holder) avlh_link((avl), (holder), AVL_LEFT)
#define avlh_right(avl, holder) avlh_link((avl), (holder), AVL_RIGHT)
void avl_destroy(struct avl *avl);
#define avlh_thr_tst(avl, holder, side) (avlh_link(avl, holder, side) == NULL)
#define avlh_child(avl, holder, side) (avlh_link((avl),(holder),(side)))
#define avlh_has_child(avl, holder, side) (!avlh_thr_tst(avl, holder, side))
void avl_clear(struct avl *avl, void (*destruct)(struct avlh *));
#define avl_searchfn(avl) ((avl)->search)
#define avl_cmp(avl) ((avl)->cmp)
#define avl_count(avl) ((avl)->count)
#define avl_height(avl) ((avl)->height)
#define avl_anchor(avl) (&(avl)->anchor)
#define avl_top(avl) (avlh_right(avl, avl_anchor(avl)))
#define avl_head(avl) (avl_end((avl), AVL_LEFT))
#define avl_tail(avl) (avl_end((avl), AVL_RIGHT))
int avl_insert(struct avl *avl, struct avlh *holder);
/*
* From "Bit twiddling hacks", returns v < 0 ? -1 : (v > 0 ? 1 : 0)
*/
#define avl_sign(v) \
({ \
typeof(v) _v = (v); \
((_v) > 0) - ((_v) < 0); \
})
int avl_prepend(struct avl *avl, struct avlh *holder);
/*
* Variation on the same theme.
*/
#define avl_cmp_sign(l, r) \
({ \
typeof(l) _l = (l); \
typeof(r) _r = (r); \
(_l > _r) - (_l < _r); \
})
#ifdef AVL_PSHARED
static inline struct avlh *
avlh_link(const struct avl *const avl,
const struct avlh *const holder, unsigned int dir)
{
return (void *)avl + holder->link[avl_type2index(dir)].offset;
}
int avl_append(struct avl *avl, struct avlh *holder);
static inline void
avlh_set_link(struct avl *const avl, struct avlh *lhs, int dir, struct avlh *rhs)
{
lhs->link[avl_type2index(dir)].offset = (void *)rhs - (void *)avl;
}
struct avlh *avl_update(struct avl *avl, struct avlh *holder);
static inline struct avlh *avl_end(const struct avl *const avl, int dir)
{
return (void *)avl + avl->end[avl_type2index(dir)].offset;
}
struct avlh *avl_set(struct avl *avl, struct avlh *holder);
static inline void
avl_set_end(struct avl *const avl, int dir, struct avlh *holder)
{
avl->end[avl_type2index(dir)].offset = (void *)holder - (void *)avl;
}
int avl_delete(struct avl *avl, struct avlh *node);
#else /* !PSHARED */
static inline struct avlh *avl_gettop(struct avl *const avl)
{
struct avlh *const holder = avl_top(avl);
#define avlh_link(avl, holder, dir) ((holder)->link[avl_type2index(dir)].ptr)
if (holder != avl_anchor(avl))
return holder;
#define avl_end(avl, dir) ((avl)->end[avl_type2index(dir)].ptr)
return NULL;
static inline void
avlh_set_link(struct avl *const avl, struct avlh *lhs, int dir, struct avlh *rhs)
{
avlh_link(avl, lhs, dir) = rhs;
}
static inline struct avlh *avl_gethead(struct avl *const avl)
static inline void
avl_set_end(struct avl *const avl, int dir, struct avlh *holder)
{
struct avlh *const holder = avl_head(avl);
avl_end(avl, dir) = holder;
}
if (holder != avl_anchor(avl))
return holder;
#endif /* !PSHARED */
return NULL;
static inline struct avlh *
__avl_search_inner(const struct avl *const avl, const struct avlh *n, int *delta)
{
return avl_searchfn(avl)(avl, n, delta, 0);
}
static inline struct avlh *avl_gettail(struct avl *const avl)
static inline struct avlh *avl_gettop(const struct avl *const avl)
{
struct avlh *const holder = avl_tail(avl);
return avl_top(avl);
}
if (holder != avl_anchor(avl))
return holder;
static inline struct avlh *avl_gethead(const struct avl *const avl)
{
return avl_head(avl);
}
return NULL;
static inline struct avlh *avl_gettail(const struct avl *const avl)
{
return avl_tail(avl);
}
static inline unsigned avl_getcount(struct avl *const avl)
static inline unsigned int avl_getcount(const struct avl *const avl)
{
return avl_count(avl);
}
static inline struct avlh *avl_inorder(struct avl *const avl,
struct avlh *const holder,
const int dir)
static inline struct avlh *
avl_inorder(const struct avl *const avl,
struct avlh *holder,
const int dir)
{
/* Assume dir == AVL_RIGHT in comments. */
struct avlh *child = avlh_link(holder, dir);
struct avlh *next;
/* If the current node is not right threaded, then go down left, starting
from its right child. */
if (!avlh_thr_tst(holder, dir)) {
/*
* If the current node is not right threaded, then go down left,
* starting from its right child.
*/
if (avlh_has_child(avl, holder, dir)) {
const int opp_dir = avl_opposite(dir);
while (!avlh_thr_tst(child, opp_dir))
child = avlh_link(child, opp_dir);
} else
/* Else follow its right thread. */
if (child != avl_anchor(avl))
return child;
else
return NULL;
holder = avlh_link(avl, holder, dir);
while ((next = avlh_child(avl, holder, opp_dir)))
holder = next;
next = holder;
} else {
for(;;) {
next = avlh_up(avl, holder);
if (next == avl_anchor(avl))
return NULL;
if (holder->type != dir)
break;
holder = next;
}
}
return child;
return next;
}
static inline struct avlh *avl_postorder(struct avl *const avl,
struct avlh *const holder, const int dir)
static inline struct avlh *
avl_postorder(const struct avl *const avl,
struct avlh *const holder, const int dir)
{
/* Assume dir == AVL_RIGHT in comments. */
struct avlh *next = avlh_up(holder);
struct avlh *next = avlh_up(avl, holder);
if (holder->type != dir)
/* If the current node is not a right node, follow the nodes in inorder
until we find a right threaded node. */
while (!avlh_thr_tst(next, dir))
/*
* If the current node is not a right node, follow the nodes in
* inorder until we find a right threaded node.
*/
while (avlh_has_child(avl, next, dir))
next = avl_inorder(avl, next, dir);
else
/* else the current node is a right node, its parent is the next in
postorder. */
if (next != avl_anchor(avl))
return next;
else
return NULL;
/*
* else the current node is a right node, its parent is the
* next in postorder.
*/
if (next == avl_anchor(avl))
next = NULL;
return next;
}
static inline struct avlh *avl_preorder(struct avl *const avl,
struct avlh *holder, const int dir)
static inline struct avlh *
avl_preorder(const struct avl *const avl,
struct avlh *holder, const int dir)
{
struct avlh *next;
/* Assume dir == AVL_RIGHT in comments. */
/* If the current node has a left child (hence is not left threaded), then
return it. */
if (!avlh_thr_tst(holder, avl_opposite(dir)))
return avlh_link(holder, avl_opposite(dir));
/* Else follow the right threads until we find a node which is not right
threaded (hence has a right child) and return its right child. */
/*
* If the current node has a left child (hence is not left threaded),
* then return it.
*/
if (avlh_has_child(avl, holder, avl_opposite(dir)))
return avlh_link(avl, holder, avl_opposite(dir));
/*
* Else follow the right threads until we find a node which is not right
* threaded (hence has a right child) and return its right child.
*/
next = holder;
while (avlh_thr_tst(next, dir)) {
next = avlh_link(next, dir);
if (next == avl_anchor(avl))
goto ret_null;
while (!avlh_has_child(avl, next, dir)) {
next = avl_inorder(avl, next, dir);
if (next == NULL)
return NULL;
}
return avlh_link(next, dir);
ret_null:
return NULL;
return avlh_link(avl, next, dir);
}
/**
* Get next node in symmetrical a.k.a inorder ordering.
*/
static inline struct avlh *avl_next(struct avl *const avl, struct avlh *const holder)
static inline struct avlh *
avl_next(const struct avl *const avl, struct avlh *const holder)
{
return avl_inorder(avl, holder, AVL_RIGHT);
}
/**
* Get previous node in symmetrical a.k.a inorder ordering.
*/
static inline struct avlh *avl_prev(struct avl *const avl, struct avlh *const holder)
static inline struct avlh *
avl_prev(const struct avl *const avl, struct avlh *const holder)
{
return avl_inorder(avl, holder, AVL_LEFT);
}
static inline struct avlh *avl_postorder_next(struct avl *const avl, struct avlh *const holder)
static inline struct avlh *
avl_postorder_next(const struct avl *const avl, struct avlh *const holder)
{
return avl_postorder(avl, holder, AVL_RIGHT);
}
static inline struct avlh *avl_postorder_prev(struct avl *const avl, struct avlh *const holder)
static inline struct avlh *
avl_postorder_prev(const struct avl *const avl, struct avlh *const holder)
{
return avl_postorder(avl, holder, AVL_LEFT);
}
static inline struct avlh *avl_preorder_next(struct avl *const avl, struct avlh *const holder)
static inline struct avlh *
avl_preorder_next(const struct avl *const avl, struct avlh *const holder)
{
return avl_preorder(avl, holder, AVL_RIGHT);
}
static inline struct avlh *avl_preorder_prev(struct avl *const avl, struct avlh *const holder)
static inline struct avlh *
avl_preorder_prev(const struct avl *const avl, struct avlh *const holder)
{
return avl_preorder(avl, holder, AVL_LEFT);
}
static inline void avlh_init(struct avlh *const holder)
{
*(unsigned long *)holder = 0UL; /* Please valgrind */
holder->thr = AVL_THR_LEFT | AVL_THR_RIGHT;
holder->balance = 0;
holder->type = 0;
}
static inline struct avlh *avl_search(struct avl *const avl, const struct avlh *node)
static inline struct avlh *
avl_search(const struct avl *const avl, const struct avlh *node)
{
struct avlh *holder;
int delta;
holder = avl_searchfn(avl)(avl, node, &delta);
holder = __avl_search_inner(avl, node, &delta);
if (!delta)
return holder;
return NULL;
}
#ifdef __cplusplus
static inline struct avlh *
avl_search_nearest(const struct avl *const avl, const struct avlh *node, int dir)
{
struct avlh *holder;
int delta;
holder = __avl_search_inner(avl, node, &delta);
if (!holder || delta != dir)
return holder;
return avl_inorder(avl, holder, dir);
}
static inline struct avlh *
avl_search_le(const struct avl *const avl, const struct avlh *node)
{
return avl_search_nearest(avl, node, AVL_LEFT);
}
static inline struct avlh *
avl_search_ge(const struct avl *const avl, const struct avlh *node)
{
return avl_search_nearest(avl, node, AVL_RIGHT);
}
int avl_insert_front(struct avl *avl, struct avlh *holder);
int avl_insert_back(struct avl *avl, struct avlh *holder);
static inline struct avlh *
avl_search_multi(const struct avl *const avl, const struct avlh *node, int dir)
{
struct avlh *holder;
int delta;
holder = avl_searchfn(avl)(avl, node, &delta, dir);
if (!delta)
return holder;
if (!holder)
return NULL;
return avl_inorder(avl, holder, -dir);
}
#endif
/**
static inline struct avlh *
avl_search_first(const struct avl *const avl, const struct avlh *node)
{
return avl_search_multi(avl, node, AVL_LEFT);
}
static inline struct avlh *
avl_search_last(const struct avl *const avl, const struct avlh *node)
{
return avl_search_multi(avl, node, AVL_RIGHT);
}
/*
* Search a node, return its parent if it could not be found.
*/
#define DECLARE_AVL_SEARCH(avl_search_inner, cmp) \
static struct avlh *avl_search_inner(const struct avl *const avl, \
const struct avlh *const node, \
int *const pdelta) \
{ \
int delta = avl_type2sign(AVL_RIGHT); \
struct avlh *holder = avl_top(avl); \
#define DECLARE_AVL_SEARCH(avl_search_inner, cmp) \
struct avlh *avl_search_inner(const struct avl *const avl, \
const struct avlh *const node, \
int *const pdelta, int dir) \
{ \
int delta = AVL_RIGHT; \
struct avlh *holder = avl_top(avl), *next; \
\
if (holder != avl_anchor(avl)) { \
while ((delta = cmp(holder, node))) { \
delta = delta < 0 ? -1 : 1; \
if (avlh_thr_tst(holder,avl_sign2type(delta))) \
if (holder == NULL) \
goto done; \
\
for (;;) { \
delta = cmp(node, holder); \
/* \
* Handle duplicates keys here, according to \
* "dir", if dir is: \
* - AVL_LEFT, the leftmost node is returned, \
* - AVL_RIGHT, the rightmost node is returned, \
* - 0, the first match is returned. \
*/ \
if (!(delta ?: dir)) \
break; \
next = avlh_child(avl, holder, delta ?: dir); \
if (next == NULL) \
break; \
holder = avlh_link(holder, avl_sign2type(delta)); \
holder = next; \
} \
} \
*pdelta = delta; \
return holder; \
} \
\
done: \
*pdelta = delta; \
return holder; \
}
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void avl_init(struct avl *const avl,
avl_search_t *searchfn, avlh_cmp_t *cmp);
void avl_destroy(struct avl *const avl);
int avl_insert(struct avl *const avl, struct avlh *const holder);
int avl_insert_front(struct avl *avl, struct avlh *holder);
int avl_insert_back(struct avl *avl, struct avlh *holder);
int avl_insert_at(struct avl *const avl,
struct avlh *parent, int dir, struct avlh *child);
int avl_prepend(struct avl *const avl, struct avlh *const holder);
int avl_append(struct avl *const avl, struct avlh *const holder);
int avl_delete(struct avl *const avl, struct avlh *node);
int avl_replace(struct avl *avl, struct avlh *oldh,
struct avlh *newh);
struct avlh *avl_update(struct avl *const avl,
struct avlh *const holder);
struct avlh *avl_set(struct avl *const avl,
struct avlh *const holder);
void avl_clear(struct avl *const avl, void (*destruct)(struct avlh *));
int avl_check(const struct avl *avl);
void avl_dump(FILE *file, const struct avl *const avl,
avlh_prn_t *prn, unsigned int indent, unsigned int len);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* !_BOILERPLATE_AVL_H */
......@@ -22,67 +22,123 @@
*/
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <memory.h>
#include <boilerplate/avl.h>
static inline unsigned int avlh_thr(const struct avl *const avl, const struct avlh *h)
{
unsigned int result = 0;
if (avlh_link(avl, h, AVL_LEFT) == NULL)
result |= AVL_THR_LEFT;
if (avlh_link(avl, h, AVL_RIGHT) == NULL)
result |= AVL_THR_RIGHT;
return result;
}
static inline void
avlh_set_parent_link(struct avl *const avl, struct avlh *lhs, struct avlh *rhs)