cleanup, some still TODO
This commit is contained in:
parent
397259c09d
commit
9a3da0c8cf
3 changed files with 192 additions and 315 deletions
4
Makefile
4
Makefile
|
@ -5,8 +5,8 @@ SHARED_LIB = libskiplist.so
|
||||||
|
|
||||||
# https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
|
# https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
|
||||||
#CFLAGS = -Wall -Wextra -Wpedantic -Of -std=c99 -Iinclude/ -fPIC
|
#CFLAGS = -Wall -Wextra -Wpedantic -Of -std=c99 -Iinclude/ -fPIC
|
||||||
#CFLAGS = -Wall -Wextra -Wpedantic -Og -g -std=c99 -Iinclude/ -fPIC
|
CFLAGS = -Wall -Wextra -Wpedantic -Og -g -std=c99 -Iinclude/ -fPIC
|
||||||
CFLAGS = -Wall -Wextra -Wpedantic -Og -g -fsanitize=address,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=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
|
#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
|
#env ASAN_OPTIONS=detect_leaks=1 LSAN_OPTIONS=verbosity=1:log_threads=1 ./examples/mls
|
||||||
|
|
||||||
|
|
260
examples/slm.c
260
examples/slm.c
|
@ -1,5 +1,5 @@
|
||||||
#pragma GCC push_options
|
#pragma GCC push_options
|
||||||
#pragma GCC optimize ("O0")
|
#pragma GCC optimize("O0")
|
||||||
|
|
||||||
// OPTIONS to set before including sl.h
|
// OPTIONS to set before including sl.h
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
@ -18,23 +18,19 @@
|
||||||
|
|
||||||
// Local demo application OPTIONS:
|
// Local demo application OPTIONS:
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
#define TEST_ARRAY_SIZE 1000
|
#define TEST_ARRAY_SIZE 10
|
||||||
#define VALIDATE
|
#define VALIDATE
|
||||||
#define SNAPSHOTS
|
#define SNAPSHOTS
|
||||||
|
//define TODO_RESTORE_SNAPSHOTS
|
||||||
#define DOT
|
#define DOT
|
||||||
#ifdef DOT
|
#ifdef DOT
|
||||||
size_t gen = 0;
|
size_t gen = 0;
|
||||||
FILE *of = 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
|
#endif
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
#ifdef VALIDATE
|
#ifdef VALIDATE
|
||||||
#define CHECK __skip_integrity_check_esempio(list, 0)
|
#define CHECK __skip_integrity_check_ex(list, 0)
|
||||||
#else
|
#else
|
||||||
#define CHECK ((void)0)
|
#define CHECK ((void)0)
|
||||||
#endif
|
#endif
|
||||||
|
@ -42,7 +38,7 @@ void sprintf_esempio_node(esempio_node_t *node, char *buf);
|
||||||
/*
|
/*
|
||||||
* SKIPLIST EXAMPLE:
|
* 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.
|
* 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
|
* node against another, logic you'll provide in SKIP_DECL as a
|
||||||
* block below.
|
* block below.
|
||||||
*/
|
*/
|
||||||
struct esempio_node {
|
struct ex_node {
|
||||||
int key;
|
int key;
|
||||||
char *value;
|
char *value;
|
||||||
SKIPLIST_ENTRY(esempio) entries;
|
SKIPLIST_ENTRY(ex) entries;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Generate all the access functions for our type of Skiplist.
|
* Generate all the access functions for our type of Skiplist.
|
||||||
*/
|
*/
|
||||||
SKIPLIST_DECL(
|
SKIPLIST_DECL(
|
||||||
esempio, api_, entries,
|
ex, api_, entries,
|
||||||
/* compare entries: list, a, b, aux */
|
/* compare entries: list, a, b, aux */
|
||||||
{
|
{
|
||||||
(void)list;
|
(void)list;
|
||||||
|
@ -119,7 +115,7 @@ SKIPLIST_DECL(
|
||||||
* list or when `a == b`. In those cases the comparison function
|
* list or when `a == b`. In those cases the comparison function
|
||||||
* returns before using the code in your block, don't panic. :)
|
* returns before using the code in your block, don't panic. :)
|
||||||
int
|
int
|
||||||
__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)list;
|
||||||
(void)aux;
|
(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.
|
* extract data from within your nodes.
|
||||||
*/
|
*/
|
||||||
SKIPLIST_DECL_ACCESS(
|
SKIPLIST_DECL_ACCESS(
|
||||||
esempio, api_, key, int, value, char *,
|
ex, api_, key, int, value, char *,
|
||||||
/* query blk */ { query.key = key; },
|
/* query blk */ { query.key = key; },
|
||||||
/* return blk */ { return node->value; })
|
/* return blk */ { return node->value; })
|
||||||
|
|
||||||
|
@ -150,7 +146,7 @@ SKIPLIST_DECL_ACCESS(
|
||||||
* Enable functions that enable returning to an earlier point in
|
* Enable functions that enable returning to an earlier point in
|
||||||
* time when a snapshot was created.
|
* time when a snapshot was created.
|
||||||
*/
|
*/
|
||||||
SKIPLIST_DECL_SNAPSHOTS(esempio, api_, entries)
|
SKIPLIST_DECL_SNAPSHOTS(ex, api_, entries)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Optional: Archive to/from bytes
|
* 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
|
* Enable functions that can write/read the content of your Skiplist
|
||||||
* out/in to/from an array of bytes.
|
* out/in to/from an array of bytes.
|
||||||
*/
|
*/
|
||||||
SKIPLIST_DECL_ARCHIVE(esempio, api_, entries)
|
// TODO SKIPLIST_DECL_ARCHIVE(ex, api_, entries)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Optional: As Hashtable
|
* Optional: As Hashtable
|
||||||
*
|
*
|
||||||
* Turn your Skiplist into a hash table.
|
* Turn your Skiplist into a hash table.
|
||||||
*/
|
*/
|
||||||
//TODO SKIPLIST_DECL_HASHTABLE(esempio, api_, entries, snaps)
|
// TODO SKIPLIST_DECL_HASHTABLE(ex, api_, entries, snaps)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Optional: Check Skiplists at runtime
|
* Optional: Check Skiplists at runtime
|
||||||
*
|
*
|
||||||
* Create a functions that validate the integrity of a Skiplist.
|
* Create a functions that validate the integrity of a Skiplist.
|
||||||
*/
|
*/
|
||||||
SKIPLIST_DECL_VALIDATE(esempio, api_, entries)
|
SKIPLIST_DECL_VALIDATE(ex, api_, entries)
|
||||||
|
|
||||||
/* Optional: Visualize your Skiplist using DOT/Graphviz in PDF
|
/* Optional: Visualize your Skiplist using DOT/Graphviz in PDF
|
||||||
*
|
*
|
||||||
* Create the functions used to annotate a visualization of a Skiplist.
|
* Create the functions used to annotate a visualization of a Skiplist.
|
||||||
*/
|
*/
|
||||||
SKIPLIST_DECL_DOT(esempio, api_, entries)
|
SKIPLIST_DECL_DOT(ex, api_, entries)
|
||||||
|
|
||||||
void
|
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);
|
// 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", node->key, node->value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function for this demo application.
|
// 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
|
int
|
||||||
main()
|
main()
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
#ifdef SNAPSHOTS
|
#ifdef TODO_RESTORE_SNAPSHOTS
|
||||||
size_t snap_i = 0;
|
size_t n_snaps = 0;
|
||||||
uint64_t snap_ids[2048];
|
snap_info_t snaps[TEST_ARRAY_SIZE * 2 + 1];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef DOT
|
#ifdef DOT
|
||||||
|
@ -272,28 +276,23 @@ main()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Allocate and initialize a Skiplist. */
|
/* 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)
|
if (list == NULL)
|
||||||
return ENOMEM;
|
return ENOMEM;
|
||||||
|
|
||||||
rc = api_skip_init_esempio(list, -12);
|
rc = api_skip_init_ex(list, -12);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
api_skip_snapshots_init_esempio(list);
|
api_skip_snapshots_init_ex(list);
|
||||||
#ifdef DOT
|
#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
|
#endif
|
||||||
if (api_skip_get_esempio(list, 0) != NULL)
|
if (api_skip_get_ex(list, 0) != NULL)
|
||||||
perror("found a non-existent item!");
|
perror("found a non-existent item!");
|
||||||
api_skip_del_esempio(list, 0);
|
api_skip_del_ex(list, 0);
|
||||||
CHECK;
|
CHECK;
|
||||||
|
|
||||||
#ifdef SNAPSHOTS
|
/* Insert TEST_ARRAY_SIZE key/value pairs into the list. */
|
||||||
/* 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. */
|
|
||||||
int i, j;
|
int i, j;
|
||||||
char *numeral;
|
char *numeral;
|
||||||
#ifdef DOT
|
#ifdef DOT
|
||||||
|
@ -307,138 +306,143 @@ main()
|
||||||
shuffle(array, asz);
|
shuffle(array, asz);
|
||||||
|
|
||||||
for (i = 0; i < asz; i++) {
|
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
|
#ifdef SNAPSHOTS
|
||||||
if (i > TEST_ARRAY_SIZE + 1) {
|
api_skip_snapshot_ex(list);
|
||||||
snap_ids[snap_i++] = api_skip_snapshot_esempio(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;
|
CHECK;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
numeral = to_lower(int_to_roman_numeral(array[i]));
|
||||||
|
rc = api_skip_put_ex(list, array[i], numeral);
|
||||||
|
CHECK;
|
||||||
#ifdef DOT
|
#ifdef DOT
|
||||||
sprintf(msg, "put key: %d value: %s", i, numeral);
|
sprintf(msg, "put key: %d value: %s", i, numeral);
|
||||||
api_skip_dot_esempio(of, list, gen++, msg, sprintf_esempio_node);
|
api_skip_dot_ex(of, list, gen++, msg, sprintf_ex_node);
|
||||||
CHECK;
|
|
||||||
#endif
|
#endif
|
||||||
char *v = api_skip_get_esempio(list, array[i]);
|
char *v = api_skip_get_ex(list, array[i]);
|
||||||
CHECK;
|
CHECK;
|
||||||
char *upper_numeral = calloc(1, strlen(v) + 1);
|
char *upper_numeral = calloc(1, strlen(v) + 1);
|
||||||
strncpy(upper_numeral, v, strlen(v));
|
strncpy(upper_numeral, v, strlen(v));
|
||||||
|
assert(strncmp(v, upper_numeral, strlen(upper_numeral)) == 0);
|
||||||
to_upper(upper_numeral);
|
to_upper(upper_numeral);
|
||||||
api_skip_set_esempio(list, array[i], upper_numeral);
|
api_skip_set_ex(list, array[i], upper_numeral);
|
||||||
CHECK;
|
CHECK;
|
||||||
if (i == 8) {
|
|
||||||
api_skip_get_esempio(list, -2);
|
|
||||||
api_skip_get_esempio(list, -2);
|
|
||||||
api_skip_get_esempio(list, -2);
|
|
||||||
api_skip_get_esempio(list, -2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
numeral = int_to_roman_numeral(-1);
|
numeral = int_to_roman_numeral(-1);
|
||||||
api_skip_dup_esempio(list, -1, numeral);
|
api_skip_dup_ex(list, -1, numeral);
|
||||||
CHECK;
|
CHECK;
|
||||||
#ifdef DOT
|
#ifdef DOT
|
||||||
sprintf(msg, "put dup key: %d value: %s", i, numeral);
|
sprintf(msg, "put dup key: %d value: %s", i, numeral);
|
||||||
api_skip_dot_esempio(of, list, gen++, msg, sprintf_esempio_node);
|
api_skip_dot_ex(of, list, gen++, msg, sprintf_ex_node);
|
||||||
CHECK;
|
|
||||||
#endif
|
#endif
|
||||||
numeral = int_to_roman_numeral(1);
|
numeral = int_to_roman_numeral(1);
|
||||||
api_skip_dup_esempio(list, 1, numeral);
|
api_skip_dup_ex(list, 1, numeral);
|
||||||
CHECK;
|
CHECK;
|
||||||
#ifdef DOT
|
#ifdef DOT
|
||||||
sprintf(msg, "put dup key: %d value: %s", i, numeral);
|
sprintf(msg, "put dup key: %d value: %s", i, numeral);
|
||||||
api_skip_dot_esempio(of, list, gen++, msg, sprintf_esempio_node);
|
api_skip_dot_ex(of, list, gen++, msg, sprintf_ex_node);
|
||||||
CHECK;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
api_skip_del_esempio(list, 0);
|
api_skip_del_ex(list, 0);
|
||||||
CHECK;
|
CHECK;
|
||||||
if (api_skip_get_esempio(list, 0) != NULL)
|
if (api_skip_get_ex(list, 0) != NULL)
|
||||||
perror("found a deleted item!");
|
perror("found a deleted item!");
|
||||||
api_skip_del_esempio(list, 0);
|
api_skip_del_ex(list, 0);
|
||||||
CHECK;
|
CHECK;
|
||||||
if (api_skip_get_esempio(list, 0) != NULL)
|
if (api_skip_get_ex(list, 0) != NULL)
|
||||||
perror("found a deleted item!");
|
perror("found a deleted item!");
|
||||||
int key = TEST_ARRAY_SIZE + 1;
|
int key = TEST_ARRAY_SIZE + 1;
|
||||||
api_skip_del_esempio(list, key);
|
api_skip_del_ex(list, key);
|
||||||
CHECK;
|
CHECK;
|
||||||
key = -(TEST_ARRAY_SIZE)-1;
|
key = -(TEST_ARRAY_SIZE)-1;
|
||||||
api_skip_del_esempio(list, key);
|
api_skip_del_ex(list, key);
|
||||||
CHECK;
|
CHECK;
|
||||||
|
|
||||||
#ifdef DOT
|
#ifdef DOT
|
||||||
sprintf(msg, "deleted key: %d, value: %s", 0, numeral);
|
sprintf(msg, "deleted key: %d, value: %s", 0, numeral);
|
||||||
api_skip_dot_esempio(of, list, gen++, msg, sprintf_esempio_node);
|
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_ex(list, SKIP_GTE, -(TEST_ARRAY_SIZE)-1)->value, numeral) == 0);
|
||||||
|
free(numeral);
|
||||||
|
numeral = int_to_roman_numeral(-2);
|
||||||
|
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_ex(list, SKIP_GTE, 0)->value, numeral) == 0);
|
||||||
|
free(numeral);
|
||||||
|
numeral = int_to_roman_numeral(2);
|
||||||
|
assert(strcmp(api_skip_pos_ex(list, SKIP_GTE, 2)->value, numeral) == 0);
|
||||||
|
free(numeral);
|
||||||
|
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_ex(list, SKIP_GT, -(TEST_ARRAY_SIZE)-1)->value, numeral) == 0);
|
||||||
|
free(numeral);
|
||||||
|
numeral = int_to_roman_numeral(-1);
|
||||||
|
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_ex(list, SKIP_GT, 0)->value, numeral) == 0);
|
||||||
|
free(numeral);
|
||||||
|
numeral = int_to_roman_numeral(2);
|
||||||
|
assert(strcmp(api_skip_pos_ex(list, SKIP_GT, 1)->value, numeral) == 0);
|
||||||
|
free(numeral);
|
||||||
|
assert(api_skip_pos_ex(list, SKIP_GT, 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_ex(list, SKIP_LT, -1)->value, numeral) == 0);
|
||||||
|
free(numeral);
|
||||||
|
numeral = int_to_roman_numeral(-1);
|
||||||
|
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_ex(list, SKIP_LT, 2)->value, numeral) == 0);
|
||||||
|
free(numeral);
|
||||||
|
numeral = int_to_roman_numeral(TEST_ARRAY_SIZE);
|
||||||
|
assert(strcmp(api_skip_pos_ex(list, SKIP_LT, (TEST_ARRAY_SIZE + 1))->value, numeral) == 0);
|
||||||
|
free(numeral);
|
||||||
|
|
||||||
|
assert(api_skip_pos_ex(list, SKIP_LTE, -(TEST_ARRAY_SIZE)-1) == NULL);
|
||||||
|
numeral = int_to_roman_numeral(-2);
|
||||||
|
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_ex(list, SKIP_LTE, 0)->value, numeral) == 0);
|
||||||
|
free(numeral);
|
||||||
|
numeral = int_to_roman_numeral(2);
|
||||||
|
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_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;
|
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
|
#endif
|
||||||
|
|
||||||
#ifdef SNAPSHOTS
|
|
||||||
api_skip_restore_snapshot_esempio(list, snap_ids[snap_i - 1]);
|
|
||||||
api_skip_release_snapshots_esempio(list);
|
|
||||||
#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);
|
|
||||||
free(numeral);
|
|
||||||
numeral = int_to_roman_numeral(-2);
|
|
||||||
assert(strcmp(api_skip_pos_esempio(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);
|
|
||||||
free(numeral);
|
|
||||||
numeral = int_to_roman_numeral(2);
|
|
||||||
assert(strcmp(api_skip_pos_esempio(list, SKIP_GTE, 2)->value, numeral) == 0);
|
|
||||||
free(numeral);
|
|
||||||
assert(api_skip_pos_esempio(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);
|
|
||||||
free(numeral);
|
|
||||||
numeral = int_to_roman_numeral(-1);
|
|
||||||
assert(strcmp(api_skip_pos_esempio(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);
|
|
||||||
free(numeral);
|
|
||||||
numeral = int_to_roman_numeral(2);
|
|
||||||
assert(strcmp(api_skip_pos_esempio(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_esempio(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);
|
|
||||||
free(numeral);
|
|
||||||
numeral = int_to_roman_numeral(-1);
|
|
||||||
assert(strcmp(api_skip_pos_esempio(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);
|
|
||||||
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);
|
|
||||||
free(numeral);
|
|
||||||
|
|
||||||
assert(api_skip_pos_esempio(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);
|
|
||||||
free(numeral);
|
|
||||||
numeral = int_to_roman_numeral(-1);
|
|
||||||
assert(strcmp(api_skip_pos_esempio(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);
|
|
||||||
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);
|
|
||||||
free(numeral);
|
|
||||||
|
|
||||||
#ifdef DOT
|
#ifdef DOT
|
||||||
api_skip_dot_end_esempio(of, gen);
|
api_skip_dot_end_ex(of, gen);
|
||||||
fclose(of);
|
fclose(of);
|
||||||
#endif
|
#endif
|
||||||
api_skip_free_esempio(list);
|
api_skip_free_ex(list);
|
||||||
free(list);
|
free(list);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
281
include/sl.h
281
include/sl.h
|
@ -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 *); \
|
int (*compare_entries)(decl##_t *, decl##_node_t *, decl##_node_t *, void *); \
|
||||||
\
|
\
|
||||||
/* Optional: Snapshots */ \
|
/* Optional: Snapshots */ \
|
||||||
int (*preserve_node)(decl##_t * slist, const decl##_node_t *src, decl##_node_t **preserved); \
|
int (*snapshot_preserve_node)(decl##_t * slist, const decl##_node_t *src, decl##_node_t **preserved); \
|
||||||
void (*release_snapshots)(decl##_t *); \
|
void (*snapshot_release)(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 *); \
|
|
||||||
} slh_fns; \
|
} slh_fns; \
|
||||||
struct { \
|
struct { \
|
||||||
size_t era; \
|
size_t cur_era; \
|
||||||
|
size_t pres_era; \
|
||||||
decl##_node_t *pres; \
|
decl##_node_t *pres; \
|
||||||
} slh_snap; \
|
} 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_length = 0; \
|
||||||
slist->slh_max_height = SKIPLIST_MAX_HEIGHT == 1 ? (size_t)(max < 0 ? -max : max) : SKIPLIST_MAX_HEIGHT; \
|
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_snap.pres = 0; \
|
||||||
slist->slh_fns.free_entry = __skip_free_entry_fn_##decl; \
|
slist->slh_fns.free_entry = __skip_free_entry_fn_##decl; \
|
||||||
slist->slh_fns.update_entry = __skip_update_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) \
|
if (rc) \
|
||||||
goto fail; \
|
goto fail; \
|
||||||
\
|
\
|
||||||
|
/* Initial height is 0 (meaning 1 level), all next point to tail */ \
|
||||||
slist->slh_head->field.sle_height = 0; \
|
slist->slh_head->field.sle_height = 0; \
|
||||||
for (i = 0; i < slist->slh_max_height; i++) \
|
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_levels[i].next = slist->slh_tail; \
|
||||||
slist->slh_head->field.sle_prev = NULL; \
|
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; \
|
slist->slh_tail->field.sle_height = slist->slh_head->field.sle_height; \
|
||||||
for (i = 0; i < slist->slh_max_height; i++) \
|
for (i = 0; i < slist->slh_max_height; i++) \
|
||||||
slist->slh_tail->field.sle_levels[i].next = NULL; \
|
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. \
|
* 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; \
|
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); \
|
prefix##skip_free_node_##decl(slist, node); \
|
||||||
node = next; \
|
node = next; \
|
||||||
} \
|
} \
|
||||||
if (slist->slh_fns.snapshot_incr_era) \
|
if (slist->slh_snap.pres_era > 0) \
|
||||||
slist->slh_fns.snapshot_incr_era(slist); \
|
slist->slh_snap.cur_era++; \
|
||||||
return; \
|
return; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
|
@ -669,10 +670,6 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
|
||||||
} while (j-- > 1); \
|
} while (j-- > 1); \
|
||||||
/* 4) reduce height by one */ \
|
/* 4) reduce height by one */ \
|
||||||
path[i].node->field.sle_height--; \
|
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: \
|
/* (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; \
|
path[i - 1].node->field.sle_levels[path[i - 1].in].hits = 0; \
|
||||||
if (path[i - 1].in != 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; \
|
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; \
|
slist->slh_tail->field.sle_height = new_height; \
|
||||||
} \
|
} \
|
||||||
/* Record the era for this node to enable snapshots. */ \
|
/* Record the era for this node to enable snapshots. */ \
|
||||||
if (slist->slh_fns.snapshot_record_era) \
|
if (slist->slh_snap.pres_era > 0) { \
|
||||||
slist->slh_fns.snapshot_record_era(slist, new); \
|
/* 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. */ \
|
/* Set hits for rebalencing to 1 when new born. */ \
|
||||||
new->field.sle_levels[new_height].hits = 1; \
|
new->field.sle_levels[new_height].hits = 1; \
|
||||||
/* Increase our list length (aka. size, count, etc.) by one. */ \
|
/* 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; \
|
return -1; \
|
||||||
\
|
\
|
||||||
/* If the optional snapshots feature is configured, use it now. \
|
/* If the optional snapshots feature is configured, use it now. \
|
||||||
Snapshots preserve the node if it is younger than our snapshot \
|
Snapshots preserve the node if it is older than our snapshot \
|
||||||
moment. */ \
|
and about to be mutated. */ \
|
||||||
if (slist->slh_fns.preserve_node) { \
|
if (slist->slh_snap.pres_era > 0) { \
|
||||||
np = slist->slh_fns.preserve_node(slist, node, NULL); \
|
/* Preserve the node. */ \
|
||||||
|
np = slist->slh_fns.snapshot_preserve_node(slist, node, NULL); \
|
||||||
if (np > 0) \
|
if (np > 0) \
|
||||||
return np; \
|
return np; \
|
||||||
|
\
|
||||||
|
/* Increase the the list's era/age. */ \
|
||||||
|
slist->slh_snap.cur_era++; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
slist->slh_fns.update_entry(node, value); \
|
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; \
|
return rc; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
|
@ -1150,12 +1145,16 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
|
||||||
node = path[0].node; \
|
node = path[0].node; \
|
||||||
if (node) { \
|
if (node) { \
|
||||||
/* If the optional snapshots feature is configured, use it now. \
|
/* If the optional snapshots feature is configured, use it now. \
|
||||||
Snapshots preserve the node if it is younger than our snapshot \
|
Snapshots preserve the node if it is older than our snapshot \
|
||||||
moment, this node is about to be removed. */ \
|
and about to be mutated. */ \
|
||||||
if (slist->slh_fns.preserve_node) { \
|
if (slist->slh_snap.pres_era > 0) { \
|
||||||
np = slist->slh_fns.preserve_node(slist, node, NULL); \
|
/* Preserve the node. */ \
|
||||||
|
np = slist->slh_fns.snapshot_preserve_node(slist, node, NULL); \
|
||||||
if (np > 0) \
|
if (np > 0) \
|
||||||
return np; \
|
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 \
|
/* We found it, set the next->prev to the node->prev keeping in mind \
|
||||||
that the next node might be the tail). */ \
|
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) \
|
if (slist == NULL) \
|
||||||
return; \
|
return; \
|
||||||
\
|
\
|
||||||
if (slist->slh_fns.release_snapshots) \
|
if (slist->slh_snap.pres_era > 0 && slist->slh_fns.snapshot_release) \
|
||||||
slist->slh_fns.release_snapshots(slist); \
|
slist->slh_fns.snapshot_release(slist); \
|
||||||
\
|
\
|
||||||
prefix##skip_release_##decl(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) \
|
#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) \
|
if (slist == NULL) \
|
||||||
return; \
|
return 0; \
|
||||||
\
|
\
|
||||||
node->field.sle_era = slist->slh_fns.snapshot_current_era(slist); \
|
return ++slist->slh_snap.pres_era; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
/** \
|
/** \
|
||||||
|
@ -1238,9 +1240,13 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
|
||||||
void prefix##skip_release_snapshots_##decl(decl##_t *slist) \
|
void prefix##skip_release_snapshots_##decl(decl##_t *slist) \
|
||||||
{ \
|
{ \
|
||||||
decl##_node_t *node, *next; \
|
decl##_node_t *node, *next; \
|
||||||
|
\
|
||||||
if (slist == NULL) \
|
if (slist == NULL) \
|
||||||
return; \
|
return; \
|
||||||
\
|
\
|
||||||
|
if (slist->slh_snap.pres_era == 0) \
|
||||||
|
return; \
|
||||||
|
\
|
||||||
node = slist->slh_snap.pres; \
|
node = slist->slh_snap.pres; \
|
||||||
while (node) { \
|
while (node) { \
|
||||||
next = node->field.sle_levels[0].next; \
|
next = node->field.sle_levels[0].next; \
|
||||||
|
@ -1248,7 +1254,7 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
|
||||||
node = next; \
|
node = next; \
|
||||||
} \
|
} \
|
||||||
slist->slh_snap.pres = NULL; \
|
slist->slh_snap.pres = NULL; \
|
||||||
slist->slh_snap.era = 0; \
|
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) \
|
if (slist == NULL || src == NULL) \
|
||||||
return 0; \
|
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; \
|
return 0; \
|
||||||
\
|
\
|
||||||
/* (a) alloc, ... */ \
|
/* (a) alloc, ... */ \
|
||||||
|
@ -1285,18 +1299,16 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
|
||||||
if (rc) \
|
if (rc) \
|
||||||
return 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); \
|
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)); \
|
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 */ \
|
/* (c) then user-supplied copy */ \
|
||||||
slist->slh_fns.archive_entry(dest, src); \
|
slist->slh_fns.archive_entry(dest, src); \
|
||||||
if (rc) { \
|
if (rc) { \
|
||||||
prefix##skip_free_node_##decl(slist, dest); \
|
prefix##skip_free_node_##decl(slist, dest); \
|
||||||
return rc; \
|
return rc; \
|
||||||
} \
|
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
/* (d) is this a duplicate? */ \
|
/* (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 */ \
|
/* (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 */ \
|
/* (g) insert node into slh_pres list at head */ \
|
||||||
if (slist->slh_snap.pres == NULL) { \
|
if (slist->slh_snap.pres == NULL) { \
|
||||||
|
@ -1333,26 +1345,11 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
/** \
|
/** \
|
||||||
* -- skip_snapshot_ \
|
* -- skip_restore_snapshot_ TODO/WIP \
|
||||||
* \
|
|
||||||
* 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! \
|
|
||||||
* \
|
* \
|
||||||
* Restores the Skiplist to generation `era`. Once you restore `era` you \
|
* 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: \
|
* ALGORITHM: \
|
||||||
* iterate over the preserved nodes (slist->slh_snap.pres) \
|
* iterate over the preserved nodes (slist->slh_snap.pres) \
|
||||||
|
@ -1376,23 +1373,27 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
|
||||||
if (slist == NULL) \
|
if (slist == NULL) \
|
||||||
return 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; \
|
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) \
|
SKIPLIST_FOREACH_H2T(decl, prefix, field, slist, node, i) \
|
||||||
{ \
|
{ \
|
||||||
((void)i); \
|
((void)i); \
|
||||||
if (node->field.sle_era > era) \
|
if (node->field.sle_era > era) { \
|
||||||
prefix##skip_remove_node_##decl(slist, node); \
|
prefix##skip_remove_node_##decl(slist, node); \
|
||||||
|
} \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
prev = NULL; \
|
prev = NULL; \
|
||||||
node = slist->slh_snap.pres; \
|
node = slist->slh_snap.pres; \
|
||||||
while (node) { \
|
while (node) { \
|
||||||
/* (b) */ \
|
/* (b) remove nodes from slh_pres that are newer than era */ \
|
||||||
if (node->field.sle_era > era) { \
|
if (node->field.sle_era > era) { \
|
||||||
/* remove node from slh_snap.pres list */ \
|
/* remove node from slh_snap.pres list */ \
|
||||||
if (slist->slh_snap.pres == node) \
|
if (slist->slh_snap.pres == node) \
|
||||||
|
@ -1409,7 +1410,7 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
|
||||||
prefix##skip_free_node_##decl(slist, node); \
|
prefix##skip_free_node_##decl(slist, node); \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
/* c */ \
|
/* (c) restore nodes from slh_pres that match the era */ \
|
||||||
prev = NULL; \
|
prev = NULL; \
|
||||||
if (node->field.sle_era == era) { \
|
if (node->field.sle_era == era) { \
|
||||||
/* remove node from slh_snap.pres list */ \
|
/* remove node from slh_snap.pres list */ \
|
||||||
|
@ -1417,6 +1418,9 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li
|
||||||
slist->slh_snap.pres = node->field.sle_levels[0].next; \
|
slist->slh_snap.pres = node->field.sle_levels[0].next; \
|
||||||
else { \
|
else { \
|
||||||
if (node->field.sle_levels[0].next == NULL) \
|
if (node->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; \
|
prev->field.sle_levels[0].next = NULL; \
|
||||||
else \
|
else \
|
||||||
prev->field.sle_levels[0].next = node->field.sle_levels[0].next; \
|
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; \
|
node = node->field.sle_levels[0].next; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
/* (d) */ \
|
/* (d) set list's era */ \
|
||||||
slist->slh_snap.era = cur_era; \
|
slist->slh_snap.pres_era = slist->slh_snap.pres == NULL ? 0 : cur_era; \
|
||||||
\
|
\
|
||||||
return slist; \
|
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_ \
|
* -- 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) \
|
void prefix##skip_snapshots_init_##decl(decl##_t *slist) \
|
||||||
{ \
|
{ \
|
||||||
if (slist != NULL) { \
|
if (slist != NULL) { \
|
||||||
slist->slh_fns.preserve_node = __skip_preserve_node_##decl; \
|
slist->slh_fns.snapshot_preserve_node = __skip_preserve_node_##decl; \
|
||||||
slist->slh_fns.release_snapshots = prefix##skip_release_snapshots_##decl; \
|
slist->slh_fns.snapshot_release = 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; \
|
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
#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) \
|
#define SKIPLIST_DECL_VALIDATE(decl, prefix, field) \
|
||||||
/** \
|
/** \
|
||||||
* -- __skip_integrity_failure_ \
|
* -- __skip_integrity_failure_ \
|
||||||
|
|
Loading…
Reference in a new issue