splay v1/p5; rebalence desc cond

This commit is contained in:
Gregory Burd 2024-04-01 14:26:44 -04:00
parent 8703fbb5a9
commit 64c68c2283
3 changed files with 376 additions and 343 deletions

View file

@ -3,9 +3,11 @@ OBJS = skiplist.o
STATIC_LIB = libskiplist.a
SHARED_LIB = libskiplist.so
# https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
#CFLAGS = -Wall -Wextra -Wpedantic -Of -std=c99 -Iinclude/ -fPIC
CFLAGS = -Wall -Wextra -Wpedantic -Og -g -std=c99 -Iinclude/ -fPIC
#CFLAGS = -Wall -Wextra -Wpedantic -Og -g -fsanitize=address,undefined -std=c99 -Iinclude/ -fPIC
TEST_FLAGS = -Itests/
#-fsanitize=address,undefined
TESTS = tests/test
TEST_OBJS = tests/test.o tests/munit.o

View file

@ -19,11 +19,11 @@
#define VALIDATE
#define SNAPSHOTS
#define DOT
#define TEST_ARRAY_SIZE 10
#define TEST_ARRAY_SIZE 50
// ---------------------------------------------------------------------------
#ifdef VALIDATE
#define CHECK __skip_integrity_check_sample(list, 0)
#define CHECK __skip_integrity_check_esempio(list, 0)
#else
#define CHECK ((void)0)
#endif
@ -31,10 +31,8 @@
/*
* SKIPLIST EXAMPLE:
*
* This example creates a "sample" Skiplist where keys are integers, values are
* strings allocated on the heap.
*
* 'sample' - meaning: EXample
* This example creates an "esempio" (example in Italian) Skiplist where keys
* are integers, values are strings allocated on the heap.
*/
/*
@ -45,17 +43,17 @@
* node against another, logic you'll provide in SKIP_DECL as a
* block below.
*/
struct sample_node {
struct esempio_node {
int key;
char *value;
SKIPLIST_ENTRY(sample) entries;
SKIPLIST_ENTRY(esempio) entries;
};
/*
* Generate all the access functions for our type of Skiplist.
*/
SKIPLIST_DECL(
sample, api_, entries,
esempio, api_, entries,
/* compare entries: list, a, b, aux */
{
(void)list;
@ -109,7 +107,7 @@ SKIPLIST_DECL(
* list or when `a == b`. In those cases the comparison function
* returns before using the code in your block, don't panic. :)
int
__sample_key_compare(sample_t *list, sample_node_t *a, sample_node_t *b, void *aux)
__esempio_key_compare(esempio_t *list, esempio_node_t *a, esempio_node_t *b, void *aux)
{
(void)list;
(void)aux;
@ -130,7 +128,7 @@ __sample_key_compare(sample_t *list, sample_node_t *a, sample_node_t *b, void *a
* extract data from within your nodes.
*/
SKIPLIST_DECL_ACCESS(
sample, api_, key, int, value, char *,
esempio, api_, key, int, value, char *,
/* query blk */ { query.key = key; },
/* return blk */ { return node->value; })
@ -140,7 +138,7 @@ SKIPLIST_DECL_ACCESS(
* Enable functions that enable returning to an earlier point in
* time when a snapshot was created.
*/
SKIPLIST_DECL_SNAPSHOTS(sample, api_, entries)
SKIPLIST_DECL_SNAPSHOTS(esempio, api_, entries)
/*
* Optional: Archive to/from bytes
@ -148,30 +146,30 @@ SKIPLIST_DECL_SNAPSHOTS(sample, api_, entries)
* Enable functions that can write/read the content of your Skiplist
* out/in to/from an array of bytes.
*/
SKIPLIST_DECL_ARCHIVE(sample, api_, entries)
SKIPLIST_DECL_ARCHIVE(esempio, api_, entries)
/*
* Optional: As Hashtable
*
* Turn your Skiplist into a hash table.
*/
//TODO SKIPLIST_DECL_HASHTABLE(sample, api_, entries, snaps)
//TODO SKIPLIST_DECL_HASHTABLE(esempio, api_, entries, snaps)
/*
* Optional: Check Skiplists at runtime
*
* Create a functions that validate the integrity of a Skiplist.
*/
SKIPLIST_DECL_VALIDATE(sample, api_, entries)
SKIPLIST_DECL_VALIDATE(esempio, api_, entries)
/* Optional: Visualize your Skiplist using DOT/Graphviz in PDF
*
* Create the functions used to annotate a visualization of a Skiplist.
*/
SKIPLIST_DECL_DOT(sample, api_, entries)
SKIPLIST_DECL_DOT(esempio, api_, entries)
void
sprintf_sample_node(sample_node_t *node, char *buf)
sprintf_esempio_node(esempio_node_t *node, char *buf)
{
// sprintf(buf, "%d:%s (hits: %lu)", node->key, node->value, node->entries.sle_hits);
sprintf(buf, "%d:%s", node->key, node->value);
@ -259,25 +257,25 @@ main()
#endif
/* Allocate and initialize a Skiplist. */
sample_t *list = (sample_t *)malloc(sizeof(sample_t));
esempio_t *list = (esempio_t *)malloc(sizeof(esempio_t));
if (list == NULL)
return ENOMEM;
rc = api_skip_init_sample(list, -12);
rc = api_skip_init_esempio(list, -12);
if (rc)
return rc;
api_skip_snapshots_init_sample(list);
api_skip_snapshots_init_esempio(list);
#ifdef DOT
api_skip_dot_sample(of, list, gen++, "init", sprintf_sample_node);
api_skip_dot_esempio(of, list, gen++, "init", sprintf_esempio_node);
#endif
if (api_skip_get_sample(list, 0) != NULL)
if (api_skip_get_esempio(list, 0) != NULL)
perror("found a non-existent item!");
api_skip_del_sample(list, 0);
api_skip_del_esempio(list, 0);
CHECK;
#ifdef SNAPSHOTS
/* Test creating a snapshot of an empty Skiplist */
snap_ids[snap_i++] = api_skip_snapshot_sample(list);
snap_ids[snap_i++] = api_skip_snapshot_esempio(list);
#endif
/* Insert 7 key/value pairs into the list. */
@ -295,98 +293,104 @@ main()
for (i = 0; i < asz; i++) {
numeral = to_lower(int_to_roman_numeral(array[i]));
rc = api_skip_put_sample(list, array[i], numeral);
rc = api_skip_put_esempio(list, array[i], numeral);
CHECK;
#ifdef SNAPSHOTS
if (i > TEST_ARRAY_SIZE + 1) {
snap_ids[snap_i++] = api_skip_snapshot_sample(list);
snap_ids[snap_i++] = api_skip_snapshot_esempio(list);
CHECK;
}
#endif
#ifdef DOT
sprintf(msg, "put key: %d value: %s", i, numeral);
api_skip_dot_sample(of, list, gen++, msg, sprintf_sample_node);
api_skip_dot_esempio(of, list, gen++, msg, sprintf_esempio_node);
CHECK;
#endif
char *v = api_skip_get_sample(list, array[i]);
char *v = api_skip_get_esempio(list, array[i]);
CHECK;
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);
api_skip_set_esempio(list, array[i], upper_numeral);
CHECK;
if (i == 8) {
api_skip_get_esempio(list, -2);
api_skip_get_esempio(list, -2);
api_skip_get_esempio(list, -2);
api_skip_get_esempio(list, -2);
}
}
numeral = int_to_roman_numeral(-1);
api_skip_dup_sample(list, -1, numeral);
api_skip_dup_esempio(list, -1, numeral);
CHECK;
#ifdef DOT
sprintf(msg, "put dup key: %d value: %s", i, numeral);
api_skip_dot_sample(of, list, gen++, msg, sprintf_sample_node);
api_skip_dot_esempio(of, list, gen++, msg, sprintf_esempio_node);
CHECK;
#endif
numeral = int_to_roman_numeral(1);
api_skip_dup_sample(list, 1, numeral);
api_skip_dup_esempio(list, 1, numeral);
CHECK;
#ifdef DOT
sprintf(msg, "put dup key: %d value: %s", i, numeral);
api_skip_dot_sample(of, list, gen++, msg, sprintf_sample_node);
api_skip_dot_esempio(of, list, gen++, msg, sprintf_esempio_node);
CHECK;
#endif
api_skip_del_sample(list, 0);
api_skip_del_esempio(list, 0);
CHECK;
if (api_skip_get_sample(list, 0) != NULL)
if (api_skip_get_esempio(list, 0) != NULL)
perror("found a deleted item!");
api_skip_del_sample(list, 0);
api_skip_del_esempio(list, 0);
CHECK;
if (api_skip_get_sample(list, 0) != NULL)
if (api_skip_get_esempio(list, 0) != NULL)
perror("found a deleted item!");
int key = TEST_ARRAY_SIZE + 1;
api_skip_del_sample(list, key);
api_skip_del_esempio(list, key);
CHECK;
key = -(TEST_ARRAY_SIZE)-1;
numeral = int_to_roman_numeral(key);
api_skip_del_sample(list, key);
api_skip_del_esempio(list, key);
CHECK;
#ifdef DOT
sprintf(msg, "deleted key: %d, value: %s", 0, numeral);
api_skip_dot_sample(of, list, gen++, msg, sprintf_sample_node);
api_skip_dot_esempio(of, list, gen++, msg, sprintf_esempio_node);
CHECK;
#endif
#ifdef SNAPSHOTS
//api_skip_restore_snapshot_sample(list, snap_ids[snap_i - 1]);
//api_skip_release_snapshots_sample(list);
api_skip_restore_snapshot_esempio(list, snap_ids[snap_i - 1]);
api_skip_release_snapshots_esempio(list);
#endif
assert(strcmp(api_skip_pos_sample(list, SKIP_GTE, -(TEST_ARRAY_SIZE)-1)->value, int_to_roman_numeral(-(TEST_ARRAY_SIZE))) == 0);
assert(strcmp(api_skip_pos_sample(list, SKIP_GTE, -2)->value, int_to_roman_numeral(-2)) == 0);
assert(strcmp(api_skip_pos_sample(list, SKIP_GTE, 0)->value, int_to_roman_numeral(1)) == 0);
assert(strcmp(api_skip_pos_sample(list, SKIP_GTE, 2)->value, int_to_roman_numeral(2)) == 0);
assert(api_skip_pos_sample(list, SKIP_GTE, (TEST_ARRAY_SIZE + 1)) == NULL);
assert(strcmp(api_skip_pos_esempio(list, SKIP_GTE, -(TEST_ARRAY_SIZE)-1)->value, int_to_roman_numeral(-(TEST_ARRAY_SIZE))) == 0);
assert(strcmp(api_skip_pos_esempio(list, SKIP_GTE, -2)->value, int_to_roman_numeral(-2)) == 0);
assert(strcmp(api_skip_pos_esempio(list, SKIP_GTE, 0)->value, int_to_roman_numeral(1)) == 0);
assert(strcmp(api_skip_pos_esempio(list, SKIP_GTE, 2)->value, int_to_roman_numeral(2)) == 0);
assert(api_skip_pos_esempio(list, SKIP_GTE, (TEST_ARRAY_SIZE + 1)) == NULL);
assert(strcmp(api_skip_pos_sample(list, SKIP_GT, -(TEST_ARRAY_SIZE)-1)->value, int_to_roman_numeral(-(TEST_ARRAY_SIZE))) == 0);
assert(strcmp(api_skip_pos_sample(list, SKIP_GT, -2)->value, int_to_roman_numeral(-1)) == 0);
assert(strcmp(api_skip_pos_sample(list, SKIP_GT, 0)->value, int_to_roman_numeral(1)) == 0);
assert(strcmp(api_skip_pos_sample(list, SKIP_GT, 1)->value, int_to_roman_numeral(2)) == 0);
assert(api_skip_pos_sample(list, SKIP_GT, TEST_ARRAY_SIZE) == NULL);
assert(strcmp(api_skip_pos_esempio(list, SKIP_GT, -(TEST_ARRAY_SIZE)-1)->value, int_to_roman_numeral(-(TEST_ARRAY_SIZE))) == 0);
assert(strcmp(api_skip_pos_esempio(list, SKIP_GT, -2)->value, int_to_roman_numeral(-1)) == 0);
assert(strcmp(api_skip_pos_esempio(list, SKIP_GT, 0)->value, int_to_roman_numeral(1)) == 0);
assert(strcmp(api_skip_pos_esempio(list, SKIP_GT, 1)->value, int_to_roman_numeral(2)) == 0);
assert(api_skip_pos_esempio(list, SKIP_GT, TEST_ARRAY_SIZE) == NULL);
assert(api_skip_pos_sample(list, SKIP_LT, -(TEST_ARRAY_SIZE)) == NULL);
assert(strcmp(api_skip_pos_sample(list, SKIP_LT, -1)->value, int_to_roman_numeral(-2)) == 0);
assert(strcmp(api_skip_pos_sample(list, SKIP_LT, 0)->value, int_to_roman_numeral(-1)) == 0);
assert(strcmp(api_skip_pos_sample(list, SKIP_LT, 2)->value, int_to_roman_numeral(1)) == 0);
assert(strcmp(api_skip_pos_sample(list, SKIP_LT, (TEST_ARRAY_SIZE + 1))->value, int_to_roman_numeral(TEST_ARRAY_SIZE)) == 0);
assert(api_skip_pos_esempio(list, SKIP_LT, -(TEST_ARRAY_SIZE)) == NULL);
assert(strcmp(api_skip_pos_esempio(list, SKIP_LT, -1)->value, int_to_roman_numeral(-2)) == 0);
assert(strcmp(api_skip_pos_esempio(list, SKIP_LT, 0)->value, int_to_roman_numeral(-1)) == 0);
assert(strcmp(api_skip_pos_esempio(list, SKIP_LT, 2)->value, int_to_roman_numeral(1)) == 0);
assert(strcmp(api_skip_pos_esempio(list, SKIP_LT, (TEST_ARRAY_SIZE + 1))->value, int_to_roman_numeral(TEST_ARRAY_SIZE)) == 0);
assert(api_skip_pos_sample(list, SKIP_LTE, -(TEST_ARRAY_SIZE)-1) == NULL);
assert(strcmp(api_skip_pos_sample(list, SKIP_LTE, -2)->value, int_to_roman_numeral(-2)) == 0);
assert(strcmp(api_skip_pos_sample(list, SKIP_LTE, 0)->value, int_to_roman_numeral(-1)) == 0);
assert(strcmp(api_skip_pos_sample(list, SKIP_LTE, 2)->value, int_to_roman_numeral(2)) == 0);
assert(strcmp(api_skip_pos_sample(list, SKIP_LTE, (TEST_ARRAY_SIZE + 1))->value, int_to_roman_numeral(TEST_ARRAY_SIZE)) == 0);
assert(api_skip_pos_esempio(list, SKIP_LTE, -(TEST_ARRAY_SIZE)-1) == NULL);
assert(strcmp(api_skip_pos_esempio(list, SKIP_LTE, -2)->value, int_to_roman_numeral(-2)) == 0);
assert(strcmp(api_skip_pos_esempio(list, SKIP_LTE, 0)->value, int_to_roman_numeral(-1)) == 0);
assert(strcmp(api_skip_pos_esempio(list, SKIP_LTE, 2)->value, int_to_roman_numeral(2)) == 0);
assert(strcmp(api_skip_pos_esempio(list, SKIP_LTE, (TEST_ARRAY_SIZE + 1))->value, int_to_roman_numeral(TEST_ARRAY_SIZE)) == 0);
api_skip_free_sample(list);
api_skip_free_esempio(list);
#ifdef DOT
api_skip_dot_end_sample(of, gen);
api_skip_dot_end_esempio(of, gen);
fclose(of);
#endif
return rc;

View file

@ -276,6 +276,7 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
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 */ \
size_t par_hit_sum; /* sum of hits from intersection to level[1] */ \
} __skiplist_path_##decl##_t; \
\
/** \
@ -597,35 +598,58 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
} \
\
/** \
* -- __skip_rebalence_ TODO \
* -- __skip_rebalence_ TODO/WIP \
* \
* Restore balance to our list by adjusting heights and forward pointers \
* according to the algorithm put forth in "The Splay-List: A \
* Distribution-Adaptive Concurrent Skip-List". \
* \
*/ \
static void __skip_rebalence_##decl(decl##_t *slist, size_t len, __skiplist_path_##decl##_t path[], size_t par_sum) \
static void __skip_rebalence_##decl(decl##_t *slist, size_t len, __skiplist_path_##decl##_t path[]) \
{ \
size_t i; \
size_t i, j, cur_hits, prev_hits; \
double asc_cond, dsc_cond; \
\
return; /* TODO GSB */ \
/* Moving backwards along the path... */ \
/* Moving backwards along the path... \
* - path[0] contains a match, if there was one \
* - path[1..len] will be the nodes traversed along the way \
* - path[len] is where the locate() terminated, just before path[0] \
* if there was a match \
*/ \
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)) \
*/ \
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].node->field.sle_next[i] = path[i].node->field.sle_next[i]; \
path[i].node->field.sle_next[i] = slist->slh_tail; \
/* (a) Check the decent condition: \
* path[i].par_hit_sum <= hits total / (2 ^ (height of head - height of node)) \
* When met should induce: \
* 1) traverse the path backward, and ... \
* 2) propagate path[i].level[i] hits backward along path, and ... \
* 3) adjust any forward pointers along the way, then. \
* 4) lower the path[i]'s node height by 1 \
*/ \
dsc_cond = pow(2.0, slist->slh_head->field.sle_height - path[i].node->field.sle_height); \
if (path[i].par_hit_sum <= dsc_cond) { \
if (path[i - 1].node->field.sle_prev != slist->slh_head) { \
/* 1) go backwards along path from where we are until head */ \
j = i; \
cur_hits = path[j].node->field.sle_next[path[j].intersection]->field.sle_hits; \
do { \
/* 2) propagate hits */ \
prev_hits = path[j - 1].node->field.sle_next[path[j - 1].intersection]->field.sle_hits; \
path[j - 1].node->field.sle_next[path[j - 1].intersection]->field.sle_hits += cur_hits; \
cur_hits = prev_hits; \
/* 3) adjust any forward pointers */ \
path[j - 1].node->field.sle_next[j] = path[j].node->field.sle_next[j]; \
} while (j-- > 1); \
/* 4) reduce height by one */ \
path[i].node->field.sle_height--; \
path[i].node->field.sle_next[i] = slist->slh_tail; \
} \
/* 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].node->field.sle_height - 1); \
if (path[i].node->field.sle_hits > asc_cond) { \
((void)0); \
} \
} \
/* (b) Check the ascent condition: \
* path[i].par_hit_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].node->field.sle_height - 1); \
if (path[i].node->field.sle_hits > asc_cond) { \
((void)0); \
} \
} \
} \
@ -641,7 +665,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, __skiplist_path_##decl##_t path[]) \
{ \
unsigned int i; \
size_t par_sum = 0, len = 0; \
size_t len = 0; \
decl##_node_t *elm = slist->slh_head; \
\
if (slist == NULL || n == NULL) \
@ -650,19 +674,21 @@ 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 { \
path[i + 1].par_hit_sum = 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].intersection = i; \
path[i + 1].par_hit_sum += elm->field.sle_hits; \
} \
path[i + 1].node = elm; \
par_sum += elm->field.sle_hits; \
path[i + 1].node->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].node = elm; \
path[0].node->field.sle_hits++; \
__skip_rebalence_##decl(slist, len, path, par_sum); \
__skip_rebalence_##decl(slist, len, path); \
} \
return len; \
} \
@ -753,6 +779,8 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
/* Record the era for this node to enable snapshots. */ \
if (slist->slh_fns.snapshot_record_era) \
slist->slh_fns.snapshot_record_era(slist, new); \
/* Set hits for rebalencing to 1 when new born. */ \
new->field.sle_hits = 1; \
/* Increase our list length (aka. size, count, etc.) by one. */ \
slist->slh_length++; \
\
@ -1499,260 +1527,259 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
return NULL; \
}
#define SKIPLIST_DECL_VALIDATE(decl, prefix, field) \
/** \
* -- __skip_integrity_failure_ \
*/ \
static void __attribute__((format(printf, 1, 2))) __skip_integrity_failure_##decl(const char *format, ...) \
{ \
va_list args; \
va_start(args, format); \
vfprintf(stderr, format, args); \
va_end(args); \
} \
\
/** \
* -- __skip_integrity_check_ \
*/ \
static int __skip_integrity_check_##decl(decl##_t *slist, int flags) \
{ \
size_t n = 0; \
unsigned long nth, n_err = 0; \
decl##_node_t *node, *prev, *next; \
struct __skiplist_##decl##_entry *this; \
\
if (slist == NULL) { \
__skip_integrity_failure_##decl("slist was NULL, nothing to check\n"); \
n_err++; \
return n_err; \
} \
\
/* Check the Skiplist header (slh) */ \
\
if (slist->slh_head == NULL) { \
__skip_integrity_failure_##decl("skiplist slh_head is NULL\n"); \
n_err++; \
return n_err; \
} \
\
if (slist->slh_tail == NULL) { \
__skip_integrity_failure_##decl("skiplist slh_tail is NULL\n"); \
n_err++; \
return n_err; \
} \
\
if (slist->slh_fns.free_entry == NULL) { \
__skip_integrity_failure_##decl("skiplist free_entry fn is NULL\n"); \
n_err++; \
return n_err; \
} \
\
if (slist->slh_fns.update_entry == NULL) { \
__skip_integrity_failure_##decl("skiplist update_entry fn is NULL\n"); \
n_err++; \
return n_err; \
} \
\
if (slist->slh_fns.archive_entry == NULL) { \
__skip_integrity_failure_##decl("skiplist archive_entry fn is NULL\n"); \
n_err++; \
return n_err; \
} \
\
if (slist->slh_fns.sizeof_entry == NULL) { \
__skip_integrity_failure_##decl("skiplist sizeof_entry fn is NULL\n"); \
n_err++; \
return n_err; \
} \
\
if (slist->slh_fns.compare_entries == NULL) { \
__skip_integrity_failure_##decl("skiplist compare_entries fn is NULL\n"); \
n_err++; \
return n_err; \
} \
\
if (slist->slh_max_height < 1) { \
__skip_integrity_failure_##decl("skiplist max level must be 1 at minimum\n"); \
n_err++; \
if (flags) \
return n_err; \
} \
\
if (slist->slh_height >= slist->slh_max_height) { \
/* level is 0-based, max of 12 means level cannot be > 11 */ \
__skip_integrity_failure_##decl("skiplist level %lu in header was >= max %lu\n", slist->slh_height, slist->slh_max_height); \
n_err++; \
if (flags) \
return n_err; \
} \
\
if (SKIPLIST_MAX_HEIGHT < 1) { \
__skip_integrity_failure_##decl("SKIPLIST_MAX_HEIGHT cannot be less than 1\n"); \
n_err++; \
if (flags) \
return n_err; \
} \
\
if (SKIPLIST_MAX_HEIGHT > 1 && slist->slh_max_height > SKIPLIST_MAX_HEIGHT) { \
__skip_integrity_failure_##decl("slist->slh_max_height %lu cannot be greater than SKIPLIST_MAX_HEIGHT %lu\n", slist->slh_max_height, \
(size_t)SKIPLIST_MAX_HEIGHT); \
n_err++; \
if (flags) \
return n_err; \
} \
\
node = slist->slh_head; \
__SKIP_ENTRIES_B2T(field, node) \
{ \
if (node->field.sle_next[lvl] == NULL) { \
__skip_integrity_failure_##decl("the head's %lu next node should not be NULL\n", lvl); \
n_err++; \
if (flags) \
return n_err; \
} \
n = lvl; \
if (node->field.sle_next[lvl] == slist->slh_tail) \
break; \
} \
n++; \
__SKIP_ENTRIES_B2T_FROM(field, node, n) \
{ \
if (node->field.sle_next[lvl] == NULL) { \
__skip_integrity_failure_##decl("the head's %lu next node should not be NULL\n", lvl); \
n_err++; \
if (flags) \
return n_err; \
} \
if (node->field.sle_next[lvl] != slist->slh_tail) { \
__skip_integrity_failure_##decl("after internal nodes, the head's %lu next node should always be the tail\n", lvl); \
n_err++; \
if (flags) \
return n_err; \
} \
} \
\
if (slist->slh_length > 0 && slist->slh_tail->field.sle_prev == slist->slh_head) { \
__skip_integrity_failure_##decl("slist->slh_length is 0, but tail->prev == head, not an internal node\n"); \
n_err++; \
if (flags) \
return n_err; \
} \
\
/* Validate the head node */ \
\
/* Validate the tail node */ \
\
/* Validate each node */ \
SKIPLIST_FOREACH_H2T(decl, prefix, slist, node, nth) \
{ \
this = &node->field; \
\
if (this->sle_height >= slist->slh_max_height) { \
__skip_integrity_failure_##decl("the %luth node's [%p] height %lu is >= max %lu\n", nth, (void *)node, this->sle_height, \
slist->slh_max_height); \
n_err++; \
if (flags) \
return n_err; \
} \
\
if (this->sle_next == NULL) { \
__skip_integrity_failure_##decl("the %luth node's [%p] next field should never NULL\n", nth, (void *)node); \
n_err++; \
if (flags) \
return n_err; \
} \
\
if (this->sle_prev == NULL) { \
__skip_integrity_failure_##decl("the %luth node [%p] prev field should never NULL\n", nth, (void *)node); \
n_err++; \
if (flags) \
return n_err; \
} \
\
__SKIP_ENTRIES_B2T(field, node) \
{ \
if (this->sle_next[lvl] == NULL) { \
__skip_integrity_failure_##decl("the %luth node's next[%lu] should not be NULL\n", nth, lvl); \
n_err++; \
if (flags) \
return n_err; \
} \
n = lvl; \
if (this->sle_next[lvl] == slist->slh_tail) \
break; \
} \
n++; \
__SKIP_ENTRIES_B2T_FROM(field, node, n) \
{ \
if (this->sle_next[lvl] == NULL) { \
__skip_integrity_failure_##decl("after the %lunth the %luth node's next[%lu] should not be NULL\n", n, nth, lvl); \
n_err++; \
if (flags) \
return n_err; \
} else if (this->sle_next[lvl] != slist->slh_tail) { \
__skip_integrity_failure_##decl("after the %lunth the %luth node's next[%lu] should point to the tail\n", n, nth, lvl); \
n_err++; \
if (flags) \
return n_err; \
} \
} \
\
decl##_node_t *a = (decl##_node_t *)(uintptr_t)this->sle_next; \
decl##_node_t *b = (decl##_node_t *)(intptr_t)((uintptr_t)node + sizeof(decl##_node_t)); \
if (a != b) { \
__skip_integrity_failure_##decl("the %luth node's [%p] next field isn't at the proper offset relative to the node\n", nth, (void *)node); \
n_err++; \
if (flags) \
return n_err; \
} \
\
next = this->sle_next[0]; \
prev = this->sle_prev; \
if (__skip_compare_nodes_##decl(slist, node, node, slist->slh_aux) != 0) { \
__skip_integrity_failure_##decl("the %luth node [%p] is not equal to itself\n", nth, (void *)node); \
n_err++; \
if (flags) \
return n_err; \
} \
\
if (__skip_compare_nodes_##decl(slist, node, prev, slist->slh_aux) < 0) { \
__skip_integrity_failure_##decl("the %luth node [%p] is not greater than the prev node [%p]\n", nth, (void *)node, (void *)prev); \
n_err++; \
if (flags) \
return n_err; \
} \
\
if (__skip_compare_nodes_##decl(slist, node, next, slist->slh_aux) > 0) { \
__skip_integrity_failure_##decl("the %luth node [%p] is not less than the next node [%p]\n", nth, (void *)node, (void *)next); \
n_err++; \
if (flags) \
return n_err; \
} \
\
if (__skip_compare_nodes_##decl(slist, prev, node, slist->slh_aux) > 0) { \
__skip_integrity_failure_##decl("the prev node [%p] is not less than the %luth node [%p]\n", (void *)prev, nth, (void *)node); \
n_err++; \
if (flags) \
return n_err; \
} \
\
if (__skip_compare_nodes_##decl(slist, next, node, slist->slh_aux) < 0) { \
__skip_integrity_failure_##decl("the next node [%p] is not greater than the %luth node [%p]\n", (void *)next, nth, (void *)node); \
n_err++; \
if (flags) \
return n_err; \
} \
} \
\
if (slist->slh_length != nth) { \
__skip_integrity_failure_##decl("slist->slh_length (%lu) doesn't match the count (%lu) of nodes between the head and tail\n", slist->slh_length, \
nth); \
n_err++; \
if (flags) \
return n_err; \
} \
\
return 0; \
#define SKIPLIST_DECL_VALIDATE(decl, prefix, field) \
/** \
* -- __skip_integrity_failure_ \
*/ \
static void __attribute__((format(printf, 1, 2))) __skip_integrity_failure_##decl(const char *format, ...) \
{ \
va_list args; \
va_start(args, format); \
vfprintf(stderr, format, args); \
va_end(args); \
} \
\
/** \
* -- __skip_integrity_check_ \
*/ \
static int __skip_integrity_check_##decl(decl##_t *slist, int flags) \
{ \
size_t n = 0; \
unsigned long nth, n_err = 0; \
decl##_node_t *node, *prev, *next; \
struct __skiplist_##decl##_entry *this; \
\
if (slist == NULL) { \
__skip_integrity_failure_##decl("slist was NULL, nothing to check\n"); \
n_err++; \
return n_err; \
} \
\
/* Check the Skiplist header (slh) */ \
\
if (slist->slh_head == NULL) { \
__skip_integrity_failure_##decl("skiplist slh_head is NULL\n"); \
n_err++; \
return n_err; \
} \
\
if (slist->slh_tail == NULL) { \
__skip_integrity_failure_##decl("skiplist slh_tail is NULL\n"); \
n_err++; \
return n_err; \
} \
\
if (slist->slh_fns.free_entry == NULL) { \
__skip_integrity_failure_##decl("skiplist free_entry fn is NULL\n"); \
n_err++; \
return n_err; \
} \
\
if (slist->slh_fns.update_entry == NULL) { \
__skip_integrity_failure_##decl("skiplist update_entry fn is NULL\n"); \
n_err++; \
return n_err; \
} \
\
if (slist->slh_fns.archive_entry == NULL) { \
__skip_integrity_failure_##decl("skiplist archive_entry fn is NULL\n"); \
n_err++; \
return n_err; \
} \
\
if (slist->slh_fns.sizeof_entry == NULL) { \
__skip_integrity_failure_##decl("skiplist sizeof_entry fn is NULL\n"); \
n_err++; \
return n_err; \
} \
\
if (slist->slh_fns.compare_entries == NULL) { \
__skip_integrity_failure_##decl("skiplist compare_entries fn is NULL\n"); \
n_err++; \
return n_err; \
} \
\
if (slist->slh_max_height < 1) { \
__skip_integrity_failure_##decl("skiplist max level must be 1 at minimum\n"); \
n_err++; \
if (flags) \
return n_err; \
} \
\
if (slist->slh_height >= slist->slh_max_height) { \
/* level is 0-based, max of 12 means level cannot be > 11 */ \
__skip_integrity_failure_##decl("skiplist level %lu in header was >= max %lu\n", slist->slh_height, slist->slh_max_height); \
n_err++; \
if (flags) \
return n_err; \
} \
\
if (SKIPLIST_MAX_HEIGHT < 1) { \
__skip_integrity_failure_##decl("SKIPLIST_MAX_HEIGHT cannot be less than 1\n"); \
n_err++; \
if (flags) \
return n_err; \
} \
\
if (SKIPLIST_MAX_HEIGHT > 1 && slist->slh_max_height > SKIPLIST_MAX_HEIGHT) { \
__skip_integrity_failure_##decl("slist->slh_max_height %lu cannot be greater than SKIPLIST_MAX_HEIGHT %lu\n", slist->slh_max_height, \
(size_t)SKIPLIST_MAX_HEIGHT); \
n_err++; \
if (flags) \
return n_err; \
} \
\
node = slist->slh_head; \
__SKIP_ENTRIES_B2T(field, node) \
{ \
if (node->field.sle_next[lvl] == NULL) { \
__skip_integrity_failure_##decl("the head's %lu next node should not be NULL\n", lvl); \
n_err++; \
if (flags) \
return n_err; \
} \
n = lvl; \
if (node->field.sle_next[lvl] == slist->slh_tail) \
break; \
} \
n++; \
__SKIP_ENTRIES_B2T_FROM(field, node, n) \
{ \
if (node->field.sle_next[lvl] == NULL) { \
__skip_integrity_failure_##decl("the head's %lu next node should not be NULL\n", lvl); \
n_err++; \
if (flags) \
return n_err; \
} \
if (node->field.sle_next[lvl] != slist->slh_tail) { \
__skip_integrity_failure_##decl("after internal nodes, the head's %lu next node should always be the tail\n", lvl); \
n_err++; \
if (flags) \
return n_err; \
} \
} \
\
if (slist->slh_length > 0 && slist->slh_tail->field.sle_prev == slist->slh_head) { \
__skip_integrity_failure_##decl("slist->slh_length is 0, but tail->prev == head, not an internal node\n"); \
n_err++; \
if (flags) \
return n_err; \
} \
\
/* Validate the head node */ \
\
/* Validate the tail node */ \
\
/* Validate each node */ \
SKIPLIST_FOREACH_H2T(decl, prefix, slist, node, nth) \
{ \
this = &node->field; \
\
if (this->sle_height >= slist->slh_max_height) { \
__skip_integrity_failure_##decl("the %lu node's [%p] height %lu is >= max %lu\n", nth, (void *)node, this->sle_height, slist->slh_max_height); \
n_err++; \
if (flags) \
return n_err; \
} \
\
if (this->sle_next == NULL) { \
__skip_integrity_failure_##decl("the %lu node's [%p] next field should never NULL\n", nth, (void *)node); \
n_err++; \
if (flags) \
return n_err; \
} \
\
if (this->sle_prev == NULL) { \
__skip_integrity_failure_##decl("the %lu node [%p] prev field should never NULL\n", nth, (void *)node); \
n_err++; \
if (flags) \
return n_err; \
} \
\
__SKIP_ENTRIES_B2T(field, node) \
{ \
if (this->sle_next[lvl] == NULL) { \
__skip_integrity_failure_##decl("the %lu node's next[%lu] should not be NULL\n", nth, lvl); \
n_err++; \
if (flags) \
return n_err; \
} \
n = lvl; \
if (this->sle_next[lvl] == slist->slh_tail) \
break; \
} \
n++; \
__SKIP_ENTRIES_B2T_FROM(field, node, n) \
{ \
if (this->sle_next[lvl] == NULL) { \
__skip_integrity_failure_##decl("after the %lunth the %lu node's next[%lu] should not be NULL\n", n, nth, lvl); \
n_err++; \
if (flags) \
return n_err; \
} else if (this->sle_next[lvl] != slist->slh_tail) { \
__skip_integrity_failure_##decl("after the %lunth the %lu node's next[%lu] should point to the tail\n", n, nth, lvl); \
n_err++; \
if (flags) \
return n_err; \
} \
} \
\
decl##_node_t *a = (decl##_node_t *)(uintptr_t)this->sle_next; \
decl##_node_t *b = (decl##_node_t *)(intptr_t)((uintptr_t)node + sizeof(decl##_node_t)); \
if (a != b) { \
__skip_integrity_failure_##decl("the %lu node's [%p] next field isn't at the proper offset relative to the node\n", nth, (void *)node); \
n_err++; \
if (flags) \
return n_err; \
} \
\
next = this->sle_next[0]; \
prev = this->sle_prev; \
if (__skip_compare_nodes_##decl(slist, node, node, slist->slh_aux) != 0) { \
__skip_integrity_failure_##decl("the %lu node [%p] is not equal to itself\n", nth, (void *)node); \
n_err++; \
if (flags) \
return n_err; \
} \
\
if (__skip_compare_nodes_##decl(slist, node, prev, slist->slh_aux) < 0) { \
__skip_integrity_failure_##decl("the %lu node [%p] is not greater than the prev node [%p]\n", nth, (void *)node, (void *)prev); \
n_err++; \
if (flags) \
return n_err; \
} \
\
if (__skip_compare_nodes_##decl(slist, node, next, slist->slh_aux) > 0) { \
__skip_integrity_failure_##decl("the %lu node [%p] is not less than the next node [%p]\n", nth, (void *)node, (void *)next); \
n_err++; \
if (flags) \
return n_err; \
} \
\
if (__skip_compare_nodes_##decl(slist, prev, node, slist->slh_aux) > 0) { \
__skip_integrity_failure_##decl("the prev node [%p] is not less than the %lu node [%p]\n", (void *)prev, nth, (void *)node); \
n_err++; \
if (flags) \
return n_err; \
} \
\
if (__skip_compare_nodes_##decl(slist, next, node, slist->slh_aux) < 0) { \
__skip_integrity_failure_##decl("the next node [%p] is not greater than the %lu node [%p]\n", (void *)next, nth, (void *)node); \
n_err++; \
if (flags) \
return n_err; \
} \
} \
\
if (slist->slh_length != nth) { \
__skip_integrity_failure_##decl("slist->slh_length (%lu) doesn't match the count (%lu) of nodes between the head and tail\n", slist->slh_length, \
nth); \
n_err++; \
if (flags) \
return n_err; \
} \
\
return 0; \
}
#define SKIPLIST_DECL_ACCESS(decl, prefix, key, ktype, value, vtype, qblk, rblk) \