splay v1/p1; every search via locate()

This commit is contained in:
Gregory Burd 2024-03-31 16:37:14 -04:00
parent 65f4bf6bf4
commit 37321594e3
2 changed files with 874 additions and 854 deletions

View file

@ -68,16 +68,11 @@ SKIPLIST_DECL(
},
/* free entry: node */
{ free(node->value); },
/* update entry: rc, src, dest */
/* update entry: rc, node, value */
{
char *new = calloc(strlen(node->value) + 1, sizeof(char));
if (new == NULL) {
rc = ENOMEM;
} else {
strncpy(new, node->value, strlen(node->value));
if (node->value)
free(node->value);
node->value = new;
}
node->value = (char*)value;
},
/* archive an entry: rc, src, dest */
{
@ -178,6 +173,7 @@ SKIPLIST_DECL_DOT(sample, api_, entries)
void
sprintf_sample_node(sample_node_t *node, char *buf)
{
// sprintf(buf, "h:%lu <<>> %d:%s", node->entries.sle_hits, node->key, node->value);
sprintf(buf, "%d:%s", node->key, node->value);
}
@ -298,8 +294,8 @@ main()
shuffle(array, asz);
for (i = 0; i < asz; i++) {
numeral = int_to_roman_numeral(array[i]);
rc = api_skip_put_sample(list, array[i], to_lower(numeral));
numeral = to_lower(int_to_roman_numeral(array[i]));
rc = api_skip_put_sample(list, array[i], numeral);
CHECK;
#ifdef SNAPSHOTS
if (i > TEST_ARRAY_SIZE + 1) {
@ -314,7 +310,10 @@ main()
#endif
char *v = api_skip_get_sample(list, array[i]);
CHECK;
api_skip_set_sample(list, array[i], to_upper(v));
char *upper_numeral = calloc(1, strlen(v) + 1);
strncpy(upper_numeral, v, strlen(v));
to_upper(upper_numeral);
api_skip_set_sample(list, array[i], upper_numeral);
CHECK;
}
numeral = int_to_roman_numeral(-1);
@ -358,7 +357,7 @@ main()
#ifdef SNAPSHOTS
//api_skip_restore_snapshot_sample(list, snap_ids[snap_i - 1]);
api_skip_release_snapshots_sample(list);
//api_skip_release_snapshots_sample(list);
#endif
assert(strcmp(api_skip_pos_sample(list, SKIP_GTE, -(TEST_ARRAY_SIZE)-1)->value, int_to_roman_numeral(-(TEST_ARRAY_SIZE))) == 0);

View file

@ -200,6 +200,7 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
struct decl##_node *sle_prev, **sle_next; \
size_t sle_height; \
size_t sle_era; \
size_t sle_hits; \
}
#define SKIPLIST_FOREACH_H2T(decl, prefix, list, elm, iter) \
@ -235,9 +236,12 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
/* Skiplist structure and type */ \
typedef struct decl { \
size_t slh_length, slh_height, slh_max_height; \
void *slh_aux; \
decl##_node_t *slh_head; \
decl##_node_t *slh_tail; \
struct { \
void (*free_entry)(decl##_node_t *); \
int (*update_entry)(decl##_node_t *); \
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 *); \
@ -249,9 +253,9 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
size_t (*snapshot_incr_era)(struct decl *); \
void (*snapshot_record_era)(struct decl *, decl##_node_t *); \
} slh_fns; \
void *slh_aux; \
decl##_node_t *slh_head; \
decl##_node_t *slh_tail; \
struct { \
size_t threshold; \
} slh_splay; \
struct { \
size_t era; \
decl##_node_t *pres; \
@ -283,7 +287,7 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
* \
* Wraps the `update_entry_blk` code into `slh_fns.update_entry`. \
*/ \
static int __skip_update_entry_fn_##decl(decl##_node_t *node) \
static int __skip_update_entry_fn_##decl(decl##_node_t *node, void *value) \
{ \
int rc = 0; \
update_entry_blk; \
@ -575,6 +579,18 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
return nodes; \
} \
\
/** \
* -- __skip_rebalance_ TODO \
* \
*/ \
static void __skip_rebalance_##decl(decl##_t *slist, size_t len, decl##_node_t **path, size_t par_sum) \
{ \
((void)slist); \
((void)len); \
((void)path); \
((void)par_sum); \
} \
\
/** \
* -- __skip_locate_ \
* \
@ -586,7 +602,7 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
static size_t __skip_locate_##decl(decl##_t *slist, decl##_node_t *n, decl##_node_t **path) \
{ \
unsigned int i; \
size_t len = 0; \
size_t par_sum = 0, len = 0; \
decl##_node_t *elm = slist->slh_head; \
\
if (slist == NULL || n == NULL) \
@ -598,11 +614,14 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
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; \
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++; \
__skip_rebalance_##decl(slist, len, path, par_sum); \
} \
return len; \
} \
@ -616,10 +635,10 @@ 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]; \
int rc = 0; \
static decl##_node_t apath[SKIPLIST_MAX_HEIGHT + 1]; \
size_t i, len, loc = 0, cur_height, new_height; \
decl##_node_t *node, **path = (decl##_node_t **)&apath; \
decl##_node_t *node, **path = apath; \
\
if (slist == NULL || new == NULL) \
return ENOENT; \
@ -725,29 +744,33 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
* \
* Find a node that matches the node `n`. This differs from the locate() \
* API in that it does not return the path to the node, only the match. \
* \
* NOTE: This differs from _locate() in that it avoids an alloc/free \
* for the path when SKIPLIST_MAX_HEIGHT == 1. \
*/ \
decl##_node_t *prefix##skip_position_eq_##decl(decl##_t *slist, decl##_node_t *n) \
decl##_node_t *prefix##skip_position_eq_##decl(decl##_t *slist, decl##_node_t *query) \
{ \
unsigned int i; \
decl##_node_t *elm = slist->slh_head; \
static decl##_node_t *apath[SKIPLIST_MAX_HEIGHT + 1]; \
size_t len = 0; \
decl##_node_t *rc = NULL, *node, **path = apath; \
\
if (slist == NULL || n == NULL) \
return 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) \
elm = elm->field.sle_next[i]; \
} while (i--); \
elm = elm->field.sle_next[0]; \
if (__skip_compare_nodes_##decl(slist, elm, n, slist->slh_aux) == 0) { \
return elm; \
/* Allocate a buffer, or use a static one. */ \
if (SKIPLIST_MAX_HEIGHT == 1) { \
path = malloc(sizeof(decl##_node_t *) * slist->slh_max_height + 1); \
if (path == NULL) \
goto done; \
} \
return NULL; \
memset(path, 0, sizeof(decl##_node_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; \
done:; \
if (SKIPLIST_MAX_HEIGHT == 1) \
free(path); \
\
return rc; \
} \
\
/** \
@ -756,30 +779,34 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
* Position and return a cursor at the first node that is equal to \
* or greater than the provided node `n`, otherwise if the largest \
* key is less than the key in `n` return NULL. \
* \
* NOTE: This differs from _locate() in that it avoids an alloc/free \
* for the path when SKIPLIST_MAX_HEIGHT == 1. \
*/ \
decl##_node_t *prefix##skip_position_gte_##decl(decl##_t *slist, decl##_node_t *query) \
{ \
static decl##_node_t *apath[SKIPLIST_MAX_HEIGHT + 1]; \
int cmp; \
unsigned int i; \
decl##_node_t *elm = slist->slh_head; \
decl##_node_t *node, **path = apath; \
\
if (slist == NULL || query == NULL) \
return NULL; \
\
i = slist->slh_head->field.sle_height; \
/* Allocate a buffer, or use a static one. */ \
if (SKIPLIST_MAX_HEIGHT == 1) { \
path = malloc(sizeof(decl##_node_t *) * slist->slh_max_height + 1); \
if (path == NULL) \
goto done; \
} \
memset(path, 0, sizeof(decl##_node_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]; \
do { \
while (elm != slist->slh_tail && elm->field.sle_next[i] && __skip_compare_nodes_##decl(slist, elm->field.sle_next[i], query, slist->slh_aux) < 0) \
elm = elm->field.sle_next[i]; \
} while (i--); \
do { \
elm = elm->field.sle_next[0]; \
cmp = __skip_compare_nodes_##decl(slist, elm, query, slist->slh_aux); \
node = node->field.sle_next[0]; \
cmp = __skip_compare_nodes_##decl(slist, node, query, slist->slh_aux); \
} while (cmp < 0); \
return elm; \
\
done:; \
if (SKIPLIST_MAX_HEIGHT == 1) \
free(path); \
\
return node; \
} \
\
/** \
@ -788,30 +815,36 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
* Position and return a cursor at the first node that is greater than \
* the provided node `n`. If the largestkey is less than the key in `n` \
* return NULL. \
* \
* NOTE: This differs from _locate() in that it avoids an alloc/free \
* for the path when SKIPLIST_MAX_HEIGHT == 1. \
*/ \
decl##_node_t *prefix##skip_position_gt_##decl(decl##_t *slist, decl##_node_t *query) \
{ \
static decl##_node_t *apath[SKIPLIST_MAX_HEIGHT + 1]; \
int cmp; \
unsigned int i; \
decl##_node_t *elm = slist->slh_head; \
decl##_node_t *node, **path = apath; \
\
if (slist == NULL || query == NULL) \
return NULL; \
\
i = slist->slh_head->field.sle_height; \
/* Allocate a buffer, or use a static one. */ \
if (SKIPLIST_MAX_HEIGHT == 1) { \
path = malloc(sizeof(decl##_node_t *) * slist->slh_max_height + 1); \
if (path == NULL) \
goto done; \
} \
memset(path, 0, sizeof(decl##_node_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]; \
if (node == slist->slh_tail) \
goto done; \
do { \
while (elm != slist->slh_tail && elm->field.sle_next[i] && __skip_compare_nodes_##decl(slist, elm->field.sle_next[i], query, slist->slh_aux) < 0) \
elm = elm->field.sle_next[i]; \
} while (i--); \
do { \
elm = elm->field.sle_next[0]; \
cmp = __skip_compare_nodes_##decl(slist, elm, query, slist->slh_aux); \
} while (cmp <= 0); \
return elm; \
node = node->field.sle_next[0]; \
cmp = __skip_compare_nodes_##decl(slist, node, query, slist->slh_aux); \
} while (cmp <= 0 && node != slist->slh_tail); \
\
done:; \
if (SKIPLIST_MAX_HEIGHT == 1) \
free(path); \
\
return node; \
} \
\
/** \
@ -820,35 +853,32 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
* Position and return a cursor at the last node that is less than \
* or equal to node `n`. \
* Return NULL if nothing is less than or equal. \
* \
* NOTE: This differs from _locate() in that it avoids an alloc/free \
* for the path when SKIPLIST_MAX_HEIGHT == 1. \
*/ \
decl##_node_t *prefix##skip_position_lte_##decl(decl##_t *slist, decl##_node_t *query) \
{ \
int cmp; \
unsigned int i; \
decl##_node_t *elm = slist->slh_head; \
static decl##_node_t *apath[SKIPLIST_MAX_HEIGHT + 1]; \
decl##_node_t *node, **path = apath; \
\
if (slist == NULL || query == NULL) \
return 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], query, slist->slh_aux) < 0) \
elm = elm->field.sle_next[i]; \
} while (i--); \
elm = elm->field.sle_next[0]; \
if (__skip_compare_nodes_##decl(slist, elm, query, slist->slh_aux) == 0) { \
return elm; \
} else { \
do { \
elm = elm->field.sle_prev; \
cmp = __skip_compare_nodes_##decl(slist, elm, query, slist->slh_aux); \
} while (cmp >= 0); \
/* Allocate a buffer, or use a static one. */ \
if (SKIPLIST_MAX_HEIGHT == 1) { \
path = malloc(sizeof(decl##_node_t *) * slist->slh_max_height + 1); \
if (path == NULL) \
goto done; \
} \
return elm; \
memset(path, 0, sizeof(decl##_node_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]; \
if (node) \
goto done; \
node = path[1]; \
\
done:; \
if (SKIPLIST_MAX_HEIGHT == 1) \
free(path); \
\
return node; \
} \
\
/** \
@ -856,40 +886,35 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
* \
* Position and return a cursor at the last node that is less than \
* to the node `n`. Return NULL if nothing is less than or equal. \
* \
* NOTE: This differs from _locate() in that it avoids an alloc/free \
* for the path when SKIPLIST_MAX_HEIGHT == 1. \
*/ \
decl##_node_t *prefix##skip_position_lt_##decl(decl##_t *slist, decl##_node_t *query) \
{ \
int cmp; \
unsigned int i; \
decl##_node_t *elm = slist->slh_head; \
static decl##_node_t *apath[SKIPLIST_MAX_HEIGHT + 1]; \
decl##_node_t *node, **path = apath; \
\
if (slist == NULL || query == NULL) \
return NULL; \
/* Allocate a buffer, or use a static one. */ \
if (SKIPLIST_MAX_HEIGHT == 1) { \
path = malloc(sizeof(decl##_node_t *) * slist->slh_max_height + 1); \
if (path == NULL) \
goto done; \
} \
memset(path, 0, sizeof(decl##_node_t *) * slist->slh_max_height + 1); \
\
i = slist->slh_head->field.sle_height; \
/* 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]; \
\
do { \
while (elm != slist->slh_tail && elm->field.sle_next[i] && __skip_compare_nodes_##decl(slist, elm->field.sle_next[i], query, slist->slh_aux) < 0) \
elm = elm->field.sle_next[i]; \
} while (i--); \
elm = elm->field.sle_next[0]; \
do { \
elm = elm->field.sle_prev; \
cmp = __skip_compare_nodes_##decl(slist, elm, query, slist->slh_aux); \
} while (cmp >= 0); \
return elm; \
done:; \
if (SKIPLIST_MAX_HEIGHT == 1) \
free(path); \
\
return node; \
} \
\
/** \
* -- skip_position_ \
* \
* Position a cursor relative to `n`. \
* \
* NOTE: This differs from _locate() in that it avoids an alloc/free \
* for the path when SKIPLIST_MAX_HEIGHT == 1. \
*/ \
decl##_node_t *prefix##skip_position_##decl(decl##_t *slist, skip_pos_##decl_t op, decl##_node_t *query) \
{ \
@ -925,11 +950,11 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
* WARNING: Do not update the portion of the node used for ordering \
* (e.g. `key`) unless you really know what you're doing. \
*/ \
int prefix##skip_update_##decl(decl##_t *slist, decl##_node_t *dest) \
int prefix##skip_update_##decl(decl##_t *slist, decl##_node_t *query, void *value) \
{ \
static decl##_node_t apath[SKIPLIST_MAX_HEIGHT + 1]; \
static decl##_node_t *apath[SKIPLIST_MAX_HEIGHT + 1]; \
int rc = 0, np; \
decl##_node_t *src, **path = (decl##_node_t **)&apath; \
decl##_node_t *node, **path = apath; \
\
if (slist == NULL) \
return -1; \
@ -942,31 +967,29 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
} \
memset(path, 0, sizeof(decl##_node_t *) * slist->slh_max_height + 1); \
\
__skip_locate_##decl(slist, dest, path); \
src = path[0]; \
__skip_locate_##decl(slist, query, path); \
node = path[0]; \
\
if (SKIPLIST_MAX_HEIGHT == 1) \
free(path); \
\
if (src == NULL) \
if (node == NULL) \
return -1; \
\
/* If the optional snapshots feature is configured, use it now. \
Snapshots preserve the node if it is younger than our snapshot \
moment. */ \
if (slist->slh_fns.preserve_node) { \
np = slist->slh_fns.preserve_node(slist, src, NULL); \
np = slist->slh_fns.preserve_node(slist, node, NULL); \
if (np > 0) \
return np; \
if (slist->slh_fns.snapshot_incr_era) \
slist->slh_fns.snapshot_incr_era(slist); \
} \
\
slist->slh_fns.update_entry(src); \
slist->slh_fns.update_entry(node, value); \
\
/* Record the era for this node to enable snapshots. */ \
if (slist->slh_fns.snapshot_record_era) \
slist->slh_fns.snapshot_record_era(slist, src); \
slist->slh_fns.snapshot_record_era(slist, node); \
\
return rc; \
} \
@ -978,10 +1001,10 @@ 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 decl##_node_t *apath[SKIPLIST_MAX_HEIGHT + 1]; \
int np = 0; \
size_t i, len, height; \
decl##_node_t *node, **path = (decl##_node_t **)&apath; \
decl##_node_t *node, **path = apath; \
\
if (slist == NULL || query == NULL) \
return -1; \
@ -1007,8 +1030,6 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
np = slist->slh_fns.preserve_node(slist, node, NULL); \
if (np > 0) \
return np; \
if (slist->slh_fns.snapshot_incr_era) \
slist->slh_fns.snapshot_incr_era(slist); \
} \
/* We found it, set the next->prev to the node->prev keeping in mind \
that the next node might be the tail). */ \
@ -1192,6 +1213,7 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
if (slist == NULL) \
return 0; \
slist->slh_snap.era = slist->slh_fns.snapshot_current_era(slist); \
slist->slh_fns.snapshot_incr_era(slist); \
return slist->slh_snap.era; \
} \
\
@ -1796,8 +1818,7 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
{ \
decl##_node_t node; \
node.key = key; \
node.value = value; \
return prefix##skip_update_##decl(slist, &node); \
return prefix##skip_update_##decl(slist, &node, (void *)value); \
} \
\
/** \