cleanup, some still TODO

This commit is contained in:
Gregory Burd 2024-04-03 15:40:39 -04:00
parent 397259c09d
commit 9a3da0c8cf
3 changed files with 192 additions and 315 deletions

View file

@ -5,8 +5,8 @@ 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,leak,object-size,pointer-compare,pointer-subtract,null,return,bounds,pointer-overflow,undefined -fsanitize-address-use-after-scope -std=c99 -Iinclude/ -fPIC
CFLAGS = -Wall -Wextra -Wpedantic -Og -g -std=c99 -Iinclude/ -fPIC
#CFLAGS = -Wall -Wextra -Wpedantic -Og -g -fsanitize=address,leak,object-size,pointer-compare,pointer-subtract,null,return,bounds,pointer-overflow,undefined -fsanitize-address-use-after-scope -std=c99 -Iinclude/ -fPIC
#CFLAGS = -Wall -Wextra -Wpedantic -Og -g -fsanitize=all -fhardened -std=c99 -Iinclude/ -fPIC
#env ASAN_OPTIONS=detect_leaks=1 LSAN_OPTIONS=verbosity=1:log_threads=1 ./examples/mls

View file

@ -1,5 +1,5 @@
#pragma GCC push_options
#pragma GCC optimize ("O0")
#pragma GCC optimize("O0")
// OPTIONS to set before including sl.h
// ---------------------------------------------------------------------------
@ -18,23 +18,19 @@
// Local demo application OPTIONS:
// ---------------------------------------------------------------------------
#define TEST_ARRAY_SIZE 1000
#define TEST_ARRAY_SIZE 10
#define VALIDATE
#define SNAPSHOTS
//define TODO_RESTORE_SNAPSHOTS
#define DOT
#ifdef DOT
size_t gen = 0;
FILE *of = 0;
typedef struct esempio esempio_t;
typedef struct esempio_node esempio_node_t;
static int __skip_integrity_check_esempio(esempio_t *slist, int flags);
int api_skip_dot_esempio(FILE *os, esempio_t *slist, size_t nsg, char *msg, void (*fn)(struct esempio_node *, char *));
void sprintf_esempio_node(esempio_node_t *node, char *buf);
#endif
// ---------------------------------------------------------------------------
#ifdef VALIDATE
#define CHECK __skip_integrity_check_esempio(list, 0)
#define CHECK __skip_integrity_check_ex(list, 0)
#else
#define CHECK ((void)0)
#endif
@ -42,7 +38,7 @@ void sprintf_esempio_node(esempio_node_t *node, char *buf);
/*
* SKIPLIST EXAMPLE:
*
* This example creates an "esempio" (example in Italian) Skiplist where keys
* This example creates an "ex" (example in Italian) Skiplist where keys
* are integers, values are strings allocated on the heap.
*/
@ -54,17 +50,17 @@ void sprintf_esempio_node(esempio_node_t *node, char *buf);
* node against another, logic you'll provide in SKIP_DECL as a
* block below.
*/
struct esempio_node {
struct ex_node {
int key;
char *value;
SKIPLIST_ENTRY(esempio) entries;
SKIPLIST_ENTRY(ex) entries;
};
/*
* Generate all the access functions for our type of Skiplist.
*/
SKIPLIST_DECL(
esempio, api_, entries,
ex, api_, entries,
/* compare entries: list, a, b, aux */
{
(void)list;
@ -119,7 +115,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
__esempio_key_compare(esempio_t *list, esempio_node_t *a, esempio_node_t *b, void *aux)
__ex_key_compare(ex_t *list, ex_node_t *a, ex_node_t *b, void *aux)
{
(void)list;
(void)aux;
@ -140,7 +136,7 @@ __esempio_key_compare(esempio_t *list, esempio_node_t *a, esempio_node_t *b, voi
* extract data from within your nodes.
*/
SKIPLIST_DECL_ACCESS(
esempio, api_, key, int, value, char *,
ex, api_, key, int, value, char *,
/* query blk */ { query.key = key; },
/* return blk */ { return node->value; })
@ -150,7 +146,7 @@ SKIPLIST_DECL_ACCESS(
* Enable functions that enable returning to an earlier point in
* time when a snapshot was created.
*/
SKIPLIST_DECL_SNAPSHOTS(esempio, api_, entries)
SKIPLIST_DECL_SNAPSHOTS(ex, api_, entries)
/*
* Optional: Archive to/from bytes
@ -158,33 +154,33 @@ SKIPLIST_DECL_SNAPSHOTS(esempio, api_, entries)
* Enable functions that can write/read the content of your Skiplist
* out/in to/from an array of bytes.
*/
SKIPLIST_DECL_ARCHIVE(esempio, api_, entries)
// TODO SKIPLIST_DECL_ARCHIVE(ex, api_, entries)
/*
* Optional: As Hashtable
*
* Turn your Skiplist into a hash table.
*/
//TODO SKIPLIST_DECL_HASHTABLE(esempio, api_, entries, snaps)
// TODO SKIPLIST_DECL_HASHTABLE(ex, api_, entries, snaps)
/*
* Optional: Check Skiplists at runtime
*
* Create a functions that validate the integrity of a Skiplist.
*/
SKIPLIST_DECL_VALIDATE(esempio, api_, entries)
SKIPLIST_DECL_VALIDATE(ex, 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(esempio, api_, entries)
SKIPLIST_DECL_DOT(ex, api_, entries)
void
sprintf_esempio_node(esempio_node_t *node, char *buf)
sprintf_ex_node(ex_node_t *node, char *buf)
{
sprintf(buf, "%d:%s (hits: %lu)", node->key, node->value, node->entries.sle_levels[0].hits);
//TODO sprintf(buf, "%d:%s", node->key, node->value);
// sprintf(buf, "%d:%s (hits: %lu)", node->key, node->value, node->entries.sle_levels[0].hits);
sprintf(buf, "%d:%s", node->key, node->value);
}
// Function for this demo application.
@ -253,14 +249,22 @@ shuffle(int *array, size_t n)
}
}
#ifdef TODO_RESTORE_SNAPSHOTS
typedef struct {
size_t length;
size_t key;
size_t snap_id;
} snap_info_t;
#endif
// ---------------------------------------------------------------------------
int
main()
{
int rc;
#ifdef SNAPSHOTS
size_t snap_i = 0;
uint64_t snap_ids[2048];
#ifdef TODO_RESTORE_SNAPSHOTS
size_t n_snaps = 0;
snap_info_t snaps[TEST_ARRAY_SIZE * 2 + 1];
#endif
#ifdef DOT
@ -272,28 +276,23 @@ main()
#endif
/* Allocate and initialize a Skiplist. */
esempio_t *list = (esempio_t *)malloc(sizeof(esempio_t));
ex_t *list = (ex_t *)malloc(sizeof(ex_t));
if (list == NULL)
return ENOMEM;
rc = api_skip_init_esempio(list, -12);
rc = api_skip_init_ex(list, -12);
if (rc)
return rc;
api_skip_snapshots_init_esempio(list);
api_skip_snapshots_init_ex(list);
#ifdef DOT
api_skip_dot_esempio(of, list, gen++, "init", sprintf_esempio_node);
api_skip_dot_ex(of, list, gen++, "init", sprintf_ex_node);
#endif
if (api_skip_get_esempio(list, 0) != NULL)
if (api_skip_get_ex(list, 0) != NULL)
perror("found a non-existent item!");
api_skip_del_esempio(list, 0);
api_skip_del_ex(list, 0);
CHECK;
#ifdef SNAPSHOTS
/* Test creating a snapshot of an empty Skiplist */
snap_ids[snap_i++] = api_skip_snapshot_esempio(list);
#endif
/* Insert 7 key/value pairs into the list. */
/* Insert TEST_ARRAY_SIZE key/value pairs into the list. */
int i, j;
char *numeral;
#ifdef DOT
@ -307,138 +306,143 @@ main()
shuffle(array, asz);
for (i = 0; i < asz; i++) {
numeral = to_lower(int_to_roman_numeral(array[i]));
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_esempio(list);
api_skip_snapshot_ex(list);
#elif defined(TODO_RESTORE_SNAPSHOTS)
/* Snapshot the first iteration, and then every 5th after that. */
if (i % 5 == 0) {
snaps[i].length = api_skip_length_ex(list);
snaps[i].key = array[i];
snaps[i].snap_id = api_skip_snapshot_ex(list);
n_snaps++;
CHECK;
}
#endif
numeral = to_lower(int_to_roman_numeral(array[i]));
rc = api_skip_put_ex(list, array[i], numeral);
CHECK;
#ifdef DOT
sprintf(msg, "put key: %d value: %s", i, numeral);
api_skip_dot_esempio(of, list, gen++, msg, sprintf_esempio_node);
CHECK;
api_skip_dot_ex(of, list, gen++, msg, sprintf_ex_node);
#endif
char *v = api_skip_get_esempio(list, array[i]);
char *v = api_skip_get_ex(list, array[i]);
CHECK;
char *upper_numeral = calloc(1, strlen(v) + 1);
strncpy(upper_numeral, v, strlen(v));
assert(strncmp(v, upper_numeral, strlen(upper_numeral)) == 0);
to_upper(upper_numeral);
api_skip_set_esempio(list, array[i], upper_numeral);
api_skip_set_ex(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_esempio(list, -1, numeral);
api_skip_dup_ex(list, -1, numeral);
CHECK;
#ifdef DOT
sprintf(msg, "put dup key: %d value: %s", i, numeral);
api_skip_dot_esempio(of, list, gen++, msg, sprintf_esempio_node);
CHECK;
api_skip_dot_ex(of, list, gen++, msg, sprintf_ex_node);
#endif
numeral = int_to_roman_numeral(1);
api_skip_dup_esempio(list, 1, numeral);
api_skip_dup_ex(list, 1, numeral);
CHECK;
#ifdef DOT
sprintf(msg, "put dup key: %d value: %s", i, numeral);
api_skip_dot_esempio(of, list, gen++, msg, sprintf_esempio_node);
CHECK;
api_skip_dot_ex(of, list, gen++, msg, sprintf_ex_node);
#endif
api_skip_del_esempio(list, 0);
api_skip_del_ex(list, 0);
CHECK;
if (api_skip_get_esempio(list, 0) != NULL)
if (api_skip_get_ex(list, 0) != NULL)
perror("found a deleted item!");
api_skip_del_esempio(list, 0);
api_skip_del_ex(list, 0);
CHECK;
if (api_skip_get_esempio(list, 0) != NULL)
if (api_skip_get_ex(list, 0) != NULL)
perror("found a deleted item!");
int key = TEST_ARRAY_SIZE + 1;
api_skip_del_esempio(list, key);
api_skip_del_ex(list, key);
CHECK;
key = -(TEST_ARRAY_SIZE)-1;
api_skip_del_esempio(list, key);
api_skip_del_ex(list, key);
CHECK;
#ifdef DOT
sprintf(msg, "deleted key: %d, value: %s", 0, numeral);
api_skip_dot_esempio(of, list, gen++, msg, sprintf_esempio_node);
CHECK;
#endif
#ifdef SNAPSHOTS
api_skip_restore_snapshot_esempio(list, snap_ids[snap_i - 1]);
api_skip_release_snapshots_esempio(list);
api_skip_dot_ex(of, list, gen++, msg, sprintf_ex_node);
#endif
numeral = int_to_roman_numeral(-(TEST_ARRAY_SIZE));
assert(strcmp(api_skip_pos_esempio(list, SKIP_GTE, -(TEST_ARRAY_SIZE)-1)->value, numeral) == 0);
assert(strcmp(api_skip_pos_ex(list, SKIP_GTE, -(TEST_ARRAY_SIZE)-1)->value, numeral) == 0);
free(numeral);
numeral = int_to_roman_numeral(-2);
assert(strcmp(api_skip_pos_esempio(list, SKIP_GTE, -2)->value, numeral) == 0);
assert(strcmp(api_skip_pos_ex(list, SKIP_GTE, -2)->value, numeral) == 0);
free(numeral);
numeral = int_to_roman_numeral(1);
assert(strcmp(api_skip_pos_esempio(list, SKIP_GTE, 0)->value, numeral) == 0);
assert(strcmp(api_skip_pos_ex(list, SKIP_GTE, 0)->value, numeral) == 0);
free(numeral);
numeral = int_to_roman_numeral(2);
assert(strcmp(api_skip_pos_esempio(list, SKIP_GTE, 2)->value, numeral) == 0);
assert(strcmp(api_skip_pos_ex(list, SKIP_GTE, 2)->value, numeral) == 0);
free(numeral);
assert(api_skip_pos_esempio(list, SKIP_GTE, (TEST_ARRAY_SIZE + 1)) == NULL);
assert(api_skip_pos_ex(list, SKIP_GTE, (TEST_ARRAY_SIZE + 1)) == NULL);
numeral = int_to_roman_numeral(-(TEST_ARRAY_SIZE));
assert(strcmp(api_skip_pos_esempio(list, SKIP_GT, -(TEST_ARRAY_SIZE)-1)->value, numeral) == 0);
assert(strcmp(api_skip_pos_ex(list, SKIP_GT, -(TEST_ARRAY_SIZE)-1)->value, numeral) == 0);
free(numeral);
numeral = int_to_roman_numeral(-1);
assert(strcmp(api_skip_pos_esempio(list, SKIP_GT, -2)->value, numeral) == 0);
assert(strcmp(api_skip_pos_ex(list, SKIP_GT, -2)->value, numeral) == 0);
free(numeral);
numeral = int_to_roman_numeral(1);
assert(strcmp(api_skip_pos_esempio(list, SKIP_GT, 0)->value, numeral) == 0);
assert(strcmp(api_skip_pos_ex(list, SKIP_GT, 0)->value, numeral) == 0);
free(numeral);
numeral = int_to_roman_numeral(2);
assert(strcmp(api_skip_pos_esempio(list, SKIP_GT, 1)->value, numeral) == 0);
assert(strcmp(api_skip_pos_ex(list, SKIP_GT, 1)->value, numeral) == 0);
free(numeral);
assert(api_skip_pos_esempio(list, SKIP_GT, TEST_ARRAY_SIZE) == NULL);
assert(api_skip_pos_ex(list, SKIP_GT, TEST_ARRAY_SIZE) == NULL);
assert(api_skip_pos_esempio(list, SKIP_LT, -(TEST_ARRAY_SIZE)) == NULL);
assert(api_skip_pos_ex(list, SKIP_LT, -(TEST_ARRAY_SIZE)) == NULL);
numeral = int_to_roman_numeral(-2);
assert(strcmp(api_skip_pos_esempio(list, SKIP_LT, -1)->value, numeral) == 0);
assert(strcmp(api_skip_pos_ex(list, SKIP_LT, -1)->value, numeral) == 0);
free(numeral);
numeral = int_to_roman_numeral(-1);
assert(strcmp(api_skip_pos_esempio(list, SKIP_LT, 0)->value, numeral) == 0);
assert(strcmp(api_skip_pos_ex(list, SKIP_LT, 0)->value, numeral) == 0);
free(numeral);
numeral = int_to_roman_numeral(1);
assert(strcmp(api_skip_pos_esempio(list, SKIP_LT, 2)->value, numeral) == 0);
assert(strcmp(api_skip_pos_ex(list, SKIP_LT, 2)->value, numeral) == 0);
free(numeral);
numeral = int_to_roman_numeral(TEST_ARRAY_SIZE);
assert(strcmp(api_skip_pos_esempio(list, SKIP_LT, (TEST_ARRAY_SIZE + 1))->value, numeral) == 0);
assert(strcmp(api_skip_pos_ex(list, SKIP_LT, (TEST_ARRAY_SIZE + 1))->value, numeral) == 0);
free(numeral);
assert(api_skip_pos_esempio(list, SKIP_LTE, -(TEST_ARRAY_SIZE)-1) == NULL);
assert(api_skip_pos_ex(list, SKIP_LTE, -(TEST_ARRAY_SIZE)-1) == NULL);
numeral = int_to_roman_numeral(-2);
assert(strcmp(api_skip_pos_esempio(list, SKIP_LTE, -2)->value, numeral) == 0);
assert(strcmp(api_skip_pos_ex(list, SKIP_LTE, -2)->value, numeral) == 0);
free(numeral);
numeral = int_to_roman_numeral(-1);
assert(strcmp(api_skip_pos_esempio(list, SKIP_LTE, 0)->value, numeral) == 0);
assert(strcmp(api_skip_pos_ex(list, SKIP_LTE, 0)->value, numeral) == 0);
free(numeral);
numeral = int_to_roman_numeral(2);
assert(strcmp(api_skip_pos_esempio(list, SKIP_LTE, 2)->value, numeral) == 0);
assert(strcmp(api_skip_pos_ex(list, SKIP_LTE, 2)->value, numeral) == 0);
free(numeral);
numeral = int_to_roman_numeral(TEST_ARRAY_SIZE);
assert(strcmp(api_skip_pos_esempio(list, SKIP_LTE, (TEST_ARRAY_SIZE + 1))->value, numeral) == 0);
assert(strcmp(api_skip_pos_ex(list, SKIP_LTE, (TEST_ARRAY_SIZE + 1))->value, numeral) == 0);
free(numeral);
#ifdef TODO_RESTORE_SNAPSHOTS
// Walk backward by 2 and test snapshot restore.
for (i = n_snaps; i > 0; i -= 2) {
api_skip_restore_snapshot_ex(list, snaps[i].snap_id);
CHECK;
assert(api_skip_length_ex(list) == snaps[i].length);
numeral = int_to_roman_numeral(snaps[i].key);
assert(strncmp(api_skip_get_ex(list, snaps[i].key), numeral, strlen(numeral)) == 0);
free(numeral);
}
api_skip_release_snapshots_ex(list);
#endif
#ifdef DOT
api_skip_dot_end_esempio(of, gen);
api_skip_dot_end_ex(of, gen);
fclose(of);
#endif
api_skip_free_esempio(list);
api_skip_free_ex(list);
free(list);
return rc;
}

View file

@ -265,14 +265,12 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
int (*compare_entries)(decl##_t *, decl##_node_t *, decl##_node_t *, void *); \
\
/* Optional: Snapshots */ \
int (*preserve_node)(decl##_t * slist, const decl##_node_t *src, decl##_node_t **preserved); \
void (*release_snapshots)(decl##_t *); \
size_t (*snapshot_current_era)(decl##_t *); \
size_t (*snapshot_incr_era)(decl##_t *); \
void (*snapshot_record_era)(decl##_t *, decl##_node_t *); \
int (*snapshot_preserve_node)(decl##_t * slist, const decl##_node_t *src, decl##_node_t **preserved); \
void (*snapshot_release)(decl##_t *); \
} slh_fns; \
struct { \
size_t era; \
size_t cur_era; \
size_t pres_era; \
decl##_node_t *pres; \
} slh_snap; \
}; \
@ -419,7 +417,8 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
\
slist->slh_length = 0; \
slist->slh_max_height = SKIPLIST_MAX_HEIGHT == 1 ? (size_t)(max < 0 ? -max : max) : SKIPLIST_MAX_HEIGHT; \
slist->slh_snap.era = 0; \
slist->slh_snap.cur_era = 0; \
slist->slh_snap.pres_era = 0; \
slist->slh_snap.pres = 0; \
slist->slh_fns.free_entry = __skip_free_entry_fn_##decl; \
slist->slh_fns.update_entry = __skip_update_entry_fn_##decl; \
@ -433,11 +432,13 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
if (rc) \
goto fail; \
\
/* Initial height is 0 (meaning 1 level), all next point to tail */ \
slist->slh_head->field.sle_height = 0; \
for (i = 0; i < slist->slh_max_height; i++) \
slist->slh_head->field.sle_levels[i].next = slist->slh_tail; \
slist->slh_head->field.sle_prev = NULL; \
\
/* Initial height is 0 (meaning 1 level), all next point to tail */ \
slist->slh_tail->field.sle_height = slist->slh_head->field.sle_height; \
for (i = 0; i < slist->slh_max_height; i++) \
slist->slh_tail->field.sle_levels[i].next = NULL; \
@ -473,7 +474,7 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
* \
* Returns the current length of the list. \
*/ \
int prefix##skip_length_##decl(decl##_t *slist) \
size_t prefix##skip_length_##decl(decl##_t *slist) \
{ \
return slist->slh_length; \
} \
@ -561,8 +562,8 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
prefix##skip_free_node_##decl(slist, node); \
node = next; \
} \
if (slist->slh_fns.snapshot_incr_era) \
slist->slh_fns.snapshot_incr_era(slist); \
if (slist->slh_snap.pres_era > 0) \
slist->slh_snap.cur_era++; \
return; \
} \
\
@ -669,10 +670,6 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
} while (j-- > 1); \
/* 4) reduce height by one */ \
path[i].node->field.sle_height--; \
/* TODO: remove me \
__skip_integrity_check_##decl(slist, 0); \
prefix##skip_dot_##decl(of, slist, gen, "dsc_cond", sprintf_##decl##_node); \
*/ \
} \
} \
/* (b) Check the ascent condition: \
@ -694,10 +691,6 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
path[i - 1].node->field.sle_levels[path[i - 1].in].hits = 0; \
if (path[i - 1].in != 0) \
path[i - 1].node->field.sle_levels[path[i - 1].in].next->field.sle_levels[path[i - 1].in].next = path[i].node; \
/* TODO: remove me \
__skip_integrity_check_##decl(slist, 0); \
prefix##skip_dot_##decl(of, slist, gen, "asc_cond", sprintf_##decl##_node); \
*/ \
} \
} \
} \
@ -829,8 +822,10 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
slist->slh_tail->field.sle_height = new_height; \
} \
/* Record the era for this node to enable snapshots. */ \
if (slist->slh_fns.snapshot_record_era) \
slist->slh_fns.snapshot_record_era(slist, new); \
if (slist->slh_snap.pres_era > 0) { \
/* Increase the the list's era/age and record it. */ \
new->field.sle_era = slist->slh_snap.cur_era++; \
} \
/* Set hits for rebalencing to 1 when new born. */ \
new->field.sle_levels[new_height].hits = 1; \
/* Increase our list length (aka. size, count, etc.) by one. */ \
@ -1102,20 +1097,20 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
return -1; \
\
/* If the optional snapshots feature is configured, use it now. \
Snapshots preserve the node if it is younger than our snapshot \
moment. */ \
if (slist->slh_fns.preserve_node) { \
np = slist->slh_fns.preserve_node(slist, node, NULL); \
Snapshots preserve the node if it is older than our snapshot \
and about to be mutated. */ \
if (slist->slh_snap.pres_era > 0) { \
/* Preserve the node. */ \
np = slist->slh_fns.snapshot_preserve_node(slist, node, NULL); \
if (np > 0) \
return np; \
\
/* Increase the the list's era/age. */ \
slist->slh_snap.cur_era++; \
} \
\
slist->slh_fns.update_entry(node, value); \
\
/* Record the era for this node to enable snapshots. */ \
if (slist->slh_fns.snapshot_record_era) \
slist->slh_fns.snapshot_record_era(slist, node); \
\
return rc; \
} \
\
@ -1150,12 +1145,16 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
node = path[0].node; \
if (node) { \
/* If the optional snapshots feature is configured, use it now. \
Snapshots preserve the node if it is younger than our snapshot \
moment, this node is about to be removed. */ \
if (slist->slh_fns.preserve_node) { \
np = slist->slh_fns.preserve_node(slist, node, NULL); \
Snapshots preserve the node if it is older than our snapshot \
and about to be mutated. */ \
if (slist->slh_snap.pres_era > 0) { \
/* Preserve the node. */ \
np = slist->slh_fns.snapshot_preserve_node(slist, node, NULL); \
if (np > 0) \
return np; \
\
/* Increase the the list's era/age. */ \
slist->slh_snap.cur_era++; \
} \
/* We found it, set the next->prev to the node->prev keeping in mind \
that the next node might be the tail). */ \
@ -1208,8 +1207,8 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
if (slist == NULL) \
return; \
\
if (slist->slh_fns.release_snapshots) \
slist->slh_fns.release_snapshots(slist); \
if (slist->slh_snap.pres_era > 0 && slist->slh_fns.snapshot_release) \
slist->slh_fns.snapshot_release(slist); \
\
prefix##skip_release_##decl(slist); \
\
@ -1220,15 +1219,18 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
#define SKIPLIST_DECL_SNAPSHOTS(decl, prefix, field) \
\
/** \
* -- __skip_snapshot_record_era_ \
* -- skip_snapshot_ \
* \
* A snapshot is a read-only view of a Skiplist at a point in time. Once \
* taken, a snapshot must be restored or released. Any number of snapshots \
* can be created. Return the `era` of the snapshot. \
*/ \
static void __skip_snapshot_record_era_##decl(decl##_t *slist, decl##_node_t *node) \
uint64_t prefix##skip_snapshot_##decl(decl##_t *slist) \
{ \
if (slist == NULL) \
return; \
return 0; \
\
node->field.sle_era = slist->slh_fns.snapshot_current_era(slist); \
return ++slist->slh_snap.pres_era; \
} \
\
/** \
@ -1238,17 +1240,21 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
void prefix##skip_release_snapshots_##decl(decl##_t *slist) \
{ \
decl##_node_t *node, *next; \
\
if (slist == NULL) \
return; \
\
if (slist->slh_snap.pres_era == 0) \
return; \
\
node = slist->slh_snap.pres; \
while (node) { \
next = node->field.sle_levels[0].next; \
prefix##skip_free_node_##decl(slist, node); \
node = next; \
} \
slist->slh_snap.pres = NULL; \
slist->slh_snap.era = 0; \
slist->slh_snap.pres = NULL; \
slist->slh_snap.pres_era = 0; \
} \
\
/** \
@ -1276,7 +1282,15 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
if (slist == NULL || src == NULL) \
return 0; \
\
if (src->field.sle_era > slist->slh_snap.era) \
/* Never preserve the head or the tail. */ \
if (src == slist->slh_head || src == slist->slh_tail) \
return 0; \
\
/* If the era into which the node `src` was born preceeded the latest \
snapshot era, then we need to preserve the older version of this \
node. Said another way, we preserve anything with an era that is \
less than the slh_snap.cur_era. */ \
if (src->field.sle_era > slist->slh_snap.pres_era) \
return 0; \
\
/* (a) alloc, ... */ \
@ -1285,18 +1299,16 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
if (rc) \
return rc; \
\
/* (b) shallow copy, fix sle_levels pointer, ... */ \
/* (b) shallow copy, copied sle_levels pointer is to the src list, so \
update that to point to the offset in this heap object, ... */ \
memcpy(dest, src, sizeof(decl##_node_t) + sle_arr_sz); \
dest->field.sle_levels = (struct __skiplist_##decl##_level *)((uintptr_t)dest + sizeof(decl##_node_t)); \
\
/* ... if we're not preserving the head or the tail, ... */ \
if (!(src == slist->slh_head || src == slist->slh_tail)) { \
/* (c) then user-supplied copy */ \
slist->slh_fns.archive_entry(dest, src); \
if (rc) { \
prefix##skip_free_node_##decl(slist, dest); \
return rc; \
} \
/* (c) then user-supplied copy */ \
slist->slh_fns.archive_entry(dest, src); \
if (rc) { \
prefix##skip_free_node_##decl(slist, dest); \
return rc; \
} \
\
/* (d) is this a duplicate? */ \
@ -1312,7 +1324,7 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
} \
\
/* (f) set duplicate flag */ \
dest->field.sle_levels[1].next = (decl##_node_t *)is_dup; \
dest->field.sle_levels[1].next = is_dup; \
\
/* (g) insert node into slh_pres list at head */ \
if (slist->slh_snap.pres == NULL) { \
@ -1333,29 +1345,14 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
} \
\
/** \
* -- skip_snapshot_ \
* \
* A snapshot is a read-only view of a Skiplist at a point in time. Once \
* taken, a snapshot must be restored or released. Any number of snapshots \
* can be created. \
*/ \
uint64_t prefix##skip_snapshot_##decl(decl##_t *slist) \
{ \
if (slist == NULL) \
return 0; \
slist->slh_snap.era = slist->slh_fns.snapshot_current_era(slist); \
slist->slh_fns.snapshot_incr_era(slist); \
return slist->slh_snap.era; \
} \
\
/** \
* -- skip_restore_snapshot_ TODO test! \
* -- skip_restore_snapshot_ TODO/WIP \
* \
* Restores the Skiplist to generation `era`. Once you restore `era` you \
* can no longer access any generations > `era`. \
* can no longer restore any [era, current era] only those earlier than \
* era. \
* \
* ALGORITHM: \
* iterate over the preserved nodes (slist->slh_snap.pres) \
* iterate over the preserved nodes (slist->slh_snap.pres) \
* a) remove/free nodes with node->era > era from slist \
* b) remove/free nodes > era from slh_pres \
* c) restore nodes == era by... \
@ -1376,31 +1373,35 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
if (slist == NULL) \
return NULL; \
\
if (era >= slist->slh_snap.era || slist->slh_snap.pres == NULL) \
if (slist->slh_snap.pres_era == 0) \
return NULL; \
\
if (era >= slist->slh_snap.cur_era || slist->slh_snap.pres == NULL) \
return slist; \
\
cur_era = slist->slh_fns.snapshot_current_era(slist); \
cur_era = slist->slh_snap.cur_era; \
\
/* (a) */ \
/* (a) remove and free nodes from slist that are newer than era */ \
SKIPLIST_FOREACH_H2T(decl, prefix, field, slist, node, i) \
{ \
((void)i); \
if (node->field.sle_era > era) \
if (node->field.sle_era > era) { \
prefix##skip_remove_node_##decl(slist, node); \
} \
} \
\
prev = NULL; \
node = slist->slh_snap.pres; \
while (node) { \
/* (b) */ \
/* (b) remove nodes from slh_pres that are newer than era */ \
if (node->field.sle_era > era) { \
/* remove node from slh_snap.pres list */ \
if (slist->slh_snap.pres == node) \
slist->slh_snap.pres = node->field.sle_levels[0].next; \
else { \
if (node->field.sle_levels[0].next == NULL) \
if (node == slist->slh_snap.pres) \
slist->slh_snap.pres = NULL; \
if (node == slist->slh_snap.pres) \
slist->slh_snap.pres = NULL; \
else \
prev->field.sle_levels[0].next = NULL; \
else \
@ -1409,7 +1410,7 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
prefix##skip_free_node_##decl(slist, node); \
} \
\
/* c */ \
/* (c) restore nodes from slh_pres that match the era */ \
prev = NULL; \
if (node->field.sle_era == era) { \
/* remove node from slh_snap.pres list */ \
@ -1417,7 +1418,10 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
slist->slh_snap.pres = node->field.sle_levels[0].next; \
else { \
if (node->field.sle_levels[0].next == NULL) \
prev->field.sle_levels[0].next = NULL; \
if (node == slist->slh_snap.pres) \
slist->slh_snap.pres = NULL; \
else \
prev->field.sle_levels[0].next = NULL; \
else \
prev->field.sle_levels[0].next = node->field.sle_levels[0].next; \
} \
@ -1434,32 +1438,12 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
node = node->field.sle_levels[0].next; \
} \
\
/* (d) */ \
slist->slh_snap.era = cur_era; \
/* (d) set list's era */ \
slist->slh_snap.pres_era = slist->slh_snap.pres == NULL ? 0 : cur_era; \
\
return slist; \
} \
\
/** \
* -- __skip_snapshot_current_era \
* \
* Returns the current era for snapshot purposes. \
*/ \
static size_t __skip_snapshot_current_era_##decl(decl##_t *slist) \
{ \
return slist->slh_snap.era; \
} \
\
/** \
* -- __skip_snapshot_incr_era \
* \
* Increments the snapshot era. \
*/ \
static size_t __skip_snapshot_incr_era_##decl(decl##_t *slist) \
{ \
return ++slist->slh_snap.era; \
} \
\
/** \
* -- skip_snapshots_init_ \
* \
@ -1468,122 +1452,11 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
void prefix##skip_snapshots_init_##decl(decl##_t *slist) \
{ \
if (slist != NULL) { \
slist->slh_fns.preserve_node = __skip_preserve_node_##decl; \
slist->slh_fns.release_snapshots = prefix##skip_release_snapshots_##decl; \
slist->slh_fns.snapshot_current_era = __skip_snapshot_current_era_##decl; \
slist->slh_fns.snapshot_incr_era = __skip_snapshot_incr_era_##decl; \
slist->slh_fns.snapshot_record_era = __skip_snapshot_record_era_##decl; \
slist->slh_fns.snapshot_preserve_node = __skip_preserve_node_##decl; \
slist->slh_fns.snapshot_release = prefix##skip_release_snapshots_##decl; \
} \
}
#define SKIPLIST_DECL_ARCHIVE(decl, prefix, field) \
\
/* Archive of a Skip List */ \
typedef struct decl##_archive { \
decl##_t list; \
decl##_node_t *nodes; \
size_t bytes; \
} decl##_archive_t; \
\
/** \
* -- skip_to_bytes_ TODO/WIP \
* \
* Writes out list and node content to a portable array of bytes \
* suitable for archiving to disk. \
*/ \
decl##_archive_t *prefix##skip_to_bytes_##decl(decl##_t *slist) \
{ \
int rc = 0; \
size_t size = 0, bytes, i; \
decl##_archive_t *archive; \
decl##_node_t *src; \
\
if (slist == NULL) \
return NULL; \
\
bytes = sizeof(decl##_archive_t) + (slist->slh_length * sizeof(decl##_node_t)); \
src = prefix##skip_head_##decl(slist); \
while (src) { \
slist->slh_fns.sizeof_entry(src); \
bytes += sizeof(size_t); \
bytes += size; \
src = prefix##skip_next_node_##decl(slist, src); \
} \
archive = (decl##_archive_t *)calloc(1, bytes); \
if (archive == NULL) \
return NULL; \
\
archive->bytes = bytes; \
archive->list.slh_length = slist->slh_length; \
archive->list.slh_max_height = slist->slh_max_height; \
archive->nodes = (decl##_node_t *)(archive + sizeof(decl##_archive_t)); \
\
i = 0; \
src = prefix##skip_head_##decl(slist); \
while (src) { \
decl##_node_t *n = (decl##_node_t *)archive->nodes + (i++ * sizeof(decl##_node_t)); \
slist->slh_fns.archive_entry(n, src); \
if (rc) \
return NULL; \
src = prefix##skip_next_node_##decl(slist, src); \
} \
return archive; \
} \
\
/** \
* -- skip_from_bytes_ TODO/WIP \
* \
*/ \
decl##_t *prefix##skip_from_bytes_##decl(decl##_archive_t *archive) \
{ \
int rc; \
size_t i; \
decl##_t *slist; \
decl##_node_t *src, *dest; \
\
if (archive == NULL) \
return 0; \
slist = (decl##_t *)calloc(1, sizeof(decl##_t)); \
if (slist == NULL) \
return NULL; \
\
slist->slh_max_height = archive->list.slh_max_height; \
\
rc = prefix##skip_alloc_node_##decl(slist, &slist->slh_head); \
if (rc) \
goto fail; \
rc = prefix##skip_alloc_node_##decl(slist, &slist->slh_tail); \
if (rc) \
goto fail; \
\
slist->slh_head->field.sle_height = 0; \
for (i = 0; i < slist->slh_max_height; i++) \
slist->slh_head->field.sle_levels[i].next = slist->slh_tail; \
slist->slh_head->field.sle_prev = NULL; \
\
slist->slh_tail->field.sle_height = slist->slh_max_height; \
for (i = 0; i < slist->slh_max_height; i++) \
slist->slh_tail->field.sle_levels[i].next = NULL; \
slist->slh_tail->field.sle_prev = slist->slh_head; \
\
i = 0; \
while (archive->list.slh_length > 0) { \
decl##_node_t *n = (decl##_node_t *)archive->nodes + (i++ * sizeof(decl##_node_t)); \
src = (decl##_node_t *)&n; \
rc = prefix##skip_alloc_node_##decl(slist, &dest); \
slist->slh_fns.archive_entry(dest, src); \
__skip_insert_##decl(slist, dest, 1); \
archive->list.slh_length--; \
} \
return slist; \
fail:; \
if (slist->slh_head) \
free(slist->slh_head); \
if (slist->slh_tail) \
free(slist->slh_tail); \
return NULL; \
}
#define SKIPLIST_DECL_VALIDATE(decl, prefix, field) \
/** \
* -- __skip_integrity_failure_ \
@ -1709,12 +1582,12 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
return n_err; \
} \
/* TODO: really? \
if (node->field.sle_levels[lvl].next != 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 (node->field.sle_levels[lvl].next != 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; \
} \
*/ \
} \
\