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 STATIC_LIB = libskiplist.a
SHARED_LIB = libskiplist.so 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 -std=c99 -Iinclude/ -fPIC
#CFLAGS = -Wall -Wextra -Wpedantic -Og -g -fsanitize=address,undefined -std=c99 -Iinclude/ -fPIC
TEST_FLAGS = -Itests/ TEST_FLAGS = -Itests/
#-fsanitize=address,undefined
TESTS = tests/test TESTS = tests/test
TEST_OBJS = tests/test.o tests/munit.o TEST_OBJS = tests/test.o tests/munit.o

View file

@ -19,11 +19,11 @@
#define VALIDATE #define VALIDATE
#define SNAPSHOTS #define SNAPSHOTS
#define DOT #define DOT
#define TEST_ARRAY_SIZE 10 #define TEST_ARRAY_SIZE 50
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
#ifdef VALIDATE #ifdef VALIDATE
#define CHECK __skip_integrity_check_sample(list, 0) #define CHECK __skip_integrity_check_esempio(list, 0)
#else #else
#define CHECK ((void)0) #define CHECK ((void)0)
#endif #endif
@ -31,10 +31,8 @@
/* /*
* SKIPLIST EXAMPLE: * SKIPLIST EXAMPLE:
* *
* This example creates a "sample" Skiplist where keys are integers, values are * This example creates an "esempio" (example in Italian) Skiplist where keys
* strings allocated on the heap. * are integers, values are strings allocated on the heap.
*
* 'sample' - meaning: EXample
*/ */
/* /*
@ -45,17 +43,17 @@
* node against another, logic you'll provide in SKIP_DECL as a * node against another, logic you'll provide in SKIP_DECL as a
* block below. * block below.
*/ */
struct sample_node { struct esempio_node {
int key; int key;
char *value; char *value;
SKIPLIST_ENTRY(sample) entries; SKIPLIST_ENTRY(esempio) entries;
}; };
/* /*
* Generate all the access functions for our type of Skiplist. * Generate all the access functions for our type of Skiplist.
*/ */
SKIPLIST_DECL( SKIPLIST_DECL(
sample, api_, entries, esempio, api_, entries,
/* compare entries: list, a, b, aux */ /* compare entries: list, a, b, aux */
{ {
(void)list; (void)list;
@ -109,7 +107,7 @@ SKIPLIST_DECL(
* list or when `a == b`. In those cases the comparison function * list or when `a == b`. In those cases the comparison function
* returns before using the code in your block, don't panic. :) * returns before using the code in your block, don't panic. :)
int 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)list;
(void)aux; (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. * extract data from within your nodes.
*/ */
SKIPLIST_DECL_ACCESS( SKIPLIST_DECL_ACCESS(
sample, api_, key, int, value, char *, esempio, api_, key, int, value, char *,
/* query blk */ { query.key = key; }, /* query blk */ { query.key = key; },
/* return blk */ { return node->value; }) /* return blk */ { return node->value; })
@ -140,7 +138,7 @@ SKIPLIST_DECL_ACCESS(
* Enable functions that enable returning to an earlier point in * Enable functions that enable returning to an earlier point in
* time when a snapshot was created. * time when a snapshot was created.
*/ */
SKIPLIST_DECL_SNAPSHOTS(sample, api_, entries) SKIPLIST_DECL_SNAPSHOTS(esempio, api_, entries)
/* /*
* Optional: Archive to/from bytes * 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 * Enable functions that can write/read the content of your Skiplist
* out/in to/from an array of bytes. * out/in to/from an array of bytes.
*/ */
SKIPLIST_DECL_ARCHIVE(sample, api_, entries) SKIPLIST_DECL_ARCHIVE(esempio, api_, entries)
/* /*
* Optional: As Hashtable * Optional: As Hashtable
* *
* Turn your Skiplist into a hash table. * 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 * Optional: Check Skiplists at runtime
* *
* Create a functions that validate the integrity of a Skiplist. * 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 /* Optional: Visualize your Skiplist using DOT/Graphviz in PDF
* *
* Create the functions used to annotate a visualization of a Skiplist. * Create the functions used to annotate a visualization of a Skiplist.
*/ */
SKIPLIST_DECL_DOT(sample, api_, entries) SKIPLIST_DECL_DOT(esempio, api_, entries)
void 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 (hits: %lu)", node->key, node->value, node->entries.sle_hits);
sprintf(buf, "%d:%s", node->key, node->value); sprintf(buf, "%d:%s", node->key, node->value);
@ -259,25 +257,25 @@ main()
#endif #endif
/* Allocate and initialize a Skiplist. */ /* 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) if (list == NULL)
return ENOMEM; return ENOMEM;
rc = api_skip_init_sample(list, -12); rc = api_skip_init_esempio(list, -12);
if (rc) if (rc)
return rc; return rc;
api_skip_snapshots_init_sample(list); api_skip_snapshots_init_esempio(list);
#ifdef DOT #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 #endif
if (api_skip_get_sample(list, 0) != NULL) if (api_skip_get_esempio(list, 0) != NULL)
perror("found a non-existent item!"); perror("found a non-existent item!");
api_skip_del_sample(list, 0); api_skip_del_esempio(list, 0);
CHECK; CHECK;
#ifdef SNAPSHOTS #ifdef SNAPSHOTS
/* Test creating a snapshot of an empty Skiplist */ /* 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 #endif
/* Insert 7 key/value pairs into the list. */ /* Insert 7 key/value pairs into the list. */
@ -295,98 +293,104 @@ main()
for (i = 0; i < asz; i++) { for (i = 0; i < asz; i++) {
numeral = to_lower(int_to_roman_numeral(array[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; CHECK;
#ifdef SNAPSHOTS #ifdef SNAPSHOTS
if (i > TEST_ARRAY_SIZE + 1) { 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; CHECK;
} }
#endif #endif
#ifdef DOT #ifdef DOT
sprintf(msg, "put key: %d value: %s", i, numeral); 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; CHECK;
#endif #endif
char *v = api_skip_get_sample(list, array[i]); char *v = api_skip_get_esempio(list, array[i]);
CHECK; CHECK;
char *upper_numeral = calloc(1, strlen(v) + 1); char *upper_numeral = calloc(1, strlen(v) + 1);
strncpy(upper_numeral, v, strlen(v)); strncpy(upper_numeral, v, strlen(v));
to_upper(upper_numeral); to_upper(upper_numeral);
api_skip_set_sample(list, array[i], upper_numeral); api_skip_set_esempio(list, array[i], upper_numeral);
CHECK; 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); numeral = int_to_roman_numeral(-1);
api_skip_dup_sample(list, -1, numeral); api_skip_dup_esempio(list, -1, numeral);
CHECK; CHECK;
#ifdef DOT #ifdef DOT
sprintf(msg, "put dup key: %d value: %s", i, numeral); 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; CHECK;
#endif #endif
numeral = int_to_roman_numeral(1); numeral = int_to_roman_numeral(1);
api_skip_dup_sample(list, 1, numeral); api_skip_dup_esempio(list, 1, numeral);
CHECK; CHECK;
#ifdef DOT #ifdef DOT
sprintf(msg, "put dup key: %d value: %s", i, numeral); 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; CHECK;
#endif #endif
api_skip_del_sample(list, 0); api_skip_del_esempio(list, 0);
CHECK; CHECK;
if (api_skip_get_sample(list, 0) != NULL) if (api_skip_get_esempio(list, 0) != NULL)
perror("found a deleted item!"); perror("found a deleted item!");
api_skip_del_sample(list, 0); api_skip_del_esempio(list, 0);
CHECK; CHECK;
if (api_skip_get_sample(list, 0) != NULL) if (api_skip_get_esempio(list, 0) != NULL)
perror("found a deleted item!"); perror("found a deleted item!");
int key = TEST_ARRAY_SIZE + 1; int key = TEST_ARRAY_SIZE + 1;
api_skip_del_sample(list, key); api_skip_del_esempio(list, key);
CHECK; CHECK;
key = -(TEST_ARRAY_SIZE)-1; key = -(TEST_ARRAY_SIZE)-1;
numeral = int_to_roman_numeral(key); numeral = int_to_roman_numeral(key);
api_skip_del_sample(list, key); api_skip_del_esempio(list, key);
CHECK; CHECK;
#ifdef DOT #ifdef DOT
sprintf(msg, "deleted key: %d, value: %s", 0, numeral); 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; CHECK;
#endif #endif
#ifdef SNAPSHOTS #ifdef SNAPSHOTS
//api_skip_restore_snapshot_sample(list, snap_ids[snap_i - 1]); api_skip_restore_snapshot_esempio(list, snap_ids[snap_i - 1]);
//api_skip_release_snapshots_sample(list); api_skip_release_snapshots_esempio(list);
#endif #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_esempio(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_esempio(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_esempio(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(strcmp(api_skip_pos_esempio(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(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_esempio(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_esempio(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_esempio(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(strcmp(api_skip_pos_esempio(list, SKIP_GT, 1)->value, int_to_roman_numeral(2)) == 0);
assert(api_skip_pos_sample(list, SKIP_GT, TEST_ARRAY_SIZE) == NULL); 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(api_skip_pos_esempio(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_esempio(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_esempio(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_esempio(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(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(api_skip_pos_esempio(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_esempio(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_esempio(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_esempio(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(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 #ifdef DOT
api_skip_dot_end_sample(of, gen); api_skip_dot_end_esempio(of, gen);
fclose(of); fclose(of);
#endif #endif
return rc; 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 { \ typedef struct __skiplist_path_##decl { \
decl##_node_t *node; /* node traversed in the act of location */ \ decl##_node_t *node; /* node traversed in the act of location */ \
size_t intersection; /* level at which the node was intersected */ \ 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; \ } __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; \ 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++) { \ for (i = 1; i < len; i++) { \
if (par_sum > 0) { \ /* (a) Check the decent condition: \
/* check the decent condition: \ * path[i].par_hit_sum <= hits total / (2 ^ (height of head - height of node)) \
* par_sum <= hits total / (2 ^ (height of head - height of node)) \ * When met should induce: \
*/ \ * 1) traverse the path backward, and ... \
dsc_cond = pow(2.0, slist->slh_head->field.sle_height - path[i].node->field.sle_height); \ * 2) propagate path[i].level[i] hits backward along path, and ... \
if (par_sum <= dsc_cond) { \ * 3) adjust any forward pointers along the way, then. \
/* reduce height by one, change forward pointer */ \ * 4) lower the path[i]'s node height by 1 \
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; \ 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_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)) \ /* (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) { \ asc_cond = pow(2.0, slist->slh_head->field.sle_height - path[i].node->field.sle_height - 1); \
((void)0); \ 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[]) \ static size_t __skip_locate_##decl(decl##_t *slist, decl##_node_t *n, __skiplist_path_##decl##_t path[]) \
{ \ { \
unsigned int i; \ unsigned int i; \
size_t par_sum = 0, len = 0; \ size_t len = 0; \
decl##_node_t *elm = slist->slh_head; \ decl##_node_t *elm = slist->slh_head; \
\ \
if (slist == NULL || n == NULL) \ 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. */ \ /* Find the node that matches `node` or NULL. */ \
i = slist->slh_head->field.sle_height; \ i = slist->slh_head->field.sle_height; \
do { \ 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) { \ 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]; \ elm = elm->field.sle_next[i]; \
path[i + 1].intersection = i; \ path[i + 1].intersection = i; \
path[i + 1].par_hit_sum += elm->field.sle_hits; \
} \ } \
path[i + 1].node = elm; \ path[i + 1].node = elm; \
par_sum += elm->field.sle_hits; \ path[i + 1].node->field.sle_hits++; \
len++; \ len++; \
} while (i--); \ } while (i--); \
elm = elm->field.sle_next[0]; \ elm = elm->field.sle_next[0]; \
if (__skip_compare_nodes_##decl(slist, elm, n, slist->slh_aux) == 0) { \ if (__skip_compare_nodes_##decl(slist, elm, n, slist->slh_aux) == 0) { \
path[0].node = elm; \ path[0].node = elm; \
path[0].node->field.sle_hits++; \ path[0].node->field.sle_hits++; \
__skip_rebalence_##decl(slist, len, path, par_sum); \ __skip_rebalence_##decl(slist, len, path); \
} \ } \
return len; \ 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. */ \ /* Record the era for this node to enable snapshots. */ \
if (slist->slh_fns.snapshot_record_era) \ if (slist->slh_fns.snapshot_record_era) \
slist->slh_fns.snapshot_record_era(slist, new); \ 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. */ \ /* Increase our list length (aka. size, count, etc.) by one. */ \
slist->slh_length++; \ slist->slh_length++; \
\ \
@ -1499,260 +1527,259 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
return NULL; \ return NULL; \
} }
#define SKIPLIST_DECL_VALIDATE(decl, prefix, field) \ #define SKIPLIST_DECL_VALIDATE(decl, prefix, field) \
/** \ /** \
* -- __skip_integrity_failure_ \ * -- __skip_integrity_failure_ \
*/ \ */ \
static void __attribute__((format(printf, 1, 2))) __skip_integrity_failure_##decl(const char *format, ...) \ static void __attribute__((format(printf, 1, 2))) __skip_integrity_failure_##decl(const char *format, ...) \
{ \ { \
va_list args; \ va_list args; \
va_start(args, format); \ va_start(args, format); \
vfprintf(stderr, format, args); \ vfprintf(stderr, format, args); \
va_end(args); \ va_end(args); \
} \ } \
\ \
/** \ /** \
* -- __skip_integrity_check_ \ * -- __skip_integrity_check_ \
*/ \ */ \
static int __skip_integrity_check_##decl(decl##_t *slist, int flags) \ static int __skip_integrity_check_##decl(decl##_t *slist, int flags) \
{ \ { \
size_t n = 0; \ size_t n = 0; \
unsigned long nth, n_err = 0; \ unsigned long nth, n_err = 0; \
decl##_node_t *node, *prev, *next; \ decl##_node_t *node, *prev, *next; \
struct __skiplist_##decl##_entry *this; \ struct __skiplist_##decl##_entry *this; \
\ \
if (slist == NULL) { \ if (slist == NULL) { \
__skip_integrity_failure_##decl("slist was NULL, nothing to check\n"); \ __skip_integrity_failure_##decl("slist was NULL, nothing to check\n"); \
n_err++; \ n_err++; \
return n_err; \ return n_err; \
} \ } \
\ \
/* Check the Skiplist header (slh) */ \ /* Check the Skiplist header (slh) */ \
\ \
if (slist->slh_head == NULL) { \ if (slist->slh_head == NULL) { \
__skip_integrity_failure_##decl("skiplist slh_head is NULL\n"); \ __skip_integrity_failure_##decl("skiplist slh_head is NULL\n"); \
n_err++; \ n_err++; \
return n_err; \ return n_err; \
} \ } \
\ \
if (slist->slh_tail == NULL) { \ if (slist->slh_tail == NULL) { \
__skip_integrity_failure_##decl("skiplist slh_tail is NULL\n"); \ __skip_integrity_failure_##decl("skiplist slh_tail is NULL\n"); \
n_err++; \ n_err++; \
return n_err; \ return n_err; \
} \ } \
\ \
if (slist->slh_fns.free_entry == NULL) { \ if (slist->slh_fns.free_entry == NULL) { \
__skip_integrity_failure_##decl("skiplist free_entry fn is NULL\n"); \ __skip_integrity_failure_##decl("skiplist free_entry fn is NULL\n"); \
n_err++; \ n_err++; \
return n_err; \ return n_err; \
} \ } \
\ \
if (slist->slh_fns.update_entry == NULL) { \ if (slist->slh_fns.update_entry == NULL) { \
__skip_integrity_failure_##decl("skiplist update_entry fn is NULL\n"); \ __skip_integrity_failure_##decl("skiplist update_entry fn is NULL\n"); \
n_err++; \ n_err++; \
return n_err; \ return n_err; \
} \ } \
\ \
if (slist->slh_fns.archive_entry == NULL) { \ if (slist->slh_fns.archive_entry == NULL) { \
__skip_integrity_failure_##decl("skiplist archive_entry fn is NULL\n"); \ __skip_integrity_failure_##decl("skiplist archive_entry fn is NULL\n"); \
n_err++; \ n_err++; \
return n_err; \ return n_err; \
} \ } \
\ \
if (slist->slh_fns.sizeof_entry == NULL) { \ if (slist->slh_fns.sizeof_entry == NULL) { \
__skip_integrity_failure_##decl("skiplist sizeof_entry fn is NULL\n"); \ __skip_integrity_failure_##decl("skiplist sizeof_entry fn is NULL\n"); \
n_err++; \ n_err++; \
return n_err; \ return n_err; \
} \ } \
\ \
if (slist->slh_fns.compare_entries == NULL) { \ if (slist->slh_fns.compare_entries == NULL) { \
__skip_integrity_failure_##decl("skiplist compare_entries fn is NULL\n"); \ __skip_integrity_failure_##decl("skiplist compare_entries fn is NULL\n"); \
n_err++; \ n_err++; \
return n_err; \ return n_err; \
} \ } \
\ \
if (slist->slh_max_height < 1) { \ if (slist->slh_max_height < 1) { \
__skip_integrity_failure_##decl("skiplist max level must be 1 at minimum\n"); \ __skip_integrity_failure_##decl("skiplist max level must be 1 at minimum\n"); \
n_err++; \ n_err++; \
if (flags) \ if (flags) \
return n_err; \ return n_err; \
} \ } \
\ \
if (slist->slh_height >= slist->slh_max_height) { \ if (slist->slh_height >= slist->slh_max_height) { \
/* level is 0-based, max of 12 means level cannot be > 11 */ \ /* 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); \ __skip_integrity_failure_##decl("skiplist level %lu in header was >= max %lu\n", slist->slh_height, slist->slh_max_height); \
n_err++; \ n_err++; \
if (flags) \ if (flags) \
return n_err; \ return n_err; \
} \ } \
\ \
if (SKIPLIST_MAX_HEIGHT < 1) { \ if (SKIPLIST_MAX_HEIGHT < 1) { \
__skip_integrity_failure_##decl("SKIPLIST_MAX_HEIGHT cannot be less than 1\n"); \ __skip_integrity_failure_##decl("SKIPLIST_MAX_HEIGHT cannot be less than 1\n"); \
n_err++; \ n_err++; \
if (flags) \ if (flags) \
return n_err; \ return n_err; \
} \ } \
\ \
if (SKIPLIST_MAX_HEIGHT > 1 && slist->slh_max_height > SKIPLIST_MAX_HEIGHT) { \ 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, \ __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); \ (size_t)SKIPLIST_MAX_HEIGHT); \
n_err++; \ n_err++; \
if (flags) \ if (flags) \
return n_err; \ return n_err; \
} \ } \
\ \
node = slist->slh_head; \ node = slist->slh_head; \
__SKIP_ENTRIES_B2T(field, node) \ __SKIP_ENTRIES_B2T(field, node) \
{ \ { \
if (node->field.sle_next[lvl] == NULL) { \ if (node->field.sle_next[lvl] == NULL) { \
__skip_integrity_failure_##decl("the head's %lu next node should not be NULL\n", lvl); \ __skip_integrity_failure_##decl("the head's %lu next node should not be NULL\n", lvl); \
n_err++; \ n_err++; \
if (flags) \ if (flags) \
return n_err; \ return n_err; \
} \ } \
n = lvl; \ n = lvl; \
if (node->field.sle_next[lvl] == slist->slh_tail) \ if (node->field.sle_next[lvl] == slist->slh_tail) \
break; \ break; \
} \ } \
n++; \ n++; \
__SKIP_ENTRIES_B2T_FROM(field, node, n) \ __SKIP_ENTRIES_B2T_FROM(field, node, n) \
{ \ { \
if (node->field.sle_next[lvl] == NULL) { \ if (node->field.sle_next[lvl] == NULL) { \
__skip_integrity_failure_##decl("the head's %lu next node should not be NULL\n", lvl); \ __skip_integrity_failure_##decl("the head's %lu next node should not be NULL\n", lvl); \
n_err++; \ n_err++; \
if (flags) \ if (flags) \
return n_err; \ return n_err; \
} \ } \
if (node->field.sle_next[lvl] != slist->slh_tail) { \ 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); \ __skip_integrity_failure_##decl("after internal nodes, the head's %lu next node should always be the tail\n", lvl); \
n_err++; \ n_err++; \
if (flags) \ if (flags) \
return n_err; \ return n_err; \
} \ } \
} \ } \
\ \
if (slist->slh_length > 0 && slist->slh_tail->field.sle_prev == slist->slh_head) { \ 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"); \ __skip_integrity_failure_##decl("slist->slh_length is 0, but tail->prev == head, not an internal node\n"); \
n_err++; \ n_err++; \
if (flags) \ if (flags) \
return n_err; \ return n_err; \
} \ } \
\ \
/* Validate the head node */ \ /* Validate the head node */ \
\ \
/* Validate the tail node */ \ /* Validate the tail node */ \
\ \
/* Validate each node */ \ /* Validate each node */ \
SKIPLIST_FOREACH_H2T(decl, prefix, slist, node, nth) \ SKIPLIST_FOREACH_H2T(decl, prefix, slist, node, nth) \
{ \ { \
this = &node->field; \ this = &node->field; \
\ \
if (this->sle_height >= slist->slh_max_height) { \ 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, \ __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); \
slist->slh_max_height); \ n_err++; \
n_err++; \ if (flags) \
if (flags) \ return n_err; \
return n_err; \ } \
} \ \
\ if (this->sle_next == NULL) { \
if (this->sle_next == NULL) { \ __skip_integrity_failure_##decl("the %lu node's [%p] next field should never NULL\n", nth, (void *)node); \
__skip_integrity_failure_##decl("the %luth node's [%p] next field should never NULL\n", nth, (void *)node); \ n_err++; \
n_err++; \ if (flags) \
if (flags) \ return n_err; \
return n_err; \ } \
} \ \
\ if (this->sle_prev == NULL) { \
if (this->sle_prev == NULL) { \ __skip_integrity_failure_##decl("the %lu node [%p] prev field should never NULL\n", nth, (void *)node); \
__skip_integrity_failure_##decl("the %luth node [%p] prev field should never NULL\n", nth, (void *)node); \ n_err++; \
n_err++; \ if (flags) \
if (flags) \ return n_err; \
return n_err; \ } \
} \ \
\ __SKIP_ENTRIES_B2T(field, node) \
__SKIP_ENTRIES_B2T(field, node) \ { \
{ \ if (this->sle_next[lvl] == NULL) { \
if (this->sle_next[lvl] == NULL) { \ __skip_integrity_failure_##decl("the %lu node's next[%lu] should not be NULL\n", nth, lvl); \
__skip_integrity_failure_##decl("the %luth node's next[%lu] should not be NULL\n", nth, lvl); \ n_err++; \
n_err++; \ if (flags) \
if (flags) \ return n_err; \
return n_err; \ } \
} \ n = lvl; \
n = lvl; \ if (this->sle_next[lvl] == slist->slh_tail) \
if (this->sle_next[lvl] == slist->slh_tail) \ break; \
break; \ } \
} \ n++; \
n++; \ __SKIP_ENTRIES_B2T_FROM(field, node, n) \
__SKIP_ENTRIES_B2T_FROM(field, node, n) \ { \
{ \ if (this->sle_next[lvl] == NULL) { \
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); \
__skip_integrity_failure_##decl("after the %lunth the %luth node's next[%lu] should not be NULL\n", n, nth, lvl); \ n_err++; \
n_err++; \ if (flags) \
if (flags) \ return n_err; \
return n_err; \ } else if (this->sle_next[lvl] != slist->slh_tail) { \
} 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); \
__skip_integrity_failure_##decl("after the %lunth the %luth node's next[%lu] should point to the tail\n", n, nth, lvl); \ n_err++; \
n_err++; \ if (flags) \
if (flags) \ return n_err; \
return n_err; \ } \
} \ } \
} \ \
\ decl##_node_t *a = (decl##_node_t *)(uintptr_t)this->sle_next; \
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)); \
decl##_node_t *b = (decl##_node_t *)(intptr_t)((uintptr_t)node + sizeof(decl##_node_t)); \ if (a != b) { \
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); \
__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++; \
n_err++; \ if (flags) \
if (flags) \ return n_err; \
return n_err; \ } \
} \ \
\ next = this->sle_next[0]; \
next = this->sle_next[0]; \ prev = this->sle_prev; \
prev = this->sle_prev; \ if (__skip_compare_nodes_##decl(slist, node, node, slist->slh_aux) != 0) { \
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); \
__skip_integrity_failure_##decl("the %luth node [%p] is not equal to itself\n", nth, (void *)node); \ n_err++; \
n_err++; \ if (flags) \
if (flags) \ return n_err; \
return n_err; \ } \
} \ \
\ if (__skip_compare_nodes_##decl(slist, node, prev, slist->slh_aux) < 0) { \
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); \
__skip_integrity_failure_##decl("the %luth node [%p] is not greater than the prev node [%p]\n", nth, (void *)node, (void *)prev); \ n_err++; \
n_err++; \ if (flags) \
if (flags) \ return n_err; \
return n_err; \ } \
} \ \
\ if (__skip_compare_nodes_##decl(slist, node, next, slist->slh_aux) > 0) { \
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); \
__skip_integrity_failure_##decl("the %luth node [%p] is not less than the next node [%p]\n", nth, (void *)node, (void *)next); \ n_err++; \
n_err++; \ if (flags) \
if (flags) \ return n_err; \
return n_err; \ } \
} \ \
\ if (__skip_compare_nodes_##decl(slist, prev, node, slist->slh_aux) > 0) { \
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); \
__skip_integrity_failure_##decl("the prev node [%p] is not less than the %luth node [%p]\n", (void *)prev, nth, (void *)node); \ n_err++; \
n_err++; \ if (flags) \
if (flags) \ return n_err; \
return n_err; \ } \
} \ \
\ if (__skip_compare_nodes_##decl(slist, next, node, slist->slh_aux) < 0) { \
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); \
__skip_integrity_failure_##decl("the next node [%p] is not greater than the %luth node [%p]\n", (void *)next, nth, (void *)node); \ n_err++; \
n_err++; \ if (flags) \
if (flags) \ return n_err; \
return n_err; \ } \
} \ } \
} \ \
\ if (slist->slh_length != nth) { \
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, \
__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); \
nth); \ n_err++; \
n_err++; \ if (flags) \
if (flags) \ return n_err; \
return n_err; \ } \
} \ \
\ return 0; \
return 0; \
} }
#define SKIPLIST_DECL_ACCESS(decl, prefix, key, ktype, value, vtype, qblk, rblk) \ #define SKIPLIST_DECL_ACCESS(decl, prefix, key, ktype, value, vtype, qblk, rblk) \