Prefer functions to macros.
This commit is contained in:
parent
db246210e8
commit
7a15b70530
2 changed files with 140 additions and 101 deletions
|
@ -1,39 +1,26 @@
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <errno.h>
|
||||||
#include "../include/sl.h"
|
#include "../include/sl.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SKIPLIST EXAMPLE:
|
* SKIPLIST EXAMPLE:
|
||||||
*
|
*
|
||||||
* This example creates a Skiplist keys and values are integers.
|
* This example creates a Skiplist keys and values are integers.
|
||||||
|
* 'slex' - meaning: SkipList EXample
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Create a type of Skiplist that maps int -> int. */
|
/* Create a type of Skiplist that maps int -> int. */
|
||||||
struct entry {
|
struct slex_node {
|
||||||
int key;
|
int key;
|
||||||
int value;
|
int value;
|
||||||
SKIP_ENTRY(entry) entries;
|
SKIP_ENTRY(slex_node) entries;
|
||||||
};
|
};
|
||||||
SKIP_HEAD(skip, entry);
|
SKIP_HEAD(slex, slex_node);
|
||||||
|
|
||||||
/* Generate a function to compare keys to order our list.
|
/* Generate all the access functions for our type of Skiplist. */
|
||||||
*
|
SKIP_DECL(slex, api_, entries, {
|
||||||
* The function takes three arguments:
|
|
||||||
* - `a` and `b` of the provided type (`entry` in this case)
|
|
||||||
* - `aux` an additional auxiliary argument
|
|
||||||
* and returns:
|
|
||||||
* a < b : return -1
|
|
||||||
* a == b : return 0
|
|
||||||
* a > b : return 1
|
|
||||||
*
|
|
||||||
* This function is only called when a and b are not NULL and valid
|
|
||||||
* pointers to the entry type.
|
|
||||||
*
|
|
||||||
* The function will be named: __skip_cmp_<entry> which should be
|
|
||||||
* passed as the 2nd argument in the SKIP_HEAD_INITIALIZER.
|
|
||||||
*/
|
|
||||||
SKIP_COMPARATOR(skip, entry, {
|
|
||||||
(void)aux;
|
(void)aux;
|
||||||
if (a->key < b->key)
|
if (a->key < b->key)
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -42,36 +29,38 @@ SKIP_HEAD(skip, entry);
|
||||||
return 0;
|
return 0;
|
||||||
})
|
})
|
||||||
|
|
||||||
typedef struct skip skip_t;
|
|
||||||
int main() {
|
int main() {
|
||||||
/* Allocate and initialize a Skiplist. */
|
/* Allocate and initialize a Skiplist. */
|
||||||
|
slex_t _list = SKIP_HEAD_DEFAULT_INITIALIZER(__skip_key_compare_slex);
|
||||||
|
_list.slh_tail = (struct slex_node *)&_list.slh_head; // TODO...
|
||||||
|
/* Dynamic allocation, init. */
|
||||||
|
slex_t *list = (slex_t *)malloc(sizeof(slex_t));
|
||||||
|
SKIP_DEFAULT_INIT(list, __skip_key_compare_slex, slex_node, entries);
|
||||||
#ifdef STATIC_INIT
|
#ifdef STATIC_INIT
|
||||||
skip_t _list = SKIP_HEAD_DEFAULT_INITIALIZER(__skip_cmp_entry);
|
free(list);
|
||||||
_list.slh_tail = (struct entry *)&_list.slh_head; // TODO...
|
slex_t *list = &_list;
|
||||||
skip_t *list = &_list;
|
#else
|
||||||
#else /* Dynamic allocation, init. */
|
|
||||||
skip_t *list = (skip_t *)malloc(sizeof(skip_t));
|
|
||||||
SKIP_DEFAULT_INIT(list, __skip_cmp_entry, entry, entries);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Insert 10 key/value pairs into the list. */
|
struct slex_node *n;
|
||||||
struct entry *n;
|
SKIP_ALLOC_NODE(list, n, slex_node, entries);
|
||||||
for (int i = 0; i < 10; i++) {
|
|
||||||
SKIP_ALLOC_NODE(list, n, entry, entries);
|
|
||||||
n->key = i;
|
|
||||||
n->value = i;
|
|
||||||
SKIP_INSERT(list, entry, n, entries);
|
|
||||||
}
|
|
||||||
SKIP_ALLOC_NODE(list, n, entry, entries);
|
|
||||||
n->key = -1;
|
n->key = -1;
|
||||||
n->value = -1;
|
n->value = -1;
|
||||||
SKIP_INSERT(list, entry, n, entries);
|
api_skip_insert_slex(list, n);
|
||||||
|
|
||||||
|
/* Insert 10 key/value pairs into the list. */
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
SKIP_ALLOC_NODE(list, n, slex_node, entries);
|
||||||
|
n->key = i;
|
||||||
|
n->value = i;
|
||||||
|
api_skip_insert_slex(list, n);
|
||||||
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
/* Delete a specific element in the list. */
|
/* Delete a specific element in the list. */
|
||||||
struct entry query;
|
struct slex_node query;
|
||||||
query.key = 4;
|
query.key = 4;
|
||||||
struct entry *removed = SKIP_REMOVE(list, q, entries);
|
struct slex_node *removed = SKIP_REMOVE(list, q, entries);
|
||||||
free(removed);
|
free(removed);
|
||||||
/* Forward traversal. */
|
/* Forward traversal. */
|
||||||
SKIP_FOREACH(np, &head, entries)
|
SKIP_FOREACH(np, &head, entries)
|
||||||
|
|
140
include/sl.h
140
include/sl.h
|
@ -13,6 +13,21 @@
|
||||||
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
* PERFORMANCE OF THIS SOFTWARE.
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
* I'd like to thank others for thoughtfully licensing their work, the
|
||||||
|
* community of software engineers succeeds when we work together.
|
||||||
|
*
|
||||||
|
* Portions of this code are derived from copyrighted work:
|
||||||
|
*
|
||||||
|
* - MIT LICENSE
|
||||||
|
* - https://github.com/greensky00/skiplist
|
||||||
|
* 2017-2024 Jung-Sang Ahn <jungsang.ahn@gmail.com>
|
||||||
|
* - https://github.com/paulross/skiplist
|
||||||
|
* Copyright (c) 2017-2023 Paul Ross
|
||||||
|
* - gist skiplist.c
|
||||||
|
* - queue.h
|
||||||
|
* - khash.h
|
||||||
|
* - async_nif.h
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _SYS_SKIPLIST_H_
|
#ifndef _SYS_SKIPLIST_H_
|
||||||
|
@ -100,9 +115,9 @@ struct sl_trace {
|
||||||
*/
|
*/
|
||||||
#define ARRAY_ALLOC(var, type, size) do { \
|
#define ARRAY_ALLOC(var, type, size) do { \
|
||||||
(size) = (size == 0 ? 254 : size); \
|
(size) = (size == 0 ? 254 : size); \
|
||||||
(var) = (struct type**)calloc(sizeof(struct type*), size + 2); \
|
(var) = (type**)calloc(sizeof(type*), size + 2); \
|
||||||
if ((var) != NULL) { \
|
if ((var) != NULL) { \
|
||||||
*(var)++ = (struct type *)size; \
|
*(var)++ = (type *)size; \
|
||||||
*(var)++ = 0; \
|
*(var)++ = 0; \
|
||||||
} \
|
} \
|
||||||
} while(0)
|
} while(0)
|
||||||
|
@ -234,7 +249,7 @@ struct sl_trace {
|
||||||
|
|
||||||
#define SKIP_ALLOC_NODE(head, var, type, field) do { \
|
#define SKIP_ALLOC_NODE(head, var, type, field) do { \
|
||||||
(var) = (struct type *)calloc(1, sizeof(struct type)); \
|
(var) = (struct type *)calloc(1, sizeof(struct type)); \
|
||||||
ARRAY_ALLOC((var)->field.sle_next, type, (head)->max); \
|
ARRAY_ALLOC((var)->field.sle_next, struct type, (head)->max); \
|
||||||
if ((var) && (var)->field.sle_next) { \
|
if ((var) && (var)->field.sle_next) { \
|
||||||
ARRAY_SET_SIZE((var)->field.sle_next, (head)->max); \
|
ARRAY_SET_SIZE((var)->field.sle_next, (head)->max); \
|
||||||
ARRAY_SET_LENGTH((var)->field.sle_next, 0); \
|
ARRAY_SET_LENGTH((var)->field.sle_next, 0); \
|
||||||
|
@ -246,58 +261,93 @@ struct sl_trace {
|
||||||
free((node)); \
|
free((node)); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define __SKIP_TOSS(var, max, fanout) do { \
|
#define SKIP_DECL(decl, prefix, field, key_cmp_logic) \
|
||||||
(var) = 0; \
|
/* Skiplist type */ \
|
||||||
while ((var) + 1 < (max)) { \
|
typedef struct decl decl##_t; \
|
||||||
if (rand() % (fanout) == 0) /* NOLINT(*-msc50-cpp) */ \
|
\
|
||||||
(var)++; \
|
/* Skiplist node type */ \
|
||||||
|
typedef struct decl##_node decl##_node_t; \
|
||||||
|
\
|
||||||
|
/* -- __skip_key_compare_decl \
|
||||||
|
* \
|
||||||
|
* This function takes four arguments: \
|
||||||
|
* - a reference to the Skiplist \
|
||||||
|
* - the two nodes to compare, `a` and `b` \
|
||||||
|
* - `aux` an additional auxiliary argument \
|
||||||
|
* and returns: \
|
||||||
|
* a < b : return -1 \
|
||||||
|
* a == b : return 0 \
|
||||||
|
* a > b : return 1 \
|
||||||
|
*/ \
|
||||||
|
static int __skip_key_compare_##decl(decl##_t *slist, decl##_node_t *a, decl##_node_t *b, void *aux) { \
|
||||||
|
if (a == b) \
|
||||||
|
return 0; \
|
||||||
|
if (a == slist->slh_head || b == slist->slh_tail) \
|
||||||
|
return -1; \
|
||||||
|
if (a == slist->slh_tail || b == slist->slh_head) \
|
||||||
|
return 1; \
|
||||||
|
do { key_cmp_logic } while(0); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
/* -- __skip_toss_decl */ \
|
||||||
|
static int __skip_toss_##decl(size_t max, size_t fanout) { \
|
||||||
|
size_t level = 0; \
|
||||||
|
while (level + 1 < max) { \
|
||||||
|
if (rand() % fanout == 0) /* NOLINT(*-msc50-cpp) */ \
|
||||||
|
level++; \
|
||||||
else \
|
else \
|
||||||
break; \
|
break; \
|
||||||
} \
|
} \
|
||||||
} while(0)
|
return level; \
|
||||||
|
} \
|
||||||
#define SKIP_INSERT(head, type, listelm, field) do { \
|
\
|
||||||
if ((listelm) == NULL) break; \
|
/* -- prefixskip_insert_decl */ \
|
||||||
struct type *__prev, *__elm = (head)->slh_head; \
|
int prefix##skip_insert_##decl(decl##_t *slist, decl##_node_t *n) { \
|
||||||
unsigned int __i; \
|
if (n == NULL) \
|
||||||
struct type **__path; \
|
return ENOMEM; \
|
||||||
__i = (head)->level; \
|
decl##_node_t *prev, *elm = slist->slh_head; \
|
||||||
ARRAY_ALLOC(__path, type, (head)->max); \
|
unsigned int i; \
|
||||||
if (__path == NULL) break; /* ENOMEM */ \
|
decl##_node_t **path; \
|
||||||
|
i = slist->level; \
|
||||||
|
ARRAY_ALLOC(path, decl##_node_t, slist->max); \
|
||||||
|
if (path == NULL) \
|
||||||
|
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 && (head)->cmp((head), __elm->field.sle_next[__i], (listelm), (head)->aux) < 0) \
|
while(elm && slist->cmp(slist, elm->field.sle_next[i], n, 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 ((head)->cmp((head), __elm, listelm, (head)->aux) == 0) { \
|
if (slist->cmp(slist, elm, n, slist->aux) == 0) { \
|
||||||
ARRAY_FREE(__path); \
|
ARRAY_FREE(path); \
|
||||||
break; /* Don't overwrite, to do that use _REPLACE not _INSERT */ \
|
/* Don't overwrite, to do that use _REPLACE not _INSERT */ \
|
||||||
|
return -1; \
|
||||||
} \
|
} \
|
||||||
unsigned int __level; \
|
size_t level = __skip_toss_##decl(slist->max, slist->fanout); \
|
||||||
__SKIP_TOSS(__level, (head)->max, (head)->fanout); \
|
ARRAY_SET_LENGTH(n->field.sle_next, level); \
|
||||||
ARRAY_SET_LENGTH((listelm)->field.sle_next, __level); \
|
if (level > slist->level) { \
|
||||||
if (__level > (head)->level) { \
|
for (i = slist->level + 1; i <= level; i++) { \
|
||||||
for (__i = (head)->level + 1; __i <= __level; __i++) { \
|
path[i] = slist->slh_tail; \
|
||||||
__path[__i] = (head)->slh_tail; \
|
|
||||||
} \
|
} \
|
||||||
(head)->level = __level; \
|
slist->level = level; \
|
||||||
} \
|
} \
|
||||||
for (__i = 0; __i <= __level; __i++) { \
|
for (i = 0; i <= level; i++) { \
|
||||||
(listelm)->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] = (listelm); \
|
path[i]->field.sle_next[i] = n; \
|
||||||
} \
|
} \
|
||||||
(listelm)->field.sle_prev = __prev; \
|
n->field.sle_prev = prev; \
|
||||||
if ((listelm)->field.sle_next[0] == (head)->slh_tail) { \
|
if (n->field.sle_next[0] == slist->slh_tail) { \
|
||||||
(head)->slh_tail->field.sle_prev = (listelm); \
|
slist->slh_tail->field.sle_prev = n; \
|
||||||
} \
|
} \
|
||||||
(head)->length++; \
|
slist->length++; \
|
||||||
ARRAY_FREE(__path); \
|
ARRAY_FREE(path); \
|
||||||
} while (0)
|
return 0; \
|
||||||
|
} \
|
||||||
|
/* END */
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
#define SKIP_REMOVE(head, elm, field) do { \
|
#define SKIP_REMOVE(head, elm, field) do { \
|
||||||
|
|
Loading…
Reference in a new issue