diff --git a/Makefile b/Makefile index 142d61c..5c368bc 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/examples/slm.c b/examples/slm.c index 973bd8d..c888310 100644 --- a/examples/slm.c +++ b/examples/slm.c @@ -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; diff --git a/include/sl.h b/include/sl.h index fe7cc1f..d5dcc78 100644 --- a/include/sl.h +++ b/include/sl.h @@ -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) \