snapshot v2/p9; bugs
This commit is contained in:
parent
326ed38410
commit
c03edbfdd3
3 changed files with 461 additions and 326 deletions
2
Makefile
2
Makefile
|
@ -58,6 +58,8 @@ examples/%.o: examples/%.c
|
||||||
examples/mls.c: examples/slm.c
|
examples/mls.c: examples/slm.c
|
||||||
$(CC) $(CFLAGS) -C -E examples/slm.c | sed -e '1,7d' -e '/^# [0-9]* "/d' | clang-format > examples/mls.c
|
$(CC) $(CFLAGS) -C -E examples/slm.c | sed -e '1,7d' -e '/^# [0-9]* "/d' | clang-format > examples/mls.c
|
||||||
|
|
||||||
|
# $(CC) $(CFLAGS) -C -E examples/slm.c | sed -e '1,7d' -e 's/^#\( [0-9]* ".*$$\)/\/\* \1 \*\//' | clang-format > examples/mls.c
|
||||||
|
|
||||||
examples/mls: examples/mls.o $(STATIC_LIB)
|
examples/mls: examples/mls.o $(STATIC_LIB)
|
||||||
$(CC) $^ -o $@ $(CFLAGS) $(TEST_FLAGS) -pthread
|
$(CC) $^ -o $@ $(CFLAGS) $(TEST_FLAGS) -pthread
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,13 @@ struct slex_node {
|
||||||
SKIPLIST_DECL(
|
SKIPLIST_DECL(
|
||||||
slex, api_, entries,
|
slex, api_, entries,
|
||||||
/* free node */ { free(node->value); },
|
/* free node */ { free(node->value); },
|
||||||
/* update node */ { node->value = new->value; },
|
/* update node */
|
||||||
|
{
|
||||||
|
//char *old = node->value;
|
||||||
|
node->value = new->value;
|
||||||
|
// In this case, don't free, we're just calling to_upper and using the same memory.
|
||||||
|
// free(old);
|
||||||
|
},
|
||||||
/* archive a node */
|
/* archive a node */
|
||||||
{
|
{
|
||||||
new->key = node->key;
|
new->key = node->key;
|
||||||
|
@ -183,8 +189,16 @@ shuffle(int *array, size_t n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//define DOT
|
#define INTEGRITY
|
||||||
#define TEST_ARRAY_SIZE 8
|
#ifdef INTEGRITY
|
||||||
|
#define INTEGRITY_CHK __skip_integrity_check_slex(list, 0)
|
||||||
|
#else
|
||||||
|
#define INTEGRITY_CKH ((void)0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//define SNAPSHOTS
|
||||||
|
#define DOT
|
||||||
|
#define TEST_ARRAY_SIZE 5
|
||||||
|
|
||||||
int
|
int
|
||||||
main()
|
main()
|
||||||
|
@ -192,6 +206,7 @@ main()
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
#ifdef DOT
|
#ifdef DOT
|
||||||
|
size_t gen = 0;
|
||||||
FILE *of = fopen("/tmp/slm.dot", "w");
|
FILE *of = fopen("/tmp/slm.dot", "w");
|
||||||
if (!of) {
|
if (!of) {
|
||||||
perror("Failed to open file /tmp/slm.dot");
|
perror("Failed to open file /tmp/slm.dot");
|
||||||
|
@ -211,9 +226,11 @@ main()
|
||||||
api_skip_dot_slex(of, list, gen++, sprintf_slex_node);
|
api_skip_dot_slex(of, list, gen++, sprintf_slex_node);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef SNAPSHOTS
|
||||||
/* Test creating a snapshot of an empty Skiplist */
|
/* Test creating a snapshot of an empty Skiplist */
|
||||||
size_t snp[TEST_ARRAY_SIZE * 2 + 10];
|
size_t snp[TEST_ARRAY_SIZE * 2 + 10];
|
||||||
snp[0] = api_skip_snapshot_slex(list);
|
snp[0] = api_skip_snapshot_slex(list);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Insert 7 key/value pairs into the list. */
|
/* Insert 7 key/value pairs into the list. */
|
||||||
int i;
|
int i;
|
||||||
|
@ -225,34 +242,57 @@ main()
|
||||||
|
|
||||||
for (i = 0; i < asz; i++) {
|
for (i = 0; i < asz; i++) {
|
||||||
rc = api_skip_put_slex(list, array[i], to_lower(int_to_roman_numeral(array[i])));
|
rc = api_skip_put_slex(list, array[i], to_lower(int_to_roman_numeral(array[i])));
|
||||||
|
INTEGRITY_CHK;
|
||||||
|
#ifdef SNAPSHOTS
|
||||||
snp[i + 1] = api_skip_snapshot_slex(list);
|
snp[i + 1] = api_skip_snapshot_slex(list);
|
||||||
|
#endif
|
||||||
|
INTEGRITY_CHK;
|
||||||
#ifdef DOT
|
#ifdef DOT
|
||||||
api_skip_dot_slex(of, list, gen++, sprintf_slex_node);
|
api_skip_dot_slex(of, list, gen++, sprintf_slex_node);
|
||||||
|
INTEGRITY_CHK;
|
||||||
#endif
|
#endif
|
||||||
char *v = api_skip_get_slex(list, array[i]);
|
char *v = api_skip_get_slex(list, array[i]);
|
||||||
|
INTEGRITY_CHK;
|
||||||
api_skip_set_slex(list, array[i], to_upper(v));
|
api_skip_set_slex(list, array[i], to_upper(v));
|
||||||
|
INTEGRITY_CHK;
|
||||||
}
|
}
|
||||||
|
#ifdef SNAPSHOTS
|
||||||
int r = i;
|
int r = i;
|
||||||
|
#endif
|
||||||
api_skip_dup_slex(list, -1, int_to_roman_numeral(-1));
|
api_skip_dup_slex(list, -1, int_to_roman_numeral(-1));
|
||||||
|
INTEGRITY_CHK;
|
||||||
#ifdef DOT
|
#ifdef DOT
|
||||||
api_skip_dot_slex(of, list, gen++, sprintf_slex_node);
|
api_skip_dot_slex(of, list, gen++, sprintf_slex_node);
|
||||||
|
INTEGRITY_CHK;
|
||||||
#endif
|
#endif
|
||||||
api_skip_dup_slex(list, 1, int_to_roman_numeral(1));
|
api_skip_dup_slex(list, 1, int_to_roman_numeral(1));
|
||||||
|
INTEGRITY_CHK;
|
||||||
#ifdef DOT
|
#ifdef DOT
|
||||||
api_skip_dot_slex(of, list, gen++, sprintf_slex_node);
|
api_skip_dot_slex(of, list, gen++, sprintf_slex_node);
|
||||||
|
INTEGRITY_CHK;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef SNAPSHOTS
|
||||||
snp[++i] = api_skip_snapshot_slex(list);
|
snp[++i] = api_skip_snapshot_slex(list);
|
||||||
|
INTEGRITY_CHK;
|
||||||
|
#endif
|
||||||
|
|
||||||
api_skip_del_slex(list, 0);
|
api_skip_del_slex(list, 0);
|
||||||
|
INTEGRITY_CHK;
|
||||||
|
#ifdef SNAPSHOTS
|
||||||
snp[++i] = api_skip_snapshot_slex(list);
|
snp[++i] = api_skip_snapshot_slex(list);
|
||||||
|
INTEGRITY_CHK;
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef DOT
|
#ifdef DOT
|
||||||
api_skip_dot_slex(of, list, gen++, sprintf_slex_node);
|
api_skip_dot_slex(of, list, gen++, sprintf_slex_node);
|
||||||
|
INTEGRITY_CHK;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef SNAPSHOTS
|
||||||
slex_t *restored = api_skip_restore_snapshot_slex(list, snp[r]);
|
slex_t *restored = api_skip_restore_snapshot_slex(list, snp[r]);
|
||||||
api_skip_dispose_snapshot_slex(list, r+1);
|
api_skip_dispose_snapshot_slex(list, r+1);
|
||||||
api_skip_destroy_slex(restored);
|
api_skip_destroy_slex(restored);
|
||||||
|
#endif
|
||||||
|
|
||||||
assert(strcmp(api_skip_pos_slex(list, SKIP_GTE, -(TEST_ARRAY_SIZE)-1)->value, int_to_roman_numeral(-(TEST_ARRAY_SIZE))) == 0);
|
assert(strcmp(api_skip_pos_slex(list, SKIP_GTE, -(TEST_ARRAY_SIZE)-1)->value, int_to_roman_numeral(-(TEST_ARRAY_SIZE))) == 0);
|
||||||
assert(strcmp(api_skip_pos_slex(list, SKIP_GTE, -2)->value, int_to_roman_numeral(-2)) == 0);
|
assert(strcmp(api_skip_pos_slex(list, SKIP_GTE, -2)->value, int_to_roman_numeral(-2)) == 0);
|
||||||
|
|
739
include/sl.h
739
include/sl.h
|
@ -94,7 +94,7 @@
|
||||||
* volume = {33}, number = {6}, issn = {0001-0782},
|
* volume = {33}, number = {6}, issn = {0001-0782},
|
||||||
* url = {https://doi.org/10.1145/78973.78977},
|
* url = {https://doi.org/10.1145/78973.78977},
|
||||||
* doi = {10.1145/78973.78977},
|
* doi = {10.1145/78973.78977},
|
||||||
* journal = {Commun. ACM}, month = {jun}, pages = {668–676}, numpages = {9},
|
* journal = {Commun. ACM}, month = {jun}, pages = {668-676}, numpages = {9},
|
||||||
* keywords = {trees, searching, data structures},
|
* keywords = {trees, searching, data structures},
|
||||||
* download = {https://www.cl.cam.ac.uk/teaching/2005/Algorithms/skiplists.pdf}
|
* download = {https://www.cl.cam.ac.uk/teaching/2005/Algorithms/skiplists.pdf}
|
||||||
* }
|
* }
|
||||||
|
@ -241,17 +241,18 @@
|
||||||
fn_blk \
|
fn_blk \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define SKIPLIST_EACH_H2T(decl, prefix, list, elm, iter) \
|
#define SKIPLIST_FOREACH_H2T(decl, prefix, list, elm, iter) \
|
||||||
for (iter = 0, (elm) = prefix##skip_head_##decl(list); (elm) != NULL; iter++, (elm) = prefix##skip_next_node_##decl(list, elm))
|
for (iter = 0, (elm) = prefix##skip_head_##decl(list); (elm) != NULL; iter++, (elm) = prefix##skip_next_node_##decl(list, elm))
|
||||||
|
|
||||||
#define SKIPLIST_EACH_T2H(decl, prefix, list, elm, iter) \
|
/* Iterate from tail to head over the nodes. */
|
||||||
|
#define SKIPLIST_FOREACH_T2H(decl, prefix, list, elm, iter) \
|
||||||
for (iter = prefix##skip_size_##decl(list), (elm) = prefix##skip_tailf_##decl(list); (elm) != NULL; \
|
for (iter = prefix##skip_size_##decl(list), (elm) = prefix##skip_tailf_##decl(list); (elm) != NULL; \
|
||||||
iter--, (elm) = prefix##skip_prev_node_##decl(list, elm))
|
iter--, (elm) = prefix##skip_prev_node_##decl(list, elm))
|
||||||
|
|
||||||
#define __SKIP_NEXT_ENTRIES_T2B(field, elm) for (size_t lvl = elm->field.sle.height; lvl != (size_t)-1; lvl--)
|
#define __SKIP_ENTRIES_T2B(field, elm) for (size_t lvl = elm->field.sle.height; lvl != (size_t)-1; lvl--)
|
||||||
#define __SKIP_IS_LAST_ENTRY_T2B() if (lvl == 0)
|
#define __SKIP_IS_LAST_ENTRY_T2B() if (lvl == 0)
|
||||||
|
|
||||||
#define __SKIP_NEXT_ENTRIES_B2T(field, elm) for (size_t lvl = 0; lvl <= elm->field.sle.height; lvl++)
|
#define __SKIP_ENTRIES_B2T(field, elm) for (size_t lvl = 0; lvl < elm->field.sle.height + 1; lvl++)
|
||||||
#define __SKIP_IS_LAST_ENTRY_B2T() if (lvl + 1 == elm->field.sle.height)
|
#define __SKIP_IS_LAST_ENTRY_B2T() if (lvl + 1 == elm->field.sle.height)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -439,6 +440,72 @@
|
||||||
return len; \
|
return len; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
|
/* -- __skip_preserve_node_ \
|
||||||
|
* Preserve given node in the slh_pres list. \
|
||||||
|
* \
|
||||||
|
* ALGORITHM: \
|
||||||
|
* a) allocate a new node and copy this node into it without \
|
||||||
|
* copying the user-supplied additional memory (that will \
|
||||||
|
* happen iff an _update() is called on the node). \
|
||||||
|
* b) determine if this is a duplicate, if so in (d) we set \
|
||||||
|
* the sle.next[1] field to 0x1 as a reminder to re-insert \
|
||||||
|
* this element as a duplicate in the restore function. \
|
||||||
|
* c) zero out the next sle.prev/next[] pointers \
|
||||||
|
* d) mark as duplicate, set sle.next[1] = 0x1 \
|
||||||
|
* e) insert the node's copy into the slh_pres singly-linked \
|
||||||
|
* list. \
|
||||||
|
*/ \
|
||||||
|
static int __skip_preserve_node_##decl(decl##_t *slist, decl##_node_t *node) \
|
||||||
|
{ \
|
||||||
|
int rc = 0; \
|
||||||
|
size_t amt, i; \
|
||||||
|
char *d; \
|
||||||
|
const char *s; \
|
||||||
|
decl##_node_t *dest, *is_dup; \
|
||||||
|
\
|
||||||
|
if (slist == NULL || node == NULL) \
|
||||||
|
return 0; \
|
||||||
|
\
|
||||||
|
/* (a) alloc and copy */ \
|
||||||
|
rc = prefix##skip_alloc_node_##decl(slist, &dest); \
|
||||||
|
if (rc) \
|
||||||
|
return rc; \
|
||||||
|
is_dup = 0; \
|
||||||
|
amt = sizeof(decl##_node_t); \
|
||||||
|
s = (const char *)node; \
|
||||||
|
d = (char *)dest; \
|
||||||
|
for (i = 0; i < amt; i++) \
|
||||||
|
d[i] = s[i]; \
|
||||||
|
\
|
||||||
|
/* (b) is this a duplicate? */ \
|
||||||
|
if (__skip_key_compare_##decl(slist, dest, dest->field.sle.next[0], slist->aux) == 0 || \
|
||||||
|
__skip_key_compare_##decl(slist, dest, dest->field.sle.prev, slist->aux) == 0) \
|
||||||
|
is_dup = (decl##_node_t *)0x1; \
|
||||||
|
\
|
||||||
|
/* (c) zero out the next pointers */ \
|
||||||
|
dest->field.sle.prev = NULL; \
|
||||||
|
__SKIP_ENTRIES_B2T(field, dest) \
|
||||||
|
{ \
|
||||||
|
dest->field.sle.next[lvl] = NULL; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
/* (d) set duplicate flag */ \
|
||||||
|
dest->field.sle.next[0] = (decl##_node_t *)is_dup; \
|
||||||
|
\
|
||||||
|
/* (e) insert node into slh_pres list at head */ \
|
||||||
|
if (slist->slh_pres == NULL) \
|
||||||
|
slist->slh_pres = dest; \
|
||||||
|
else { \
|
||||||
|
/* The next[0] pointer forms the singly-linked list when \
|
||||||
|
preserved. */ \
|
||||||
|
dest->field.sle.next[0] = slist->slh_pres; \
|
||||||
|
slist->slh_pres = dest; \
|
||||||
|
} \
|
||||||
|
rc++; \
|
||||||
|
\
|
||||||
|
return -rc; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
/* -- __skip_preserve_ \
|
/* -- __skip_preserve_ \
|
||||||
* Preserve nodes for snapshots if necessary. Returns > 0 are \
|
* Preserve nodes for snapshots if necessary. Returns > 0 are \
|
||||||
* errors, 0 means nothing preserved, negative number represents \
|
* errors, 0 means nothing preserved, negative number represents \
|
||||||
|
@ -448,67 +515,36 @@
|
||||||
* Foreach node in `path`, if the generation in that element \
|
* Foreach node in `path`, if the generation in that element \
|
||||||
* is less than the current generation for the list then \
|
* is less than the current generation for the list then \
|
||||||
* that node must be preserved before being mutated for this \
|
* that node must be preserved before being mutated for this \
|
||||||
* insert. So we: \
|
* insert. So we preserve that node, see _preserve_node(). \
|
||||||
* a) allocate a new node and copy this node into it without \
|
|
||||||
* copying the user-supplied additional memory (that will \
|
|
||||||
* happen iff an _update() is called on the node). \
|
|
||||||
* b) determine if this is a duplicate, if so we set the \
|
|
||||||
* sle.next[1] field to 0x1 as a reminder to re-insert \
|
|
||||||
* this element as a duplicate in the restore function. \
|
|
||||||
* c) zero out the next sle.prev/next[] pointers \
|
|
||||||
* d) insert the node's copy into the slh_pres singly-linked \
|
|
||||||
* list. \
|
|
||||||
* Meanwhile, don't duplicate head and the tail nodes if they \
|
* Meanwhile, don't duplicate head and the tail nodes if they \
|
||||||
* are in the path[]. \
|
* are in the path[]. \
|
||||||
*/ \
|
*/ \
|
||||||
static int __skip_preserve_##decl(decl##_t *slist, decl##_node_t **path, size_t len) \
|
static int __skip_preserve_##decl(decl##_t *slist, decl##_node_t **path, size_t len) \
|
||||||
{ \
|
{ \
|
||||||
int rc = 0, n = 0; \
|
int rc = 0, n; \
|
||||||
size_t i = 0; \
|
size_t i; \
|
||||||
decl##_node_t *node; \
|
|
||||||
\
|
\
|
||||||
if (path == NULL) \
|
if (path == NULL) \
|
||||||
return 0; \
|
return 0; \
|
||||||
\
|
\
|
||||||
for (i = 0; i < len; i++) { \
|
for (i = 0; i < len; i++) { \
|
||||||
node = path[i]; \
|
/* This is the case when there was no match, path[0] will be NULL. */ \
|
||||||
if (node == NULL) \
|
if (path[i] == NULL) \
|
||||||
continue; \
|
continue; \
|
||||||
|
/* No need to preserve the head or tail sentry nodes. */ \
|
||||||
|
if (path[i] == slist->slh_head || path[i] == slist->slh_tail) \
|
||||||
|
continue; \
|
||||||
|
\
|
||||||
|
/* When the generation of the node in the path is < the list's \
|
||||||
|
current generation, we must preserve it. */ \
|
||||||
if (path[i]->field.sle.gen < slist->gen) { \
|
if (path[i]->field.sle.gen < slist->gen) { \
|
||||||
if (path[i] == slist->slh_head || path[i] == slist->slh_tail) \
|
n = __skip_preserve_node_##decl(slist, path[i]); \
|
||||||
continue; \
|
if (n > 0) \
|
||||||
decl##_node_t *src = node, *dest, *this; \
|
return n; \
|
||||||
rc = prefix##skip_alloc_node_##decl(slist, &dest); \
|
rc += n; \
|
||||||
if (rc) \
|
|
||||||
return rc; \
|
|
||||||
size_t amt = sizeof(decl##_node_t) + sizeof(struct __skiplist_##decl_idx) * slist->max; \
|
|
||||||
char *d = NULL; \
|
|
||||||
const char *s = (const char *)src; \
|
|
||||||
d = (char *)dest; \
|
|
||||||
for (size_t i = 0; i < amt; i++) \
|
|
||||||
d[i] = s[i]; \
|
|
||||||
\
|
|
||||||
if (__skip_key_compare_##decl(slist, dest, dest->field.sle.next[0], slist->aux) == 0 || \
|
|
||||||
__skip_key_compare_##decl(slist, dest, dest->field.sle.prev, slist->aux) == 0) \
|
|
||||||
dest->field.sle.next[0] = (decl##_node_t *)0x1; \
|
|
||||||
\
|
|
||||||
this = dest; \
|
|
||||||
this->field.sle.prev = NULL; \
|
|
||||||
__SKIP_NEXT_ENTRIES_B2T(field, this) \
|
|
||||||
{ \
|
|
||||||
this->field.sle.next[lvl] = NULL; \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
if (slist->slh_pres == NULL) \
|
|
||||||
slist->slh_pres = dest; \
|
|
||||||
else { \
|
|
||||||
dest->field.sle.next[0] = slist->slh_pres; \
|
|
||||||
slist->slh_pres = dest; \
|
|
||||||
} \
|
|
||||||
n++; \
|
|
||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
return -n; \
|
return -rc; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
/* -- __skip_insert_ */ \
|
/* -- __skip_insert_ */ \
|
||||||
|
@ -516,48 +552,64 @@
|
||||||
{ \
|
{ \
|
||||||
int rc = 0; \
|
int rc = 0; \
|
||||||
static decl##_node_t apath[SKIPLIST_MAX_HEIGHT + 1]; \
|
static decl##_node_t apath[SKIPLIST_MAX_HEIGHT + 1]; \
|
||||||
size_t i, len, level; \
|
size_t i, len, cur_height, new_height; \
|
||||||
decl##_node_t *node, **path = (decl##_node_t **)&apath; \
|
decl##_node_t *node, **path = (decl##_node_t **)&apath; \
|
||||||
\
|
\
|
||||||
if (slist == NULL || n == NULL) \
|
if (slist == NULL || n == NULL) \
|
||||||
return ENOENT; \
|
return ENOENT; \
|
||||||
\
|
\
|
||||||
/* Allocate a buffer */ \
|
/* Allocate a buffer, or use a static one. */ \
|
||||||
if (SKIPLIST_MAX_HEIGHT == 1) { \
|
if (SKIPLIST_MAX_HEIGHT == 1) { \
|
||||||
path = malloc(sizeof(decl##_node_t *) * slist->max + 1); \
|
path = malloc(sizeof(decl##_node_t *) * slist->max + 1); \
|
||||||
if (path == NULL) \
|
if (path == NULL) \
|
||||||
return ENOMEM; \
|
return ENOMEM; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
|
/* Find the node `n` and path to it of `n` steps, if matched it's in `path[0]` */ \
|
||||||
len = __skip_locate_##decl(slist, n, path); \
|
len = __skip_locate_##decl(slist, n, path); \
|
||||||
node = path[0]; \
|
node = path[0]; \
|
||||||
if (len > 0) { \
|
if (len > 0) { \
|
||||||
if ((node != NULL) && (flags == 0)) { \
|
if ((node != NULL) && (flags == 0)) { \
|
||||||
/* Don't insert, duplicate flag not set. */ \
|
/* Don't insert, duplicate if flag not set. */ \
|
||||||
return -1; \
|
return -1; \
|
||||||
} \
|
} \
|
||||||
level = __skip_toss_##decl(slist->max - 1); \
|
/* Generation for snapshots */ \
|
||||||
n->field.sle.gen = slist->gen; \
|
n->field.sle.gen = slist->gen; \
|
||||||
n->field.sle.height = level; \
|
/* Coin toss to determine level of this new node [0, max) */ \
|
||||||
|
cur_height = slist->slh_head->field.sle.height; \
|
||||||
|
new_height = __skip_toss_##decl(slist->max - 1); \
|
||||||
|
n->field.sle.height = new_height; \
|
||||||
|
/* Trim the path to at most the new height for the new node. */ \
|
||||||
|
if (new_height > cur_height) { \
|
||||||
|
for (i = cur_height + 1; i <= new_height; i++) { \
|
||||||
|
path[i + 1] = slist->slh_tail; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
/* Snapshots preserve those nodes in the `path` that are will be \
|
||||||
|
modified on insert. */ \
|
||||||
rc = __skip_preserve_##decl(slist, path, len); \
|
rc = __skip_preserve_##decl(slist, path, len); \
|
||||||
|
/* > 0 is an error, 0 is no nodes were preserved, < 0 is -(rc) nodes \
|
||||||
|
preserved. */ \
|
||||||
if (rc > 0) \
|
if (rc > 0) \
|
||||||
return rc; \
|
return rc; \
|
||||||
for (i = slist->slh_head->field.sle.height + 1; i < n->field.sle.height + 1; i++) { \
|
/* Adjust all forward pointers for each element in the path. */ \
|
||||||
path[i + 1] = slist->slh_tail; \
|
for (i = 0; i <= new_height; i++) { \
|
||||||
} \
|
|
||||||
for (i = 0; i < n->field.sle.height + 1; i++) { \
|
|
||||||
n->field.sle.next[i] = path[i + 1]->field.sle.next[i]; \
|
n->field.sle.next[i] = path[i + 1]->field.sle.next[i]; \
|
||||||
path[i + 1]->field.sle.next[i] = n; \
|
path[i + 1]->field.sle.next[i] = n; \
|
||||||
} \
|
} \
|
||||||
|
/* Adujust the previous pointers in the nodes. */ \
|
||||||
n->field.sle.prev = path[1]; \
|
n->field.sle.prev = path[1]; \
|
||||||
n->field.sle.next[0]->field.sle.prev = n; \
|
n->field.sle.next[0]->field.sle.prev = n; \
|
||||||
|
/* Account for insert at tail. */ \
|
||||||
if (n->field.sle.next[0] == slist->slh_tail) { \
|
if (n->field.sle.next[0] == slist->slh_tail) { \
|
||||||
slist->slh_tail->field.sle.prev = n; \
|
slist->slh_tail->field.sle.prev = n; \
|
||||||
} \
|
} \
|
||||||
if (level > slist->slh_head->field.sle.height) { \
|
/* Adjust the head/tail boundary node heights if necessary. */ \
|
||||||
slist->slh_head->field.sle.height = level; \
|
if (new_height > cur_height) { \
|
||||||
slist->slh_tail->field.sle.height = level; \
|
slist->slh_head->field.sle.height = new_height; \
|
||||||
|
slist->slh_tail->field.sle.height = new_height; \
|
||||||
} \
|
} \
|
||||||
|
/* Increase our list length (aka. size, count, etc.) by one. */ \
|
||||||
slist->length++; \
|
slist->length++; \
|
||||||
\
|
\
|
||||||
if (SKIPLIST_MAX_HEIGHT == 1) \
|
if (SKIPLIST_MAX_HEIGHT == 1) \
|
||||||
|
@ -792,14 +844,14 @@
|
||||||
{ \
|
{ \
|
||||||
static decl##_node_t apath[SKIPLIST_MAX_HEIGHT + 1]; \
|
static decl##_node_t apath[SKIPLIST_MAX_HEIGHT + 1]; \
|
||||||
int cmp, np, rc = 0; \
|
int cmp, np, rc = 0; \
|
||||||
size_t len; \
|
size_t len, i; \
|
||||||
const unsigned char *p1, *p2; \
|
const unsigned char *p1, *p2; \
|
||||||
decl##_node_t *node, *new = NULL, **path = (decl##_node_t **)&apath; \
|
decl##_node_t *node, *new = NULL, **path = (decl##_node_t **)&apath; \
|
||||||
\
|
\
|
||||||
if (slist == NULL || n == NULL) \
|
if (slist == NULL || n == NULL) \
|
||||||
return -1; \
|
return -1; \
|
||||||
\
|
\
|
||||||
/* Allocate a buffer */ \
|
/* Allocate a buffer, or use a static one. */ \
|
||||||
if (SKIPLIST_MAX_HEIGHT == 1) { \
|
if (SKIPLIST_MAX_HEIGHT == 1) { \
|
||||||
path = malloc(sizeof(decl##_node_t *) * slist->max + 1); \
|
path = malloc(sizeof(decl##_node_t *) * slist->max + 1); \
|
||||||
if (path == NULL) \
|
if (path == NULL) \
|
||||||
|
@ -819,26 +871,27 @@
|
||||||
return np; \
|
return np; \
|
||||||
if (np < 0 && node->field.sle.gen < slist->gen) { \
|
if (np < 0 && node->field.sle.gen < slist->gen) { \
|
||||||
/* < 0 is the negated amount of nodes in path[] that were \
|
/* < 0 is the negated amount of nodes in path[] that were \
|
||||||
preserved. \
|
* preserved. \
|
||||||
\ \
|
* \
|
||||||
ALGORITHM: \
|
* ALGORITHM: \
|
||||||
At this point we've preserved a node but not the data \
|
* At this point we've preserved a node but not the data \
|
||||||
that we don't control in the entry (i.e. the "value" if \
|
* that we don't control in the entry (i.e. the "value" if \
|
||||||
it's a pointer to some heap memory, we have copied the \
|
* it's a pointer to some heap memory, we have copied the \
|
||||||
pointer in _preserve() but not anything else). We purposly \
|
* pointer in _preserve() but not anything else). We purposly \
|
||||||
delayed that allocation/copy to this point in time. \
|
* delayed that allocation/copy to this point in time. \
|
||||||
\ \
|
* \
|
||||||
So, we need to find the new node in slist->slh_pres, and \
|
* So, we need to find the new node in slist->slh_pres, and \
|
||||||
copy the data and then mark it. The only way to find that \
|
* copy the data and then mark it. The only way to find that \
|
||||||
node is to first use the comparison function and second \
|
* node is to first use the comparison function and second \
|
||||||
compare the bytes in the node that are not part of the entry \
|
* compare the bytes in the node that are not part of the entry \
|
||||||
structure. */ \
|
* structure. \
|
||||||
|
*/ \
|
||||||
do { \
|
do { \
|
||||||
new = (new == NULL ? slist->slh_pres : new->field.sle.next[0]); \
|
new = (new == NULL ? slist->slh_pres : new->field.sle.next[0]); \
|
||||||
cmp = __skip_key_compare_##decl(slist, node, new, slist->aux); \
|
cmp = __skip_key_compare_##decl(slist, node, new, slist->aux); \
|
||||||
p1 = (unsigned char *)((uintptr_t) new); \
|
p1 = (unsigned char *)((uintptr_t) new); \
|
||||||
p2 = (unsigned char *)((uintptr_t)node); \
|
p2 = (unsigned char *)((uintptr_t)node); \
|
||||||
for (size_t i = 0; i < (sizeof(decl##_node_t) - sizeof(struct __skiplist_##decl_entry)); i++) { \
|
for (i = 0; i < (sizeof(decl##_node_t) - sizeof(struct __skiplist_##decl_entry)); i++) { \
|
||||||
if (p1[i] != p2[i]) { \
|
if (p1[i] != p2[i]) { \
|
||||||
cmp = p1[i] < p2[i] ? -1 : 1; \
|
cmp = p1[i] < p2[i] ? -1 : 1; \
|
||||||
break; \
|
break; \
|
||||||
|
@ -846,11 +899,13 @@
|
||||||
} \
|
} \
|
||||||
} while (cmp != 0); \
|
} while (cmp != 0); \
|
||||||
if (cmp != 0) \
|
if (cmp != 0) \
|
||||||
return ENOMEM; /* TODO */ \
|
return ENOMEM; /* TODO we didn't find the node, why? */ \
|
||||||
new->field.sle.prev = (decl##_node_t *)0x1; \
|
if (new->field.sle.prev == NULL) { \
|
||||||
archive_node_blk; \
|
new->field.sle.prev = (decl##_node_t *)0x1; \
|
||||||
if (rc) \
|
archive_node_blk; \
|
||||||
return rc; \
|
if (rc) \
|
||||||
|
return rc; \
|
||||||
|
} \
|
||||||
} \
|
} \
|
||||||
new = n; \
|
new = n; \
|
||||||
update_node_blk; \
|
update_node_blk; \
|
||||||
|
@ -900,7 +955,8 @@
|
||||||
} \
|
} \
|
||||||
if (SKIPLIST_MAX_HEIGHT == 1) \
|
if (SKIPLIST_MAX_HEIGHT == 1) \
|
||||||
free(path); \
|
free(path); \
|
||||||
/* if we didn't preserve any nodes we can free this one */ \
|
\
|
||||||
|
/* If we didn't preserve any nodes we can free this one. */ \
|
||||||
if (np == 0) \
|
if (np == 0) \
|
||||||
free_node_blk; \
|
free_node_blk; \
|
||||||
\
|
\
|
||||||
|
@ -989,7 +1045,7 @@
|
||||||
decl##_t *prefix##skip_restore_snapshot_##decl(decl##_t *slist, unsigned gen) \
|
decl##_t *prefix##skip_restore_snapshot_##decl(decl##_t *slist, unsigned gen) \
|
||||||
{ \
|
{ \
|
||||||
size_t i; \
|
size_t i; \
|
||||||
decl##_node_t *node, *next, *prev; \
|
decl##_node_t *node, *prev; \
|
||||||
\
|
\
|
||||||
if (slist == NULL) \
|
if (slist == NULL) \
|
||||||
return NULL; \
|
return NULL; \
|
||||||
|
@ -1011,7 +1067,8 @@
|
||||||
* - the `node->field.sle.next[0]` forms a singly-linked list. \
|
* - the `node->field.sle.next[0]` forms a singly-linked list. \
|
||||||
*/ \
|
*/ \
|
||||||
\
|
\
|
||||||
SKIPLIST_EACH_H2T(decl, prefix, slist, node, i) \
|
/* (a) */ \
|
||||||
|
SKIPLIST_FOREACH_H2T(decl, prefix, slist, node, i) \
|
||||||
{ \
|
{ \
|
||||||
((void)i); \
|
((void)i); \
|
||||||
if (node->field.sle.gen > gen) \
|
if (node->field.sle.gen > gen) \
|
||||||
|
@ -1021,28 +1078,34 @@
|
||||||
prev = NULL; \
|
prev = NULL; \
|
||||||
node = slist->slh_pres; \
|
node = slist->slh_pres; \
|
||||||
while (node) { \
|
while (node) { \
|
||||||
next = node->field.sle.next[0]; \
|
/* (b) */ \
|
||||||
if (node->field.sle.gen > gen) { \
|
if (node->field.sle.gen > gen) { \
|
||||||
if (prev == NULL) \
|
/* remove node from slh_pres list */ \
|
||||||
slist->slh_pres = next; \
|
if (slist->slh_pres == node) \
|
||||||
else \
|
slist->slh_pres = node->field.sle.next[0]; \
|
||||||
prev->field.sle.next[0] = next; \
|
else { \
|
||||||
if (node->field.sle.next[0] == NULL) \
|
if (node->field.sle.next[0] == NULL) \
|
||||||
prev->field.sle.next[0] = NULL; \
|
prev->field.sle.next[0] = NULL; \
|
||||||
else \
|
else \
|
||||||
prev->field.sle.next[0] = next; \
|
prev->field.sle.next[0] = node->field.sle.next[0]; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
prefix##skip_free_node_##decl(node); \
|
prefix##skip_free_node_##decl(node); \
|
||||||
} \
|
} \
|
||||||
|
\
|
||||||
|
/* c */ \
|
||||||
prev = NULL; \
|
prev = NULL; \
|
||||||
if (node->field.sle.gen == gen) { \
|
if (node->field.sle.gen == gen) { \
|
||||||
if (prev == NULL) \
|
/* remove node from slh_pres list */ \
|
||||||
slist->slh_pres = next; \
|
if (slist->slh_pres == node) \
|
||||||
else \
|
slist->slh_pres = node->field.sle.next[0]; \
|
||||||
prev->field.sle.next[0] = next; \
|
else { \
|
||||||
if (node->field.sle.next[0] == NULL) \
|
if (node->field.sle.next[0] == NULL) \
|
||||||
prev->field.sle.next[0] = NULL; \
|
prev->field.sle.next[0] = NULL; \
|
||||||
else \
|
else \
|
||||||
prev->field.sle.next[0] = next; \
|
prev->field.sle.next[0] = node->field.sle.next[0]; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
node->field.sle.prev = NULL; \
|
node->field.sle.prev = NULL; \
|
||||||
if (node->field.sle.next[1] != 0) { \
|
if (node->field.sle.next[1] != 0) { \
|
||||||
node->field.sle.next[1] = NULL; \
|
node->field.sle.next[1] = NULL; \
|
||||||
|
@ -1052,9 +1115,12 @@
|
||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
prev = node; \
|
prev = node; \
|
||||||
node = next; \
|
node = node->field.sle.next[0]; \
|
||||||
} \
|
} \
|
||||||
|
\
|
||||||
|
/* (d) */ \
|
||||||
slist->max_gen = gen; \
|
slist->max_gen = gen; \
|
||||||
|
\
|
||||||
return slist; \
|
return slist; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
|
@ -1192,200 +1258,227 @@
|
||||||
return NULL; \
|
return NULL; \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define SKIPLIST_INTEGRITY_CHECK(decl, prefix, field) \
|
#define SKIPLIST_INTEGRITY_CHECK(decl, prefix, field) \
|
||||||
/* -- __skip_integrity_failure_ */ \
|
/* -- __skip_integrity_failure_ */ \
|
||||||
static void __attribute__((format(printf, 1, 2))) __skip_integrity_failure_##decl(const char *fmt, ...) \
|
static void __attribute__((format(printf, 1, 2))) __skip_integrity_failure_##decl(const char *fmt, ...) \
|
||||||
{ \
|
{ \
|
||||||
va_list args; \
|
va_list args; \
|
||||||
__skip_debugf(fmt, args); \
|
__skip_debugf(fmt, 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) \
|
||||||
{ \
|
{ \
|
||||||
unsigned 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_idx *this; \
|
struct __skiplist_##decl_idx *this; \
|
||||||
\
|
\
|
||||||
if (slist == NULL) { \
|
if (slist == NULL) { \
|
||||||
__skip_integrity_failure_##decl("slist was NULL, nothing to check"); \
|
__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"); \
|
__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"); \
|
__skip_integrity_failure_##decl("skiplist slh_tail is NULL\n"); \
|
||||||
n_err++; \
|
n_err++; \
|
||||||
return n_err; \
|
return n_err; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
if (slist->cmp == NULL) { \
|
if (slist->cmp == NULL) { \
|
||||||
__skip_integrity_failure_##decl("skiplist comparison function (cmp) is NULL"); \
|
__skip_integrity_failure_##decl("skiplist comparison function (cmp) is NULL\n"); \
|
||||||
n_err++; \
|
n_err++; \
|
||||||
return n_err; \
|
return n_err; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
if (slist->max < 2) { \
|
if (slist->max < 2) { \
|
||||||
__skip_integrity_failure_##decl("skiplist max level must be 1 at minimum"); \
|
__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->level >= slist->max) { \
|
if (slist->level >= slist->max) { \
|
||||||
/* 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 in header was >= max"); \
|
__skip_integrity_failure_##decl("skiplist level in header was >= max\n"); \
|
||||||
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"); \
|
__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 > 0 && slist->max > SKIPLIST_MAX_HEIGHT) { \
|
if (SKIPLIST_MAX_HEIGHT > 0 && slist->max > SKIPLIST_MAX_HEIGHT) { \
|
||||||
__skip_integrity_failure_##decl("slist->max cannot be greater than SKIPLIST_MAX_HEIGHT"); \
|
__skip_integrity_failure_##decl("slist->max cannot be greater than SKIPLIST_MAX_HEIGHT\n"); \
|
||||||
n_err++; \
|
n_err++; \
|
||||||
if (flags) \
|
if (flags) \
|
||||||
return n_err; \
|
return n_err; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
node = slist->slh_head; \
|
node = slist->slh_head; \
|
||||||
for (nth = 0; nth < node->field.sle.height; nth++) { \
|
for (nth = 0; nth < node->field.sle.height; nth++) { \
|
||||||
if (node->field.sle.next[nth] == NULL) { \
|
if (node->field.sle.next[nth] == NULL) { \
|
||||||
__skip_integrity_failure_##decl("the head's %u next node should not be NULL", nth); \
|
__skip_integrity_failure_##decl("the head's %lu next node should not be NULL", nth); \
|
||||||
n_err++; \
|
n_err++; \
|
||||||
if (flags) \
|
if (flags) \
|
||||||
return n_err; \
|
return n_err; \
|
||||||
} \
|
} \
|
||||||
if (node->field.sle.next[nth] == slist->slh_tail) \
|
if (node->field.sle.next[nth] == slist->slh_tail) \
|
||||||
break; \
|
break; \
|
||||||
} \
|
} \
|
||||||
for (; nth < node->field.sle.height; nth++) { \
|
for (; nth < node->field.sle.height; nth++) { \
|
||||||
if (node->field.sle.next[nth] == NULL) { \
|
if (node->field.sle.next[nth] == NULL) { \
|
||||||
__skip_integrity_failure_##decl("the head's %u next node should not be NULL", nth); \
|
__skip_integrity_failure_##decl("the head's %lu next node should not be NULL", nth); \
|
||||||
n_err++; \
|
n_err++; \
|
||||||
if (flags) \
|
if (flags) \
|
||||||
return n_err; \
|
return n_err; \
|
||||||
} \
|
} \
|
||||||
if (node->field.sle.next[nth] != slist->slh_tail) { \
|
if (node->field.sle.next[nth] != slist->slh_tail) { \
|
||||||
__skip_integrity_failure_##decl("after internal nodes, the head's %u next node should always be the tail", nth); \
|
__skip_integrity_failure_##decl("after internal nodes, the head's %lu next node should always be the tail", nth); \
|
||||||
n_err++; \
|
n_err++; \
|
||||||
if (flags) \
|
if (flags) \
|
||||||
return n_err; \
|
return n_err; \
|
||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
if (slist->length > 0 && slist->slh_tail->field.sle.prev == slist->slh_head) { \
|
if (slist->length > 0 && slist->slh_tail->field.sle.prev == slist->slh_head) { \
|
||||||
__skip_integrity_failure_##decl("slist->length is 0, but tail->prev == head, not an internal node"); \
|
__skip_integrity_failure_##decl("slist->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; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
/* TODO: ensure that the entries structure is the first or last element \
|
/* TODO: ensure that the entries structure is the last element \
|
||||||
in a node so that snapshots work. */ \
|
in a node so that snapshots work. GSB */ \
|
||||||
if (slist->length > 0 && slist->slh_tail->field.sle.prev == slist->slh_head) { \
|
if (slist->length > 0 && slist->slh_tail->field.sle.prev == slist->slh_head) { \
|
||||||
__skip_integrity_failure_##decl("slist->length is 0, but tail->prev == head, not an internal node"); \
|
__skip_integrity_failure_##decl("slist->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 */ \
|
||||||
nth = 0; \
|
nth = 0; \
|
||||||
node = prefix##skip_head_##decl(slist); \
|
node = prefix##skip_head_##decl(slist); \
|
||||||
while (node) { \
|
while (node != slist->slh_tail && node != NULL) { \
|
||||||
this = &node->field.sle; \
|
this = &node->field.sle; \
|
||||||
\
|
\
|
||||||
if (this->next == NULL) { \
|
if (this->next == NULL) { \
|
||||||
__skip_integrity_failure_##decl("the %uth node's [%p] next field should never NULL", 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->prev == NULL) { \
|
if (this->prev == NULL) { \
|
||||||
__skip_integrity_failure_##decl("the %u node [%p] prev field should never NULL", 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; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
uintptr_t a = (uintptr_t)this->next; \
|
/* TODO \
|
||||||
uintptr_t b = (intptr_t)((uintptr_t)node + sizeof(decl##_node_t)); \
|
size_t n; \
|
||||||
if (a != b) { \
|
for (n = 0; n < (this->height + 1); n++) { \
|
||||||
__skip_integrity_failure_##decl("the %uth node's [%p] next field isn't at the proper offset relative to the node", nth, (void *)node); \
|
if (this->next[n] == NULL) { \
|
||||||
n_err++; \
|
__skip_integrity_failure_##decl("the %luth node's next[%lu] should not be NULL\n", nth, n); \
|
||||||
if (flags) \
|
n_err++; \
|
||||||
return n_err; \
|
if (flags) \
|
||||||
} \
|
return n_err; \
|
||||||
\
|
} \
|
||||||
next = this->next[0]; \
|
if (this->next[n] == slist->slh_tail) \
|
||||||
prev = this->prev; \
|
break; \
|
||||||
if (__skip_key_compare_##decl(slist, node, node, slist->aux) != 0) { \
|
} \
|
||||||
__skip_integrity_failure_##decl("the %uth node [%p] is not equal to itself", nth, (void *)node); \
|
for (size_t m = n; n < (this->height + 1); n++) { \
|
||||||
n_err++; \
|
if (this->next[n] == NULL) { \
|
||||||
if (flags) \
|
__skip_integrity_failure_##decl("after the %lunth the %luth node's next[%lu] should not be NULL\n", m, nth, n); \
|
||||||
return n_err; \
|
n_err++; \
|
||||||
} \
|
if (flags) \
|
||||||
\
|
return n_err; \
|
||||||
if (__skip_key_compare_##decl(slist, node, prev, slist->aux) < 0) { \
|
} else if (this->next[n] != slist->slh_tail) { \
|
||||||
__skip_integrity_failure_##decl("the %uth node [%p] is not greater than the prev node [%p]", nth, (void *)node, (void *)prev); \
|
__skip_integrity_failure_##decl("after the %lunth the %luth node's next[%lu] should point to the tail\n", m, nth, n); \
|
||||||
n_err++; \
|
n_err++; \
|
||||||
if (flags) \
|
if (flags) \
|
||||||
return n_err; \
|
return n_err; \
|
||||||
} \
|
} \
|
||||||
\
|
} \
|
||||||
if (__skip_key_compare_##decl(slist, node, next, slist->aux) > 0) { \
|
*/ \
|
||||||
__skip_integrity_failure_##decl("the %uth node [%p] is not less than the next node [%p]", nth, (void *)node, (void *)next); \
|
\
|
||||||
n_err++; \
|
decl##_node_t *a = (decl##_node_t *)(uintptr_t)this->next; \
|
||||||
if (flags) \
|
decl##_node_t *b = (decl##_node_t *)(intptr_t)((uintptr_t)node + sizeof(decl##_node_t)); \
|
||||||
return n_err; \
|
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 (__skip_key_compare_##decl(slist, prev, node, slist->aux) > 0) { \
|
if (flags) \
|
||||||
__skip_integrity_failure_##decl("the prev node [%p] is not less than the %uth node [%p]", (void *)prev, nth, (void *)node); \
|
return n_err; \
|
||||||
n_err++; \
|
} \
|
||||||
if (flags) \
|
\
|
||||||
return n_err; \
|
next = this->next[0]; \
|
||||||
} \
|
prev = this->prev; \
|
||||||
\
|
if (__skip_key_compare_##decl(slist, node, node, slist->aux) != 0) { \
|
||||||
if (__skip_key_compare_##decl(slist, next, node, slist->aux) < 0) { \
|
__skip_integrity_failure_##decl("the %luth node [%p] is not equal to itself\n", nth, (void *)node); \
|
||||||
__skip_integrity_failure_##decl("the next node [%p] is not greater than the %uth node [%p]", (void *)next, nth, (void *)node); \
|
n_err++; \
|
||||||
n_err++; \
|
if (flags) \
|
||||||
if (flags) \
|
return n_err; \
|
||||||
return n_err; \
|
} \
|
||||||
} \
|
\
|
||||||
\
|
if (__skip_key_compare_##decl(slist, node, prev, slist->aux) < 0) { \
|
||||||
node = prefix##skip_next_node_##decl(slist, node); \
|
__skip_integrity_failure_##decl("the %luth node [%p] is not greater than the prev node [%p]\n", nth, (void *)node, (void *)prev); \
|
||||||
nth++; \
|
n_err++; \
|
||||||
} \
|
if (flags) \
|
||||||
\
|
return n_err; \
|
||||||
if (slist->length != nth) { \
|
} \
|
||||||
__skip_integrity_failure_##decl("slist->length doesn't match the count of nodes between the head and tail"); \
|
\
|
||||||
n_err++; \
|
if (__skip_key_compare_##decl(slist, node, next, slist->aux) > 0) { \
|
||||||
if (flags) \
|
__skip_integrity_failure_##decl("the %luth node [%p] is not less than the next node [%p]\n", nth, (void *)node, (void *)next); \
|
||||||
return n_err; \
|
n_err++; \
|
||||||
} \
|
if (flags) \
|
||||||
\
|
return n_err; \
|
||||||
return 0; \
|
} \
|
||||||
|
\
|
||||||
|
if (__skip_key_compare_##decl(slist, prev, node, slist->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_key_compare_##decl(slist, next, node, slist->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; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
node = prefix##skip_next_node_##decl(slist, node); \
|
||||||
|
nth++; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
if (slist->length != nth) { \
|
||||||
|
__skip_integrity_failure_##decl("slist->length doesn't match the count of nodes between the head and tail\n"); \
|
||||||
|
n_err++; \
|
||||||
|
if (flags) \
|
||||||
|
return n_err; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
return 0; \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define SKIPLIST_KV_ACCESS(decl, prefix, key, ktype, value, vtype, qblk, rblk) \
|
#define SKIPLIST_KV_ACCESS(decl, prefix, key, ktype, value, vtype, qblk, rblk) \
|
||||||
|
@ -1496,9 +1589,9 @@
|
||||||
static inline void __skip_dot_write_node_##decl(FILE *os, size_t nsg, decl##_node_t *node) \
|
static inline void __skip_dot_write_node_##decl(FILE *os, size_t nsg, decl##_node_t *node) \
|
||||||
{ \
|
{ \
|
||||||
if (node) \
|
if (node) \
|
||||||
fprintf(os, "\"node%zu %p\"", nsg, (void *)node); \
|
fprintf(os, "\"node%lu %p\"", nsg, (void *)node); \
|
||||||
else \
|
else \
|
||||||
fprintf(os, "\"node%zu NULL\"", nsg); \
|
fprintf(os, "\"node%lu NULL\"", nsg); \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
/* -- __skip_dot_node_ \
|
/* -- __skip_dot_node_ \
|
||||||
|
@ -1513,11 +1606,11 @@
|
||||||
__skip_dot_write_node_##decl(os, nsg, node); \
|
__skip_dot_write_node_##decl(os, nsg, node); \
|
||||||
fprintf(os, " [label = \""); \
|
fprintf(os, " [label = \""); \
|
||||||
fflush(os); \
|
fflush(os); \
|
||||||
__SKIP_NEXT_ENTRIES_T2B(field, node) \
|
__SKIP_ENTRIES_T2B(field, node) \
|
||||||
{ \
|
{ \
|
||||||
next = (node->field.sle.next[lvl] == slist->slh_tail) ? NULL : node->field.sle.next[lvl]; \
|
next = (node->field.sle.next[lvl] == slist->slh_tail) ? NULL : node->field.sle.next[lvl]; \
|
||||||
width = __skip_dot_width_##decl(slist, node, next ? next : slist->slh_tail); \
|
width = __skip_dot_width_##decl(slist, node, next ? next : slist->slh_tail); \
|
||||||
fprintf(os, " { <w%zu> %zu | <f%zu> ", lvl, width, lvl); \
|
fprintf(os, " { <w%lu> %lu | <f%lu> ", lvl, width, lvl); \
|
||||||
if (next) \
|
if (next) \
|
||||||
fprintf(os, "%p } |", (void *)next); \
|
fprintf(os, "%p } |", (void *)next); \
|
||||||
else \
|
else \
|
||||||
|
@ -1526,22 +1619,22 @@
|
||||||
} \
|
} \
|
||||||
if (fn) { \
|
if (fn) { \
|
||||||
fn(node, buf); \
|
fn(node, buf); \
|
||||||
fprintf(os, " <f0> \u219F %zu \u226B %s \"\n", node->field.sle.height + 1, buf); \
|
fprintf(os, " <f0> \u219F %lu \u226B %s \"\n", node->field.sle.height + 1, buf); \
|
||||||
} else { \
|
} else { \
|
||||||
fprintf(os, " <f0> \u219F %zu \"\n", node->field.sle.height); \
|
fprintf(os, " <f0> \u219F %lu \"\n", node->field.sle.height); \
|
||||||
} \
|
} \
|
||||||
fprintf(os, "shape = \"record\"\n"); \
|
fprintf(os, "shape = \"record\"\n"); \
|
||||||
fprintf(os, "];\n"); \
|
fprintf(os, "];\n"); \
|
||||||
fflush(os); \
|
fflush(os); \
|
||||||
\
|
\
|
||||||
/* Now edges */ \
|
/* Now edges */ \
|
||||||
__SKIP_NEXT_ENTRIES_B2T(field, node) \
|
__SKIP_ENTRIES_B2T(field, node) \
|
||||||
{ \
|
{ \
|
||||||
next = (node->field.sle.next[lvl] == slist->slh_tail) ? NULL : node->field.sle.next[lvl]; \
|
next = (node->field.sle.next[lvl] == slist->slh_tail) ? NULL : node->field.sle.next[lvl]; \
|
||||||
__skip_dot_write_node_##decl(os, nsg, node); \
|
__skip_dot_write_node_##decl(os, nsg, node); \
|
||||||
fprintf(os, ":f%zu -> ", lvl); \
|
fprintf(os, ":f%lu -> ", lvl); \
|
||||||
__skip_dot_write_node_##decl(os, nsg, next); \
|
__skip_dot_write_node_##decl(os, nsg, next); \
|
||||||
fprintf(os, ":w%zu [];\n", lvl); \
|
fprintf(os, ":w%lu [];\n", lvl); \
|
||||||
fflush(os); \
|
fflush(os); \
|
||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
|
@ -1559,7 +1652,7 @@
|
||||||
*/ \
|
*/ \
|
||||||
fprintf(os, "node0 [shape=record, label = \""); \
|
fprintf(os, "node0 [shape=record, label = \""); \
|
||||||
for (i = 0; i < nsg; ++i) { \
|
for (i = 0; i < nsg; ++i) { \
|
||||||
fprintf(os, "<f%zu> | ", i); \
|
fprintf(os, "<f%lu> | ", i); \
|
||||||
} \
|
} \
|
||||||
fprintf(os, "\", style=invis, width=0.01];\n"); \
|
fprintf(os, "\", style=invis, width=0.01];\n"); \
|
||||||
\
|
\
|
||||||
|
@ -1569,7 +1662,7 @@
|
||||||
* node0:f1 -> HeadNode1 [style=invis]; \
|
* node0:f1 -> HeadNode1 [style=invis]; \
|
||||||
*/ \
|
*/ \
|
||||||
for (i = 0; i < nsg; ++i) { \
|
for (i = 0; i < nsg; ++i) { \
|
||||||
fprintf(os, "node0:f%zu -> HeadNode%zu [style=invis];\n", i, i); \
|
fprintf(os, "node0:f%lu -> HeadNode%lu [style=invis];\n", i, i); \
|
||||||
} \
|
} \
|
||||||
nsg = 0; \
|
nsg = 0; \
|
||||||
} \
|
} \
|
||||||
|
@ -1607,10 +1700,10 @@
|
||||||
fprintf(os, "node [fontsize = \"12\" shape = \"ellipse\"];\n"); \
|
fprintf(os, "node [fontsize = \"12\" shape = \"ellipse\"];\n"); \
|
||||||
fprintf(os, "edge [];\n\n"); \
|
fprintf(os, "edge [];\n\n"); \
|
||||||
} \
|
} \
|
||||||
fprintf(os, "subgraph cluster%zu {\n", nsg); \
|
fprintf(os, "subgraph cluster%lu {\n", nsg); \
|
||||||
fprintf(os, "style=dashed\n"); \
|
fprintf(os, "style=dashed\n"); \
|
||||||
fprintf(os, "label=\"Skip list iteration %zu\"\n\n", nsg); \
|
fprintf(os, "label=\"Skip list iteration %lu\"\n\n", nsg); \
|
||||||
fprintf(os, "\"HeadNode%zu\" [\n", nsg); \
|
fprintf(os, "\"HeadNode%lu\" [\n", nsg); \
|
||||||
fprintf(os, "label = \""); \
|
fprintf(os, "label = \""); \
|
||||||
\
|
\
|
||||||
if (slist->slh_head->field.sle.height || slist->slh_head->field.sle.next[0] != slist->slh_tail) \
|
if (slist->slh_head->field.sle.height || slist->slh_head->field.sle.next[0] != slist->slh_tail) \
|
||||||
|
@ -1619,11 +1712,11 @@
|
||||||
/* Write out the fields */ \
|
/* Write out the fields */ \
|
||||||
node = slist->slh_head; \
|
node = slist->slh_head; \
|
||||||
if (letitgo) { \
|
if (letitgo) { \
|
||||||
__SKIP_NEXT_ENTRIES_T2B(field, node) \
|
__SKIP_ENTRIES_T2B(field, node) \
|
||||||
{ \
|
{ \
|
||||||
next = (node->field.sle.next[lvl] == slist->slh_tail) ? NULL : node->field.sle.next[lvl]; \
|
next = (node->field.sle.next[lvl] == slist->slh_tail) ? NULL : node->field.sle.next[lvl]; \
|
||||||
width = __skip_dot_width_##decl(slist, node, next ? next : slist->slh_tail); \
|
width = __skip_dot_width_##decl(slist, node, next ? next : slist->slh_tail); \
|
||||||
fprintf(os, "{ %zu | <f%zu> ", width, lvl); \
|
fprintf(os, "{ %lu | <f%lu> ", width, lvl); \
|
||||||
if (next) \
|
if (next) \
|
||||||
fprintf(os, "%p }", (void *)next); \
|
fprintf(os, "%p }", (void *)next); \
|
||||||
else \
|
else \
|
||||||
|
@ -1643,12 +1736,12 @@
|
||||||
node = slist->slh_head; \
|
node = slist->slh_head; \
|
||||||
if (letitgo) { \
|
if (letitgo) { \
|
||||||
node = slist->slh_head; \
|
node = slist->slh_head; \
|
||||||
__SKIP_NEXT_ENTRIES_B2T(field, node) \
|
__SKIP_ENTRIES_B2T(field, node) \
|
||||||
{ \
|
{ \
|
||||||
next = (node->field.sle.next[lvl] == slist->slh_tail) ? NULL : node->field.sle.next[lvl]; \
|
next = (node->field.sle.next[lvl] == slist->slh_tail) ? NULL : node->field.sle.next[lvl]; \
|
||||||
fprintf(os, "\"HeadNode%zu\":f%zu -> ", nsg, lvl); \
|
fprintf(os, "\"HeadNode%lu\":f%lu -> ", nsg, lvl); \
|
||||||
__skip_dot_write_node_##decl(os, nsg, next); \
|
__skip_dot_write_node_##decl(os, nsg, next); \
|
||||||
fprintf(os, ":w%zu [];\n", lvl); \
|
fprintf(os, ":w%lu [];\n", lvl); \
|
||||||
} \
|
} \
|
||||||
fprintf(os, "\n"); \
|
fprintf(os, "\n"); \
|
||||||
} \
|
} \
|
||||||
|
@ -1657,7 +1750,7 @@
|
||||||
/* Now all nodes via level 0, if non-empty */ \
|
/* Now all nodes via level 0, if non-empty */ \
|
||||||
node = slist->slh_head; \
|
node = slist->slh_head; \
|
||||||
if (letitgo) { \
|
if (letitgo) { \
|
||||||
SKIPLIST_EACH_H2T(decl, prefix, slist, next, i) \
|
SKIPLIST_FOREACH_H2T(decl, prefix, slist, next, i) \
|
||||||
{ \
|
{ \
|
||||||
((void)i); \
|
((void)i); \
|
||||||
__skip_dot_node_##decl(os, slist, next, nsg, fn); \
|
__skip_dot_node_##decl(os, slist, next, nsg, fn); \
|
||||||
|
@ -1672,10 +1765,10 @@
|
||||||
__skip_dot_write_node_##decl(os, nsg, NULL); \
|
__skip_dot_write_node_##decl(os, nsg, NULL); \
|
||||||
fprintf(os, " [label = \""); \
|
fprintf(os, " [label = \""); \
|
||||||
node = slist->slh_tail; \
|
node = slist->slh_tail; \
|
||||||
size_t th = slist->slh_head->field.sle.height == slist->max ? slist->max : slist->slh_head->field.sle.height + 1; \
|
size_t th = slist->slh_head->field.sle.height; \
|
||||||
for (size_t lvl = th; lvl != (size_t)-1; lvl--) { \
|
for (size_t lvl = th; lvl != (size_t)-1; lvl--) { \
|
||||||
next = (node->field.sle.next[lvl] == slist->slh_tail) ? NULL : node->field.sle.next[lvl]; \
|
next = (node->field.sle.next[lvl] == slist->slh_tail) ? NULL : node->field.sle.next[lvl]; \
|
||||||
fprintf(os, "<w%zu> NULL", lvl); \
|
fprintf(os, "<w%lu> NULL", lvl); \
|
||||||
__SKIP_IS_LAST_ENTRY_T2B() continue; \
|
__SKIP_IS_LAST_ENTRY_T2B() continue; \
|
||||||
fprintf(os, " | "); \
|
fprintf(os, " | "); \
|
||||||
} \
|
} \
|
||||||
|
|
Loading…
Reference in a new issue