splay v1/p4; __skiplist_path with intersection

This commit is contained in:
Gregory Burd 2024-04-01 10:50:07 -04:00
parent b62fd4bf6a
commit 8703fbb5a9

View file

@ -195,12 +195,12 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
#endif
/*
* A Skiplist contains elements, a portion of which is used to manage those
* elements while the rest is defined by the use case for this declaration. The
* housekeeping portion is the SKIPLIST_ENTRY below. It maintains the array of
* forward pointers to nodes and has a height (a zero-based count of levels, so
* a height of `0` means one (1) level and a height of `4` means five (5)
* levels).
* Every Skiplist node has to hvae an additional section of data used to manage
* nodes in the list. The rest of the datastructure is defined by the use case.
* This housekeeping portion is the SKIPLIST_ENTRY, see below. It maintains the
* array of forward pointers to nodes and has a height, this height is a a
* zero-based count of levels, so a height of `0` means one (1) level and a
* height of `4` means five (5) forward pointers (levels) in the node, [0-4).
*/
#define SKIPLIST_ENTRY(decl) \
struct __skiplist_##decl##_entry { \
@ -240,8 +240,11 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
/* Skiplist node type */ \
typedef struct decl##_node decl##_node_t; \
\
/* Skiplist structure and type */ \
typedef struct decl { \
/* Skiplist type. */ \
typedef struct decl decl##_t; \
\
/* Skiplist structure */ \
struct decl { \
size_t slh_length, slh_height, slh_max_height; \
void *slh_aux; \
decl##_node_t *slh_head; \
@ -251,14 +254,14 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
int (*update_entry)(decl##_node_t *, void *); \
int (*archive_entry)(decl##_node_t *, const decl##_node_t *); \
size_t (*sizeof_entry)(decl##_node_t *); \
int (*compare_entries)(struct decl *, decl##_node_t *, decl##_node_t *, void *); \
int (*compare_entries)(decl##_t *, decl##_node_t *, decl##_node_t *, void *); \
\
/* Optional: Snapshots */ \
int (*preserve_node)(struct decl * slist, const decl##_node_t *src, decl##_node_t **preserved); \
void (*release_snapshots)(struct decl *); \
size_t (*snapshot_current_era)(struct decl *); \
size_t (*snapshot_incr_era)(struct decl *); \
void (*snapshot_record_era)(struct decl *, decl##_node_t *); \
int (*preserve_node)(decl##_t * slist, const decl##_node_t *src, decl##_node_t **preserved); \
void (*release_snapshots)(decl##_t *); \
size_t (*snapshot_current_era)(decl##_t *); \
size_t (*snapshot_incr_era)(decl##_t *); \
void (*snapshot_record_era)(decl##_t *, decl##_node_t *); \
} slh_fns; \
struct { \
size_t threshold; /* `k`, the floor of log(max height) */ \
@ -268,7 +271,12 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
size_t era; \
decl##_node_t *pres; \
} slh_snap; \
} decl##_t; \
}; \
\
typedef struct __skiplist_path_##decl { \
decl##_node_t *node; /* node traversed in the act of location */ \
size_t intersection; /* level at which the node was intersected */ \
} __skiplist_path_##decl##_t; \
\
/** \
* -- __skip_compare_entries_fn_ \
@ -592,29 +600,31 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
* -- __skip_rebalence_ TODO \
* \
*/ \
static void __skip_rebalence_##decl(decl##_t *slist, size_t len, decl##_node_t **path, size_t par_sum) \
static void __skip_rebalence_##decl(decl##_t *slist, size_t len, __skiplist_path_##decl##_t path[], size_t par_sum) \
{ \
size_t i; \
double asc_cond, dsc_cond; \
\
return; /* TODO GSB */ \
/* Moving backwards along the path... */ \
for (i = 1; i < len; i++) { \
if (par_sum > 0) { \
/* check the decent condition: \
par_sum <= hits total / (2 ^ (height of head - height of node)) \
* par_sum <= hits total / (2 ^ (height of head - height of node)) \
*/ \
dsc_cond = pow(2.0, slist->slh_head->field.sle_height - path[i]->field.sle_height); \
dsc_cond = pow(2.0, slist->slh_head->field.sle_height - path[i].node->field.sle_height); \
if (par_sum <= dsc_cond) { \
/* reduce height by one, change forward pointer */ \
path[i - 1]->field.sle_next[i] = path[i]->field.sle_next[i]; \
path[i]->field.sle_next[i] = slist->slh_tail; \
path[i]->field.sle_height--; \
path[i - 1].node->field.sle_next[i] = path[i].node->field.sle_next[i]; \
path[i].node->field.sle_next[i] = slist->slh_tail; \
path[i].node->field.sle_height--; \
} \
/* check the ascent condition \
par_sum + node_hits > hits total / (2 ^ (height of head - height of node - 1)) \
/* check the ascent condition: \
* par_sum + node_hits > hits total / (2 ^ (height of head - height of node - 1)) \
*/ \
asc_cond = pow(2.0, slist->slh_head->field.sle_height - path[i]->field.sle_height - 1); \
if (+path[i]->field.sle_hits > asc_cond) { \
asc_cond = pow(2.0, slist->slh_head->field.sle_height - path[i].node->field.sle_height - 1); \
if (path[i].node->field.sle_hits > asc_cond) { \
((void)0); \
} \
} \
} \
@ -628,7 +638,7 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
* node in path[0], or NULL at path[0] where there wasn't a match. \
* sizeof(path) should be `slist->slh_max_height + 1` \
*/ \
static size_t __skip_locate_##decl(decl##_t *slist, decl##_node_t *n, decl##_node_t **path) \
static size_t __skip_locate_##decl(decl##_t *slist, decl##_node_t *n, __skiplist_path_##decl##_t path[]) \
{ \
unsigned int i; \
size_t par_sum = 0, len = 0; \
@ -640,16 +650,18 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
/* Find the node that matches `node` or NULL. */ \
i = slist->slh_head->field.sle_height; \
do { \
while (elm != slist->slh_tail && elm->field.sle_next[i] && __skip_compare_nodes_##decl(slist, elm->field.sle_next[i], n, slist->slh_aux) < 0) \
while (elm != slist->slh_tail && elm->field.sle_next[i] && __skip_compare_nodes_##decl(slist, elm->field.sle_next[i], n, slist->slh_aux) < 0) { \
elm = elm->field.sle_next[i]; \
path[i + 1] = elm; \
path[i + 1].intersection = i; \
} \
path[i + 1].node = elm; \
par_sum += elm->field.sle_hits; \
len++; \
} while (i--); \
elm = elm->field.sle_next[0]; \
if (__skip_compare_nodes_##decl(slist, elm, n, slist->slh_aux) == 0) { \
path[0] = elm; \
path[0]->field.sle_hits++; \
path[0].node = elm; \
path[0].node->field.sle_hits++; \
__skip_rebalence_##decl(slist, len, path, par_sum); \
} \
return len; \
@ -664,25 +676,26 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
*/ \
static int __skip_insert_##decl(decl##_t *slist, decl##_node_t *new, int flags) \
{ \
static decl##_node_t *apath[SKIPLIST_MAX_HEIGHT + 1]; \
static __skiplist_path_##decl##_t apath[SKIPLIST_MAX_HEIGHT + 1]; \
int rc = 0; \
size_t i, len, loc = 0, cur_height, new_height; \
decl##_node_t *node, **path = apath; \
decl##_node_t *node; \
__skiplist_path_##decl##_t *path = apath; \
\
if (slist == NULL || new == NULL) \
return ENOENT; \
\
/* Allocate a buffer, or use a static one. */ \
if (SKIPLIST_MAX_HEIGHT == 1) { \
path = malloc(sizeof(decl##_node_t *) * slist->slh_max_height + 1); \
path = malloc(sizeof(__skiplist_path_##decl##_t) * slist->slh_max_height + 1); \
if (path == NULL) \
return ENOMEM; \
} \
memset(path, 0, sizeof(decl##_node_t *) * slist->slh_max_height + 1); \
memset(path, 0, sizeof(__skiplist_path_##decl##_t) * slist->slh_max_height + 1); \
\
/* Find a `path` to `new` in the list and a match (`path[0]`) if it exists. */ \
len = __skip_locate_##decl(slist, new, path); \
node = path[0]; \
node = path[0].node; \
if (len > 0) { \
if ((node != NULL) && (flags == 0)) { \
/* Don't insert, duplicate if flag not set. */ \
@ -695,7 +708,7 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
/* Trim the path to at most the new height for the new node. */ \
if (new_height > cur_height) { \
for (i = cur_height + 1; i <= new_height; i++) { \
path[i + 1] = slist->slh_tail; \
path[i + 1].node = slist->slh_tail; \
} \
} \
/* Ensure all next[] point to tail. */ \
@ -708,23 +721,23 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
/* The tail's next[i] is always NULL, we don't want that in the \
next[i] for our new node. Also, don't set the tail's next[i] \
because it is always NULL. */ \
if (path[i + 1] != slist->slh_tail) { \
new->field.sle_next[i] = path[i + 1]->field.sle_next[i]; \
path[i + 1]->field.sle_next[i] = new; \
loc = path[i + 1] == slist->slh_head ? i : loc; \
if (path[i + 1].node != slist->slh_tail) { \
new->field.sle_next[i] = path[i + 1].node->field.sle_next[i]; \
path[i + 1].node->field.sle_next[i] = new; \
loc = path[i + 1].node == slist->slh_head ? i : loc; \
} else { \
new->field.sle_next[i] = slist->slh_tail; \
} \
} \
/* Ensure all slh_head->next[] above loc point to tail. */ \
if (path[1] == slist->slh_head) { \
if (path[1].node == slist->slh_head) { \
__SKIP_ENTRIES_B2T_FROM(field, slist->slh_head, loc + 1) \
{ \
slist->slh_head->field.sle_next[lvl] = slist->slh_tail; \
} \
} \
/* Adujust the previous pointers in the nodes. */ \
new->field.sle_prev = path[1]; \
new->field.sle_prev = path[1].node; \
new->field.sle_next[0]->field.sle_prev = new; \
/* Account for insert at tail. */ \
if (new->field.sle_next[0] == slist->slh_tail) { \
@ -778,30 +791,26 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
*/ \
decl##_node_t *prefix##skip_position_eq_##decl(decl##_t *slist, decl##_node_t *query) \
{ \
static decl##_node_t *apath[SKIPLIST_MAX_HEIGHT + 1]; \
size_t len = 0; \
decl##_node_t *rc = NULL, *node, **path = apath; \
static __skiplist_path_##decl##_t apath[SKIPLIST_MAX_HEIGHT + 1]; \
decl##_node_t *node = NULL; \
__skiplist_path_##decl##_t *path = apath; \
\
/* Allocate a buffer, or use a static one. */ \
if (SKIPLIST_MAX_HEIGHT == 1) { \
path = malloc(sizeof(decl##_node_t *) * slist->slh_max_height + 1); \
path = malloc(sizeof(__skiplist_path_##decl##_t) * slist->slh_max_height + 1); \
if (path == NULL) \
goto done; \
} \
memset(path, 0, sizeof(decl##_node_t *) * slist->slh_max_height + 1); \
memset(path, 0, sizeof(__skiplist_path_##decl##_t) * slist->slh_max_height + 1); \
\
/* Find a `path` to `new` in the list and a match (`path[0]`) if it exists. */ \
len = __skip_locate_##decl(slist, query, path); \
if (len == 0) \
goto done; \
node = path[0]; \
if (node != NULL) \
rc = node; \
__skip_locate_##decl(slist, query, path); \
node = path[0].node; \
done:; \
if (SKIPLIST_MAX_HEIGHT == 1) \
free(path); \
\
return rc; \
return node; \
} \
\
/** \
@ -813,21 +822,22 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
*/ \
decl##_node_t *prefix##skip_position_gte_##decl(decl##_t *slist, decl##_node_t *query) \
{ \
static decl##_node_t *apath[SKIPLIST_MAX_HEIGHT + 1]; \
static __skiplist_path_##decl##_t apath[SKIPLIST_MAX_HEIGHT + 1]; \
int cmp; \
decl##_node_t *node, **path = apath; \
decl##_node_t *node; \
__skiplist_path_##decl##_t *path = apath; \
\
/* Allocate a buffer, or use a static one. */ \
if (SKIPLIST_MAX_HEIGHT == 1) { \
path = malloc(sizeof(decl##_node_t *) * slist->slh_max_height + 1); \
path = malloc(sizeof(__skiplist_path_##decl##_t) * slist->slh_max_height + 1); \
if (path == NULL) \
goto done; \
} \
memset(path, 0, sizeof(decl##_node_t *) * slist->slh_max_height + 1); \
memset(path, 0, sizeof(__skiplist_path_##decl##_t) * slist->slh_max_height + 1); \
\
/* Find a `path` to `new` in the list and a match (`path[0]`) if it exists. */ \
__skip_locate_##decl(slist, query, path); \
node = path[1]; \
node = path[1].node; \
do { \
node = node->field.sle_next[0]; \
cmp = __skip_compare_nodes_##decl(slist, node, query, slist->slh_aux); \
@ -849,21 +859,22 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
*/ \
decl##_node_t *prefix##skip_position_gt_##decl(decl##_t *slist, decl##_node_t *query) \
{ \
static decl##_node_t *apath[SKIPLIST_MAX_HEIGHT + 1]; \
static __skiplist_path_##decl##_t apath[SKIPLIST_MAX_HEIGHT + 1]; \
int cmp; \
decl##_node_t *node, **path = apath; \
decl##_node_t *node; \
__skiplist_path_##decl##_t *path = apath; \
\
/* Allocate a buffer, or use a static one. */ \
if (SKIPLIST_MAX_HEIGHT == 1) { \
path = malloc(sizeof(decl##_node_t *) * slist->slh_max_height + 1); \
path = malloc(sizeof(__skiplist_path_##decl##_t) * slist->slh_max_height + 1); \
if (path == NULL) \
goto done; \
} \
memset(path, 0, sizeof(decl##_node_t *) * slist->slh_max_height + 1); \
memset(path, 0, sizeof(__skiplist_path_##decl##_t) * slist->slh_max_height + 1); \
\
/* Find a `path` to `new` in the list and a match (`path[0]`) if it exists. */ \
__skip_locate_##decl(slist, query, path); \
node = path[1]; \
node = path[1].node; \
if (node == slist->slh_tail) \
goto done; \
do { \
@ -887,23 +898,24 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
*/ \
decl##_node_t *prefix##skip_position_lte_##decl(decl##_t *slist, decl##_node_t *query) \
{ \
static decl##_node_t *apath[SKIPLIST_MAX_HEIGHT + 1]; \
decl##_node_t *node, **path = apath; \
static __skiplist_path_##decl##_t apath[SKIPLIST_MAX_HEIGHT + 1]; \
decl##_node_t *node; \
__skiplist_path_##decl##_t *path = apath; \
\
/* Allocate a buffer, or use a static one. */ \
if (SKIPLIST_MAX_HEIGHT == 1) { \
path = malloc(sizeof(decl##_node_t *) * slist->slh_max_height + 1); \
path = malloc(sizeof(__skiplist_path_##decl##_t) * slist->slh_max_height + 1); \
if (path == NULL) \
goto done; \
} \
memset(path, 0, sizeof(decl##_node_t *) * slist->slh_max_height + 1); \
memset(path, 0, sizeof(__skiplist_path_##decl##_t) * slist->slh_max_height + 1); \
\
/* Find a `path` to `new` in the list and a match (`path[0]`) if it exists. */ \
__skip_locate_##decl(slist, query, path); \
node = path[0]; \
node = path[0].node; \
if (node) \
goto done; \
node = path[1]; \
node = path[1].node; \
\
done:; \
if (SKIPLIST_MAX_HEIGHT == 1) \
@ -920,20 +932,21 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
*/ \
decl##_node_t *prefix##skip_position_lt_##decl(decl##_t *slist, decl##_node_t *query) \
{ \
static decl##_node_t *apath[SKIPLIST_MAX_HEIGHT + 1]; \
decl##_node_t *node, **path = apath; \
static __skiplist_path_##decl##_t apath[SKIPLIST_MAX_HEIGHT + 1]; \
decl##_node_t *node; \
__skiplist_path_##decl##_t *path = apath; \
\
/* Allocate a buffer, or use a static one. */ \
if (SKIPLIST_MAX_HEIGHT == 1) { \
path = malloc(sizeof(decl##_node_t *) * slist->slh_max_height + 1); \
path = malloc(sizeof(__skiplist_path_##decl##_t) * slist->slh_max_height + 1); \
if (path == NULL) \
goto done; \
} \
memset(path, 0, sizeof(decl##_node_t *) * slist->slh_max_height + 1); \
memset(path, 0, sizeof(__skiplist_path_##decl##_t) * slist->slh_max_height + 1); \
\
/* Find a `path` to `new` in the list and a match (`path[0]`) if it exists. */ \
__skip_locate_##decl(slist, query, path); \
node = path[1]; \
node = path[1].node; \
\
done:; \
if (SKIPLIST_MAX_HEIGHT == 1) \
@ -983,23 +996,24 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
*/ \
int prefix##skip_update_##decl(decl##_t *slist, decl##_node_t *query, void *value) \
{ \
static decl##_node_t *apath[SKIPLIST_MAX_HEIGHT + 1]; \
static __skiplist_path_##decl##_t apath[SKIPLIST_MAX_HEIGHT + 1]; \
int rc = 0, np; \
decl##_node_t *node, **path = apath; \
decl##_node_t *node; \
__skiplist_path_##decl##_t *path = apath; \
\
if (slist == NULL) \
return -1; \
\
/* Allocate a buffer, or use a static one. */ \
if (SKIPLIST_MAX_HEIGHT == 1) { \
path = malloc(sizeof(decl##_node_t *) * slist->slh_max_height + 1); \
path = malloc(sizeof(__skiplist_path_##decl##_t) * slist->slh_max_height + 1); \
if (path == NULL) \
return ENOMEM; \
} \
memset(path, 0, sizeof(decl##_node_t *) * slist->slh_max_height + 1); \
memset(path, 0, sizeof(__skiplist_path_##decl##_t) * slist->slh_max_height + 1); \
\
__skip_locate_##decl(slist, query, path); \
node = path[0]; \
node = path[0].node; \
\
if (SKIPLIST_MAX_HEIGHT == 1) \
free(path); \
@ -1032,10 +1046,11 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
*/ \
int prefix##skip_remove_node_##decl(decl##_t *slist, decl##_node_t *query) \
{ \
static decl##_node_t *apath[SKIPLIST_MAX_HEIGHT + 1]; \
static __skiplist_path_##decl##_t apath[SKIPLIST_MAX_HEIGHT + 1]; \
int np = 0; \
size_t i, len, height; \
decl##_node_t *node, **path = apath; \
decl##_node_t *node; \
__skiplist_path_##decl##_t *path = apath; \
\
if (slist == NULL || query == NULL) \
return -1; \
@ -1044,15 +1059,15 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
\
/* Allocate a buffer */ \
if (SKIPLIST_MAX_HEIGHT == 1) { \
path = malloc(sizeof(decl##_node_t *) * slist->slh_max_height + 1); \
path = malloc(sizeof(__skiplist_path_##decl##_t) * slist->slh_max_height + 1); \
if (path == NULL) \
return ENOMEM; \
} \
memset(path, 0, sizeof(decl##_node_t *) * slist->slh_max_height + 1); \
memset(path, 0, sizeof(__skiplist_path_##decl##_t) * slist->slh_max_height + 1); \
\
/* Attempt to locate the node in the list. */ \
len = __skip_locate_##decl(slist, query, path); \
node = path[0]; \
node = path[0].node; \
if (node) { \
/* If the optional snapshots feature is configured, use it now. \
Snapshots preserve the node if it is younger than our snapshot \
@ -1068,15 +1083,15 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
/* Walk the path, stop when the next node is not the one we're \
removing. At each step along our walk... */ \
for (i = 0; i < len; i++) { \
if (path[i + 1]->field.sle_next[i] != node) \
if (path[i + 1].node->field.sle_next[i] != node) \
break; \
/* ... adjust the next pointer at that level. */ \
path[i + 1]->field.sle_next[i] = node->field.sle_next[i]; \
path[i + 1].node->field.sle_next[i] = node->field.sle_next[i]; \
/* Adjust the height so we're only pointing at the tail once at \
the top so we don't waste steps later when searching. */ \
if (path[i + 1]->field.sle_next[i] == slist->slh_tail) { \
height = path[i + 1]->field.sle_height; \
path[i + 1]->field.sle_height = height - 1; \
if (path[i + 1].node->field.sle_next[i] == slist->slh_tail) { \
height = path[i + 1].node->field.sle_height; \
path[i + 1].node->field.sle_height = height - 1; \
} \
} \
/* Account for delete at tail. */ \