splay v1/p5; rebalence desc cond
This commit is contained in:
parent
8703fbb5a9
commit
64c68c2283
3 changed files with 376 additions and 343 deletions
4
Makefile
4
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
|
||||
|
|
132
examples/slm.c
132
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;
|
||||
|
|
583
include/sl.h
583
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) \
|
||||
|
|
Loading…
Reference in a new issue