From 710f714417b6cafb4da56a7fd566ba9aac9aafcb Mon Sep 17 00:00:00 2001 From: Greg Burd Date: Sun, 24 Mar 2024 09:32:47 -0400 Subject: [PATCH] snapshot v2/p7; update --- examples/slm.c | 52 ++++++++++++++++++++++++++++-------------- include/sl.h | 62 +++++++++++++++++++++++++++++++++++++------------- 2 files changed, 81 insertions(+), 33 deletions(-) diff --git a/examples/slm.c b/examples/slm.c index 2416ba9..6e0ee00 100644 --- a/examples/slm.c +++ b/examples/slm.c @@ -38,6 +38,7 @@ struct slex_node { int key; char *value; + /* NOTE: This _must_ be last element in node for snapshots to work!!! */ SKIPLIST_ENTRY(slex_node) entries; }; @@ -53,8 +54,11 @@ SKIPLIST_DECL( new->key = node->key; char *nv = calloc(strlen(node->value) + 1, sizeof(char)); if (nv == NULL) - rc = ENOMEM; // leaks some memory... TODO - new->value = strncpy(nv, node->value, strlen(node->value)); + rc = ENOMEM; + else { + strncpy(nv, node->value, strlen(node->value)); + new->value = nv; + } }, /* size in bytes of the content stored in an entry by you */ { size = strlen(node->value) + 1; }) @@ -179,18 +183,21 @@ shuffle(int *array, size_t n) } } +//define DOT #define TEST_ARRAY_SIZE 8 int main() { - int gen = 0, rc = 0; + int rc = 0; +#ifdef DOT FILE *of = fopen("/tmp/slm.dot", "w"); if (!of) { perror("Failed to open file /tmp/slm.dot"); return 1; } +#endif /* Allocate and initialize a Skiplist. */ slex_t *list = (slex_t *)malloc(sizeof(slex_t)); @@ -200,44 +207,53 @@ main() rc = api_skip_init_slex(list, 12, __slm_key_compare); if (rc) return rc; +#ifdef DOT 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 + /* 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. */ + int i; int amt = TEST_ARRAY_SIZE, asz = (amt * 2) + 1; int array[(TEST_ARRAY_SIZE * 2) + 1]; for (int j = 0, i = -amt; i <= amt; i++, j++) array[j] = i; 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]))); + snp[i + 1] = api_skip_snapshot_slex(list); +#ifdef DOT api_skip_dot_slex(of, list, gen++, sprintf_slex_node); +#endif char *v = api_skip_get_slex(list, array[i]); api_skip_set_slex(list, array[i], to_upper(v)); } + int r = i; api_skip_dup_slex(list, -1, int_to_roman_numeral(-1)); +#ifdef DOT api_skip_dot_slex(of, list, gen++, sprintf_slex_node); +#endif api_skip_dup_slex(list, 1, int_to_roman_numeral(1)); +#ifdef DOT 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_dot_slex(of, list, gen++, sprintf_slex_node); + snp[++i] = api_skip_snapshot_slex(list); -#if 0 //TODO - snap = api_skip_snapshot_slex(list); - restored = api_skip_restore_snapshot_slex(snap, __skip_key_compare_slex); - api_skip_dispose_snapshot_slex(snap); - api_skip_destroy_slex(restored); +#ifdef DOT + api_skip_dot_slex(of, list, gen++, sprintf_slex_node); #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, -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); @@ -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); api_skip_destroy_slex(list); +#ifdef DOT api_skip_dot_end_slex(of, gen); fclose(of); +#endif return rc; } diff --git a/include/sl.h b/include/sl.h index 6fd417b..aaa593f 100644 --- a/include/sl.h +++ b/include/sl.h @@ -161,6 +161,8 @@ * 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) * levels). + * + * NOTE: This _must_ be last element in your node for snapshots to work!!! */ #define SKIPLIST_ENTRY(type) \ struct __skiplist_##decl_entry { \ @@ -450,10 +452,10 @@ * a) allocate a new node and copy this node into it without \ * copying the user-supplied additional memory (that will \ * happen iff an _update() is called on the node). \ - * b) zero out the next sle.prev/next[] pointers \ - * c) determine if this is a duplicate, if so we set the \ + * b) determine if this is a duplicate, if so we set the \ * sle.next[1] field to 0x1 as a reminder to re-insert \ * this element as a duplicate in the restore function. \ + * c) zero out the next sle.prev/next[] pointers \ * d) insert the node's copy into the slh_pres singly-linked \ * list. \ * Meanwhile, don't duplicate head and the tail nodes if they \ @@ -476,16 +478,20 @@ if (path[i] == slist->slh_head || path[i] == slist->slh_tail) \ continue; \ 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); \ if (rc) \ return rc; \ + size_t amt = sizeof(decl##_node_t) + sizeof(struct __skiplist_##decl_idx) * slist->max; \ + char *d = NULL; \ + const char *s = (const char *)src; \ d = (char *)dest; \ for (size_t i = 0; i < amt; i++) \ d[i] = s[i]; \ \ + if (__skip_key_compare_##decl(slist, dest, dest->field.sle.next[0], slist->aux) == 0 || \ + __skip_key_compare_##decl(slist, dest, dest->field.sle.prev, slist->aux) == 0) \ + dest->field.sle.next[0] = (decl##_node_t *)0x1; \ + \ this = dest; \ this->field.sle.prev = NULL; \ __SKIP_NEXT_ENTRIES_B2T(field, this) \ @@ -493,10 +499,6 @@ 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) \ slist->slh_pres = dest; \ else { \ @@ -789,9 +791,10 @@ int prefix##skip_update_##decl(decl##_t *slist, decl##_node_t *n) \ { \ static decl##_node_t apath[SKIPLIST_MAX_HEIGHT + 1]; \ - int np, rc = 0; \ + int cmp, np, rc = 0; \ 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) \ return -1; \ @@ -812,15 +815,42 @@ if (node) { \ np = __skip_preserve_##decl(slist, path, len); \ if (np > 0) \ + /* > 0 is an error code like ENOMEM, return that */ \ return np; \ if (np < 0 && node->field.sle.gen < slist->gen) { \ - /* find the new node which was path[0], so the np'th in the \ - slh_pres linked list */ \ - new = slist->slh_pres; \ - while (np++ < 0) \ - new = new->field.sle.next[0]; \ + /* < 0 is the negated amount of nodes in path[] that were \ + preserved. \ + \ \ + ALGORITHM: \ + 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; \ archive_node_blk; \ + if (rc) \ + return rc; \ } \ new = n; \ update_node_blk; \