impl prev/next api

This commit is contained in:
Gregory Burd 2024-03-19 12:37:16 -04:00
parent de415b0d65
commit f0e4c84fab

View file

@ -111,345 +111,353 @@
fn \ fn \
} }
#define SKIPLIST_DECL(decl, prefix, field, free_node_blk, update_node_blk) \ #define SKIPLIST_DECL(decl, prefix, field, free_node_blk, update_node_blk) \
\ \
/* Skiplist node type */ \ /* Skiplist node type */ \
typedef struct decl##_node decl##_node_t; \ typedef struct decl##_node decl##_node_t; \
\ \
/* Skiplist type */ \ /* Skiplist type */ \
typedef struct decl { \ typedef struct decl { \
size_t level, length, max, fanout; \ size_t level, length, max, fanout; \
int (*cmp)(struct decl *, decl##_node_t *, decl##_node_t *, void *); \ int (*cmp)(struct decl *, decl##_node_t *, decl##_node_t *, void *); \
void *aux; \ void *aux; \
decl##_node_t *slh_head; \ decl##_node_t *slh_head; \
decl##_node_t *slh_tail; \ decl##_node_t *slh_tail; \
} decl##_t; \ } decl##_t; \
\ \
/* -- __skip_key_compare_ \ /* -- __skip_key_compare_ \
* \ * \
* This function takes four arguments: \ * This function takes four arguments: \
* - a reference to the Skiplist \ * - a reference to the Skiplist \
* - the two nodes to compare, `a` and `b` \ * - the two nodes to compare, `a` and `b` \
* - `aux` an additional auxiliary argument \ * - `aux` an additional auxiliary argument \
* and returns: \ * and returns: \
* a < b : return -1 \ * a < b : return -1 \
* a == b : return 0 \ * a == b : return 0 \
* a > b : return 1 \ * a > b : return 1 \
*/ \ */ \
static int __skip_key_compare_##decl(decl##_t *slist, decl##_node_t *a, \ static int __skip_key_compare_##decl(decl##_t *slist, decl##_node_t *a, \
decl##_node_t *b, void *aux) \ decl##_node_t *b, void *aux) \
{ \ { \
if (a == b) \ if (a == b) \
return 0; \ return 0; \
if (a == slist->slh_head || b == slist->slh_tail) \ if (a == slist->slh_head || b == slist->slh_tail) \
return -1; \ return -1; \
if (a == slist->slh_tail || b == slist->slh_head) \ if (a == slist->slh_tail || b == slist->slh_head) \
return 1; \ return 1; \
return slist->cmp(slist, a, b, aux); \ return slist->cmp(slist, a, b, aux); \
} \ } \
\ \
/* -- __skip_toss_ */ \ /* -- __skip_toss_ */ \
static int __skip_toss_##decl(size_t max, size_t fanout) \ static int __skip_toss_##decl(size_t max, size_t fanout) \
{ \ { \
size_t level = 0; \ size_t level = 0; \
while (level + 1 < max) { \ while (level + 1 < max) { \
if (rand() % fanout == 0) /* NOLINT(*-msc50-cpp) */ \ if (rand() % fanout == 0) /* NOLINT(*-msc50-cpp) */ \
level++; \ level++; \
else \ else \
break; \ break; \
} \ } \
return level; \ return level; \
} \ } \
\ \
/* -- skip_alloc_node_ */ \ /* -- skip_alloc_node_ */ \
int prefix##skip_alloc_node_##decl(decl##_t *slist, decl##_node_t **node) \ int prefix##skip_alloc_node_##decl(decl##_t *slist, decl##_node_t **node) \
{ \ { \
decl##_node_t *n; \ decl##_node_t *n; \
n = (decl##_node_t *)calloc(1, sizeof(decl##_node_t)); \ n = (decl##_node_t *)calloc(1, sizeof(decl##_node_t)); \
ARRAY_ALLOC(n->field.sle_next, struct decl##_node, slist->max); \ ARRAY_ALLOC(n->field.sle_next, struct decl##_node, slist->max); \
if (n && n->field.sle_next) { \ if (n && n->field.sle_next) { \
ARRAY_SET_SIZE(n->field.sle_next, slist->max); \ ARRAY_SET_SIZE(n->field.sle_next, slist->max); \
ARRAY_SET_LENGTH(n->field.sle_next, 0); \ ARRAY_SET_LENGTH(n->field.sle_next, 0); \
*node = n; \ *node = n; \
return 0; \ return 0; \
} \ } \
return ENOMEM; \ return ENOMEM; \
} \ } \
\ \
/* -- skip_init_ \ /* -- skip_init_ \
* max: 12, fanout: 4 are good defaults. \ * max: 12, fanout: 4 are good defaults. \
*/ \ */ \
int prefix##skip_init_##decl(decl##_t *slist, size_t max, size_t fanout, \ int prefix##skip_init_##decl(decl##_t *slist, size_t max, size_t fanout, \
int (*cmp)(struct decl *, decl##_node_t *, decl##_node_t *, void *)) \ int (*cmp)(struct decl *, decl##_node_t *, decl##_node_t *, void *)) \
{ \ { \
int rc = 0; \ int rc = 0; \
slist->level = 0; \ slist->level = 0; \
slist->length = 0; \ slist->length = 0; \
slist->max = max; \ slist->max = max; \
slist->fanout = fanout; \ slist->fanout = fanout; \
slist->cmp = cmp; \ slist->cmp = cmp; \
rc = prefix##skip_alloc_node_##decl(slist, &slist->slh_head); \ rc = prefix##skip_alloc_node_##decl(slist, &slist->slh_head); \
if (rc) \ if (rc) \
goto fail; \ goto fail; \
rc = prefix##skip_alloc_node_##decl(slist, &slist->slh_tail); \ rc = prefix##skip_alloc_node_##decl(slist, &slist->slh_tail); \
if (rc) \ if (rc) \
goto fail; \ goto fail; \
ARRAY_SET_LENGTH(slist->slh_head->field.sle_next, max); \ ARRAY_SET_LENGTH(slist->slh_head->field.sle_next, max); \
for (size_t __i = 0; __i < max; __i++) \ for (size_t __i = 0; __i < max; __i++) \
slist->slh_head->field.sle_next[__i] = slist->slh_tail; \ slist->slh_head->field.sle_next[__i] = slist->slh_tail; \
slist->slh_head->field.sle_prev = NULL; \ slist->slh_head->field.sle_prev = NULL; \
ARRAY_SET_LENGTH(slist->slh_tail->field.sle_next, max); \ ARRAY_SET_LENGTH(slist->slh_tail->field.sle_next, max); \
for (size_t __i = 0; __i < max; __i++) \ for (size_t __i = 0; __i < max; __i++) \
slist->slh_tail->field.sle_next[__i] = NULL; \ slist->slh_tail->field.sle_next[__i] = NULL; \
slist->slh_head->field.sle_prev = slist->slh_tail; \ slist->slh_head->field.sle_prev = slist->slh_tail; \
fail:; \ fail:; \
return rc; \ return rc; \
} \ } \
\ \
/* -- skip_free_node_ */ \ /* -- skip_free_node_ */ \
void prefix##skip_free_node_##decl(decl##_node_t *node) \ void prefix##skip_free_node_##decl(decl##_node_t *node) \
{ \ { \
free_node_blk; \ free_node_blk; \
free(node->field.sle_next); \ free(node->field.sle_next); \
free(node); \ free(node); \
} \ } \
\ \
/* -- skip_size_ */ \ /* -- skip_size_ */ \
int prefix##skip_size_##decl(decl##_t *slist) \ int prefix##skip_size_##decl(decl##_t *slist) \
{ \ { \
return slist->length; \ return slist->length; \
} \ } \
\ \
/* -- skip_empty_ */ \ /* -- skip_empty_ */ \
int prefix##skip_empty_##decl(decl##_t *slist) \ int prefix##skip_empty_##decl(decl##_t *slist) \
{ \ { \
return slist->length == 0; \ return slist->length == 0; \
} \ } \
\ \
/* -- skip_head_ */ \ /* -- skip_head_ */ \
decl##_node_t *prefix##skip_head_##decl(decl##_t *slist) \ decl##_node_t *prefix##skip_head_##decl(decl##_t *slist) \
{ \ { \
return slist->slh_head; \ return slist->slh_head; \
} \ } \
\ \
/* -- skip_tail_ */ \ /* -- skip_tail_ */ \
decl##_node_t *prefix##skip_tail_##decl(decl##_t *slist) \ decl##_node_t *prefix##skip_tail_##decl(decl##_t *slist) \
{ \ { \
return slist->slh_tail; \ return slist->slh_tail; \
} \ } \
\ \
/* -- skip_locate_ */ \ /* -- skip_locate_ */ \
decl##_node_t **prefix##skip_locate_##decl(decl##_t *slist, \ decl##_node_t **prefix##skip_locate_##decl(decl##_t *slist, \
decl##_node_t *n) \ decl##_node_t *n) \
{ \ { \
unsigned int i; \ unsigned int i; \
decl##_node_t **path; \ decl##_node_t **path; \
decl##_node_t *elm = slist->slh_head; \ decl##_node_t *elm = slist->slh_head; \
\ \
if (n == NULL) \ if (n == NULL) \
return NULL; \ return NULL; \
\ \
i = slist->max + 1; \ i = slist->max + 1; \
ARRAY_ALLOC(path, decl##_node_t, i); \ ARRAY_ALLOC(path, decl##_node_t, i); \
ARRAY_SET_LENGTH(path, 1); \ ARRAY_SET_LENGTH(path, 1); \
i = slist->level; \ i = slist->level; \
if (path == NULL) \ if (path == NULL) \
return NULL; \ return NULL; \
\ \
/* Find the node that matches `node` or NULL. */ \ /* Find the node that matches `node` or NULL. */ \
do { \ do { \
while (elm && \ while (elm && \
__skip_key_compare_##decl(slist, elm->field.sle_next[i], n, \ __skip_key_compare_##decl(slist, elm->field.sle_next[i], n, \
slist->aux) < 0) \ slist->aux) < 0) \
elm = elm->field.sle_next[i]; \ elm = elm->field.sle_next[i]; \
path[i + 1] = elm; \ path[i + 1] = elm; \
ARRAY_SET_LENGTH(path, ARRAY_LENGTH(path) + 1); \ ARRAY_SET_LENGTH(path, ARRAY_LENGTH(path) + 1); \
} while (i--); \ } while (i--); \
elm = elm->field.sle_next[0]; \ elm = elm->field.sle_next[0]; \
if (__skip_key_compare_##decl(slist, elm, n, slist->aux) == 0) { \ if (__skip_key_compare_##decl(slist, elm, n, slist->aux) == 0) { \
path[0] = elm; \ path[0] = elm; \
return path; \ return path; \
} \ } \
ARRAY_FREE(path); \ ARRAY_FREE(path); \
return NULL; \ return NULL; \
} \ } \
\ \
/* -- skip_insert_ */ \ /* -- skip_insert_ */ \
int prefix##skip_insert_##decl(decl##_t *slist, decl##_node_t *n) \ int prefix##skip_insert_##decl(decl##_t *slist, decl##_node_t *n) \
{ \ { \
unsigned int i; \ unsigned int i; \
decl##_node_t *prev, *elm = slist->slh_head; \ decl##_node_t *prev, *elm = slist->slh_head; \
decl##_node_t **path; \ decl##_node_t **path; \
\ \
if (n == NULL) \ if (n == NULL) \
return ENOENT; \ return ENOENT; \
\ \
i = slist->level; \ i = slist->level; \
ARRAY_ALLOC(path, decl##_node_t, slist->max); \ ARRAY_ALLOC(path, decl##_node_t, slist->max); \
if (path == NULL) \ if (path == NULL) \
return ENOMEM; \ return ENOMEM; \
\ \
/* Find the position in the list where this element belongs. */ \ /* Find the position in the list where this element belongs. */ \
do { \ do { \
while (elm && \ while (elm && \
__skip_key_compare_##decl(slist, elm->field.sle_next[i], n, \ __skip_key_compare_##decl(slist, elm->field.sle_next[i], n, \
slist->aux) < 0) \ slist->aux) < 0) \
elm = elm->field.sle_next[i]; \ elm = elm->field.sle_next[i]; \
path[i] = elm; \ path[i] = elm; \
ARRAY_SET_LENGTH(path, ARRAY_LENGTH(path) + 1); \ ARRAY_SET_LENGTH(path, ARRAY_LENGTH(path) + 1); \
} while (i--); \ } while (i--); \
i = 0; \ i = 0; \
prev = elm; \ prev = elm; \
elm = elm->field.sle_next[0]; \ elm = elm->field.sle_next[0]; \
if (__skip_key_compare_##decl(slist, elm, n, slist->aux) == 0) { \ if (__skip_key_compare_##decl(slist, elm, n, slist->aux) == 0) { \
/* Don't overwrite, to do that use _REPLACE not _INSERT */ \ /* Don't overwrite, to do that use _REPLACE not _INSERT */ \
ARRAY_FREE(path); \ ARRAY_FREE(path); \
return -1; \ return -1; \
} \ } \
size_t level = __skip_toss_##decl(slist->max, slist->fanout); \ size_t level = __skip_toss_##decl(slist->max, slist->fanout); \
ARRAY_SET_LENGTH(n->field.sle_next, level); \ ARRAY_SET_LENGTH(n->field.sle_next, level); \
if (level > slist->level) { \ if (level > slist->level) { \
for (i = slist->level + 1; i <= level; i++) { \ for (i = slist->level + 1; i <= level; i++) { \
path[i] = slist->slh_tail; \ path[i] = slist->slh_tail; \
} \ } \
slist->level = level; \ slist->level = level; \
} \ } \
for (i = 0; i <= level; i++) { \ for (i = 0; i <= level; i++) { \
n->field.sle_next[i] = path[i]->field.sle_next[i]; \ n->field.sle_next[i] = path[i]->field.sle_next[i]; \
path[i]->field.sle_next[i] = n; \ path[i]->field.sle_next[i] = n; \
} \ } \
n->field.sle_prev = prev; \ n->field.sle_prev = prev; \
if (n->field.sle_next[0] == slist->slh_tail) { \ if (n->field.sle_next[0] == slist->slh_tail) { \
slist->slh_tail->field.sle_prev = n; \ slist->slh_tail->field.sle_prev = n; \
} \ } \
slist->length++; \ slist->length++; \
ARRAY_FREE(path); \ ARRAY_FREE(path); \
return 0; \ return 0; \
} \ } \
\ \
/* -- skip_insert_dup_ TODO \ /* -- skip_insert_dup_ TODO \
int prefix##skip_insert_dup_##decl(decl##_t *slist, decl##_node_t *n) \ int prefix##skip_insert_dup_##decl(decl##_t *slist, decl##_node_t *n) \
{ \ { \
((void)slist); \ ((void)slist); \
((void)n); \ ((void)n); \
return 0; \ return 0; \
} \ } \
*/ \ */ \
\ \
/* -- skip_update_ \ /* -- skip_update_ \
* Locates a node in the list that equals the `new` node and then \ * Locates a node in the list that equals the `new` node and then \
* uses the `update_node_blk` to update the contents. \ * uses the `update_node_blk` to update the contents. \
* WARNING: Do not update the portion of the node used for ordering \ * WARNING: Do not update the portion of the node used for ordering \
* (e.g. `key`) unless you really know what you're doing. \ * (e.g. `key`) unless you really know what you're doing. \
*/ \ */ \
int prefix##skip_update_##decl(decl##_t *slist, decl##_node_t *new) \ int prefix##skip_update_##decl(decl##_t *slist, decl##_node_t *new) \
{ \ { \
decl##_node_t **path, *node; \ decl##_node_t **path, *node; \
\ \
if (!slist || !new) \ if (!slist || !new) \
return -1; \ return -1; \
\ \
path = prefix##skip_locate_##decl(slist, new); \ path = prefix##skip_locate_##decl(slist, new); \
if (ARRAY_SIZE(path) > 0) { \ if (ARRAY_SIZE(path) > 0) { \
node = path[0]; \ node = path[0]; \
ARRAY_FREE(path); \ ARRAY_FREE(path); \
update_node_blk; \ update_node_blk; \
return 0; \ return 0; \
} \ } \
return -1; \ return -1; \
} \ } \
\ \
/* -- skip_remove_ */ \ /* -- skip_remove_ */ \
int prefix##skip_remove_##decl(decl##_t *slist, decl##_node_t *n) \ int prefix##skip_remove_##decl(decl##_t *slist, decl##_node_t *n) \
{ \ { \
size_t i, s; \ size_t i, s; \
decl##_node_t **path, *node; \ decl##_node_t **path, *node; \
\ \
if (!slist || !n) \ if (!slist || !n) \
return -1; \ return -1; \
if (slist->length == 0) \ if (slist->length == 0) \
return 0; \ return 0; \
\ \
path = prefix##skip_locate_##decl(slist, n); \ path = prefix##skip_locate_##decl(slist, n); \
s = ARRAY_LENGTH(path); \ s = ARRAY_LENGTH(path); \
node = path[0]; \ node = path[0]; \
if (s > 0) { \ if (s > 0) { \
node->field.sle_next[0]->field.sle_prev = node->field.sle_prev; \ node->field.sle_next[0]->field.sle_prev = node->field.sle_prev; \
for (i = 1; i < s; i++) { \ for (i = 1; i < s; i++) { \
if (path[i]->field.sle_next[i - 1] != node) \ if (path[i]->field.sle_next[i - 1] != node) \
break; \ break; \
path[i]->field.sle_next[i - 1] = node->field.sle_next[i - 1]; \ path[i]->field.sle_next[i - 1] = node->field.sle_next[i - 1]; \
if (path[i]->field.sle_next[i - 1] == slist->slh_tail) { \ if (path[i]->field.sle_next[i - 1] == slist->slh_tail) { \
size_t h = ARRAY_LENGTH(path[i]->field.sle_next); \ size_t h = ARRAY_LENGTH(path[i]->field.sle_next); \
ARRAY_SET_LENGTH(path[i]->field.sle_next, h - 1); \ ARRAY_SET_LENGTH(path[i]->field.sle_next, h - 1); \
} \ } \
} \ } \
ARRAY_FREE(path); \ ARRAY_FREE(path); \
free_node_blk; \ free_node_blk; \
/* Find all levels in the first element in the list that point \ /* Find all levels in the first element in the list that point \
at the tail and shrink the level*/ \ at the tail and shrink the level*/ \
while (slist->level > 0 && \ while (slist->level > 0 && \
slist->slh_head->field.sle_next[slist->level] == slist->slh_tail) { \ slist->slh_head->field.sle_next[slist->level] == slist->slh_tail) { \
slist->level--; \ slist->level--; \
} \ } \
slist->length--; \ slist->length--; \
} \ } \
return 0; \ return 0; \
} \ } \
\ \
/* -- skip_find_ */ \ /* -- skip_find_ */ \
int prefix##skip_find_##decl(decl##_t *slist, decl##_node_t *n) \ decl##_node_t *prefix##skip_find_##decl(decl##_t *slist, decl##_node_t *n) \
{ \ { \
((void)slist); /* TODO */ \ ((void)slist); /* TODO */ \
((void)n); \ ((void)n); \
return 0; \ return 0; \
} \ } \
\ \
/* -- skip_find_gte */ \ /* -- skip_find_gte */ \
int prefix##skip_find_gte_##decl(decl##_t *slist, decl##_node_t *n) \ decl##_node_t *prefix##skip_find_gte_##decl(decl##_t *slist, \
{ \ decl##_node_t *n) \
((void)slist); /* TODO */ \ { \
((void)n); \ ((void)slist); /* TODO */ \
return 0; \ ((void)n); \
} \ return 0; \
\ } \
/* -- skip_find_lte */ \ \
int prefix##skip_find_lte_##decl(decl##_t *slist, decl##_node_t *n) \ /* -- skip_find_lte */ \
{ \ decl##_node_t *prefix##skip_find_lte_##decl(decl##_t *slist, \
((void)slist); /* TODO */ \ decl##_node_t *n) \
((void)n); \ { \
return 0; \ ((void)slist); /* TODO */ \
} \ ((void)n); \
\ return 0; \
/* -- skip_next_node_ */ \ } \
int prefix##skip_next_node_##decl(decl##_t *slist, decl##_node_t *n) \ \
{ \ /* -- skip_next_node_ */ \
((void)slist); /* TODO */ \ decl##_node_t *prefix##skip_next_node_##decl(decl##_t *slist, \
((void)n); \ decl##_node_t *n) \
return 0; \ { \
} \ if (!slist || !n) \
\ return NULL; \
/* -- skip_prev_node_ */ \ if (n->field.sle_next[0] == slist->slh_tail) \
int prefix##skip_prev_node_##decl(decl##_t *slist, decl##_node_t *n) \ return NULL; \
{ \ return n->field.sle_next[0]; \
((void)slist); /* TODO */ \ } \
((void)n); \ \
return 0; \ /* -- skip_prev_node_ */ \
} \ decl##_node_t *prefix##skip_prev_node_##decl(decl##_t *slist, \
\ decl##_node_t *n) \
/* -- skip_destroy_ */ \ { \
int prefix##skip_destroy_##decl(decl##_t *slist, decl##_node_t *n) \ if (!slist || !n) \
{ \ return NULL; \
((void)slist); /* TODO */ \ if (n->field.sle_prev == slist->slh_head) \
((void)n); \ return NULL; \
return 0; \ return n->field.sle_prev; \
} \ } \
\ \
/* -- __skip_integrity_check_ */ \ /* -- skip_destroy_ */ \
static int __skip_integrity_check_##decl(decl##_t *slist) \ int prefix##skip_destroy_##decl(decl##_t *slist, decl##_node_t *n) \
{ \ { \
((void)slist); /* TODO */ \ ((void)slist); /* TODO */ \
return 0; \ ((void)n); \
return 0; \
} \
\
/* -- __skip_integrity_check_ */ \
static int __skip_integrity_check_##decl(decl##_t *slist) \
{ \
((void)slist); /* TODO */ \
return 0; \
} }
#define SKIPLIST_GETTER(decl, prefix, name, ktype, vtype, qblk, rblk) \ #define SKIPLIST_GETTER(decl, prefix, name, ktype, vtype, qblk, rblk) \