adding queue-style macro skiplist
This commit is contained in:
parent
3b1a03c86f
commit
bf685e5476
6 changed files with 477 additions and 43 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -4,6 +4,8 @@ build_unix/**
|
|||
libskiplist.so
|
||||
libskiplist.a
|
||||
tests/test
|
||||
examples/example
|
||||
examples/skip
|
||||
examples/mls
|
||||
examples/slm
|
||||
.direnv/
|
||||
.idea/
|
||||
|
|
44
Makefile
44
Makefile
|
@ -1,19 +1,17 @@
|
|||
|
||||
CFLAGS = -Wall -Wextra -Wpedantic -Og -g -std=c99 -Iinclude/ -fPIC
|
||||
TEST_FLAGS = -Itests/
|
||||
#-fsanitize=address,undefined
|
||||
|
||||
OBJS = skiplist.o
|
||||
STATIC_LIB = libskiplist.a
|
||||
SHARED_LIB = libskiplist.so
|
||||
|
||||
CFLAGS = -Wall -Wextra -Wpedantic -Og -g -std=c99 -Iinclude/ -fPIC
|
||||
TEST_FLAGS = -Itests/
|
||||
#-fsanitize=address,undefined
|
||||
|
||||
TESTS = tests/test
|
||||
EXAMPLES = examples/example
|
||||
TEST_OBJS = tests/test.o tests/munit.o
|
||||
EXAMPLES = examples/skip examples/slm
|
||||
|
||||
.PHONY: all shared static clean tests examples
|
||||
|
||||
%.o: src/%.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $^
|
||||
.PHONY: all shared static clean test examples mls
|
||||
|
||||
all: static shared
|
||||
|
||||
|
@ -27,26 +25,38 @@ $(STATIC_LIB): $(OBJS)
|
|||
$(SHARED_LIB): $(OBJS)
|
||||
$(CC) $(CFLAGS) -o $@ $? -shared
|
||||
|
||||
tests/%.o: tests/%.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $^
|
||||
examples: $(EXAMPLES)
|
||||
|
||||
mls: examples/mls
|
||||
|
||||
test: $(TESTS)
|
||||
./tests/test
|
||||
# env LSAN_OPTIONS=verbosity=1:log_threads=1 ./tests/test
|
||||
|
||||
tests/test: tests/test.o tests/munit.o $(STATIC_LIB)
|
||||
$(CC) $^ -o $@ $(CFLAGS) $(TEST_FLAGS) -pthread
|
||||
|
||||
examples: $(EXAMPLES)
|
||||
|
||||
examples/example: examples/example.c $(STATIC_LIB)
|
||||
tests/test: $(TEST_OBJS) $(STATIC_LIB)
|
||||
$(CC) $^ -o $@ $(CFLAGS) $(TEST_FLAGS) -pthread
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) munit.o test.o
|
||||
rm -f examples/mls.c
|
||||
rm -f $(STATIC_LIB)
|
||||
rm -f $(TESTS)
|
||||
rm -f $(EXAMPLES)
|
||||
|
||||
format:
|
||||
clang-format -i include/*.h src/*.c tests/*.c tests/*.h
|
||||
|
||||
%.o: src/%.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $^
|
||||
|
||||
tests/%.o: tests/%.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $^
|
||||
|
||||
examples/%.o: examples/%.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $^
|
||||
|
||||
examples/mls.c: examples/slm.c
|
||||
$(CC) $(CFLAGS) -C -E examples/slm.c | sed -e '1,7d' -e '/^# [0-9]* "/d' | clang-format > examples/mls.c
|
||||
|
||||
examples/mls: examples/mls.o $(STATIC_LIB)
|
||||
$(CC) $^ -o $@ $(CFLAGS) $(TEST_FLAGS) -pthread
|
||||
|
|
93
examples/slm.c
Normal file
93
examples/slm.c
Normal file
|
@ -0,0 +1,93 @@
|
|||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include "../include/sl.h"
|
||||
|
||||
/*
|
||||
* SKIPLIST EXAMPLE:
|
||||
*
|
||||
* This example creates a Skiplist keys and values are integers.
|
||||
*/
|
||||
|
||||
/* Create a type of Skiplist that maps int -> int. */
|
||||
struct entry {
|
||||
int key;
|
||||
int value;
|
||||
SKIP_ENTRY(entry) entries;
|
||||
};
|
||||
SKIP_HEAD(skip, entry);
|
||||
|
||||
/* Generate a function to compare keys to order our list.
|
||||
*
|
||||
* 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;
|
||||
if (a->key < b->key)
|
||||
return -1;
|
||||
if (a->key > b->key)
|
||||
return 1;
|
||||
return 0;
|
||||
})
|
||||
|
||||
typedef struct skip skip_t;
|
||||
int main() {
|
||||
/* Allocate and initialize a Skiplist. */
|
||||
#ifdef STATIC_INIT
|
||||
skip_t _list = SKIP_HEAD_DEFAULT_INITIALIZER(__skip_cmp_entry);
|
||||
skip_t *list = &_list;
|
||||
#else /* Dynamic allocation, init. */
|
||||
skip_t *list = (skip_t *)malloc(sizeof(skip_t));
|
||||
SKIP_DEFAULT_INIT(list, __skip_cmp_entry);
|
||||
#endif
|
||||
|
||||
/* Insert 10 key/value pairs into the list. */
|
||||
for (int i = 0; i < 10; i++) {
|
||||
struct entry *n;
|
||||
SKIP_ALLOC_NODE(list, n, entry, entries);
|
||||
n->key = i;
|
||||
n->value = i;
|
||||
SKIP_INSERT(list, entry, n, entries);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Delete a specific element in the list. */
|
||||
struct entry query;
|
||||
query.key = 4;
|
||||
struct entry *removed = SKIP_REMOVE(list, q, entries);
|
||||
free(removed);
|
||||
/* Forward traversal. */
|
||||
SKIP_FOREACH(np, &head, entries)
|
||||
np-> ...
|
||||
/* Reverse traversal. */
|
||||
SKIP_FOREACH_REVERSE(np, &head, skiphead, entries)
|
||||
np-> ...
|
||||
/* Skiplist Deletion. */
|
||||
while (!SKIP_EMPTY(&head)) {
|
||||
n1 = SKIP_FIRST(&head);
|
||||
SKIP_REMOVE(&head, n1, entries);
|
||||
free(n1);
|
||||
}
|
||||
/* Faster Skiplist Deletion. */
|
||||
n1 = SKIP_FIRST(&head);
|
||||
while (n1 != NULL) {
|
||||
n2 = SKIP_NEXT(n1, entries);
|
||||
free(n1);
|
||||
n1 = n2;
|
||||
}
|
||||
SKIP_INIT(&head);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
303
include/sl.h
Normal file
303
include/sl.h
Normal file
|
@ -0,0 +1,303 @@
|
|||
/*
|
||||
* Copyright (c) 2024
|
||||
* Gregory Burd <greg@burd.me>. All rights reserved.
|
||||
*
|
||||
* ISC License Permission to use, copy, modify, and/or distribute this software
|
||||
* for any purpose with or without fee is hereby granted, provided that the
|
||||
* above copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
* 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
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _SYS_SKIPLIST_H_
|
||||
#define _SYS_SKIPLIST_H_
|
||||
|
||||
/*
|
||||
* This file defines a skiplist data structure with a similar API to those
|
||||
* datastructures defined in FreeBSD's <sys/queue.h> header.
|
||||
*
|
||||
* In 1990 William Pugh published:
|
||||
* - Skiplists: a probabilistic alternative to balanced trees.
|
||||
* https://www.cl.cam.ac.uk/teaching/2005/Algorithms/skiplists.pdf
|
||||
* A Skiplist is an ordered data structure providing expected O(Log(n)) lookup,
|
||||
* insertion, and deletion complexity.
|
||||
*
|
||||
*
|
||||
* For details on the use of these macros, see the queue(3) manual page.
|
||||
*
|
||||
*
|
||||
* SKIP SPLAYED_SKIP
|
||||
* _HEAD + +
|
||||
* _HEAD_INITIALIZER + +
|
||||
* _ENTRY + +
|
||||
* _INIT + +
|
||||
* _DISPOSE + +
|
||||
* _EMPTY + +
|
||||
* _FIRST + +
|
||||
* _NEXT + +
|
||||
* _PREV + +
|
||||
* _LAST - +
|
||||
* _FOREACH + +
|
||||
* _FOREACH_REVERSE + +
|
||||
* _INSERT + +
|
||||
* _UPDATE + +
|
||||
* _BULK_INSERT + +
|
||||
* _REMOVE + +
|
||||
* _LOCATE + +
|
||||
*
|
||||
*/
|
||||
|
||||
#define SKIPLIST_MACRO_DEBUG 0
|
||||
#if SKIPLIST_MACRO_DEBUG
|
||||
/* Store the last 2 places the element or head was altered */
|
||||
struct sl_trace {
|
||||
char * lastfile;
|
||||
int lastline;
|
||||
char * prevfile;
|
||||
int prevline;
|
||||
};
|
||||
|
||||
#define TRACEBUF struct sl_trace trace;
|
||||
#define TRASHIT(x) do {(x) = (void *)-1;} while (0)
|
||||
|
||||
#define SLD_TRACE_HEAD(head) do { \
|
||||
(head)->trace.prevline = (head)->trace.lastline; \
|
||||
(head)->trace.prevfile = (head)->trace.lastfile; \
|
||||
(head)->trace.lastline = __LINE__; \
|
||||
(head)->trace.lastfile = __FILE__; \
|
||||
} while (0)
|
||||
|
||||
#define SLD_TRACE_ELEM(elem) do { \
|
||||
(elem)->trace.prevline = (elem)->trace.lastline; \
|
||||
(elem)->trace.prevfile = (elem)->trace.lastfile; \
|
||||
(elem)->trace.lastline = __LINE__; \
|
||||
(elem)->trace.lastfile = __FILE__; \
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
#define SLD_TRACE_ELEM(elem)
|
||||
#define SLD_TRACE_HEAD(head)
|
||||
#define TRACEBUF
|
||||
#define TRASHIT(x)
|
||||
#endif /* QUEUE_MACRO_DEBUG */
|
||||
|
||||
|
||||
/*
|
||||
* Private, internal API.
|
||||
*/
|
||||
|
||||
/* NOTE:
|
||||
* This array is a bit unusual, while values are 0-based the array is allocated
|
||||
* with space for two more pointers which are used to store max size the array
|
||||
* can grow to (`array[-2]`) and the length, or number of elements used in the
|
||||
* array so far (`array[-1]`).
|
||||
*/
|
||||
#define ARRAY_ALLOC(var, type, size) do { \
|
||||
(size) = (size == 0 ? 254 : size); \
|
||||
(var) = (struct type**)calloc(sizeof(struct type*), size + 2); \
|
||||
if ((var) != NULL) { \
|
||||
*(var)++ = (struct type *)size; \
|
||||
*(var)++ = 0; \
|
||||
} \
|
||||
} while(0)
|
||||
#define ARRAY_FREE(var) free((var)-2)
|
||||
#define ARRAY_SIZE(list) (list)[-2]
|
||||
#define ARRAY_SET_SIZE(list, size) (list)[-2] = (void *)(uintptr_t)(size)
|
||||
#define ARRAY_LENGTH(list) (list)[-1]
|
||||
#define ARRAY_SET_LENGTH(list, len) (list)[-1] = (void *)(uintptr_t)(len)
|
||||
#define ARRAY_FOREACH(var, list) \
|
||||
for(unsigned int (var) = 0; (var) < ARRAY_LENGTH(list); (var)++)
|
||||
|
||||
|
||||
/*
|
||||
* Skiplist declarations.
|
||||
*/
|
||||
#define SKIP_HEAD(name, type) \
|
||||
struct name { \
|
||||
size_t level, length, max, fanout; \
|
||||
int (*cmp)(struct name *, struct type *, struct type *, void *); \
|
||||
void *aux; \
|
||||
struct type **slh_head; \
|
||||
struct type **slh_tail; \
|
||||
TRACEBUF \
|
||||
}
|
||||
|
||||
#define SKIP_HEAD_DEFAULT_INITIALIZER(cmp) \
|
||||
{ 0, 0, 12, 4, cmp, NULL, NULL, NULL }
|
||||
|
||||
#define SKIP_HEAD_INITIALIZER(cmp, max, fanout) \
|
||||
{ 0, 0, max, fanout, cmp, NULL, NULL, NULL }
|
||||
|
||||
#define SKIP_ENTRY(type) \
|
||||
struct { \
|
||||
struct type **sle_next; \
|
||||
struct type *sle_prev; \
|
||||
TRACEBUF \
|
||||
}
|
||||
|
||||
/*
|
||||
* Skip List access methods.
|
||||
*/
|
||||
#define SKIP_FIRST(head) ((head)->slh_head)
|
||||
#define SKIP_NEXT(elm, field) ((elm)->field.sle_next[0])
|
||||
#define SKIP_PREV(elm, field) ((elm)->field.sle_prev)
|
||||
#define SKIP_EMPTY(head) ((head)->slh_head == NULL)
|
||||
|
||||
#if 0
|
||||
#define SKIP_FOREACH(var, head, field) \
|
||||
for((var) = SKIP_FIRST(head); \
|
||||
(var)!= SKIP_END(head); \
|
||||
(var) = SKIP_NEXT(var, field))
|
||||
|
||||
#define SKIP_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = SKIP_FIRST(head); \
|
||||
(var) && ((tvar) = SKIP_NEXT(var, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
|
||||
for((var) = TAILQ_LAST(head, headname); \
|
||||
(var) != TAILQ_END(head); \
|
||||
(var) = TAILQ_PREV(var, headname, field))
|
||||
|
||||
#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
|
||||
for ((var) = TAILQ_LAST(head, headname); \
|
||||
(var) != TAILQ_END(head) && \
|
||||
((tvar) = TAILQ_PREV(var, headname, field), 1); \
|
||||
(var) = (tvar))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Skip List functions.
|
||||
*/
|
||||
|
||||
#define SKIP_COMPARATOR(list, type, fn) \
|
||||
int __skip_cmp_##type(struct list *head, struct type *a, struct type *b, void *aux) { \
|
||||
if (a == b) \
|
||||
return 0; \
|
||||
if (a == (head)->slh_head || b == (head)->slh_tail) \
|
||||
return -1; \
|
||||
if (a == (head)->slh_tail || b == (head)->slh_head) \
|
||||
return 1; \
|
||||
fn }
|
||||
|
||||
#define SKIP_INIT(head, max, fanout, fn) do { \
|
||||
(head)->level = 0; \
|
||||
(head)->length = 0; \
|
||||
(head)->max = max; \
|
||||
(head)->fanout = fanout; \
|
||||
(head)->cmp = fn; \
|
||||
(head)->slh_head = NULL; \
|
||||
(head)->slh_tail = NULL; \
|
||||
SLD_TRACE_HEAD(head); \
|
||||
} while (0)
|
||||
|
||||
#define SKIP_DEFAULT_INIT(head, fn) do { \
|
||||
(head)->level = 0; \
|
||||
(head)->length = 0; \
|
||||
(head)->max = 12; \
|
||||
(head)->fanout = 4; \
|
||||
(head)->cmp = fn; \
|
||||
(head)->slh_head = NULL; \
|
||||
(head)->slh_tail = NULL; \
|
||||
SLD_TRACE_HEAD(head); \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define SKIP_ALLOC_NODE(head, var, type, field) do { \
|
||||
size_t amt = sizeof(struct type *) * (head)->max; \
|
||||
(var) = (struct type *)calloc(1, sizeof(struct type) + amt + 3); \
|
||||
if ((var) != NULL) { \
|
||||
(var)->field.sle_prev = (struct type *)(var) + sizeof(struct type); \
|
||||
(var)->field.sle_next = (struct type **)(var) + sizeof(struct type) + (sizeof(struct type *) * 3); \
|
||||
ARRAY_SET_SIZE((var)->field.sle_next, (head)->max); \
|
||||
ARRAY_SET_LENGTH((var)->field.sle_next, 0); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define __SKIP_TOSS(var, max, fanout) do { \
|
||||
(var) = 0; \
|
||||
while ((var) + 1 < (max)) { \
|
||||
if (rand() % (fanout) == 0) /* NOLINT(*-msc50-cpp) */ \
|
||||
(var)++; \
|
||||
else \
|
||||
break; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define SKIP_INSERT(head, type, listelm, field) do { \
|
||||
struct type *__elm = SKIP_FIRST(head); \
|
||||
unsigned int __i = (head)->level; \
|
||||
struct type **__path; \
|
||||
if (__elm == NULL) { \
|
||||
/* Empty list, setup header and add first element. */ \
|
||||
ARRAY_ALLOC((head)->slh_head, type, (head)->max); \
|
||||
ARRAY_ALLOC((head)->slh_tail, type, (head)->max); \
|
||||
ARRAY_FOREACH(__j, (head)->slh_head) { \
|
||||
(head)->slh_head[__j] = (head)->slh_tail; \
|
||||
(head)->slh_tail[__j] = (head)->slh_head; \
|
||||
} \
|
||||
(head)->slh_head[0] = (listelm); \
|
||||
(head)->slh_tail[0] = (listelm); \
|
||||
(listelm)->field.sle_next[0] = (head)->slh_tail; \
|
||||
(listelm)->field.sle_prev = (head)->slh_head; \
|
||||
(head)->length = 1; \
|
||||
break; \
|
||||
} \
|
||||
ARRAY_ALLOC(__path, type, (head)->max); \
|
||||
if (__path == NULL) break; /* ENOMEM */ \
|
||||
/* Find the position in the list where this element belongs. */ \
|
||||
do { \
|
||||
while(__elm && (head)->cmp((head), __elm, __elm->field.sle_next[__i], (head)->aux) < 0) \
|
||||
__elm = __elm->field.sle_next[__i]; \
|
||||
__path[__i] = __elm; \
|
||||
} while(__i--); \
|
||||
__elm = __elm->field.sle_next[0]; \
|
||||
if ((head)->cmp((head), __elm, listelm, (head)->aux) == 0) { \
|
||||
ARRAY_FREE(__path); \
|
||||
break; /* Don't overwrite, to do that use _REPLACE not _INSERT */ \
|
||||
} \
|
||||
unsigned int __level; \
|
||||
__SKIP_TOSS(__level, (head)->max, (head)->fanout); \
|
||||
ARRAY_SET_LENGTH((listelm)->field.sle_next, __level); \
|
||||
if (__level > (head)->level) { \
|
||||
for (__i = (head)->level; __i < __level; __i++) { \
|
||||
__path[__i] = (list)->slh_head; \
|
||||
} \
|
||||
(head)->level = __level; \
|
||||
} \
|
||||
for (__i = 0; __i < __level; __i++) { \
|
||||
(listelm)->field.sle_next[__i] = __path[__i]->field.sle_next[__i];\
|
||||
__path[__i]->field.sle_next[__i] = (listelm); \
|
||||
} \
|
||||
(listelm)->field.sle_prev = __elm->field.sle_prev; \
|
||||
ARRAY_FREE(__path); \
|
||||
} while (0)
|
||||
|
||||
#if 0
|
||||
#define SKIP_REMOVE(head, elm, field) do { \
|
||||
if ((elm)->field.le_next != NULL) \
|
||||
(elm)->field.le_next->field.le_prev = \
|
||||
(elm)->field.le_prev; \
|
||||
*(elm)->field.le_prev = (elm)->field.le_next; \
|
||||
_Q_INVALIDATE((elm)->field.le_prev); \
|
||||
_Q_INVALIDATE((elm)->field.le_next); \
|
||||
} while (0)
|
||||
|
||||
#define SKIP_REPLACE(elm, elm2, field) do { \
|
||||
if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
|
||||
(elm2)->field.le_next->field.le_prev = \
|
||||
&(elm2)->field.le_next; \
|
||||
(elm2)->field.le_prev = (elm)->field.le_prev; \
|
||||
*(elm2)->field.le_prev = (elm2); \
|
||||
_Q_INVALIDATE((elm)->field.le_prev); \
|
||||
_Q_INVALIDATE((elm)->field.le_next); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#endif /* _SYS_SKIPLIST_H_ */
|
76
tests/test.c
76
tests/test.c
|
@ -121,6 +121,29 @@ uint32_key_cmp(sl_node *a, sl_node *b, void *aux)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static size_t
|
||||
__populate_slist(ex_sl_t *slist){
|
||||
size_t inserted = 0;
|
||||
uint32_t n, key;
|
||||
ex_node_t *node;
|
||||
|
||||
n = munit_rand_int_range(1024, 4196);
|
||||
while (n--) {
|
||||
key = munit_rand_int_range(0, (((uint32_t)0) - 1) / 10);
|
||||
node = (ex_node_t *)calloc(sizeof(ex_node_t), 1);
|
||||
if (node == NULL)
|
||||
return MUNIT_ERROR;
|
||||
sl_init_node(&node->snode);
|
||||
node->key = key;
|
||||
node->value = key;
|
||||
if (sl_insert_nodup(slist, &node->snode) == -1)
|
||||
continue; /* a random duplicate appeared */
|
||||
else
|
||||
inserted++;
|
||||
}
|
||||
return inserted;
|
||||
}
|
||||
|
||||
static void *
|
||||
test_api_setup(const MunitParameter params[], void *user_data)
|
||||
{
|
||||
|
@ -157,29 +180,6 @@ test_api_tear_down(void *fixture)
|
|||
free(fixture);
|
||||
}
|
||||
|
||||
static size_t
|
||||
__populate_slist(ex_sl_t *slist){
|
||||
size_t inserted = 0;
|
||||
uint32_t n, key;
|
||||
ex_node_t *node;
|
||||
|
||||
n = munit_rand_int_range(1024, 4196);
|
||||
while (n--) {
|
||||
key = munit_rand_int_range(0, (((uint32_t)0) - 1) / 10);
|
||||
node = (ex_node_t *)calloc(sizeof(ex_node_t), 1);
|
||||
if (node == NULL)
|
||||
return MUNIT_ERROR;
|
||||
sl_init_node(&node->snode);
|
||||
node->key = key;
|
||||
node->value = key;
|
||||
if (sl_insert_nodup(slist, &node->snode) == -1)
|
||||
continue; /* a random duplicate appeared */
|
||||
else
|
||||
inserted++;
|
||||
}
|
||||
return inserted;
|
||||
}
|
||||
|
||||
static void *
|
||||
test_api_insert_setup(const MunitParameter params[], void *user_data)
|
||||
{
|
||||
|
@ -269,7 +269,19 @@ test_api_remove(const MunitParameter params[], void *data)
|
|||
static void *
|
||||
test_api_find_setup(const MunitParameter params[], void *user_data)
|
||||
{
|
||||
return test_api_setup(params, user_data);
|
||||
sl_raw *slist = (sl_raw *)test_api_setup(params, user_data);
|
||||
ex_node_t *node;
|
||||
for (int i = 1; i <= 100; ++i) {
|
||||
node = calloc(sizeof(ex_node_t), 1);
|
||||
if (node == NULL)
|
||||
return NULL;
|
||||
node = (ex_node_t *)calloc(sizeof(ex_node_t), 1);
|
||||
sl_init_node(&node->snode);
|
||||
node->key = i;
|
||||
node->value = i;
|
||||
sl_insert(slist, &node->snode);
|
||||
}
|
||||
return (void *)slist;
|
||||
}
|
||||
static void
|
||||
test_api_find_tear_down(void *fixture)
|
||||
|
@ -281,7 +293,19 @@ test_api_find(const MunitParameter params[], void *data)
|
|||
{
|
||||
sl_raw *slist = (sl_raw *)data;
|
||||
(void)params;
|
||||
(void)slist;
|
||||
|
||||
/* find equal every value */
|
||||
assert_ptr_not_null(data);
|
||||
for (int i = 1; i <= 100; i++) {
|
||||
ex_node_t query;
|
||||
query.key = i;
|
||||
sl_node *cursor = sl_find(slist, &query.snode);
|
||||
assert_ptr_not_null(cursor);
|
||||
ex_node_t *entry = sl_get_entry(cursor, ex_node_t, snode);
|
||||
assert_uint32(entry->key, ==, i);
|
||||
}
|
||||
|
||||
/* */
|
||||
return MUNIT_OK;
|
||||
}
|
||||
|
||||
|
@ -420,3 +444,5 @@ main(int argc, char *argv[MUNIT_ARRAY_PARAM(argc + 1)])
|
|||
struct user_data info;
|
||||
return munit_suite_main(&main_test_suite, (void *)&info, argc, argv);
|
||||
}
|
||||
|
||||
/* ARGS: --no-fork --seed 8675309 */
|
||||
|
|
Loading…
Reference in a new issue