From b5ec8aca21afa788ed4d575f56710b52c398d49d Mon Sep 17 00:00:00 2001 From: Greg Burd Date: Tue, 28 May 2024 22:46:47 -0400 Subject: [PATCH] WIP --- examples/ex2.c | 51 +++++++++++++++++------------ include/sl.h | 89 ++++++++++++++++++++++++++------------------------ 2 files changed, 77 insertions(+), 63 deletions(-) diff --git a/examples/ex2.c b/examples/ex2.c index fb24c4b..099fe4d 100644 --- a/examples/ex2.c +++ b/examples/ex2.c @@ -14,8 +14,8 @@ // --------------------------------------------------------------------------- #define TEST_ARRAY_SIZE 10 #define VALIDATE -//define SNAPSHOTS -//define TODO_RESTORE_SNAPSHOTS +// define SNAPSHOTS +// define TODO_RESTORE_SNAPSHOTS #define STABLE_SEED #define DOT @@ -189,24 +189,24 @@ int __xorshift32_state = 0; static uint32_t xorshift32() { - uint32_t x = __xorshift32_state; - if (x == 0) - x = 123456789; - x ^= x << 13; - x ^= x >> 17; - x ^= x << 5; - __xorshift32_state = x; - return x; + uint32_t x = __xorshift32_state; + if (x == 0) + x = 123456789; + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + __xorshift32_state = x; + return x; } static void xorshift32_seed() { - // Seed the PRNG + // Seed the PRNG #ifdef STABLE_SEED - __xorshift32_state = 8675309; + __xorshift32_state = 8675309; #else - __xorshift32_state = (unsigned int)time(NULL) ^ getpid(); + __xorshift32_state = (unsigned int)time(NULL) ^ getpid(); #endif } @@ -358,7 +358,7 @@ main() rc = api_skip_put_ex(list, array[i], numeral); CHECK; #ifdef DOT - sprintf(msg, "put key: %d value: %s", i, numeral); + sprintf(msg, "put key: %d value: %s", array[i], numeral); api_skip_dot_ex(of, list, gen++, msg, sprintf_ex_node); #endif char *v = api_skip_get_ex(list, array[i]); @@ -369,24 +369,38 @@ main() to_upper(upper_numeral); api_skip_set_ex(list, array[i], upper_numeral); CHECK; + + for (size_t j = 0; j < api_skip_length_ex(list); j++) { + int n = xorshift32() % api_skip_length_ex(list); + api_skip_contains_ex(list, n); + CHECK; +#if 0 + sprintf(msg, "locate key: %d", n); + api_skip_dot_ex(of, list, gen++, msg, sprintf_ex_node); +#endif + } } numeral = int_to_roman_numeral(-1); api_skip_dup_ex(list, -1, numeral); CHECK; #ifdef DOT - sprintf(msg, "put dup key: %d value: %s", i, numeral); + sprintf(msg, "put dup key: %d value: %s", -1, numeral); api_skip_dot_ex(of, list, gen++, msg, sprintf_ex_node); #endif numeral = int_to_roman_numeral(1); api_skip_dup_ex(list, 1, numeral); CHECK; #ifdef DOT - sprintf(msg, "put dup key: %d value: %s", i, numeral); + sprintf(msg, "put dup key: %d value: %s", 1, numeral); api_skip_dot_ex(of, list, gen++, msg, sprintf_ex_node); #endif api_skip_del_ex(list, 0); CHECK; +#ifdef DOT + sprintf(msg, "deleted key: %d, value: %s", 0, numeral); + api_skip_dot_ex(of, list, gen++, msg, sprintf_ex_node); +#endif if (api_skip_get_ex(list, 0) != NULL) perror("found a deleted item!"); api_skip_del_ex(list, 0); @@ -400,11 +414,6 @@ main() api_skip_del_ex(list, key); CHECK; -#ifdef DOT - sprintf(msg, "deleted key: %d, value: %s", 0, numeral); - 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); diff --git a/include/sl.h b/include/sl.h index 8827231..2ca17a8 100644 --- a/include/sl.h +++ b/include/sl.h @@ -61,7 +61,7 @@ * A skip-list is a sorted list with O(log(n)) on average for most operations. * It is a probabilistic datastructure, meaning that it does not guarantee * O(log(n)), but it has been shown to approximate it over time. This - * implementation includes the rebalancing techniques that improve on that + * implementation includes the re-balancing techniques that improve on that * approximation using an adaptive technique called "splay-list". It is similar * to a standard skip-list, with the key distinction that the height of each * element adapts dynamically to its access rate: popular elements increase in @@ -231,12 +231,12 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li for (iter = (list)->slh_length, (elm) = (list)->slh_tail; ((elm) = (elm)->field.sle_prev) != (list)->slh_head; iter--) /* Iterate over the next pointers in a node from bottom to top (B2T) or top to bottom (T2B). */ -#define __SKIP_ALL_ENTRIES_T2B(field, elm) for (size_t lvl = slist->slh_head->entries.sle_height - 1; lvl != (size_t)-1; lvl--) +#define __SKIP_ALL_ENTRIES_T2B(field, elm) for (size_t lvl = slist->slh_head->field.sle_height - 1; lvl != (size_t)-1; lvl--) #define __SKIP_ENTRIES_T2B(field, elm) for (size_t lvl = elm->field.sle_height; lvl != (size_t)-1; lvl--) #define __SKIP_ENTRIES_T2B_FROM(field, elm, off) for (size_t lvl = off; lvl != (size_t)-1; lvl--) #define __SKIP_IS_LAST_ENTRY_T2B() if (lvl == 0) -#define __SKIP_ALL_ENTRIES_B2T(field, elm) for (size_t lvl = 0; lvl < slist->slh_head->entries.sle_height - 1; lvl++) +#define __SKIP_ALL_ENTRIES_B2T(field, elm) for (size_t lvl = 0; lvl < slist->slh_head->field.sle_height - 1; lvl++) #define __SKIP_ENTRIES_B2T(field, elm) for (size_t lvl = 0; lvl <= elm->field.sle_height; lvl++) #define __SKIP_ENTRIES_B2T_FROM(field, elm, off) for (size_t lvl = off; lvl <= elm->field.sle_height; lvl++) #define __SKIP_IS_LAST_ENTRY_B2T() if (lvl + 1 == elm->field.sle_height) @@ -635,8 +635,8 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li */ \ static void __skip_rebalance_##decl(decl##_t *slist, size_t len, __skiplist_path_##decl##_t path[]) \ { \ - size_t i, u_hits, hits_CHu = 0, hits_CHv = 0, delta_height, cur_hits, prv_hits; \ - size_t k_threshold, m_total_hits; \ + size_t i, lvl, u_hits, hits_CHu = 0, hits_CHv = 0, delta_height; \ + size_t k_threshold, m_total_hits, expected_height; \ double asc_cond, dsc_cond; \ __skiplist_path_##decl##_t *p, path_u, path_v, *cur; \ decl##_node_t *prv_node; \ @@ -644,6 +644,16 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li /* Total hits, `k`, across all nodes. */ \ m_total_hits = slist->slh_head->field.sle_levels[slist->slh_head->field.sle_height].hits; \ \ + /* Should we adjust the height? */ \ + if (m_total_hits > 0) { \ + expected_height = floor(log(m_total_hits) / log(2)); \ + if (expected_height > slist->slh_head->field.sle_height && expected_height < SKIPLIST_MAX_HEIGHT - 1) { \ + slist->slh_head->field.sle_height++; \ + slist->slh_head->field.sle_levels[slist->slh_head->field.sle_height].next = slist->slh_tail; \ + slist->slh_tail->field.sle_height = slist->slh_head->field.sle_height; \ + } \ + } \ + \ /* Height of the head node, should be equal to floor(log(m_total_hits)). */ \ k_threshold = slist->slh_head->field.sle_height; \ \ @@ -660,8 +670,8 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li if (path_u.node == slist->slh_head || path_u.node == slist->slh_tail) \ continue; \ path_v = *(p + 1); \ - if ((*(p + 1)).node->entries.sle_levels[path_u.in].next != path_u.node) { \ - path_v.node = (*(p + 1)).node->entries.sle_levels[path_u.in].next; \ + if ((*(p + 1)).node->field.sle_levels[path_u.in].next != path_u.node) { \ + path_v.node = (*(p + 1)).node->field.sle_levels[path_u.in].next; \ path_v.pu = 0; \ } \ \ @@ -688,21 +698,16 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li dsc_cond = m_total_hits / pow(2.0, delta_height); \ u_hits = hits_CHu + hits_CHv; \ if (u_hits <= dsc_cond && path_u.node->field.sle_height > 0) { \ - if (path_u.node->field.sle_prev != slist->slh_head) { \ - /* 1) go backwards along path from where we are until head */ \ - cur = &path[i]; \ - cur_hits = cur->node->field.sle_levels[cur->in].hits; \ - prv_node = (*(p + 1)).node->entries.sle_levels[path_u.in].next; \ - /* 2) propagate hits */ \ - prv_hits = prv_node->field.sle_levels[cur->in].hits; \ - prv_node->field.sle_levels[cur->in].hits += 1; \ - cur_hits = prv_hits; \ - /* 3) adjust forward pointers */ \ - if (prv_node->field.sle_levels[cur->in].next == cur->node) \ - prv_node->field.sle_levels[cur->in].next = cur->node->field.sle_levels[cur->in].next; \ - /* 4) reduce height by one */ \ - cur->node->field.sle_height--; \ - } \ + /* 1) go backwards along path from where we are until head */ \ + cur = &path[i]; \ + prv_node = (*(p + 1)).node->field.sle_levels[path_u.in].next; \ + /* 2) propagate hits */ \ + prv_node->field.sle_levels[cur->in].hits += 1; \ + /* 3) adjust forward pointers */ \ + if (prv_node->field.sle_levels[cur->in].next == cur->node) \ + prv_node->field.sle_levels[cur->in].next = cur->node->field.sle_levels[cur->in].next; \ + /* 4) reduce height by one */ \ + cur->node->field.sle_height--; \ } \ /* (b) Check the ascent condition: \ * path[i].pu + node_hits > hits total / (2 ^ (height of head - height of node - 1)) \ @@ -714,16 +719,16 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li */ \ /* 1) check ascent condition */ \ asc_cond = m_total_hits / pow(2.0, delta_height - 1); \ - if (path_u.pu > asc_cond && path_u.node->field.sle_height < SKIPLIST_MAX_HEIGHT) { \ + if (path_u.pu > asc_cond && path_u.node->field.sle_height < SKIPLIST_MAX_HEIGHT - 1) { \ /* 2) increase height by one */ \ - path_u.node->field.sle_height++; \ + lvl = ++path_u.node->field.sle_height; \ /* 3) update hit counter */ \ - path_u.node->field.sle_levels[path_v.in].hits = path_v.node->field.sle_levels[path_v.in].hits; \ + path_u.node->field.sle_levels[lvl].hits = path_v.node->field.sle_levels[lvl].hits; \ /* 4) reset the prev node hits to 0 */ \ - path_v.node->field.sle_levels[path_v.in].hits = 0; \ + path_v.node->field.sle_levels[lvl].hits = 0; \ /* 5) update forward pointers in the level */ \ - path_u.node->field.sle_levels[path_v.in].next = path_v.node->field.sle_levels[path_v.in].next; \ - path_v.node->field.sle_levels[path_v.in].next = path_u.node; \ + path_u.node->field.sle_levels[lvl].next = path_v.node->field.sle_levels[lvl].next; \ + path_v.node->field.sle_levels[lvl].next = path_u.node; \ } \ } \ } \ @@ -734,7 +739,7 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li * Locates a node that matches another node updating `path` and then \ * returning the length of that path + 1 to the node and the matching \ * node in path[0], or NULL at path[0] where there wasn't a match. \ - * sizeof(path) should be `slist->slh_head->entries.sle_height + 1` \ + * sizeof(path) should be `slist->slh_head->field.sle_height + 1` \ */ \ static size_t __skip_locate_##decl(decl##_t *slist, decl##_node_t *n, __skiplist_path_##decl##_t path[]) \ { \ @@ -767,7 +772,7 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li path[0].node = elm; \ path[0].node->field.sle_levels[0].hits++; \ cur->pu += path[0].node->field.sle_levels[0].hits; \ - slist->slh_head->entries.sle_levels[slist->slh_head->entries.sle_height].hits++; \ + slist->slh_head->field.sle_levels[slist->slh_head->field.sle_height].hits++; \ __skip_rebalance_##decl(slist, len, path); \ } \ return len; \ @@ -784,7 +789,7 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li { \ static __skiplist_path_##decl##_t apath[SKIPLIST_MAX_HEIGHT + 1]; \ int rc = 0; \ - size_t i, len, loc = 0, cur_height, new_height; \ + size_t i, len, loc = 0, current_height, new_height; \ decl##_node_t *node; \ __skiplist_path_##decl##_t *path = apath; \ \ @@ -801,12 +806,12 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li /* Don't insert, duplicate if flag not set. */ \ return -1; \ } \ + current_height = slist->slh_head->field.sle_height - 1; \ /* Coin toss to determine level of this new node [0, current max height) */ \ - cur_height = slist->slh_head->field.sle_height - 1; \ - new_height = __skip_toss_##decl(slist, cur_height); \ + new_height = __skip_toss_##decl(slist, current_height); \ new->field.sle_height = new_height; \ /* Trim the path to at most the new height for the new node. */ \ - for (i = cur_height + 1; i <= new_height; i++) { \ + for (i = current_height + 1; i <= new_height; i++) { \ path[i + 1].node = slist->slh_tail; \ } \ /* Ensure all next[] point to tail. */ \ @@ -933,7 +938,7 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li * -- skip_position_gt_ \ * \ * Position and return a cursor at the first node that is greater than \ - * the provided node `n`. If the largestkey is less than the key in `n` \ + * the provided node `n`. If the largest key is less than the key in `n` \ * return NULL. \ */ \ decl##_node_t *prefix##skip_position_gt_##decl(decl##_t *slist, decl##_node_t *query) \ @@ -1263,7 +1268,7 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li return 0; \ \ /* (a) alloc, ... */ \ - size_t sle_arr_sz = sizeof(struct __skiplist_##decl##_level) * slist->slh_head->entries.sle_height - 1; \ + size_t sle_arr_sz = sizeof(struct __skiplist_##decl##_level) * slist->slh_head->field.sle_height - 1; \ rc = prefix##skip_alloc_node_##decl(&dest); \ if (rc) \ return rc; \ @@ -1498,28 +1503,28 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li return n_err; \ } \ \ - if (slist->slh_head->entries.sle_height > SKIPLIST_MAX_HEIGHT) { \ + if (slist->slh_head->field.sle_height > SKIPLIST_MAX_HEIGHT) { \ __skip_integrity_failure_##decl("skiplist head height > SKIPLIST_MAX_HEIGHT\n"); \ n_err++; \ if (flags) \ return n_err; \ } \ \ - if (slist->slh_tail->entries.sle_height > SKIPLIST_MAX_HEIGHT) { \ + if (slist->slh_tail->field.sle_height > SKIPLIST_MAX_HEIGHT) { \ __skip_integrity_failure_##decl("skiplist tail height > SKIPLIST_MAX_HEIGHT\n"); \ n_err++; \ if (flags) \ return n_err; \ } \ \ - if (slist->slh_head->entries.sle_height != slist->slh_tail->entries.sle_height) { \ + if (slist->slh_head->field.sle_height != slist->slh_tail->field.sle_height) { \ __skip_integrity_failure_##decl("skiplist head & tail height are not equal\n"); \ n_err++; \ if (flags) \ return n_err; \ } \ \ - /* TODO: slh_head->entries.sle_height should == log(m) where m is the sum of all hits on all nodes */ \ + /* TODO: slh_head->field.sle_height should == log(m) where m is the sum of all hits on all nodes */ \ \ if (SKIPLIST_MAX_HEIGHT < 1) { \ __skip_integrity_failure_##decl("SKIPLIST_MAX_HEIGHT cannot be less than 1\n"); \ @@ -1576,9 +1581,9 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li { \ this = &node->field; \ \ - if (this->sle_height > slist->slh_head->entries.sle_height) { \ + if (this->sle_height > slist->slh_head->field.sle_height) { \ __skip_integrity_failure_##decl("the %lu node's [%p] height %lu is > head %lu\n", nth, (void *)node, this->sle_height, \ - slist->slh_head->entries.sle_height); \ + slist->slh_head->field.sle_height); \ n_err++; \ if (flags) \ return n_err; \