snapshot v2/p7; update
This commit is contained in:
parent
f2b7048036
commit
710f714417
|
@ -38,6 +38,7 @@
|
||||||
struct slex_node {
|
struct slex_node {
|
||||||
int key;
|
int key;
|
||||||
char *value;
|
char *value;
|
||||||
|
/* NOTE: This _must_ be last element in node for snapshots to work!!! */
|
||||||
SKIPLIST_ENTRY(slex_node) entries;
|
SKIPLIST_ENTRY(slex_node) entries;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -53,8 +54,11 @@ SKIPLIST_DECL(
|
||||||
new->key = node->key;
|
new->key = node->key;
|
||||||
char *nv = calloc(strlen(node->value) + 1, sizeof(char));
|
char *nv = calloc(strlen(node->value) + 1, sizeof(char));
|
||||||
if (nv == NULL)
|
if (nv == NULL)
|
||||||
rc = ENOMEM; // leaks some memory... TODO
|
rc = ENOMEM;
|
||||||
new->value = strncpy(nv, node->value, strlen(node->value));
|
else {
|
||||||
|
strncpy(nv, node->value, strlen(node->value));
|
||||||
|
new->value = nv;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
/* size in bytes of the content stored in an entry by you */
|
/* size in bytes of the content stored in an entry by you */
|
||||||
{ size = strlen(node->value) + 1; })
|
{ size = strlen(node->value) + 1; })
|
||||||
|
@ -179,18 +183,21 @@ shuffle(int *array, size_t n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//define DOT
|
||||||
#define TEST_ARRAY_SIZE 8
|
#define TEST_ARRAY_SIZE 8
|
||||||
|
|
||||||
int
|
int
|
||||||
main()
|
main()
|
||||||
{
|
{
|
||||||
int gen = 0, rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
|
#ifdef DOT
|
||||||
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");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Allocate and initialize a Skiplist. */
|
/* Allocate and initialize a Skiplist. */
|
||||||
slex_t *list = (slex_t *)malloc(sizeof(slex_t));
|
slex_t *list = (slex_t *)malloc(sizeof(slex_t));
|
||||||
|
@ -200,44 +207,53 @@ main()
|
||||||
rc = api_skip_init_slex(list, 12, __slm_key_compare);
|
rc = api_skip_init_slex(list, 12, __slm_key_compare);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
#ifdef DOT
|
||||||
api_skip_dot_slex(of, list, gen++, sprintf_slex_node);
|
api_skip_dot_slex(of, list, gen++, sprintf_slex_node);
|
||||||
|
|
||||||
#if 0 //TODO
|
|
||||||
/* Test creating a snapshot of an empty Skiplist */
|
|
||||||
slex_t *snap = api_skip_snapshot_slex(list);
|
|
||||||
slex_t *restored = api_skip_restore_snapshot_slex(snap, __skip_key_compare_slex);
|
|
||||||
api_skip_dispose_snapshot_slex(snap);
|
|
||||||
api_skip_destroy_slex(restored);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Test creating a snapshot of an empty Skiplist */
|
||||||
|
size_t snp[TEST_ARRAY_SIZE * 2 + 10];
|
||||||
|
snp[0] = api_skip_snapshot_slex(list);
|
||||||
|
|
||||||
/* Insert 7 key/value pairs into the list. */
|
/* Insert 7 key/value pairs into the list. */
|
||||||
|
int i;
|
||||||
int amt = TEST_ARRAY_SIZE, asz = (amt * 2) + 1;
|
int amt = TEST_ARRAY_SIZE, asz = (amt * 2) + 1;
|
||||||
int array[(TEST_ARRAY_SIZE * 2) + 1];
|
int array[(TEST_ARRAY_SIZE * 2) + 1];
|
||||||
for (int j = 0, i = -amt; i <= amt; i++, j++)
|
for (int j = 0, i = -amt; i <= amt; i++, j++)
|
||||||
array[j] = i;
|
array[j] = i;
|
||||||
shuffle(array, asz);
|
shuffle(array, asz);
|
||||||
|
|
||||||
for (int 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])));
|
||||||
|
snp[i + 1] = api_skip_snapshot_slex(list);
|
||||||
|
#ifdef DOT
|
||||||
api_skip_dot_slex(of, list, gen++, sprintf_slex_node);
|
api_skip_dot_slex(of, list, gen++, sprintf_slex_node);
|
||||||
|
#endif
|
||||||
char *v = api_skip_get_slex(list, array[i]);
|
char *v = api_skip_get_slex(list, array[i]);
|
||||||
api_skip_set_slex(list, array[i], to_upper(v));
|
api_skip_set_slex(list, array[i], to_upper(v));
|
||||||
}
|
}
|
||||||
|
int r = i;
|
||||||
api_skip_dup_slex(list, -1, int_to_roman_numeral(-1));
|
api_skip_dup_slex(list, -1, int_to_roman_numeral(-1));
|
||||||
|
#ifdef DOT
|
||||||
api_skip_dot_slex(of, list, gen++, sprintf_slex_node);
|
api_skip_dot_slex(of, list, gen++, sprintf_slex_node);
|
||||||
|
#endif
|
||||||
api_skip_dup_slex(list, 1, int_to_roman_numeral(1));
|
api_skip_dup_slex(list, 1, int_to_roman_numeral(1));
|
||||||
|
#ifdef DOT
|
||||||
api_skip_dot_slex(of, list, gen++, sprintf_slex_node);
|
api_skip_dot_slex(of, list, gen++, sprintf_slex_node);
|
||||||
|
#endif
|
||||||
|
snp[++i] = api_skip_snapshot_slex(list);
|
||||||
|
|
||||||
api_skip_del_slex(list, 0);
|
api_skip_del_slex(list, 0);
|
||||||
api_skip_dot_slex(of, list, gen++, sprintf_slex_node);
|
snp[++i] = api_skip_snapshot_slex(list);
|
||||||
|
|
||||||
#if 0 //TODO
|
#ifdef DOT
|
||||||
snap = api_skip_snapshot_slex(list);
|
api_skip_dot_slex(of, list, gen++, sprintf_slex_node);
|
||||||
restored = api_skip_restore_snapshot_slex(snap, __skip_key_compare_slex);
|
|
||||||
api_skip_dispose_snapshot_slex(snap);
|
|
||||||
api_skip_destroy_slex(restored);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
slex_t *restored = api_skip_restore_snapshot_slex(list, snp[r]);
|
||||||
|
api_skip_dispose_snapshot_slex(list, r+1);
|
||||||
|
api_skip_destroy_slex(restored);
|
||||||
|
|
||||||
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);
|
||||||
assert(strcmp(api_skip_pos_slex(list, SKIP_GTE, 0)->value, int_to_roman_numeral(1)) == 0);
|
assert(strcmp(api_skip_pos_slex(list, SKIP_GTE, 0)->value, int_to_roman_numeral(1)) == 0);
|
||||||
|
@ -263,7 +279,9 @@ main()
|
||||||
assert(strcmp(api_skip_pos_slex(list, SKIP_LTE, (TEST_ARRAY_SIZE + 1))->value, int_to_roman_numeral(TEST_ARRAY_SIZE)) == 0);
|
assert(strcmp(api_skip_pos_slex(list, SKIP_LTE, (TEST_ARRAY_SIZE + 1))->value, int_to_roman_numeral(TEST_ARRAY_SIZE)) == 0);
|
||||||
|
|
||||||
api_skip_destroy_slex(list);
|
api_skip_destroy_slex(list);
|
||||||
|
#ifdef DOT
|
||||||
api_skip_dot_end_slex(of, gen);
|
api_skip_dot_end_slex(of, gen);
|
||||||
fclose(of);
|
fclose(of);
|
||||||
|
#endif
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
62
include/sl.h
62
include/sl.h
|
@ -161,6 +161,8 @@
|
||||||
* forward pointers to nodes and has a height (a zero-based count of levels, so
|
* forward pointers to nodes and has a height (a zero-based count of levels, so
|
||||||
* a height of `0` means one (1) level and a height of `4` means five (5)
|
* a height of `0` means one (1) level and a height of `4` means five (5)
|
||||||
* levels).
|
* levels).
|
||||||
|
*
|
||||||
|
* NOTE: This _must_ be last element in your node for snapshots to work!!!
|
||||||
*/
|
*/
|
||||||
#define SKIPLIST_ENTRY(type) \
|
#define SKIPLIST_ENTRY(type) \
|
||||||
struct __skiplist_##decl_entry { \
|
struct __skiplist_##decl_entry { \
|
||||||
|
@ -450,10 +452,10 @@
|
||||||
* a) allocate a new node and copy this node into it without \
|
* a) allocate a new node and copy this node into it without \
|
||||||
* copying the user-supplied additional memory (that will \
|
* copying the user-supplied additional memory (that will \
|
||||||
* happen iff an _update() is called on the node). \
|
* happen iff an _update() is called on the node). \
|
||||||
* b) zero out the next sle.prev/next[] pointers \
|
* b) determine if this is a duplicate, if so we set the \
|
||||||
* c) determine if this is a duplicate, if so we set the \
|
|
||||||
* sle.next[1] field to 0x1 as a reminder to re-insert \
|
* sle.next[1] field to 0x1 as a reminder to re-insert \
|
||||||
* this element as a duplicate in the restore function. \
|
* 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 \
|
* d) insert the node's copy into the slh_pres singly-linked \
|
||||||
* list. \
|
* list. \
|
||||||
* Meanwhile, don't duplicate head and the tail nodes if they \
|
* Meanwhile, don't duplicate head and the tail nodes if they \
|
||||||
|
@ -476,16 +478,20 @@
|
||||||
if (path[i] == slist->slh_head || path[i] == slist->slh_tail) \
|
if (path[i] == slist->slh_head || path[i] == slist->slh_tail) \
|
||||||
continue; \
|
continue; \
|
||||||
decl##_node_t *src = node, *dest, *this; \
|
decl##_node_t *src = node, *dest, *this; \
|
||||||
size_t amt = sizeof(src); \
|
|
||||||
char *d = NULL; \
|
|
||||||
const char *s = (const char *)src; \
|
|
||||||
rc = prefix##skip_alloc_node_##decl(slist, &dest); \
|
rc = prefix##skip_alloc_node_##decl(slist, &dest); \
|
||||||
if (rc) \
|
if (rc) \
|
||||||
return 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; \
|
d = (char *)dest; \
|
||||||
for (size_t i = 0; i < amt; i++) \
|
for (size_t i = 0; i < amt; i++) \
|
||||||
d[i] = s[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 = dest; \
|
||||||
this->field.sle.prev = NULL; \
|
this->field.sle.prev = NULL; \
|
||||||
__SKIP_NEXT_ENTRIES_B2T(field, this) \
|
__SKIP_NEXT_ENTRIES_B2T(field, this) \
|
||||||
|
@ -493,10 +499,6 @@
|
||||||
this->field.sle.next[lvl] = NULL; \
|
this->field.sle.next[lvl] = NULL; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
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; \
|
|
||||||
\
|
|
||||||
if (slist->slh_pres == NULL) \
|
if (slist->slh_pres == NULL) \
|
||||||
slist->slh_pres = dest; \
|
slist->slh_pres = dest; \
|
||||||
else { \
|
else { \
|
||||||
|
@ -789,9 +791,10 @@
|
||||||
int prefix##skip_update_##decl(decl##_t *slist, decl##_node_t *n) \
|
int prefix##skip_update_##decl(decl##_t *slist, decl##_node_t *n) \
|
||||||
{ \
|
{ \
|
||||||
static decl##_node_t apath[SKIPLIST_MAX_HEIGHT + 1]; \
|
static decl##_node_t apath[SKIPLIST_MAX_HEIGHT + 1]; \
|
||||||
int np, rc = 0; \
|
int cmp, np, rc = 0; \
|
||||||
size_t len; \
|
size_t len; \
|
||||||
decl##_node_t *node, *new, **path = (decl##_node_t **)&apath; \
|
const unsigned char *p1, *p2; \
|
||||||
|
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; \
|
||||||
|
@ -812,15 +815,42 @@
|
||||||
if (node) { \
|
if (node) { \
|
||||||
np = __skip_preserve_##decl(slist, path, len); \
|
np = __skip_preserve_##decl(slist, path, len); \
|
||||||
if (np > 0) \
|
if (np > 0) \
|
||||||
|
/* > 0 is an error code like ENOMEM, return that */ \
|
||||||
return np; \
|
return np; \
|
||||||
if (np < 0 && node->field.sle.gen < slist->gen) { \
|
if (np < 0 && node->field.sle.gen < slist->gen) { \
|
||||||
/* find the new node which was path[0], so the np'th in the \
|
/* < 0 is the negated amount of nodes in path[] that were \
|
||||||
slh_pres linked list */ \
|
preserved. \
|
||||||
new = slist->slh_pres; \
|
\ \
|
||||||
while (np++ < 0) \
|
ALGORITHM: \
|
||||||
new = new->field.sle.next[0]; \
|
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 \
|
||||||
|
it's a pointer to some heap memory, we have copied the \
|
||||||
|
pointer in _preserve() but not anything else). We purposly \
|
||||||
|
delayed that allocation/copy to this point in time. \
|
||||||
|
\ \
|
||||||
|
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 \
|
||||||
|
node is to first use the comparison function and second \
|
||||||
|
compare the bytes in the node that are not part of the entry \
|
||||||
|
structure. */ \
|
||||||
|
do { \
|
||||||
|
new = (new == NULL ? slist->slh_pres : new->field.sle.next[0]); \
|
||||||
|
cmp = __skip_key_compare_##decl(slist, node, new, slist->aux); \
|
||||||
|
p1 = (unsigned char *)((uintptr_t) new); \
|
||||||
|
p2 = (unsigned char *)((uintptr_t)node); \
|
||||||
|
for (size_t i = 0; i < (sizeof(decl##_node_t) - sizeof(struct __skiplist_##decl_entry)); i++) { \
|
||||||
|
if (p1[i] != p2[i]) { \
|
||||||
|
cmp = p1[i] < p2[i] ? -1 : 1; \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} while (cmp != 0); \
|
||||||
|
if (cmp != 0) \
|
||||||
|
return ENOMEM; /* TODO */ \
|
||||||
new->field.sle.prev = (decl##_node_t *)0x1; \
|
new->field.sle.prev = (decl##_node_t *)0x1; \
|
||||||
archive_node_blk; \
|
archive_node_blk; \
|
||||||
|
if (rc) \
|
||||||
|
return rc; \
|
||||||
} \
|
} \
|
||||||
new = n; \
|
new = n; \
|
||||||
update_node_blk; \
|
update_node_blk; \
|
||||||
|
|
Loading…
Reference in a new issue