From e8d3645ed46e852c7ec4305fea0739ab792f950e Mon Sep 17 00:00:00 2001 From: Greg Burd Date: Mon, 18 Mar 2024 21:19:26 -0400 Subject: [PATCH] moving more to decl --- .clang-format | 6 +- .clang-tidy | 2 +- Makefile | 6 +- examples/skip.c | 238 +++-- examples/slm.c | 94 +- include/skiplist.h | 40 +- include/sl.h | 907 ++++++++-------- src/skiplist.c | 1336 ++++++++++++----------- tests/api.c | 164 +-- tests/munit.c | 2508 ++++++++++++++++++++++---------------------- tests/munit.h | 349 +++--- tests/test.c | 439 ++++---- 12 files changed, 3071 insertions(+), 3018 deletions(-) diff --git a/.clang-format b/.clang-format index 928b1a5..3013d6f 100644 --- a/.clang-format +++ b/.clang-format @@ -99,9 +99,9 @@ IndentPPDirectives: None Language: Cpp NamespaceIndentation: None PointerAlignment: Right -ContinuationIndentWidth: 4 -IndentWidth: 4 -TabWidth: 4 +ContinuationIndentWidth: 2 +IndentWidth: 2 +TabWidth: 2 ColumnLimit: 80 UseTab: Never SpaceAfterCStyleCast: false diff --git a/.clang-tidy b/.clang-tidy index 5c5a906..764f500 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,5 +1,6 @@ # Generated from CLion Inspection settings #bugprone-reserved-identifier, +#misc-no-recursion, --- Checks: '-*, -deprecated-non-prototype @@ -74,7 +75,6 @@ hicpp-exception-baseclass, hicpp-multiway-paths-covered, misc-misplaced-const, misc-new-delete-overloads, -misc-no-recursion, misc-non-copyable-objects, misc-throw-by-value-catch-by-reference, misc-unconventional-assign-operator, diff --git a/Makefile b/Makefile index 17f6899..f642d2c 100644 --- a/Makefile +++ b/Makefile @@ -44,7 +44,7 @@ clean: rm -f $(EXAMPLES) format: - clang-format -i include/*.h src/*.c tests/*.c tests/*.h + clang-format -i include/*.h src/*.c tests/*.c tests/*.h examples/*.c %.o: src/%.c $(CC) $(CFLAGS) -c -o $@ $^ @@ -60,3 +60,7 @@ examples/mls.c: examples/slm.c examples/mls: examples/mls.o $(STATIC_LIB) $(CC) $^ -o $@ $(CFLAGS) $(TEST_FLAGS) -pthread + +#dot: +# ./examples/mls +# dot -Tpdf /tmp/slm.dot -o /tmp/slm.pdf >/dev/null 2>&1 diff --git a/examples/skip.c b/examples/skip.c index acb18c2..0c729bc 100644 --- a/examples/skip.c +++ b/examples/skip.c @@ -8,30 +8,30 @@ // Define a node that contains key and value pair. struct my_node { - // Metadata for skiplist node. - sl_node snode; - // My data here: {int, int} pair. - int key; - int value; + // Metadata for skiplist node. + sl_node snode; + // My data here: {int, int} pair. + int key; + int value; }; // Define a comparison function for `my_node`. static int my_cmp(sl_node *a, sl_node *b, void *aux) { - // Get `my_node` from skiplist node `a` and `b`. - struct my_node *aa, *bb; - aa = sl_get_entry(a, struct my_node, snode); - bb = sl_get_entry(b, struct my_node, snode); + // Get `my_node` from skiplist node `a` and `b`. + struct my_node *aa, *bb; + aa = sl_get_entry(a, struct my_node, snode); + bb = sl_get_entry(b, struct my_node, snode); - // aa < bb: return neg - // aa == bb: return 0 - // aa > bb: return pos - if (aa->key < bb->key) - return -1; - if (aa->key > bb->key) - return 1; - return 0; + // aa < bb: return neg + // aa == bb: return 0 + // aa > bb: return pos + if (aa->key < bb->key) + return -1; + if (aa->key > bb->key) + return 1; + return 0; } #define NUM_NODES 10000 @@ -39,118 +39,116 @@ my_cmp(sl_node *a, sl_node *b, void *aux) int main() { - // seed the PRNG - srandom((unsigned)(time(NULL) | getpid())); + // seed the PRNG + srandom((unsigned)(time(NULL) | getpid())); - sl_raw slist; + sl_raw slist; - // Initialize skiplist. - sl_init(&slist, my_cmp); + // Initialize skiplist. + sl_init(&slist, my_cmp); - // << Insertion >> - // Allocate & insert NUM_NODES KV pairs: {0, 0}, {1, 10}, {2, 20}. - struct my_node *nodes[NUM_NODES]; - for (int i = 0; i < NUM_NODES; ++i) { - // Allocate memory. - nodes[i] = (struct my_node *)malloc(sizeof(struct my_node)); - // Initialize node. - sl_init_node(&nodes[i]->snode); - // Assign key and value. - nodes[i]->key = i; - nodes[i]->value = i * 10; - // Insert into skiplist. - sl_insert(&slist, &nodes[i]->snode); + // << Insertion >> + // Allocate & insert NUM_NODES KV pairs: {0, 0}, {1, 10}, {2, 20}. + struct my_node *nodes[NUM_NODES]; + for (int i = 0; i < NUM_NODES; ++i) { + // Allocate memory. + nodes[i] = (struct my_node *)malloc(sizeof(struct my_node)); + // Initialize node. + sl_init_node(&nodes[i]->snode); + // Assign key and value. + nodes[i]->key = i; + nodes[i]->value = i * 10; + // Insert into skiplist. + sl_insert(&slist, &nodes[i]->snode); + } + + // << Point lookup >> + for (int i = 0; i < NUM_NODES; ++i) { + // Define a query. + struct my_node query; + int min = 1, max = NUM_NODES - 1; + int k = min + (int)random() / (RAND_MAX / (max - min + 1) + 1); + query.key = k; + // Find a skiplist node `cursor`. + sl_node *cursor = sl_find(&slist, &query.snode); + // If `cursor` is NULL, key doesn't exist. + if (!cursor) + continue; + // Get `my_node` from `cursor`. + // Note: found->snode == *cursor + struct my_node *found = sl_get_entry(cursor, struct my_node, snode); + printf("[point lookup] key: %d, value: %d\n", found->key, found->value); + if (found->key != found->value / 10) { + printf("FAILURE: key: %d * 10 != value: %d\n", found->key, found->value); + exit(-1); } + // Release `cursor` (== &found->snode). + // Other thread cannot free `cursor` until `cursor` is released. + sl_release_node(cursor); + } - // << Point lookup >> - for (int i = 0; i < NUM_NODES; ++i) { - // Define a query. - struct my_node query; - int min = 1, max = NUM_NODES - 1; - int k = min + (int)random() / (RAND_MAX / (max - min + 1) + 1); - query.key = k; - // Find a skiplist node `cursor`. - sl_node *cursor = sl_find(&slist, &query.snode); - // If `cursor` is NULL, key doesn't exist. - if (!cursor) - continue; - // Get `my_node` from `cursor`. - // Note: found->snode == *cursor - struct my_node *found = sl_get_entry(cursor, struct my_node, snode); - printf("[point lookup] key: %d, value: %d\n", found->key, found->value); - if (found->key != found->value / 10) { - printf("FAILURE: key: %d * 10 != value: %d\n", found->key, - found->value); - exit(-1); - } - // Release `cursor` (== &found->snode). - // Other thread cannot free `cursor` until `cursor` is released. - sl_release_node(cursor); + // << Erase >> + // Erase the KV pair for key 1: {1, 10}. + { + // Define a query. + struct my_node query; + query.key = 1; + // Find a skiplist node `cursor`. + sl_node *cursor = sl_find(&slist, &query.snode); + // Get `my_node` from `cursor`. + // Note: found->snode == *cursor + struct my_node *found = sl_get_entry(cursor, struct my_node, snode); + printf("[erase] key: %d, value: %d\n", found->key, found->value); + + // Detach `found` from skiplist. + sl_erase_node(&slist, &found->snode); + // Release `found`, to free its memory. + sl_release_node(&found->snode); + // Free `found` after it becomes safe. + sl_wait_for_free(&found->snode); + sl_free_node(&found->snode); + free(found); + } + + // << Iteration >> + { + // Get the first cursor. + sl_node *cursor = sl_begin(&slist); + while (cursor) { + // Get `entry` from `cursor`. + // Note: entry->snode == *cursor + struct my_node *entry = sl_get_entry(cursor, struct my_node, snode); + printf("[iteration] key: %d, value: %d\n", entry->key, entry->value); + // Get next `cursor`. + cursor = sl_next(&slist, cursor); + // Release `entry`. + sl_release_node(&entry->snode); } + } - // << Erase >> - // Erase the KV pair for key 1: {1, 10}. - { - // Define a query. - struct my_node query; - query.key = 1; - // Find a skiplist node `cursor`. - sl_node *cursor = sl_find(&slist, &query.snode); - // Get `my_node` from `cursor`. - // Note: found->snode == *cursor - struct my_node *found = sl_get_entry(cursor, struct my_node, snode); - printf("[erase] key: %d, value: %d\n", found->key, found->value); + // << Destroy >> + { + // Iterate and free all nodes. + sl_node *cursor = sl_begin(&slist); + while (cursor) { + struct my_node *entry = sl_get_entry(cursor, struct my_node, snode); + printf("[destroy] key: %d, value: %d\n", entry->key, entry->value); + // Get next `cursor`. + cursor = sl_next(&slist, cursor); - // Detach `found` from skiplist. - sl_erase_node(&slist, &found->snode); - // Release `found`, to free its memory. - sl_release_node(&found->snode); - // Free `found` after it becomes safe. - sl_wait_for_free(&found->snode); - sl_free_node(&found->snode); - free(found); + // Detach `entry` from skiplist. + sl_erase_node(&slist, &entry->snode); + // Release `entry`, to free its memory. + sl_release_node(&entry->snode); + // Free `entry` after it becomes safe. + sl_wait_for_free(&entry->snode); + sl_free_node(&entry->snode); + free(entry); } + } - // << Iteration >> - { - // Get the first cursor. - sl_node *cursor = sl_begin(&slist); - while (cursor) { - // Get `entry` from `cursor`. - // Note: entry->snode == *cursor - struct my_node *entry = sl_get_entry(cursor, struct my_node, snode); - printf("[iteration] key: %d, value: %d\n", entry->key, - entry->value); - // Get next `cursor`. - cursor = sl_next(&slist, cursor); - // Release `entry`. - sl_release_node(&entry->snode); - } - } + // Free skiplist. + sl_free(&slist); - // << Destroy >> - { - // Iterate and free all nodes. - sl_node *cursor = sl_begin(&slist); - while (cursor) { - struct my_node *entry = sl_get_entry(cursor, struct my_node, snode); - printf("[destroy] key: %d, value: %d\n", entry->key, entry->value); - // Get next `cursor`. - cursor = sl_next(&slist, cursor); - - // Detach `entry` from skiplist. - sl_erase_node(&slist, &entry->snode); - // Release `entry`, to free its memory. - sl_release_node(&entry->snode); - // Free `entry` after it becomes safe. - sl_wait_for_free(&entry->snode); - sl_free_node(&entry->snode); - free(entry); - } - } - - // Free skiplist. - sl_free(&slist); - - return 0; + return 0; } diff --git a/examples/slm.c b/examples/slm.c index 9afdcfa..529e8b9 100644 --- a/examples/slm.c +++ b/examples/slm.c @@ -1,9 +1,9 @@ -#include -#include +#include #include #include -#include -#include +#include +#include + #include "../include/sl.h" /* @@ -49,54 +49,61 @@ struct slex_node { * list or when `a == b`. In those cases the comparison function * returns before using the code in your block, don't panic. :) */ -SKIPLIST_DECL(slex, api_, entries, { - (void)aux; - if (a->key < b->key) - return -1; - if (a->key > b->key) - return 1; - return 0; -}) - -void sprintf_slex_node(slex_node_t *node, char *buf) { - sprintf(buf, "%d", node->key); +SKIPLIST_DECL(slex, api_, entries) +int +__slm_key_compare(slex_t *list, slex_node_t *a, slex_node_t *b, void *aux) +{ + (void)list; + (void)aux; + if (a->key < b->key) + return -1; + if (a->key > b->key) + return 1; + return 0; } -int main() { -/* 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 - free(list); - slex_t *list = &_list; -#else +#define DOT +#ifdef DOT +/* Also declare the functions used to visualize a Sliplist (DOT/Graphviz) */ +SKIPLIST_DECL_DOT(slex, api_, entries) + +void +sprintf_slex_node(slex_node_t *node, char *buf) +{ + sprintf(buf, "%d:%d", node->key, node->value); +} #endif - struct slex_node *n; - SKIP_ALLOC_NODE(list, n, slex_node, entries); - n->key = -1; - n->value = -1; - api_skip_insert_slex(list, n); +int +main() +{ + int rc = 0; + /* Allocate and initialize a Skiplist. */ + slex_t *list = (slex_t *)malloc(sizeof(slex_t)); + if (list == NULL) { + rc = ENOMEM; + goto fail; + } + rc = api_skip_init_slex(list, 12, 4, __slm_key_compare); - FILE* of = fopen("/tmp/slm.dot", "w"); + struct slex_node *n; + + /* Insert 7 key/value pairs into the list. */ + for (int i = -2; i <= 2; i++) { + SKIP_ALLOC_NODE(list, n, slex_node, entries); + n->key = i; + n->value = i; + api_skip_insert_slex(list, n); + } + + FILE *of = fopen("/tmp/slm.dot", "w"); if (!of) { - perror("Failed to open file /tmp/slm.dot"); - return EXIT_FAILURE; + perror("Failed to open file /tmp/slm.dot"); + return EXIT_FAILURE; } api_skip_dot_slex(of, list, sprintf_slex_node); fclose(of); - /* 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 /* Delete a specific element in the list. */ struct slex_node query; @@ -124,5 +131,6 @@ int main() { } SKIP_INIT(&head); #endif - return 0; +fail:; + return rc; } diff --git a/include/skiplist.h b/include/skiplist.h index 1c87ace..c496b07 100644 --- a/include/skiplist.h +++ b/include/skiplist.h @@ -50,13 +50,13 @@ extern "C" { #endif typedef struct _sl_node { - atm_node_ptr *next; - atm_bool is_fully_linked; - atm_bool being_modified; - atm_bool removed; - uint8_t top_layer; /* 0: bottom */ - atm_uint16_t ref_count; - atm_uint32_t accessing_next; + atm_node_ptr *next; + atm_bool is_fully_linked; + atm_bool being_modified; + atm_bool removed; + uint8_t top_layer; /* 0: bottom */ + atm_uint16_t ref_count; + atm_uint32_t accessing_next; } sl_node; /* @@ -67,26 +67,26 @@ typedef struct _sl_node { typedef int sl_cmp_t(sl_node *a, sl_node *b, void *aux); typedef struct { - size_t fanout; - size_t maxLayer; - void *aux; + size_t fanout; + size_t maxLayer; + void *aux; } sl_raw_config; typedef struct { - sl_node head; - sl_node tail; - sl_cmp_t *cmp_func; - void *aux; - atm_uint32_t num_entries; - atm_uint32_t *layer_entries; - atm_uint8_t top_layer; - uint8_t fanout; - uint8_t max_layer; + sl_node head; + sl_node tail; + sl_cmp_t *cmp_func; + void *aux; + atm_uint32_t num_entries; + atm_uint32_t *layer_entries; + atm_uint8_t top_layer; + uint8_t fanout; + uint8_t max_layer; } sl_raw; #ifndef sl_get_entry #define sl_get_entry(ELEM, STRUCT, MEMBER) \ - ((STRUCT *)((uint8_t *)(ELEM)-offsetof(STRUCT, MEMBER))) + ((STRUCT *)((uint8_t *)(ELEM)-offsetof(STRUCT, MEMBER))) #endif void sl_init(sl_raw *slist, sl_cmp_t *cmp_func); diff --git a/include/sl.h b/include/sl.h index 3089e2f..d746c89 100644 --- a/include/sl.h +++ b/include/sl.h @@ -30,8 +30,8 @@ * - async_nif.h */ -#ifndef _SYS_SKIPLIST_H_ -#define _SYS_SKIPLIST_H_ +#ifndef _SYS_SKIPLIST_H_ +#define _SYS_SKIPLIST_H_ /* * This file defines a skiplist data structure with a similar API to those @@ -68,40 +68,44 @@ * */ -#define SKIPLIST_MACRO_DEBUG 0 +#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; + char *lastfile; + int lastline; + char *prevfile; + int prevline; }; -#define TRACEBUF struct sl_trace trace; -#define TRASHIT(x) do {(x) = (void *)-1;} while (0) +#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_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) +#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 */ - +#define SLD_TRACE_ELEM(elem) +#define SLD_TRACE_HEAD(head) +#define TRACEBUF +#define TRASHIT(x) +#endif /* QUEUE_MACRO_DEBUG */ /* * Private, internal API. @@ -113,14 +117,15 @@ struct sl_trace { * 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) = (type**)calloc(sizeof(type*), size + 2); \ - if ((var) != NULL) { \ - *(var)++ = (type *)size; \ - *(var)++ = 0; \ - } \ - } while(0) +#define ARRAY_ALLOC(var, type, size) \ + do { \ + (size) = (size == 0 ? 254 : size); \ + (var) = (type **)calloc(sizeof(type *), size + 2); \ + if ((var) != NULL) { \ + *(var)++ = (type *)size; \ + *(var)++ = 0; \ + } \ + } while (0) #define ARRAY_FREE(var) free((var)-2) #define ARRAY_SIZE(list) (unsigned int)(uintptr_t)(list)[-2] #define ARRAY_SET_SIZE(list, size) (list)[-2] = (void *)(uintptr_t)(size) @@ -130,414 +135,492 @@ struct sl_trace { /* * 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(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_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_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 \ +#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_LAST(head) ((head)->slh_tail) -#define SKIP_NEXT(elm, field) ((elm)->field.sle_next[0]) -#define SKIP_PREV(elm, field) ((elm)->field.sle_prev) -#define SKIP_EMPTY(head) ((head)->length == 0) +#define SKIP_FIRST(head) ((head)->slh_head) +#define SKIP_LAST(head) ((head)->slh_tail) +#define SKIP_NEXT(elm, field) ((elm)->field.sle_next[0]) +#define SKIP_PREV(elm, field) ((elm)->field.sle_prev) +#define SKIP_EMPTY(head) ((head)->length == 0) #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(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 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(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)) +#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_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, type, field, fn) do { \ - (head)->level = 0; \ - (head)->length = 0; \ - (head)->max = max; \ - (head)->fanout = fanout; \ - (head)->cmp = fn; \ - SKIP_ALLOC_NODE(head, (head)->slh_head, type, field); \ - SKIP_ALLOC_NODE(head, (head)->slh_tail, type, field); \ - ARRAY_SET_LENGTH((head)->slh_head->field.sle_next, max); \ - for(size_t __i = 0; __i < ARRAY_SIZE((head)->slh_head->field.sle_next); __i++) { \ - (head)->slh_head->field.sle_next[__i] = (head)->slh_tail; \ - } \ - (head)->slh_head->field.sle_prev = NULL; \ - ARRAY_SET_LENGTH((head)->slh_tail->field.sle_next, max); \ - for(size_t __i = 0; __i < ARRAY_SIZE((head)->slh_tail->field.sle_next); __i++) { \ - (head)->slh_tail->field.sle_next[__i] = NULL; \ - } \ - (head)->slh_head->field.sle_prev = (head)->slh_tail; \ - SLD_TRACE_HEAD(head); \ +#define SKIP_INIT(head, max, fanout, type, field, fn) \ + do { \ + (head)->level = 0; \ + (head)->length = 0; \ + (head)->max = max; \ + (head)->fanout = fanout; \ + (head)->cmp = fn; \ + SKIP_ALLOC_NODE(head, (head)->slh_head, type, field); \ + SKIP_ALLOC_NODE(head, (head)->slh_tail, type, field); \ + ARRAY_SET_LENGTH((head)->slh_head->field.sle_next, max); \ + for (size_t __i = 0; __i < ARRAY_SIZE((head)->slh_head->field.sle_next); \ + __i++) { \ + (head)->slh_head->field.sle_next[__i] = (head)->slh_tail; \ + } \ + (head)->slh_head->field.sle_prev = NULL; \ + ARRAY_SET_LENGTH((head)->slh_tail->field.sle_next, max); \ + for (size_t __i = 0; __i < ARRAY_SIZE((head)->slh_tail->field.sle_next); \ + __i++) { \ + (head)->slh_tail->field.sle_next[__i] = NULL; \ + } \ + (head)->slh_head->field.sle_prev = (head)->slh_tail; \ + SLD_TRACE_HEAD(head); \ } while (0) -#define SKIP_DEFAULT_INIT(head, fn, type, field) do { \ - (head)->level = 0; \ - (head)->length = 0; \ - (head)->max = 12; \ - (head)->fanout = 4; \ - (head)->cmp = fn; \ - SKIP_ALLOC_NODE(head, (head)->slh_head, type, field); \ - SKIP_ALLOC_NODE(head, (head)->slh_tail, type, field); \ - ARRAY_SET_LENGTH((head)->slh_head->field.sle_next, (head)->max); \ - for(size_t __i = 0; __i < ARRAY_SIZE((head)->slh_head->field.sle_next); __i++) { \ - (head)->slh_head->field.sle_next[__i] = (head)->slh_tail; \ - } \ - (head)->slh_head->field.sle_prev = NULL; \ - ARRAY_SET_LENGTH((head)->slh_tail->field.sle_next, (head)->max); \ - for(size_t __i = 0; __i < ARRAY_SIZE((head)->slh_tail->field.sle_next); __i++) { \ - (head)->slh_tail->field.sle_next[__i] = NULL; \ - } \ - (head)->slh_head->field.sle_prev = (head)->slh_tail; \ - SLD_TRACE_HEAD(head); \ +#define SKIP_DEFAULT_INIT(head, fn, type, field) \ + do { \ + (head)->level = 0; \ + (head)->length = 0; \ + (head)->max = 12; \ + (head)->fanout = 4; \ + (head)->cmp = fn; \ + SKIP_ALLOC_NODE(head, (head)->slh_head, type, field); \ + SKIP_ALLOC_NODE(head, (head)->slh_tail, type, field); \ + ARRAY_SET_LENGTH((head)->slh_head->field.sle_next, (head)->max); \ + for (size_t __i = 0; __i < ARRAY_SIZE((head)->slh_head->field.sle_next); \ + __i++) { \ + (head)->slh_head->field.sle_next[__i] = (head)->slh_tail; \ + } \ + (head)->slh_head->field.sle_prev = NULL; \ + ARRAY_SET_LENGTH((head)->slh_tail->field.sle_next, (head)->max); \ + for (size_t __i = 0; __i < ARRAY_SIZE((head)->slh_tail->field.sle_next); \ + __i++) { \ + (head)->slh_tail->field.sle_next[__i] = NULL; \ + } \ + (head)->slh_head->field.sle_prev = (head)->slh_tail; \ + SLD_TRACE_HEAD(head); \ } while (0) - -#define SKIP_ALLOC_NODE(head, var, type, field) do { \ - (var) = (struct type *)calloc(1, sizeof(struct type)); \ - ARRAY_ALLOC((var)->field.sle_next, struct type, (head)->max); \ - if ((var) && (var)->field.sle_next) { \ - ARRAY_SET_SIZE((var)->field.sle_next, (head)->max); \ - ARRAY_SET_LENGTH((var)->field.sle_next, 0); \ - } \ +#define SKIP_ALLOC_NODE(head, var, type, field) \ + do { \ + (var) = (struct type *)calloc(1, sizeof(struct type)); \ + ARRAY_ALLOC((var)->field.sle_next, struct type, (head)->max); \ + if ((var) && (var)->field.sle_next) { \ + ARRAY_SET_SIZE((var)->field.sle_next, (head)->max); \ + ARRAY_SET_LENGTH((var)->field.sle_next, 0); \ + } \ } while (0) -#define SKIP_FREE_NODE(node, field) do { \ - free((node)->field.sle_next); \ - free((node)); \ - } while (0) +#define SKIPLIST_DECL(decl, prefix, field) \ + \ + /* Skiplist node type */ \ + typedef struct decl##_node decl##_node_t; \ + \ + /* Skiplist type */ \ + typedef struct decl { \ + size_t level, length, max, fanout; \ + int (*cmp)(struct decl *, decl##_node_t *, decl##_node_t *, void *); \ + void *aux; \ + decl##_node_t *slh_head; \ + decl##_node_t *slh_tail; \ + TRACEBUF \ + } decl##_t; \ + \ + /* -- __skip_key_compare_ \ + * \ + * 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; \ + return slist->cmp(slist, a, b, aux); \ + } \ + \ + /* -- __skip_toss_ */ \ + 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 \ + break; \ + } \ + return level; \ + } \ + \ + /* -- skip_alloc_node_ */ \ + int prefix##skip_alloc_node_##decl(decl##_t *slist, decl##_node_t **node) \ + { \ + decl##_node_t *n; \ + n = (decl##_node_t *)calloc(1, sizeof(decl##_node_t)); \ + ARRAY_ALLOC(n->field.sle_next, struct decl##_node, slist->max); \ + if (n && n->field.sle_next) { \ + ARRAY_SET_SIZE(n->field.sle_next, slist->max); \ + ARRAY_SET_LENGTH(n->field.sle_next, 0); \ + *node = n; \ + return 0; \ + } \ + return ENOMEM; \ + } \ + \ + /* -- skip_init_ \ + * max: 12, fanout: 4 are good defaults. \ + */ \ + 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 rc = 0; \ + slist->level = 0; \ + slist->length = 0; \ + slist->max = max; \ + slist->fanout = fanout; \ + slist->cmp = cmp; \ + rc = prefix##skip_alloc_node_##decl(slist, &slist->slh_head); \ + if (rc) \ + goto fail; \ + rc = prefix##skip_alloc_node_##decl(slist, &slist->slh_tail); \ + if (rc) \ + goto fail; \ + ARRAY_SET_LENGTH(slist->slh_head->field.sle_next, max); \ + for (size_t __i = 0; __i < max; __i++) \ + slist->slh_head->field.sle_next[__i] = slist->slh_tail; \ + slist->slh_head->field.sle_prev = NULL; \ + ARRAY_SET_LENGTH(slist->slh_tail->field.sle_next, max); \ + for (size_t __i = 0; __i < max; __i++) \ + slist->slh_tail->field.sle_next[__i] = NULL; \ + slist->slh_head->field.sle_prev = slist->slh_tail; \ + fail:; \ + return rc; \ + } \ + \ + /* -- skip_free_node_ */ \ + void prefix##skip_free_node_##decl(decl##_node_t *node) \ + { \ + free(node->field.sle_next); \ + free(node); \ + } \ + \ + /* -- skip_insert_ */ \ + int prefix##skip_insert_##decl(decl##_t *slist, decl##_node_t *n) \ + { \ + if (n == NULL) \ + return ENOMEM; \ + decl##_node_t *prev, *elm = slist->slh_head; \ + unsigned int i; \ + 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. */ \ + do { \ + while (elm && \ + __skip_key_compare_##decl(slist, elm->field.sle_next[i], n, \ + slist->aux) < 0) \ + elm = elm->field.sle_next[i]; \ + path[i] = elm; \ + ARRAY_SET_LENGTH(path, ARRAY_LENGTH(path) + 1); \ + } while (i--); \ + i = 0; \ + prev = elm; \ + elm = elm->field.sle_next[0]; \ + if (__skip_key_compare_##decl(slist, elm, n, slist->aux) == 0) { \ + /* Don't overwrite, to do that use _REPLACE not _INSERT */ \ + ARRAY_FREE(path); \ + return -1; \ + } \ + size_t level = __skip_toss_##decl(slist->max, slist->fanout); \ + ARRAY_SET_LENGTH(n->field.sle_next, level); \ + if (level > slist->level) { \ + for (i = slist->level + 1; i <= level; i++) { \ + path[i] = slist->slh_tail; \ + } \ + slist->level = level; \ + } \ + for (i = 0; i <= level; i++) { \ + n->field.sle_next[i] = path[i]->field.sle_next[i]; \ + path[i]->field.sle_next[i] = n; \ + } \ + n->field.sle_prev = prev; \ + if (n->field.sle_next[0] == slist->slh_tail) { \ + slist->slh_tail->field.sle_prev = n; \ + } \ + slist->length++; \ + ARRAY_FREE(path); \ + return 0; \ + } \ + \ + /* -- __skip_integrity_check_ */ \ + static int __skip_integrity_check_##decl(decl##_t *slist) \ + { \ + ((void)slist); /* TODO */ \ + return 0; \ + } -#define SKIPLIST_DECL(decl, prefix, field, key_cmp_logic) \ - \ - /* Skiplist node type */ \ - typedef struct decl##_node decl##_node_t; \ - \ - /* Skiplist type */ \ - typedef struct decl { \ - size_t level, length, max, fanout; \ - int (*cmp)(struct decl *, decl##_node_t *, decl##_node_t *, void *); \ - void *aux; \ - decl##_node_t *slh_head; \ - decl##_node_t *slh_tail; \ - TRACEBUF \ - }decl##_t; \ - \ - /* -- __skip_key_compare_ \ - * \ - * 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_ */ \ - 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 \ - break; \ - } \ - return level; \ - } \ - \ - /* -- skip_insert_ */ \ - int prefix##skip_insert_##decl(decl##_t *slist, decl##_node_t *n) { \ - if (n == NULL) \ - return ENOMEM; \ - decl##_node_t *prev, *elm = slist->slh_head; \ - unsigned int i; \ - 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. */ \ - do { \ - while(elm && slist->cmp(slist, elm->field.sle_next[i], n, slist->aux) < 0) \ - elm = elm->field.sle_next[i]; \ - path[i] = elm; \ - ARRAY_SET_LENGTH(path, ARRAY_LENGTH(path)+1); \ - } while(i--); \ - i = 0; \ - prev = elm; \ - elm = elm->field.sle_next[0]; \ - if (slist->cmp(slist, elm, n, slist->aux) == 0) { \ - ARRAY_FREE(path); \ - /* Don't overwrite, to do that use _REPLACE not _INSERT */ \ - return -1; \ - } \ - size_t level = __skip_toss_##decl(slist->max, slist->fanout); \ - ARRAY_SET_LENGTH(n->field.sle_next, level); \ - if (level > slist->level) { \ - for (i = slist->level + 1; i <= level; i++) { \ - path[i] = slist->slh_tail; \ - } \ - slist->level = level; \ - } \ - for (i = 0; i <= level; i++) { \ - n->field.sle_next[i] = path[i]->field.sle_next[i]; \ - path[i]->field.sle_next[i] = n; \ - } \ - n->field.sle_prev = prev; \ - if (n->field.sle_next[0] == slist->slh_tail) { \ - slist->slh_tail->field.sle_prev = n; \ - } \ - slist->length++; \ - ARRAY_FREE(path); \ - return 0; \ - } \ - \ - /* -- __skip_integrity_check_ */ \ - static int __skip_integrity_check_##decl() { \ - return 0; \ - } \ - \ - /* A type for a function that writes into a char[2048] buffer \ - * a description of the value within the node. */ \ - typedef void (*skip_sprintf_node_##decl##_t)(decl##_node_t *, char *); \ - \ - /* -- __skip_dot_node_ \ - * Writes out a fragment of a DOT file representing a node. \ - */ \ - static void __skip_dot_node_##decl(FILE *os, decl##_t *slist, decl##_node_t *node, size_t nsg, skip_sprintf_node_##decl##_t fn) { \ - fprintf(os, "\"node%zu%p\"", nsg, (void*)node); \ - fprintf(os, " [label = \""); \ - size_t level = ARRAY_LENGTH(node->field.sle_next); \ - do { \ - fprintf(os, " { | %p }", level, level, (void*)node->field.sle_next[level]); \ - if (level != 0) fprintf(os, " | "); \ - } while(level--); \ - if (fn) { \ - char buf[2048]; \ - fn(node, buf); \ - fprintf(os, " %s\"\n", buf); \ - } else { \ - fprintf(os, " ?\"\n"); \ - } \ - fprintf(os, "shape = \"record\"\n"); \ - fprintf(os, "];\n"); \ - \ - /* Now edges */ \ - level = 0; \ - size_t size = ARRAY_LENGTH(node->field.sle_next); \ - for (size_t level = 0; level <= size; level++) { \ - fprintf(os, "\"node%zu%p\"", nsg, (void*)node); \ - fprintf(os, ":f%zu -> ", level); \ - fprintf(os, "\"node%zu%p\"", nsg, (void*)node->field.sle_next[level]); \ - fprintf(os, ":w%zu [];\n", level); \ - } \ - \ - if (node->field.sle_next[0]) \ - __skip_dot_node_##decl(os, slist, node->field.sle_next[0], nsg, fn); \ - } \ - \ - /* -- __skip_dot_finish_ \ - * Finalise the DOT file of the internal representation. \ - */ \ - static void __skip_dot_finish_##decl(FILE *os, size_t nsg) { \ - if (nsg > 0) { \ - /* Link the nodes together with an invisible node. \ - * node0 [shape=record, label = " | | | | | | | | | ", \ - * style=invis, \ - * width=0.01]; \ - */ \ - fprintf(os, "node0 [shape=record, label = \""); \ - for (size_t i = 0; i < nsg; ++i) { \ - fprintf(os, " | ", i); \ - } \ - fprintf(os, "\", style=invis, width=0.01];\n"); \ - \ - /* Now connect nodes with invisible edges \ - * \ - * node0:f0 -> HeadNode [style=invis]; \ - * node0:f1 -> HeadNode1 [style=invis]; \ - */ \ - for (size_t i = 0; i < nsg; ++i) { \ - fprintf(os, "node0:f%zu -> HeadNode%zu [style=invis];\n", i, i);\ - } \ - nsg = 0; \ - } \ - fprintf(os, "}\n"); \ - } \ - \ - /* -- skip_dot_start_ */ \ - static int __skip_dot_start_##decl(FILE *os, decl##_t *slist, size_t nsg, skip_sprintf_node_##decl##_t fn) { \ - if (nsg == 0) { \ - fprintf(os, "digraph Skiplist {\n"); \ - fprintf(os, "label = \"Skiplist.\"\n"); \ - fprintf(os, "graph [rankdir = \"LR\"];\n"); \ - fprintf(os, "node [fontsize = \"12\" shape = \"ellipse\"];\n"); \ - fprintf(os, "edge [];\n\n"); \ - } \ - fprintf(os, "subgraph cluster%zu {\n", nsg); \ - fprintf(os, "style=dashed\n"); \ - fprintf(os, "label=\"Skip list iteration %zu\"\n\n", nsg); \ - fprintf(os, "\"HeadNode%zu\" [\n", nsg); \ - fprintf(os, "label = \""); \ - \ - /* Write out the head node fields */ \ - decl##_node_t *head = slist->slh_head; \ - size_t level; \ - if (SKIP_EMPTY(slist)) fprintf(os, "Empty HeadNode"); else { \ - level = ARRAY_LENGTH(head->field.sle_next) - 1; \ - do { \ - decl##_node_t *node = head->field.sle_next[level]; \ - fprintf(os, "{ %p }", level, (void *)node); \ - if (level != 0) fprintf(os, " | "); \ - } while(level--); \ - } \ - fprintf(os, "\"\n"); \ - fprintf(os, "shape = \"record\"\n"); \ - fprintf(os, "];\n"); \ - \ - /* Edges for head node */ \ - decl##_node_t *node = slist->slh_head; \ - level = 0; \ - do { \ - fprintf(os, "\"HeadNode%zu\":f%zu -> ", nsg, level); \ - fprintf(os, "\"node%zu%p\"", nsg, (void*)node->field.sle_next[level]); \ - fprintf(os, ":w%zu [];\n", level); \ - } while(level++ < slist->level); \ - fprintf(os, "}\n\n"); \ - \ - /* Now all nodes via level 0, if non-empty */ \ - node = slist->slh_head; \ - if (ARRAY_LENGTH(node->field.sle_next)) \ - __skip_dot_node_##decl(os, slist, node, nsg, fn); \ - fprintf(os, "\n"); \ - \ - /* The tail, sentinal node */ \ - if (!SKIP_EMPTY(slist)) { \ - fprintf(os,"\"node%zu0x0\" [label = \"", nsg); \ - size_t level = slist->level; \ - do { \ - fprintf(os, " NULL", level); \ - if (level != 0) \ - fprintf(os, " | "); \ - } while(level-- > 0); \ - fprintf(os, "\" shape = \"record\"];\n"); \ - } \ - \ - /* End: "subgraph cluster1 {" */ \ - fprintf(os, "}\n\n"); \ - nsg += 1; \ - \ - return 0; \ - } \ - \ - /* -- skip_dot_ \ - * Create a DOT file of the internal representation of the \ - * Skiplist on the provided file descriptor (default: STDOUT). \ - * \ - * To view the output: \ - * $ dot -Tps filename.dot -o outfile.ps \ - * You can change the output format by varying the value after -T and \ - * choosing an appropriate filename extension after -o. \ - * See: https://graphviz.org/docs/outputs/ for the format options. \ - * \ - * https://en.wikipedia.org/wiki/DOT_(graph_description_language) \ - */ \ - int prefix##skip_dot_##decl(FILE *os, decl##_t *slist, skip_sprintf_node_##decl##_t fn) { \ - size_t nsg = 0; \ - if (__skip_integrity_check_##decl(slist) != 0) { \ - perror("Skiplist failed integrity checks, impossible to diagram.");\ - return -1; \ - } \ - if (os == NULL) \ - os = stdout; \ - if (!os) { \ - perror("Failed to open output file, unable to write DOT file.");\ - return -1; \ - } \ - __skip_dot_start_##decl(os, slist, nsg, fn); \ - __skip_dot_finish_##decl(os, nsg); \ - return 0; \ - } \ - /* END */ +#define SKIPLIST_DECL_DOT(decl, prefix, field) \ + \ + /* A type for a function that writes into a char[2048] buffer \ + * a description of the value within the node. */ \ + typedef void (*skip_sprintf_node_##decl##_t)(decl##_node_t *, char *); \ + \ + /* -- __skip_dot_node_ \ + * Writes out a fragment of a DOT file representing a node. \ + */ \ + static void __skip_dot_node_##decl(FILE *os, decl##_t *slist, \ + decl##_node_t *node, size_t nsg, skip_sprintf_node_##decl##_t fn) \ + { \ + char buf[2048]; \ + size_t level, height = ARRAY_LENGTH(node->field.sle_next); \ + fprintf(os, "\"node%zu%p\"", nsg, (void *)node); \ + fprintf(os, " [label = \""); \ + level = height; \ + do { \ + fprintf(os, " { | %p }", level + 1, level + 1, \ + (void *)node->field.sle_next[level]); \ + if (level != 0) \ + fprintf(os, " | "); \ + } while (level--); \ + if (fn) { \ + fn(node, buf); \ + fprintf(os, " %s\"\n", buf); \ + } else { \ + fprintf(os, " ?\"\n"); \ + } \ + fprintf(os, "shape = \"record\"\n"); \ + fprintf(os, "];\n"); \ + \ + /* Now edges */ \ + level = 0; \ + for (level = 0; level < height; level++) { \ + fprintf(os, "\"node%zu%p\"", nsg, (void *)node); \ + fprintf(os, ":f%zu -> ", level + 1); \ + fprintf(os, "\"node%zu%p\"", nsg, (void *)node->field.sle_next[level]); \ + fprintf(os, ":w%zu [];\n", level + 1); \ + } \ + \ + if (node->field.sle_next[0] != SKIP_LAST(slist)) \ + __skip_dot_node_##decl(os, slist, node->field.sle_next[0], nsg, fn); \ + } \ + \ + /* -- __skip_dot_finish_ \ + * Finalise the DOT file of the internal representation. \ + */ \ + static void __skip_dot_finish_##decl(FILE *os, size_t nsg) \ + { \ + size_t i; \ + if (nsg > 0) { \ + /* Link the nodes together with an invisible node. \ + * node0 [shape=record, label = " | | | | \ + * | | | | | ", style=invis, width=0.01]; \ + */ \ + fprintf(os, "node0 [shape=record, label = \""); \ + for (i = 0; i < nsg; ++i) { \ + fprintf(os, " | ", i); \ + } \ + fprintf(os, "\", style=invis, width=0.01];\n"); \ + \ + /* Now connect nodes with invisible edges \ + * \ + * node0:f0 -> HeadNode [style=invis]; \ + * node0:f1 -> HeadNode1 [style=invis]; \ + */ \ + for (i = 0; i < nsg; ++i) { \ + fprintf(os, "node0:f%zu -> HeadNode%zu [style=invis];\n", i, i); \ + } \ + nsg = 0; \ + } \ + fprintf(os, "}\n"); \ + } \ + \ + /* -- skip_dot_start_ */ \ + static int __skip_dot_start_##decl(FILE *os, decl##_t *slist, size_t nsg, \ + skip_sprintf_node_##decl##_t fn) \ + { \ + size_t level; \ + decl##_node_t *head; \ + if (nsg == 0) { \ + fprintf(os, "digraph Skiplist {\n"); \ + fprintf(os, "label = \"Skiplist.\"\n"); \ + fprintf(os, "graph [rankdir = \"LR\"];\n"); \ + fprintf(os, "node [fontsize = \"12\" shape = \"ellipse\"];\n"); \ + fprintf(os, "edge [];\n\n"); \ + } \ + fprintf(os, "subgraph cluster%zu {\n", nsg); \ + fprintf(os, "style=dashed\n"); \ + fprintf(os, "label=\"Skip list iteration %zu\"\n\n", nsg); \ + fprintf(os, "\"HeadNode%zu\" [\n", nsg); \ + fprintf(os, "label = \""); \ + \ + /* Write out the fields */ \ + head = slist->slh_head; \ + if (SKIP_EMPTY(slist)) \ + fprintf(os, "Empty HeadNode"); \ + else { \ + level = ARRAY_LENGTH(head->field.sle_next); \ + while (level--) { \ + decl##_node_t *node = head->field.sle_next[level]; \ + fprintf(os, "{ %p }", level + 1, (void *)node); \ + if (level + 1 != 0) \ + fprintf(os, " | "); \ + } \ + } \ + fprintf(os, "\"\n"); \ + fprintf(os, "shape = \"record\"\n"); \ + fprintf(os, "];\n"); \ + \ + /* Edges for head node */ \ + decl##_node_t *node = slist->slh_head; \ + level = 0; \ + do { \ + fprintf(os, "\"HeadNode%zu\":f%zu -> ", nsg, level + 1); \ + fprintf(os, "\"node%zu%p\"", nsg, (void *)node->field.sle_next[level]); \ + fprintf(os, ":w%zu [];\n", level + 1); \ + } while (level++ < slist->level); \ + fprintf(os, "}\n\n"); \ + \ + /* Now all nodes via level 0, if non-empty */ \ + node = slist->slh_head; \ + if (ARRAY_LENGTH(node->field.sle_next)) \ + __skip_dot_node_##decl(os, slist, node->field.sle_next[0], nsg, fn); \ + fprintf(os, "\n"); \ + \ + /* The tail, sentinal node */ \ + if (!SKIP_EMPTY(slist)) { \ + fprintf(os, "\"node%zu0x0\" [label = \"", nsg); \ + level = slist->level; \ + do { \ + fprintf(os, " NULL", level + 1); \ + if (level != 0) \ + fprintf(os, " | "); \ + } while (level-- > 0); \ + fprintf(os, "\" shape = \"record\"];\n"); \ + } \ + \ + /* End: "subgraph cluster1 {" */ \ + fprintf(os, "}\n\n"); \ + nsg += 1; \ + \ + return 0; \ + } \ + \ + /* -- skip_dot_ \ + * Create a DOT file of the internal representation of the \ + * Skiplist on the provided file descriptor (default: STDOUT). \ + * \ + * To view the output: \ + * $ dot -Tps filename.dot -o outfile.ps \ + * You can change the output format by varying the value after -T and \ + * choosing an appropriate filename extension after -o. \ + * See: https://graphviz.org/docs/outputs/ for the format options. \ + * \ + * https://en.wikipedia.org/wiki/DOT_(graph_description_language) \ + */ \ + int prefix##skip_dot_##decl(FILE *os, decl##_t *slist, \ + skip_sprintf_node_##decl##_t fn) \ + { \ + size_t nsg = 0; \ + if (__skip_integrity_check_##decl(slist) != 0) { \ + perror("Skiplist failed integrity checks, impossible to diagram."); \ + return -1; \ + } \ + if (os == NULL) \ + os = stdout; \ + if (!os) { \ + perror("Failed to open output file, unable to write DOT file."); \ + return -1; \ + } \ + __skip_dot_start_##decl(os, slist, nsg, fn); \ + __skip_dot_finish_##decl(os, nsg); \ + return 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_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) +#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_ */ diff --git a/src/skiplist.c b/src/skiplist.c index 00fa116..3ae4c97 100644 --- a/src/skiplist.c +++ b/src/skiplist.c @@ -60,13 +60,13 @@ typedef uint8_t bool; #define ATM_GET(var) (var) #define ATM_LOAD(var, val) __atomic_load(&(var), &(val), __ATOMIC_RELAXED) #define ATM_STORE(var, val) __atomic_store(&(var), &(val), __ATOMIC_RELAXED) -#define ATM_CAS(var, exp, val) \ - __atomic_compare_exchange(&(var), &(exp), &(val), 1, __ATOMIC_RELAXED, \ - __ATOMIC_RELAXED) +#define ATM_CAS(var, exp, val) \ + __atomic_compare_exchange(&(var), &(exp), &(val), 1, __ATOMIC_RELAXED, \ + __ATOMIC_RELAXED) #define ATM_FETCH_ADD(var, val) \ - __atomic_fetch_add(&(var), (val), __ATOMIC_RELAXED) + __atomic_fetch_add(&(var), (val), __ATOMIC_RELAXED) #define ATM_FETCH_SUB(var, val) \ - __atomic_fetch_sub(&(var), (val), __ATOMIC_RELAXED) + __atomic_fetch_sub(&(var), (val), __ATOMIC_RELAXED) /* * __sl_node_init -- @@ -76,25 +76,25 @@ typedef uint8_t bool; static void __sl_node_init(sl_node *node, size_t top_layer) { - bool bool_val = false; + bool bool_val = false; - if (top_layer > UINT8_MAX) - top_layer = UINT8_MAX; + if (top_layer > UINT8_MAX) + top_layer = UINT8_MAX; - assert(node->is_fully_linked == false); - assert(node->being_modified == false); + assert(node->is_fully_linked == false); + assert(node->being_modified == false); - ATM_STORE(node->is_fully_linked, bool_val); - ATM_STORE(node->being_modified, bool_val); - ATM_STORE(node->removed, bool_val); + ATM_STORE(node->is_fully_linked, bool_val); + ATM_STORE(node->being_modified, bool_val); + ATM_STORE(node->removed, bool_val); - if (node->top_layer != top_layer || node->next == NULL) { - node->top_layer = top_layer; + if (node->top_layer != top_layer || node->next == NULL) { + node->top_layer = top_layer; - if (node->next) - free(node->next); - node->next = calloc(top_layer + 1, sizeof(atm_node_ptr)); - } + if (node->next) + free(node->next); + node->next = calloc(top_layer + 1, sizeof(atm_node_ptr)); + } } /* @@ -106,35 +106,35 @@ __sl_node_init(sl_node *node, size_t top_layer) void sl_init(sl_raw *slist, sl_cmp_t *cmp_func) { - size_t layer; - bool bool_val = true; + size_t layer; + bool bool_val = true; - slist->cmp_func = NULL; - slist->aux = NULL; + slist->cmp_func = NULL; + slist->aux = NULL; - /* Fanout 4 + layer 12: 4^12 ~= upto 17M items under O(lg n) complexity. - For +17M items, complexity will grow linearly: O(k lg n). */ - slist->fanout = 4; - slist->max_layer = 12; - slist->num_entries = 0; + /* Fanout 4 + layer 12: 4^12 ~= upto 17M items under O(lg n) complexity. + For +17M items, complexity will grow linearly: O(k lg n). */ + slist->fanout = 4; + slist->max_layer = 12; + slist->num_entries = 0; - slist->layer_entries = calloc(slist->max_layer, sizeof(atm_uint32_t)); - slist->top_layer = 0; + slist->layer_entries = calloc(slist->max_layer, sizeof(atm_uint32_t)); + slist->top_layer = 0; - sl_init_node(&slist->head); - sl_init_node(&slist->tail); + sl_init_node(&slist->head); + sl_init_node(&slist->tail); - __sl_node_init(&slist->head, slist->max_layer); - __sl_node_init(&slist->tail, slist->max_layer); + __sl_node_init(&slist->head, slist->max_layer); + __sl_node_init(&slist->tail, slist->max_layer); - for (layer = 0; layer < slist->max_layer; ++layer) { - slist->head.next[layer] = &slist->tail; - slist->tail.next[layer] = NULL; - } + for (layer = 0; layer < slist->max_layer; ++layer) { + slist->head.next[layer] = &slist->tail; + slist->tail.next[layer] = NULL; + } - ATM_STORE(slist->head.is_fully_linked, bool_val); - ATM_STORE(slist->tail.is_fully_linked, bool_val); - slist->cmp_func = cmp_func; + ATM_STORE(slist->head.is_fully_linked, bool_val); + ATM_STORE(slist->tail.is_fully_linked, bool_val); + slist->cmp_func = cmp_func; } /* @@ -146,8 +146,8 @@ sl_init(sl_raw *slist, sl_cmp_t *cmp_func) void sl_free_node(sl_node *node) { - free(node->next); - node->next = NULL; + free(node->next); + node->next = NULL; } /* @@ -159,14 +159,14 @@ sl_free_node(sl_node *node) void sl_free(sl_raw *slist) { - sl_free_node(&slist->head); - sl_free_node(&slist->tail); + sl_free_node(&slist->head); + sl_free_node(&slist->tail); - free(slist->layer_entries); - slist->layer_entries = NULL; + free(slist->layer_entries); + slist->layer_entries = NULL; - slist->aux = NULL; - slist->cmp_func = NULL; + slist->aux = NULL; + slist->cmp_func = NULL; } /* @@ -178,17 +178,17 @@ sl_free(sl_raw *slist) void sl_init_node(sl_node *node) { - bool bool_false = false; + bool bool_false = false; - node->next = NULL; + node->next = NULL; - ATM_STORE(node->is_fully_linked, bool_false); - ATM_STORE(node->being_modified, bool_false); - ATM_STORE(node->removed, bool_false); + ATM_STORE(node->is_fully_linked, bool_false); + ATM_STORE(node->being_modified, bool_false); + ATM_STORE(node->removed, bool_false); - node->accessing_next = 0; - node->top_layer = 0; - node->ref_count = 0; + node->accessing_next = 0; + node->top_layer = 0; + node->ref_count = 0; } /* @@ -200,9 +200,9 @@ sl_init_node(sl_node *node) size_t sl_get_size(sl_raw *slist) { - uint32_t val; - ATM_LOAD(slist->num_entries, val); - return val; + uint32_t val; + ATM_LOAD(slist->num_entries, val); + return val; } /* @@ -214,11 +214,11 @@ sl_get_size(sl_raw *slist) sl_raw_config sl_get_default_config(void) { - sl_raw_config ret; - ret.fanout = 4; - ret.maxLayer = 12; - ret.aux = NULL; - return ret; + sl_raw_config ret; + ret.fanout = 4; + ret.maxLayer = 12; + ret.aux = NULL; + return ret; } /* @@ -230,11 +230,11 @@ sl_get_default_config(void) sl_raw_config sl_get_config(sl_raw *slist) { - sl_raw_config ret; - ret.fanout = slist->fanout; - ret.maxLayer = slist->max_layer; - ret.aux = slist->aux; - return ret; + sl_raw_config ret; + ret.fanout = slist->fanout; + ret.maxLayer = slist->max_layer; + ret.aux = slist->aux; + return ret; } /* @@ -247,14 +247,14 @@ sl_get_config(sl_raw *slist) void sl_set_config(sl_raw *slist, sl_raw_config config) { - slist->fanout = config.fanout; + slist->fanout = config.fanout; - slist->max_layer = config.maxLayer; - if (slist->layer_entries) - free(slist->layer_entries); - slist->layer_entries = calloc(sizeof(atm_uint32_t), slist->max_layer); + slist->max_layer = config.maxLayer; + if (slist->layer_entries) + free(slist->layer_entries); + slist->layer_entries = calloc(sizeof(atm_uint32_t), slist->max_layer); - slist->aux = config.aux; + slist->aux = config.aux; } /* @@ -265,13 +265,13 @@ sl_set_config(sl_raw *slist, sl_raw_config config) static int __sl_cmp(sl_raw *slist, sl_node *a, sl_node *b) { - if (a == b) - return 0; - if (a == &slist->head || b == &slist->tail) - return -1; - if (a == &slist->tail || b == &slist->head) - return 1; - return slist->cmp_func(a, b, slist->aux); + if (a == b) + return 0; + if (a == &slist->head || b == &slist->tail) + return -1; + if (a == &slist->tail || b == &slist->head) + return 1; + return slist->cmp_func(a, b, slist->aux); } /* @@ -281,9 +281,9 @@ __sl_cmp(sl_raw *slist, sl_node *a, sl_node *b) static bool __sl_valid_node(sl_node *node) { - bool is_fully_linked = false; - ATM_LOAD(node->is_fully_linked, is_fully_linked); - return is_fully_linked; + bool is_fully_linked = false; + ATM_LOAD(node->is_fully_linked, is_fully_linked); + return is_fully_linked; } /* @@ -294,23 +294,23 @@ __sl_valid_node(sl_node *node) static void __sl_read_lock_an(sl_node *node) { - for (;;) { - /* Wait for active writer to release the lock. */ - uint32_t accessing_next = 0; - ATM_LOAD(node->accessing_next, accessing_next); - while (accessing_next & 0xfff00000) { - YIELD(); - ATM_LOAD(node->accessing_next, accessing_next); - } - - ATM_FETCH_ADD(node->accessing_next, 0x1); - ATM_LOAD(node->accessing_next, accessing_next); - if ((accessing_next & 0xfff00000) == 0) { - return; - } - - ATM_FETCH_SUB(node->accessing_next, 0x1); + for (;;) { + /* Wait for active writer to release the lock. */ + uint32_t accessing_next = 0; + ATM_LOAD(node->accessing_next, accessing_next); + while (accessing_next & 0xfff00000) { + YIELD(); + ATM_LOAD(node->accessing_next, accessing_next); } + + ATM_FETCH_ADD(node->accessing_next, 0x1); + ATM_LOAD(node->accessing_next, accessing_next); + if ((accessing_next & 0xfff00000) == 0) { + return; + } + + ATM_FETCH_SUB(node->accessing_next, 0x1); + } } /* @@ -320,7 +320,7 @@ __sl_read_lock_an(sl_node *node) static void __sl_read_unlock_an(sl_node *node) { - ATM_FETCH_SUB(node->accessing_next, 0x1); + ATM_FETCH_SUB(node->accessing_next, 0x1); } /* @@ -331,28 +331,28 @@ __sl_read_unlock_an(sl_node *node) static void __sl_write_lock_an(sl_node *node) { - for (;;) { - /* Wait for active writer to release the lock. */ - uint32_t accessing_next = 0; - ATM_LOAD(node->accessing_next, accessing_next); - while (accessing_next & 0xfff00000) { - YIELD(); - ATM_LOAD(node->accessing_next, accessing_next); - } - - ATM_FETCH_ADD(node->accessing_next, 0x100000); - ATM_LOAD(node->accessing_next, accessing_next); - if ((accessing_next & 0xfff00000) == 0x100000) { - /* Wait until there are no more readers. */ - while (accessing_next & 0x000fffff) { - YIELD(); - ATM_LOAD(node->accessing_next, accessing_next); - } - return; - } - - ATM_FETCH_SUB(node->accessing_next, 0x100000); + for (;;) { + /* Wait for active writer to release the lock. */ + uint32_t accessing_next = 0; + ATM_LOAD(node->accessing_next, accessing_next); + while (accessing_next & 0xfff00000) { + YIELD(); + ATM_LOAD(node->accessing_next, accessing_next); } + + ATM_FETCH_ADD(node->accessing_next, 0x100000); + ATM_LOAD(node->accessing_next, accessing_next); + if ((accessing_next & 0xfff00000) == 0x100000) { + /* Wait until there are no more readers. */ + while (accessing_next & 0x000fffff) { + YIELD(); + ATM_LOAD(node->accessing_next, accessing_next); + } + return; + } + + ATM_FETCH_SUB(node->accessing_next, 0x100000); + } } /* @@ -362,7 +362,7 @@ __sl_write_lock_an(sl_node *node) static void __sl_write_unlock_an(sl_node *node) { - ATM_FETCH_SUB(node->accessing_next, 0x100000); + ATM_FETCH_SUB(node->accessing_next, 0x100000); } /* @@ -377,73 +377,73 @@ __sl_write_unlock_an(sl_node *node) static sl_node * __sl_fnd_next(sl_node *cur_node, size_t layer, sl_node *to_find, bool *found) { - size_t i, num_nodes = 0; - sl_node *nodes[256], *temp, *next_node = NULL; + size_t i, num_nodes = 0; + sl_node *nodes[256], *temp, *next_node = NULL; - /* Turn on `accessing_next`: - * now `cur_node` is not removable from skiplist, - * which means that `cur_node->next` will be consistent - * until clearing `accessing_next`. + /* Turn on `accessing_next`: + * now `cur_node` is not removable from skiplist, + * which means that `cur_node->next` will be consistent + * until clearing `accessing_next`. + */ + __sl_read_lock_an(cur_node); + { + if (!__sl_valid_node(cur_node)) { + __sl_read_unlock_an(cur_node); + return NULL; + } + ATM_LOAD(cur_node->next[layer], next_node); + /* Increase ref count of `next_node`: + * now `next_node` is not destroyable. + * + * << Remaining issue >> + * 1) initially: A -> B + * 2) T1: call __sl_next(A): + * A.accessing_next := true; + * next_node := B; + * ----- context switch happens here ----- + * 3) T2: insert C: + * A -> C -> B + * 4) T2: and then erase B, and free B. + * A -> C B(freed) + * ----- context switch back again ----- + * 5) T1: try to do something with B, + * but crash happens. + * + * ... maybe resolved using RW spinlock (Aug 21, 2017). */ - __sl_read_lock_an(cur_node); + assert(next_node); + ATM_FETCH_ADD(next_node->ref_count, 1); + assert(next_node->top_layer >= layer); + } + __sl_read_unlock_an(cur_node); + + while ((next_node && !__sl_valid_node(next_node)) || next_node == to_find) { + if (found && to_find == next_node) + *found = true; + + temp = next_node; + __sl_read_lock_an(temp); { - if (!__sl_valid_node(cur_node)) { - __sl_read_unlock_an(cur_node); - return NULL; - } - ATM_LOAD(cur_node->next[layer], next_node); - /* Increase ref count of `next_node`: - * now `next_node` is not destroyable. - * - * << Remaining issue >> - * 1) initially: A -> B - * 2) T1: call __sl_next(A): - * A.accessing_next := true; - * next_node := B; - * ----- context switch happens here ----- - * 3) T2: insert C: - * A -> C -> B - * 4) T2: and then erase B, and free B. - * A -> C B(freed) - * ----- context switch back again ----- - * 5) T1: try to do something with B, - * but crash happens. - * - * ... maybe resolved using RW spinlock (Aug 21, 2017). - */ - assert(next_node); - ATM_FETCH_ADD(next_node->ref_count, 1); - assert(next_node->top_layer >= layer); - } - __sl_read_unlock_an(cur_node); - - while ((next_node && !__sl_valid_node(next_node)) || next_node == to_find) { - if (found && to_find == next_node) - *found = true; - - temp = next_node; - __sl_read_lock_an(temp); - { - assert(next_node); - if (!__sl_valid_node(temp)) { - __sl_read_unlock_an(temp); - ATM_FETCH_SUB(temp->ref_count, 1); - next_node = NULL; - break; - } - ATM_LOAD(temp->next[layer], next_node); - ATM_FETCH_ADD(next_node->ref_count, 1); - nodes[num_nodes++] = temp; - assert(next_node->top_layer >= layer); - } + assert(next_node); + if (!__sl_valid_node(temp)) { __sl_read_unlock_an(temp); + ATM_FETCH_SUB(temp->ref_count, 1); + next_node = NULL; + break; + } + ATM_LOAD(temp->next[layer], next_node); + ATM_FETCH_ADD(next_node->ref_count, 1); + nodes[num_nodes++] = temp; + assert(next_node->top_layer >= layer); } + __sl_read_unlock_an(temp); + } - for (i = 0; i < num_nodes; ++i) { - ATM_FETCH_SUB(nodes[i]->ref_count, 1); - } + for (i = 0; i < num_nodes; ++i) { + ATM_FETCH_SUB(nodes[i]->ref_count, 1); + } - return next_node; + return next_node; } /* @@ -458,7 +458,7 @@ __sl_fnd_next(sl_node *cur_node, size_t layer, sl_node *to_find, bool *found) static sl_node * __sl_next(sl_node *cur_node, size_t layer) { - return __sl_fnd_next(cur_node, layer, NULL, NULL); + return __sl_fnd_next(cur_node, layer, NULL, NULL); } /* @@ -467,18 +467,18 @@ __sl_next(sl_node *cur_node, size_t layer) static size_t __sl_decide_top_layer(sl_raw *slist) { - size_t layer = 0; - while (layer + 1 < slist->max_layer) { - /* coin flip */ - if (rand() % slist->fanout == 0) { /* NOLINT(*-msc50-cpp) */ - /* grow: 1/fanout probability */ - layer++; - } else { - /* stop: 1 - 1/fanout probability */ - break; - } + size_t layer = 0; + while (layer + 1 < slist->max_layer) { + /* coin flip */ + if (rand() % slist->fanout == 0) { /* NOLINT(*-msc50-cpp) */ + /* grow: 1/fanout probability */ + layer++; + } else { + /* stop: 1 - 1/fanout probability */ + break; } - return layer; + } + return layer; } /* @@ -486,18 +486,18 @@ __sl_decide_top_layer(sl_raw *slist) */ static void __sl_clr_flags(sl_node **node_arr, unsigned long start_layer, - unsigned long top_layer) + unsigned long top_layer) { - unsigned long layer; - for (layer = start_layer; layer <= top_layer; ++layer) { - if (layer == top_layer || node_arr[layer] != node_arr[layer + 1]) { - bool exp = true; - bool bool_false = false; - if (!ATM_CAS(node_arr[layer]->being_modified, exp, bool_false)) { - assert(0); - } - } + unsigned long layer; + for (layer = start_layer; layer <= top_layer; ++layer) { + if (layer == top_layer || node_arr[layer] != node_arr[layer + 1]) { + bool exp = true; + bool bool_false = false; + if (!ATM_CAS(node_arr[layer]->being_modified, exp, bool_false)) { + assert(0); + } } + } } /* @@ -506,7 +506,7 @@ __sl_clr_flags(sl_node **node_arr, unsigned long start_layer, static bool __sl_valid_prev_next(sl_node *prev, sl_node *next) { - return __sl_valid_node(prev) && __sl_valid_node(next); + return __sl_valid_node(prev) && __sl_valid_node(next); } /* @@ -515,171 +515,169 @@ __sl_valid_prev_next(sl_node *prev, sl_node *next) static int __sl_insert(sl_raw *slist, sl_node *node, bool no_dup) { - int i, cmp, error_code, cur_layer; - size_t layer, top_layer, locked_layer, sl_top_layer; - bool bool_true = true; - sl_node *exp, *cur_node, *next_node, *next_node_again, - *prev[SKIPLIST_MAX_LAYER], *next[SKIPLIST_MAX_LAYER]; + int i, cmp, error_code, cur_layer; + size_t layer, top_layer, locked_layer, sl_top_layer; + bool bool_true = true; + sl_node *exp, *cur_node, *next_node, *next_node_again, + *prev[SKIPLIST_MAX_LAYER], *next[SKIPLIST_MAX_LAYER]; - __SLD_(uint64_t tid; pthread_threadid_np(NULL, &tid); - size_t tid_hash = (size_t)tid % 256; (void)tid_hash;); + __SLD_(uint64_t tid; pthread_threadid_np(NULL, &tid); + size_t tid_hash = (size_t)tid % 256; (void)tid_hash;); - top_layer = __sl_decide_top_layer(slist); + top_layer = __sl_decide_top_layer(slist); - /* init node before insertion */ - __sl_node_init(node, top_layer); - __sl_write_lock_an(node); + /* init node before insertion */ + __sl_node_init(node, top_layer); + __sl_write_lock_an(node); - __SLD_P("%02x ins %p begin\n", (int)tid_hash, node); + __SLD_P("%02x ins %p begin\n", (int)tid_hash, node); insert_retry:; - cur_node = &slist->head; - ATM_FETCH_ADD(cur_node->ref_count, 1); + cur_node = &slist->head; + ATM_FETCH_ADD(cur_node->ref_count, 1); - __SLD_(size_t nh = 0); - __SLD_(static __thread sl_node * history[1024]; (void)history); + __SLD_(size_t nh = 0); + __SLD_(static __thread sl_node * history[1024]; (void)history); - sl_top_layer = slist->top_layer; - if (top_layer > sl_top_layer) - sl_top_layer = top_layer; - for (cur_layer = sl_top_layer; cur_layer >= 0; --cur_layer) { - do { - __SLD_(history[nh++] = cur_node); + sl_top_layer = slist->top_layer; + if (top_layer > sl_top_layer) + sl_top_layer = top_layer; + for (cur_layer = sl_top_layer; cur_layer >= 0; --cur_layer) { + do { + __SLD_(history[nh++] = cur_node); - next_node = __sl_next(cur_node, cur_layer); - if (!next_node) { - __sl_clr_flags(prev, cur_layer + 1, top_layer); - ATM_FETCH_SUB(cur_node->ref_count, 1); - YIELD(); - goto insert_retry; - } - cmp = __sl_cmp(slist, node, next_node); - if (cmp > 0) { - /* cur_node < next_node < node - => move to next node */ - sl_node *temp = cur_node; - cur_node = next_node; - ATM_FETCH_SUB(temp->ref_count, 1); - continue; - } else { - /* otherwise: cur_node < node <= next_node */ - ATM_FETCH_SUB(next_node->ref_count, 1); - } + next_node = __sl_next(cur_node, cur_layer); + if (!next_node) { + __sl_clr_flags(prev, cur_layer + 1, top_layer); + ATM_FETCH_SUB(cur_node->ref_count, 1); + YIELD(); + goto insert_retry; + } + cmp = __sl_cmp(slist, node, next_node); + if (cmp > 0) { + /* cur_node < next_node < node + => move to next node */ + sl_node *temp = cur_node; + cur_node = next_node; + ATM_FETCH_SUB(temp->ref_count, 1); + continue; + } else { + /* otherwise: cur_node < node <= next_node */ + ATM_FETCH_SUB(next_node->ref_count, 1); + } - if (no_dup && (cmp == 0)) { - /* duplicate key is not allowed */ - __sl_clr_flags(prev, cur_layer + 1, top_layer); - ATM_FETCH_SUB(cur_node->ref_count, 1); - return -1; - } + if (no_dup && (cmp == 0)) { + /* duplicate key is not allowed */ + __sl_clr_flags(prev, cur_layer + 1, top_layer); + ATM_FETCH_SUB(cur_node->ref_count, 1); + return -1; + } - if (cur_layer <= top_layer) { - prev[cur_layer] = cur_node; - next[cur_layer] = next_node; + if (cur_layer <= top_layer) { + prev[cur_layer] = cur_node; + next[cur_layer] = next_node; - /* Both 'prev' and 'next' should be fully linked - before insertion, and no other thread should - not modify 'prev' at the same time. */ - error_code = 0; - locked_layer = cur_layer + 1; + /* Both 'prev' and 'next' should be fully linked + before insertion, and no other thread should + not modify 'prev' at the same time. */ + error_code = 0; + locked_layer = cur_layer + 1; - /* check if prev node is duplicated with upper - * layer */ - if (cur_layer < top_layer && - prev[cur_layer] == prev[cur_layer + 1]) { - /* duplicate - => which means that - 'being_modified' flag is already true - => do nothing */ - } else { - bool expected = false; - if (ATM_CAS(prev[cur_layer]->being_modified, expected, - bool_true)) { - locked_layer = cur_layer; - } else { - error_code = -1; - } - } + /* check if prev node is duplicated with upper + * layer */ + if (cur_layer < top_layer && prev[cur_layer] == prev[cur_layer + 1]) { + /* duplicate + => which means that + 'being_modified' flag is already true + => do nothing */ + } else { + bool expected = false; + if (ATM_CAS(prev[cur_layer]->being_modified, expected, bool_true)) { + locked_layer = cur_layer; + } else { + error_code = -1; + } + } - if (error_code == 0 && - !__sl_valid_prev_next(prev[cur_layer], next[cur_layer])) { - error_code = -2; - } + if (error_code == 0 && + !__sl_valid_prev_next(prev[cur_layer], next[cur_layer])) { + error_code = -2; + } - if (error_code != 0) { - __SLD_RT_INS(error_code, node, top_layer, cur_layer); - __sl_clr_flags(prev, locked_layer, top_layer); - ATM_FETCH_SUB(cur_node->ref_count, 1); - YIELD(); - goto insert_retry; - } + if (error_code != 0) { + __SLD_RT_INS(error_code, node, top_layer, cur_layer); + __sl_clr_flags(prev, locked_layer, top_layer); + ATM_FETCH_SUB(cur_node->ref_count, 1); + YIELD(); + goto insert_retry; + } - /* set current node's pointers */ - ATM_STORE(node->next[cur_layer], next[cur_layer]); + /* set current node's pointers */ + ATM_STORE(node->next[cur_layer], next[cur_layer]); - /* check if `cur_node->next` has been changed - * from `next_node` */ - next_node_again = __sl_next(cur_node, cur_layer); - ATM_FETCH_SUB(next_node_again->ref_count, 1); - if (next_node_again != next_node) { - __SLD_NC_INS(cur_node, next_node, top_layer, cur_layer); - /* clear including the current layer as - we already set modification flag - above */ - __sl_clr_flags(prev, cur_layer, top_layer); - ATM_FETCH_SUB(cur_node->ref_count, 1); - YIELD(); - goto insert_retry; - } - } + /* check if `cur_node->next` has been changed + * from `next_node` */ + next_node_again = __sl_next(cur_node, cur_layer); + ATM_FETCH_SUB(next_node_again->ref_count, 1); + if (next_node_again != next_node) { + __SLD_NC_INS(cur_node, next_node, top_layer, cur_layer); + /* clear including the current layer as + we already set modification flag + above */ + __sl_clr_flags(prev, cur_layer, top_layer); + ATM_FETCH_SUB(cur_node->ref_count, 1); + YIELD(); + goto insert_retry; + } + } - /* non-bottom layer => go down */ - if (cur_layer) - break; + /* non-bottom layer => go down */ + if (cur_layer) + break; - /* bottom layer => insertion succeeded - change prev/next nodes' prev/next pointers from 0 ~ - top_layer */ - for (layer = 0; layer <= top_layer; ++layer) { - // `accessing_next` works as a spin-lock. - __sl_write_lock_an(prev[layer]); - exp = next[layer]; - if (!ATM_CAS(prev[layer]->next[layer], exp, node)) { - __SLD_P("%02x ASSERT ins %p[%d] -> %p (expected %p)\n", - (int)tid_hash, prev[layer], cur_layer, - ATM_GET(prev[layer]->next[layer]), next[layer]); - assert(0); - } - __SLD_P("%02x ins %p[%d] -> %p -> %p\n", (int)tid_hash, - prev[layer], layer, node, ATM_GET(node->next[layer])); - __sl_write_unlock_an(prev[layer]); - } + /* bottom layer => insertion succeeded + change prev/next nodes' prev/next pointers from 0 ~ + top_layer */ + for (layer = 0; layer <= top_layer; ++layer) { + // `accessing_next` works as a spin-lock. + __sl_write_lock_an(prev[layer]); + exp = next[layer]; + if (!ATM_CAS(prev[layer]->next[layer], exp, node)) { + __SLD_P("%02x ASSERT ins %p[%d] -> %p (expected %p)\n", (int)tid_hash, + prev[layer], cur_layer, ATM_GET(prev[layer]->next[layer]), + next[layer]); + assert(0); + } + __SLD_P("%02x ins %p[%d] -> %p -> %p\n", (int)tid_hash, prev[layer], + layer, node, ATM_GET(node->next[layer])); + __sl_write_unlock_an(prev[layer]); + } - /* now this node is fully linked */ - ATM_STORE(node->is_fully_linked, bool_true); + /* now this node is fully linked */ + ATM_STORE(node->is_fully_linked, bool_true); - /* allow removing next nodes */ - __sl_write_unlock_an(node); + /* allow removing next nodes */ + __sl_write_unlock_an(node); - __SLD_P("%02x ins %p done\n", (int)tid_hash, node); + __SLD_P("%02x ins %p done\n", (int)tid_hash, node); - ATM_FETCH_ADD(slist->num_entries, 1); - ATM_FETCH_ADD(slist->layer_entries[node->top_layer], 1); - for (i = slist->max_layer - 1; i >= 0; --i) { - if (slist->layer_entries[i] > 0) { - slist->top_layer = i; - break; - } - } + ATM_FETCH_ADD(slist->num_entries, 1); + ATM_FETCH_ADD(slist->layer_entries[node->top_layer], 1); + for (i = slist->max_layer - 1; i >= 0; --i) { + if (slist->layer_entries[i] > 0) { + slist->top_layer = i; + break; + } + } - /* modification is done for all layers */ - __sl_clr_flags(prev, 0, top_layer); - ATM_FETCH_SUB(cur_node->ref_count, 1); + /* modification is done for all layers */ + __sl_clr_flags(prev, 0, top_layer); + ATM_FETCH_SUB(cur_node->ref_count, 1); - return 0; - } while (cur_node != &slist->tail); - } - return 0; + return 0; + } while (cur_node != &slist->tail); + } + return 0; } /* @@ -692,7 +690,7 @@ insert_retry:; int sl_insert(sl_raw *slist, sl_node *node) { - return __sl_insert(slist, node, false); + return __sl_insert(slist, node, false); } /* @@ -704,15 +702,15 @@ sl_insert(sl_raw *slist, sl_node *node) int sl_insert_nodup(sl_raw *slist, sl_node *node) { - return __sl_insert(slist, node, true); + return __sl_insert(slist, node, true); } typedef enum { - SM = -2, /* < less than (smaller) */ - SMEQ = -1, /* <= less than or equal */ - EQ = 0, /* == equal */ - GTEQ = 1, /* >= greater than or equal */ - GT = 2 /* > greater than */ + SM = -2, /* < less than (smaller) */ + SMEQ = -1, /* <= less than or equal */ + EQ = 0, /* == equal */ + GTEQ = 1, /* >= greater than or equal */ + GT = 2 /* > greater than */ } __sl_find_mode; /* @@ -727,67 +725,67 @@ typedef enum { static sl_node * _sl_find(sl_raw *slist, sl_node *query, __sl_find_mode mode) { - int cmp, cur_layer; - sl_node *cur_node, *next_node, *temp; - uint8_t sl_top_layer; + int cmp, cur_layer; + sl_node *cur_node, *next_node, *temp; + uint8_t sl_top_layer; find_retry:; - cur_node = &slist->head; - ATM_FETCH_ADD(cur_node->ref_count, 1); + cur_node = &slist->head; + ATM_FETCH_ADD(cur_node->ref_count, 1); - __SLD_(size_t nh = 0); - __SLD_(static __thread sl_node * history[1024]; (void)history); + __SLD_(size_t nh = 0); + __SLD_(static __thread sl_node * history[1024]; (void)history); - sl_top_layer = slist->top_layer; - for (cur_layer = sl_top_layer; cur_layer >= 0; --cur_layer) { - do { - __SLD_(history[nh++] = cur_node); + sl_top_layer = slist->top_layer; + for (cur_layer = sl_top_layer; cur_layer >= 0; --cur_layer) { + do { + __SLD_(history[nh++] = cur_node); - next_node = __sl_next(cur_node, cur_layer); - if (!next_node) { - ATM_FETCH_SUB(cur_node->ref_count, 1); - YIELD(); - goto find_retry; - } - cmp = __sl_cmp(slist, query, next_node); - if (cmp > 0) { - /* cur_node < next_node < query - => move to next node */ - temp = cur_node; - cur_node = next_node; - ATM_FETCH_SUB(temp->ref_count, 1); - continue; - } else if (-1 <= mode && mode <= 1 && cmp == 0) { - /* cur_node < query == next_node ... return */ - ATM_FETCH_SUB(cur_node->ref_count, 1); - return next_node; - } + next_node = __sl_next(cur_node, cur_layer); + if (!next_node) { + ATM_FETCH_SUB(cur_node->ref_count, 1); + YIELD(); + goto find_retry; + } + cmp = __sl_cmp(slist, query, next_node); + if (cmp > 0) { + /* cur_node < next_node < query + => move to next node */ + temp = cur_node; + cur_node = next_node; + ATM_FETCH_SUB(temp->ref_count, 1); + continue; + } else if (-1 <= mode && mode <= 1 && cmp == 0) { + /* cur_node < query == next_node ... return */ + ATM_FETCH_SUB(cur_node->ref_count, 1); + return next_node; + } - /* otherwise: cur_node < query < next_node */ - if (cur_layer) { - /* non-bottom layer => go down */ - ATM_FETCH_SUB(next_node->ref_count, 1); - break; - } + /* otherwise: cur_node < query < next_node */ + if (cur_layer) { + /* non-bottom layer => go down */ + ATM_FETCH_SUB(next_node->ref_count, 1); + break; + } - /* bottom layer */ - if (mode < 0 && cur_node != &slist->head) { - /* smaller mode */ - ATM_FETCH_SUB(next_node->ref_count, 1); - return cur_node; - } else if (mode > 0 && next_node != &slist->tail) { - /* greater mode */ - ATM_FETCH_SUB(cur_node->ref_count, 1); - return next_node; - } - /* otherwise: exact match mode OR not found */ - ATM_FETCH_SUB(cur_node->ref_count, 1); - ATM_FETCH_SUB(next_node->ref_count, 1); - return NULL; - } while (cur_node != &slist->tail); - } + /* bottom layer */ + if (mode < 0 && cur_node != &slist->head) { + /* smaller mode */ + ATM_FETCH_SUB(next_node->ref_count, 1); + return cur_node; + } else if (mode > 0 && next_node != &slist->tail) { + /* greater mode */ + ATM_FETCH_SUB(cur_node->ref_count, 1); + return next_node; + } + /* otherwise: exact match mode OR not found */ + ATM_FETCH_SUB(cur_node->ref_count, 1); + ATM_FETCH_SUB(next_node->ref_count, 1); + return NULL; + } while (cur_node != &slist->tail); + } - return NULL; + return NULL; } /* @@ -800,7 +798,7 @@ find_retry:; sl_node * sl_find(sl_raw *slist, sl_node *query) { - return _sl_find(slist, query, EQ); + return _sl_find(slist, query, EQ); } /* @@ -814,7 +812,7 @@ sl_find(sl_raw *slist, sl_node *query) sl_node * sl_find_smaller_or_equal(sl_raw *slist, sl_node *query) { - return _sl_find(slist, query, SMEQ); + return _sl_find(slist, query, SMEQ); } /* @@ -829,342 +827,338 @@ sl_find_smaller_or_equal(sl_raw *slist, sl_node *query) sl_node * sl_find_greater_or_equal(sl_raw *slist, sl_node *query) { - return _sl_find(slist, query, GTEQ); + return _sl_find(slist, query, GTEQ); } int sl_erase_node_passive(sl_raw *slist, sl_node *node) { - int i, error_code, cmp, cur_layer, top_layer, locked_layer; - bool bool_true = true, bool_false = false; - bool removed = false, is_fully_linked = false, expected = false; - bool found_node_to_erase = false; - bool node_found; - sl_node *prev[SKIPLIST_MAX_LAYER], *next[SKIPLIST_MAX_LAYER]; - sl_node *cur_node, *next_node, *next_node_again, *exp; + int i, error_code, cmp, cur_layer, top_layer, locked_layer; + bool bool_true = true, bool_false = false; + bool removed = false, is_fully_linked = false, expected = false; + bool found_node_to_erase = false; + bool node_found; + sl_node *prev[SKIPLIST_MAX_LAYER], *next[SKIPLIST_MAX_LAYER]; + sl_node *cur_node, *next_node, *next_node_again, *exp; - __SLD_(uint64_t tid; pthread_threadid_np(NULL, &tid); - size_t tid_hash = (size_t)tid % 256; (void)tid_hash;); + __SLD_(uint64_t tid; pthread_threadid_np(NULL, &tid); + size_t tid_hash = (size_t)tid % 256; (void)tid_hash;); - top_layer = node->top_layer; + top_layer = node->top_layer; - ATM_LOAD(node->removed, removed); - if (removed) - return -1; /* already removed */ + ATM_LOAD(node->removed, removed); + if (removed) + return -1; /* already removed */ - if (!ATM_CAS(node->being_modified, expected, bool_true)) { - /* already being modified ... cannot work on this node for now */ - __SLD_BM(node); - return -2; - } + if (!ATM_CAS(node->being_modified, expected, bool_true)) { + /* already being modified ... cannot work on this node for now */ + __SLD_BM(node); + return -2; + } - /* set removed flag first, so that reader cannot read this node */ - ATM_STORE(node->removed, bool_true); + /* set removed flag first, so that reader cannot read this node */ + ATM_STORE(node->removed, bool_true); - __SLD_P("%02x rmv %p begin\n", (int)tid_hash, node); + __SLD_P("%02x rmv %p begin\n", (int)tid_hash, node); erase_node_retry: - ATM_LOAD(node->is_fully_linked, is_fully_linked); - if (!is_fully_linked) { - /* already unlinked ... remove is done by other thread */ - ATM_STORE(node->removed, bool_false); - ATM_STORE(node->being_modified, bool_false); - return -3; - } - - found_node_to_erase = false; - (void)found_node_to_erase; - cur_node = &slist->head; - ATM_FETCH_ADD(cur_node->ref_count, 1); - - __SLD_(size_t nh = 0); - __SLD_(static __thread sl_node * history[1024]; (void)history); - - cur_layer = slist->top_layer; - for (; cur_layer >= 0; --cur_layer) { - do { - __SLD_(history[nh++] = cur_node); - - node_found = false; - next_node = __sl_fnd_next(cur_node, cur_layer, node, &node_found); - if (!next_node) { - __sl_clr_flags(prev, cur_layer + 1, top_layer); - ATM_FETCH_SUB(cur_node->ref_count, 1); - YIELD(); - goto erase_node_retry; - } - - /* unlike insert(), we should find exact position of `node` */ - cmp = __sl_cmp(slist, node, next_node); - if (cmp > 0 || (cur_layer <= top_layer && !node_found)) { - /* cur_node <= next_node < node - => move to next node */ - sl_node *temp = cur_node; - cur_node = next_node; - __SLD_(if (cmp > 0) { - int cmp2 = __sl_cmp(slist, cur_node, node); - if (cmp2 > 0) { - /* node < cur_node <= next_node: - not found. */ - __sl_clr_flags(prev, cur_layer + 1, top_layer); - ATM_FETCH_SUB(temp->ref_count, 1); - ATM_FETCH_SUB(next_node->ref_count, 1); - assert(0); - } - }); - ATM_FETCH_SUB(temp->ref_count, 1); - continue; - } else { - /* otherwise: cur_node <= node <= next_node */ - ATM_FETCH_SUB(next_node->ref_count, 1); - } - - if (cur_layer <= top_layer) { - prev[cur_layer] = cur_node; - /* 'next_node' and 'node' should not be - the same, as 'removed' flag is already set. */ - assert(next_node != node); - next[cur_layer] = next_node; - - /* check if prev node duplicates with upper layer */ - error_code = 0; - locked_layer = cur_layer + 1; - if (cur_layer < top_layer && - prev[cur_layer] == prev[cur_layer + 1]) { - /* duplicate with upper layer - => which means that 'being_modified' - flag is already true - => do nothing. */ - } else { - expected = false; - if (ATM_CAS(prev[cur_layer]->being_modified, expected, - bool_true)) { - locked_layer = cur_layer; - } else { - error_code = -1; - } - } - - if (error_code == 0 && - !__sl_valid_prev_next(prev[cur_layer], next[cur_layer])) { - error_code = -2; - } - - if (error_code != 0) { - __SLD_RT_RMV(error_code, node, top_layer, cur_layer); - __sl_clr_flags(prev, locked_layer, top_layer); - ATM_FETCH_SUB(cur_node->ref_count, 1); - YIELD(); - goto erase_node_retry; - } - - next_node_again = __sl_fnd_next(cur_node, cur_layer, node, - NULL); - ATM_FETCH_SUB(next_node_again->ref_count, 1); - if (next_node_again != next[cur_layer]) { - /* `next` pointer has been changed, retry */ - __SLD_NC_RMV(cur_node, next[cur_layer], top_layer, - cur_layer); - __sl_clr_flags(prev, cur_layer, top_layer); - ATM_FETCH_SUB(cur_node->ref_count, 1); - YIELD(); - goto erase_node_retry; - } - } - if (cur_layer == 0) - found_node_to_erase = true; - /* go down */ - break; - } while (cur_node != &slist->tail); - } - /* not exist in the skiplist, should not happen */ - assert(found_node_to_erase); - /* bottom layer => removal succeeded, mark this node unlinked */ - __sl_write_lock_an(node); - { - ATM_STORE(node->is_fully_linked, bool_false); - } - __sl_write_unlock_an(node); - - /* change prev nodes' next pointer from 0 ~ top_layer */ - for (cur_layer = 0; cur_layer <= top_layer; ++cur_layer) { - __sl_write_lock_an(prev[cur_layer]); - exp = node; - assert(exp != next[cur_layer]); - assert(next[cur_layer]->is_fully_linked); - if (!ATM_CAS(prev[cur_layer]->next[cur_layer], exp, next[cur_layer])) { - __SLD_P("%02x ASSERT rmv %p[%d] -> %p (node %p)\n", (int)tid_hash, - prev[cur_layer], cur_layer, - ATM_GET(prev[cur_layer]->next[cur_layer]), node); - assert(0); - } - assert(next[cur_layer]->top_layer >= cur_layer); - __SLD_P("%02x rmv %p[%d] -> %p (node %p)\n", (int)tid_hash, - prev[cur_layer], cur_layer, next[cur_layer], node); - __sl_write_unlock_an(prev[cur_layer]); - } - - __SLD_P("%02x rmv %p done\n", (int)tid_hash, node); - - ATM_FETCH_SUB(slist->num_entries, 1); - ATM_FETCH_SUB(slist->layer_entries[node->top_layer], 1); - for (i = slist->max_layer - 1; i >= 0; --i) { - if (slist->layer_entries[i] > 0) { - slist->top_layer = i; - break; - } - } - - /* modification is done for all layers */ - __sl_clr_flags(prev, 0, top_layer); - ATM_FETCH_SUB(cur_node->ref_count, 1); - + ATM_LOAD(node->is_fully_linked, is_fully_linked); + if (!is_fully_linked) { + /* already unlinked ... remove is done by other thread */ + ATM_STORE(node->removed, bool_false); ATM_STORE(node->being_modified, bool_false); + return -3; + } - return 0; + found_node_to_erase = false; + (void)found_node_to_erase; + cur_node = &slist->head; + ATM_FETCH_ADD(cur_node->ref_count, 1); + + __SLD_(size_t nh = 0); + __SLD_(static __thread sl_node * history[1024]; (void)history); + + cur_layer = slist->top_layer; + for (; cur_layer >= 0; --cur_layer) { + do { + __SLD_(history[nh++] = cur_node); + + node_found = false; + next_node = __sl_fnd_next(cur_node, cur_layer, node, &node_found); + if (!next_node) { + __sl_clr_flags(prev, cur_layer + 1, top_layer); + ATM_FETCH_SUB(cur_node->ref_count, 1); + YIELD(); + goto erase_node_retry; + } + + /* unlike insert(), we should find exact position of `node` */ + cmp = __sl_cmp(slist, node, next_node); + if (cmp > 0 || (cur_layer <= top_layer && !node_found)) { + /* cur_node <= next_node < node + => move to next node */ + sl_node *temp = cur_node; + cur_node = next_node; + __SLD_(if (cmp > 0) { + int cmp2 = __sl_cmp(slist, cur_node, node); + if (cmp2 > 0) { + /* node < cur_node <= next_node: + not found. */ + __sl_clr_flags(prev, cur_layer + 1, top_layer); + ATM_FETCH_SUB(temp->ref_count, 1); + ATM_FETCH_SUB(next_node->ref_count, 1); + assert(0); + } + }); + ATM_FETCH_SUB(temp->ref_count, 1); + continue; + } else { + /* otherwise: cur_node <= node <= next_node */ + ATM_FETCH_SUB(next_node->ref_count, 1); + } + + if (cur_layer <= top_layer) { + prev[cur_layer] = cur_node; + /* 'next_node' and 'node' should not be + the same, as 'removed' flag is already set. */ + assert(next_node != node); + next[cur_layer] = next_node; + + /* check if prev node duplicates with upper layer */ + error_code = 0; + locked_layer = cur_layer + 1; + if (cur_layer < top_layer && prev[cur_layer] == prev[cur_layer + 1]) { + /* duplicate with upper layer + => which means that 'being_modified' + flag is already true + => do nothing. */ + } else { + expected = false; + if (ATM_CAS(prev[cur_layer]->being_modified, expected, bool_true)) { + locked_layer = cur_layer; + } else { + error_code = -1; + } + } + + if (error_code == 0 && + !__sl_valid_prev_next(prev[cur_layer], next[cur_layer])) { + error_code = -2; + } + + if (error_code != 0) { + __SLD_RT_RMV(error_code, node, top_layer, cur_layer); + __sl_clr_flags(prev, locked_layer, top_layer); + ATM_FETCH_SUB(cur_node->ref_count, 1); + YIELD(); + goto erase_node_retry; + } + + next_node_again = __sl_fnd_next(cur_node, cur_layer, node, NULL); + ATM_FETCH_SUB(next_node_again->ref_count, 1); + if (next_node_again != next[cur_layer]) { + /* `next` pointer has been changed, retry */ + __SLD_NC_RMV(cur_node, next[cur_layer], top_layer, cur_layer); + __sl_clr_flags(prev, cur_layer, top_layer); + ATM_FETCH_SUB(cur_node->ref_count, 1); + YIELD(); + goto erase_node_retry; + } + } + if (cur_layer == 0) + found_node_to_erase = true; + /* go down */ + break; + } while (cur_node != &slist->tail); + } + /* not exist in the skiplist, should not happen */ + assert(found_node_to_erase); + /* bottom layer => removal succeeded, mark this node unlinked */ + __sl_write_lock_an(node); + { + ATM_STORE(node->is_fully_linked, bool_false); + } + __sl_write_unlock_an(node); + + /* change prev nodes' next pointer from 0 ~ top_layer */ + for (cur_layer = 0; cur_layer <= top_layer; ++cur_layer) { + __sl_write_lock_an(prev[cur_layer]); + exp = node; + assert(exp != next[cur_layer]); + assert(next[cur_layer]->is_fully_linked); + if (!ATM_CAS(prev[cur_layer]->next[cur_layer], exp, next[cur_layer])) { + __SLD_P("%02x ASSERT rmv %p[%d] -> %p (node %p)\n", (int)tid_hash, + prev[cur_layer], cur_layer, ATM_GET(prev[cur_layer]->next[cur_layer]), + node); + assert(0); + } + assert(next[cur_layer]->top_layer >= cur_layer); + __SLD_P("%02x rmv %p[%d] -> %p (node %p)\n", (int)tid_hash, prev[cur_layer], + cur_layer, next[cur_layer], node); + __sl_write_unlock_an(prev[cur_layer]); + } + + __SLD_P("%02x rmv %p done\n", (int)tid_hash, node); + + ATM_FETCH_SUB(slist->num_entries, 1); + ATM_FETCH_SUB(slist->layer_entries[node->top_layer], 1); + for (i = slist->max_layer - 1; i >= 0; --i) { + if (slist->layer_entries[i] > 0) { + slist->top_layer = i; + break; + } + } + + /* modification is done for all layers */ + __sl_clr_flags(prev, 0, top_layer); + ATM_FETCH_SUB(cur_node->ref_count, 1); + + ATM_STORE(node->being_modified, bool_false); + + return 0; } int sl_erase_node(sl_raw *slist, sl_node *node) { - int ret; - do { - ret = sl_erase_node_passive(slist, node); - /* if ret == -2, other thread is accessing the same node - at the same time, so try again */ - } while (ret == -2); - return ret; + int ret; + do { + ret = sl_erase_node_passive(slist, node); + /* if ret == -2, other thread is accessing the same node + at the same time, so try again */ + } while (ret == -2); + return ret; } int sl_erase(sl_raw *slist, sl_node *query) { - int ret; - sl_node *found = sl_find(slist, query); + int ret; + sl_node *found = sl_find(slist, query); - if (!found) - return -4; /* key not found */ + if (!found) + return -4; /* key not found */ - /* if ret == -2, other thread is accessing the same node - at the same time, so try again */ - do - ret = sl_erase_node_passive(slist, found); - while (ret == -2); + /* if ret == -2, other thread is accessing the same node + at the same time, so try again */ + do + ret = sl_erase_node_passive(slist, found); + while (ret == -2); - ATM_FETCH_SUB(found->ref_count, 1); - return ret; + ATM_FETCH_SUB(found->ref_count, 1); + return ret; } int sl_is_valid_node(sl_node *node) { - return __sl_valid_node(node); + return __sl_valid_node(node); } int sl_is_safe_to_free(sl_node *node) { - uint16_t ref_count; + uint16_t ref_count; - if (node->accessing_next) - return 0; - if (node->being_modified) - return 0; - if (!node->removed) - return 0; + if (node->accessing_next) + return 0; + if (node->being_modified) + return 0; + if (!node->removed) + return 0; - ref_count = 0; - ATM_LOAD(node->ref_count, ref_count); - if (ref_count) - return 0; - return 1; + ref_count = 0; + ATM_LOAD(node->ref_count, ref_count); + if (ref_count) + return 0; + return 1; } void sl_wait_for_free(sl_node *node) { - while (!sl_is_safe_to_free(node)) { - YIELD(); - } + while (!sl_is_safe_to_free(node)) { + YIELD(); + } } void sl_grab_node(sl_node *node) { - ATM_FETCH_ADD(node->ref_count, 1); + ATM_FETCH_ADD(node->ref_count, 1); } void sl_release_node(sl_node *node) { - assert(node->ref_count); - ATM_FETCH_SUB(node->ref_count, 1); + assert(node->ref_count); + ATM_FETCH_SUB(node->ref_count, 1); } sl_node * sl_next(sl_raw *slist, sl_node *node) { - /* - * << Issue >> - * If `node` is already removed and its next node is also removed - * and then released, the link update will not be applied to `node` - * as it is already unreachable from skiplist. `node` still points to - * the released node so that `__sl_next(node)` may return corrupted - * memory region. - * - * 0) initial: - * A -> B -> C -> D - * - * 1) B is `node`, which is removed but not yet released: - * B --+-> C -> D - * | - * A --+ - * - * 2) remove C, and then release: - * B -> !C! +-> D - * | - * A --------+ - * - * 3) sl_next(B): - * will fetch C, which is already released so that - * may contain garbage data. - * - * In this case, start over from the top layer, - * to find valid link (same as in prev()). - */ + /* + * << Issue >> + * If `node` is already removed and its next node is also removed + * and then released, the link update will not be applied to `node` + * as it is already unreachable from skiplist. `node` still points to + * the released node so that `__sl_next(node)` may return corrupted + * memory region. + * + * 0) initial: + * A -> B -> C -> D + * + * 1) B is `node`, which is removed but not yet released: + * B --+-> C -> D + * | + * A --+ + * + * 2) remove C, and then release: + * B -> !C! +-> D + * | + * A --------+ + * + * 3) sl_next(B): + * will fetch C, which is already released so that + * may contain garbage data. + * + * In this case, start over from the top layer, + * to find valid link (same as in prev()). + */ - sl_node *next = __sl_next(node, 0); - if (!next) - next = _sl_find(slist, node, GT); + sl_node *next = __sl_next(node, 0); + if (!next) + next = _sl_find(slist, node, GT); - if (next == &slist->tail) - return NULL; - return next; + if (next == &slist->tail) + return NULL; + return next; } sl_node * sl_prev(sl_raw *slist, sl_node *node) { - sl_node *prev = _sl_find(slist, node, SM); - if (prev == &slist->head) - return NULL; - return prev; + sl_node *prev = _sl_find(slist, node, SM); + if (prev == &slist->head) + return NULL; + return prev; } sl_node * sl_begin(sl_raw *slist) { - sl_node *next = NULL; - while (!next) { - next = __sl_next(&slist->head, 0); - } - if (next == &slist->tail) - return NULL; - return next; + sl_node *next = NULL; + while (!next) { + next = __sl_next(&slist->head, 0); + } + if (next == &slist->tail) + return NULL; + return next; } sl_node * sl_end(sl_raw *slist) { - return sl_prev(slist, &slist->tail); + return sl_prev(slist, &slist->tail); } diff --git a/tests/api.c b/tests/api.c index 9a2e76c..ae83ac1 100644 --- a/tests/api.c +++ b/tests/api.c @@ -1,196 +1,196 @@ static void * test_api_setup(const MunitParameter params[], void *user_data) { - struct test_info *info = (struct test_info *)user_data; - (void)info; - (void)params; + struct test_info *info = (struct test_info *)user_data; + (void)info; + (void)params; - ex_sl_t *slist = calloc(sizeof(ex_sl_t), 1); - if (slist == NULL) - return NULL; - sl_init(slist, uint32_key_cmp); - return (void *)(uintptr_t)slist; + ex_sl_t *slist = calloc(sizeof(ex_sl_t), 1); + if (slist == NULL) + return NULL; + sl_init(slist, uint32_key_cmp); + return (void *)(uintptr_t)slist; } static void test_api_tear_down(void *fixture) { - ex_sl_t *slist = (ex_sl_t *)fixture; - assert_ptr_not_null(slist); - sl_node *cursor = sl_begin(slist); - while (cursor) { - assert_ptr_not_null(cursor); - ex_node_t *entry = sl_get_entry(cursor, ex_node_t, snode); - assert_ptr_not_null(entry); - assert_uint32(entry->key, ==, entry->value); - cursor = sl_next(slist, cursor); - sl_erase_node(slist, &entry->snode); - sl_release_node(&entry->snode); - sl_wait_for_free(&entry->snode); - sl_free_node(&entry->snode); - free(entry); - } - sl_free(slist); - free(fixture); + ex_sl_t *slist = (ex_sl_t *)fixture; + assert_ptr_not_null(slist); + sl_node *cursor = sl_begin(slist); + while (cursor) { + assert_ptr_not_null(cursor); + ex_node_t *entry = sl_get_entry(cursor, ex_node_t, snode); + assert_ptr_not_null(entry); + assert_uint32(entry->key, ==, entry->value); + cursor = sl_next(slist, cursor); + sl_erase_node(slist, &entry->snode); + sl_release_node(&entry->snode); + sl_wait_for_free(&entry->snode); + sl_free_node(&entry->snode); + free(entry); + } + sl_free(slist); + free(fixture); } static void * test_api_insert_setup(const MunitParameter params[], void *user_data) { - return test_api_setup(params, user_data); + return test_api_setup(params, user_data); } static void test_api_insert_tear_down(void *fixture) { - test_api_tear_down(fixture); + test_api_tear_down(fixture); } static MunitResult test_api_insert(const MunitParameter params[], void *data) { - sl_raw *slist = (sl_raw *)data; - (void)params; - assert_ptr_not_null(data); - int n = munit_rand_int_range(128, 4096); - int key = munit_rand_int_range(0, (((uint32_t)0) - 1) / 10); - while (n--) { - ex_node_t *node = (ex_node_t *)calloc(sizeof(ex_node_t), 1); - sl_init_node(&node->snode); - node->key = key; - node->value = key; - sl_insert(slist, &node->snode); - } - return MUNIT_OK; + sl_raw *slist = (sl_raw *)data; + (void)params; + assert_ptr_not_null(data); + int n = munit_rand_int_range(128, 4096); + int key = munit_rand_int_range(0, (((uint32_t)0) - 1) / 10); + while (n--) { + ex_node_t *node = (ex_node_t *)calloc(sizeof(ex_node_t), 1); + sl_init_node(&node->snode); + node->key = key; + node->value = key; + sl_insert(slist, &node->snode); + } + return MUNIT_OK; } static void * test_api_remove_setup(const MunitParameter params[], void *user_data) { - return test_api_setup(params, user_data); + return test_api_setup(params, user_data); } static void test_api_remove_tear_down(void *fixture) { - test_api_tear_down(fixture); + test_api_tear_down(fixture); } static MunitResult test_api_remove(const MunitParameter params[], void *data) { - sl_raw *slist = (sl_raw *)data; - (void)params; - (void)slist; - return MUNIT_OK; + sl_raw *slist = (sl_raw *)data; + (void)params; + (void)slist; + return MUNIT_OK; } static void * test_api_find_setup(const MunitParameter params[], void *user_data) { - return test_api_setup(params, user_data); + return test_api_setup(params, user_data); } static void test_api_find_tear_down(void *fixture) { - test_api_tear_down(fixture); + test_api_tear_down(fixture); } static MunitResult test_api_find(const MunitParameter params[], void *data) { - sl_raw *slist = (sl_raw *)data; - (void)params; - (void)slist; - return MUNIT_OK; + sl_raw *slist = (sl_raw *)data; + (void)params; + (void)slist; + return MUNIT_OK; } static void * test_api_update_setup(const MunitParameter params[], void *user_data) { - return test_api_setup(params, user_data); + return test_api_setup(params, user_data); } static void test_api_update_tear_down(void *fixture) { - test_api_tear_down(fixture); + test_api_tear_down(fixture); } static MunitResult test_api_update(const MunitParameter params[], void *data) { - sl_raw *slist = (sl_raw *)data; - (void)params; - (void)slist; - return MUNIT_OK; + sl_raw *slist = (sl_raw *)data; + (void)params; + (void)slist; + return MUNIT_OK; } static void * test_api_delete_setup(const MunitParameter params[], void *user_data) { - return test_api_setup(params, user_data); + return test_api_setup(params, user_data); } static void test_api_delete_tear_down(void *fixture) { - test_api_tear_down(fixture); + test_api_tear_down(fixture); } static MunitResult test_api_delete(const MunitParameter params[], void *data) { - sl_raw *slist = (sl_raw *)data; - (void)params; - (void)slist; - return MUNIT_OK; + sl_raw *slist = (sl_raw *)data; + (void)params; + (void)slist; + return MUNIT_OK; } static void * test_api_duplicates_setup(const MunitParameter params[], void *user_data) { - return test_api_setup(params, user_data); + return test_api_setup(params, user_data); } static void test_api_duplicates_tear_down(void *fixture) { - test_api_tear_down(fixture); + test_api_tear_down(fixture); } static MunitResult test_api_duplicates(const MunitParameter params[], void *data) { - sl_raw *slist = (sl_raw *)data; - (void)params; - (void)slist; - return MUNIT_OK; + sl_raw *slist = (sl_raw *)data; + (void)params; + (void)slist; + return MUNIT_OK; } static void * test_api_size_setup(const MunitParameter params[], void *user_data) { - return test_api_setup(params, user_data); + return test_api_setup(params, user_data); } static void test_api_size_tear_down(void *fixture) { - test_api_tear_down(fixture); + test_api_tear_down(fixture); } static MunitResult test_api_size(const MunitParameter params[], void *data) { - sl_raw *slist = (sl_raw *)data; - (void)params; - (void)slist; - return MUNIT_OK; + sl_raw *slist = (sl_raw *)data; + (void)params; + (void)slist; + return MUNIT_OK; } static void * test_api_iterators_setup(const MunitParameter params[], void *user_data) { - return test_api_setup(params, user_data); + return test_api_setup(params, user_data); } static void test_api_iterators_tear_down(void *fixture) { - test_api_tear_down(fixture); + test_api_tear_down(fixture); } static MunitResult test_api_iterators(const MunitParameter params[], void *data) { - sl_raw *slist = (sl_raw *)data; - (void)params; - (void)slist; - return MUNIT_OK; + sl_raw *slist = (sl_raw *)data; + (void)params; + (void)slist; + return MUNIT_OK; } diff --git a/tests/munit.c b/tests/munit.c index d36c799..f42f8e9 100644 --- a/tests/munit.c +++ b/tests/munit.c @@ -122,10 +122,10 @@ #define MUNIT_XSTRINGIFY(x) MUNIT_STRINGIFY(x) #if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_CC) || \ - defined(__IBMCPP__) + defined(__IBMCPP__) #define MUNIT_THREAD_LOCAL __thread #elif (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201102L)) || \ - defined(_Thread_local) + defined(_Thread_local) #define MUNIT_THREAD_LOCAL _Thread_local #elif defined(_WIN32) #define MUNIT_THREAD_LOCAL __declspec(thread) @@ -170,87 +170,87 @@ static MUNIT_THREAD_LOCAL jmp_buf munit_error_jmp_buf; MUNIT_PRINTF(5, 0) static void munit_logf_exv(MunitLogLevel level, FILE *fp, const char *filename, int line, - const char *format, va_list ap) + const char *format, va_list ap) { - if (level < munit_log_level_visible) - return; + if (level < munit_log_level_visible) + return; - switch (level) { - case MUNIT_LOG_DEBUG: - fputs("Debug", fp); - break; - case MUNIT_LOG_INFO: - fputs("Info", fp); - break; - case MUNIT_LOG_WARNING: - fputs("Warning", fp); - break; - case MUNIT_LOG_ERROR: - fputs("Error", fp); - break; - default: - munit_logf_ex(MUNIT_LOG_ERROR, filename, line, "Invalid log level (%d)", - level); - return; - } + switch (level) { + case MUNIT_LOG_DEBUG: + fputs("Debug", fp); + break; + case MUNIT_LOG_INFO: + fputs("Info", fp); + break; + case MUNIT_LOG_WARNING: + fputs("Warning", fp); + break; + case MUNIT_LOG_ERROR: + fputs("Error", fp); + break; + default: + munit_logf_ex(MUNIT_LOG_ERROR, filename, line, "Invalid log level (%d)", + level); + return; + } - fputs(": ", fp); - if (filename != NULL) - fprintf(fp, "%s:%d: ", filename, line); - vfprintf(fp, format, ap); - fputc('\n', fp); + fputs(": ", fp); + if (filename != NULL) + fprintf(fp, "%s:%d: ", filename, line); + vfprintf(fp, format, ap); + fputc('\n', fp); } MUNIT_PRINTF(3, 4) static void munit_logf_internal(MunitLogLevel level, FILE *fp, const char *format, ...) { - va_list ap; + va_list ap; - va_start(ap, format); - munit_logf_exv(level, fp, NULL, 0, format, ap); - va_end(ap); + va_start(ap, format); + munit_logf_exv(level, fp, NULL, 0, format, ap); + va_end(ap); } static void munit_log_internal(MunitLogLevel level, FILE *fp, const char *message) { - munit_logf_internal(level, fp, "%s", message); + munit_logf_internal(level, fp, "%s", message); } void munit_logf_ex(MunitLogLevel level, const char *filename, int line, - const char *format, ...) + const char *format, ...) { - va_list ap; + va_list ap; - va_start(ap, format); - munit_logf_exv(level, stderr, filename, line, format, ap); - va_end(ap); + va_start(ap, format); + munit_logf_exv(level, stderr, filename, line, format, ap); + va_end(ap); - if (level >= munit_log_level_fatal) { + if (level >= munit_log_level_fatal) { #if defined(MUNIT_THREAD_LOCAL) - if (munit_error_jmp_buf_valid) - longjmp(munit_error_jmp_buf, 1); + if (munit_error_jmp_buf_valid) + longjmp(munit_error_jmp_buf, 1); #endif - abort(); - } + abort(); + } } void munit_errorf_ex(const char *filename, int line, const char *format, ...) { - va_list ap; + va_list ap; - va_start(ap, format); - munit_logf_exv(MUNIT_LOG_ERROR, stderr, filename, line, format, ap); - va_end(ap); + va_start(ap, format); + munit_logf_exv(MUNIT_LOG_ERROR, stderr, filename, line, format, ap); + va_end(ap); #if defined(MUNIT_THREAD_LOCAL) - if (munit_error_jmp_buf_valid) - longjmp(munit_error_jmp_buf, 1); + if (munit_error_jmp_buf_valid) + longjmp(munit_error_jmp_buf, 1); #endif - abort(); + abort(); } #if defined(__MINGW32__) || defined(__MINGW64__) @@ -265,19 +265,19 @@ static void munit_log_errno(MunitLogLevel level, FILE *fp, const char *msg) { #if defined(MUNIT_NO_STRERROR_R) || \ - (defined(__MINGW32__) && !defined(MINGW_HAS_SECURE_API)) - munit_logf_internal(level, fp, "%s: %s (%d)", msg, strerror(errno), errno); + (defined(__MINGW32__) && !defined(MINGW_HAS_SECURE_API)) + munit_logf_internal(level, fp, "%s: %s (%d)", msg, strerror(errno), errno); #else - char munit_error_str[MUNIT_STRERROR_LEN]; - munit_error_str[0] = '\0'; + char munit_error_str[MUNIT_STRERROR_LEN]; + munit_error_str[0] = '\0'; #if !defined(_WIN32) - strerror_r(errno, munit_error_str, MUNIT_STRERROR_LEN); + strerror_r(errno, munit_error_str, MUNIT_STRERROR_LEN); #else - strerror_s(munit_error_str, MUNIT_STRERROR_LEN, errno); + strerror_s(munit_error_str, MUNIT_STRERROR_LEN, errno); #endif - munit_logf_internal(level, fp, "%s: %s (%d)", msg, munit_error_str, errno); + munit_logf_internal(level, fp, "%s: %s (%d)", msg, munit_error_str, errno); #endif } @@ -286,18 +286,18 @@ munit_log_errno(MunitLogLevel level, FILE *fp, const char *msg) void * munit_malloc_ex(const char *filename, int line, size_t size) { - void *ptr; + void *ptr; - if (size == 0) - return NULL; + if (size == 0) + return NULL; - ptr = calloc(1, size); - if (MUNIT_UNLIKELY(ptr == NULL)) { - munit_logf_ex(MUNIT_LOG_ERROR, filename, line, - "Failed to allocate %" MUNIT_SIZE_MODIFIER "u bytes.", size); - } + ptr = calloc(1, size); + if (MUNIT_UNLIKELY(ptr == NULL)) { + munit_logf_ex(MUNIT_LOG_ERROR, filename, line, + "Failed to allocate %" MUNIT_SIZE_MODIFIER "u bytes.", size); + } - return ptr; + return ptr; } /*** Timer code ***/ @@ -340,24 +340,24 @@ munit_malloc_ex(const char *filename, int line, size_t size) #endif enum PsnipClockType { - /* This clock provides the current time, in units since 1970-01-01 - * 00:00:00 UTC not including leap seconds. In other words, UNIX - * time. Keep in mind that this clock doesn't account for leap - * seconds, and can go backwards (think NTP adjustments). */ - PSNIP_CLOCK_TYPE_WALL = 1, - /* The CPU time is a clock which increases only when the current - * process is active (i.e., it doesn't increment while blocking on - * I/O). */ - PSNIP_CLOCK_TYPE_CPU = 2, - /* Monotonic time is always running (unlike CPU time), but it only - ever moves forward unless you reboot the system. Things like NTP - adjustments have no effect on this clock. */ - PSNIP_CLOCK_TYPE_MONOTONIC = 3 + /* This clock provides the current time, in units since 1970-01-01 + * 00:00:00 UTC not including leap seconds. In other words, UNIX + * time. Keep in mind that this clock doesn't account for leap + * seconds, and can go backwards (think NTP adjustments). */ + PSNIP_CLOCK_TYPE_WALL = 1, + /* The CPU time is a clock which increases only when the current + * process is active (i.e., it doesn't increment while blocking on + * I/O). */ + PSNIP_CLOCK_TYPE_CPU = 2, + /* Monotonic time is always running (unlike CPU time), but it only + ever moves forward unless you reboot the system. Things like NTP + adjustments have no effect on this clock. */ + PSNIP_CLOCK_TYPE_MONOTONIC = 3 }; struct PsnipClockTimespec { - psnip_uint64_t seconds; - psnip_uint64_t nanoseconds; + psnip_uint64_t seconds; + psnip_uint64_t nanoseconds; }; /* Methods we support: */ @@ -398,9 +398,9 @@ struct PsnipClockTimespec { #if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0) /* These are known to work without librt. If you know of others * please let us know so we can add them. */ -#if (defined(__GLIBC__) && \ - (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 17))) || \ - (defined(__FreeBSD__)) +#if (defined(__GLIBC__) && \ + (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 17))) || \ + (defined(__FreeBSD__)) #define PSNIP_CLOCK_HAVE_CLOCK_GETTIME #elif !defined(PSNIP_CLOCK_NO_LIBRT) #define PSNIP_CLOCK_HAVE_CLOCK_GETTIME @@ -472,75 +472,73 @@ struct PsnipClockTimespec { /* Primarily here for testing. */ #if !defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ - defined(PSNIP_CLOCK_REQUIRE_MONOTONIC) + defined(PSNIP_CLOCK_REQUIRE_MONOTONIC) #error No monotonic clock found. #endif /* Implementations */ -#if (defined(PSNIP_CLOCK_CPU_METHOD) && \ - (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \ - (defined(PSNIP_CLOCK_WALL_METHOD) && \ - (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \ - (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ - (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \ - (defined(PSNIP_CLOCK_CPU_METHOD) && \ - (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK)) || \ - (defined(PSNIP_CLOCK_WALL_METHOD) && \ - (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK)) || \ - (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ - (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK)) || \ - (defined(PSNIP_CLOCK_CPU_METHOD) && \ - (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_TIME)) || \ - (defined(PSNIP_CLOCK_WALL_METHOD) && \ - (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_TIME)) || \ - (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ - (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_TIME)) +#if (defined(PSNIP_CLOCK_CPU_METHOD) && \ + (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \ + (defined(PSNIP_CLOCK_WALL_METHOD) && \ + (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \ + (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ + (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \ + (defined(PSNIP_CLOCK_CPU_METHOD) && \ + (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK)) || \ + (defined(PSNIP_CLOCK_WALL_METHOD) && \ + (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK)) || \ + (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ + (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK)) || \ + (defined(PSNIP_CLOCK_CPU_METHOD) && \ + (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_TIME)) || \ + (defined(PSNIP_CLOCK_WALL_METHOD) && \ + (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_TIME)) || \ + (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ + (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_TIME)) #include #endif -#if (defined(PSNIP_CLOCK_CPU_METHOD) && \ - (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY)) || \ - (defined(PSNIP_CLOCK_WALL_METHOD) && \ - (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY)) || \ - (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ - (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY)) +#if (defined(PSNIP_CLOCK_CPU_METHOD) && \ + (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY)) || \ + (defined(PSNIP_CLOCK_WALL_METHOD) && \ + (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY)) || \ + (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ + (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY)) #include #endif -#if (defined(PSNIP_CLOCK_CPU_METHOD) && \ - (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES)) || \ - (defined(PSNIP_CLOCK_WALL_METHOD) && \ - (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES)) || \ - (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ - (PSNIP_CLOCK_MONOTONIC_METHOD == \ - PSNIP_CLOCK_METHOD_GETPROCESSTIMES)) || \ - (defined(PSNIP_CLOCK_CPU_METHOD) && \ - (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64)) || \ - (defined(PSNIP_CLOCK_WALL_METHOD) && \ - (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64)) || \ - (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ - (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64)) +#if (defined(PSNIP_CLOCK_CPU_METHOD) && \ + (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES)) || \ + (defined(PSNIP_CLOCK_WALL_METHOD) && \ + (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES)) || \ + (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ + (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES)) || \ + (defined(PSNIP_CLOCK_CPU_METHOD) && \ + (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64)) || \ + (defined(PSNIP_CLOCK_WALL_METHOD) && \ + (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64)) || \ + (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ + (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64)) #include #endif -#if (defined(PSNIP_CLOCK_CPU_METHOD) && \ - (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE)) || \ - (defined(PSNIP_CLOCK_WALL_METHOD) && \ - (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE)) || \ - (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ - (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE)) +#if (defined(PSNIP_CLOCK_CPU_METHOD) && \ + (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE)) || \ + (defined(PSNIP_CLOCK_WALL_METHOD) && \ + (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE)) || \ + (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ + (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE)) #include #include #endif -#if (defined(PSNIP_CLOCK_CPU_METHOD) && \ - (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME)) || \ - (defined(PSNIP_CLOCK_WALL_METHOD) && \ - (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME)) || \ - (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ - (PSNIP_CLOCK_MONOTONIC_METHOD == \ - PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME)) +#if (defined(PSNIP_CLOCK_CPU_METHOD) && \ + (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME)) || \ + (defined(PSNIP_CLOCK_WALL_METHOD) && \ + (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME)) || \ + (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ + (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME)) #include #include #include @@ -550,37 +548,37 @@ struct PsnipClockTimespec { #define PSNIP_CLOCK_NSEC_PER_SEC ((psnip_uint32_t)(1000000000ULL)) -#if (defined(PSNIP_CLOCK_CPU_METHOD) && \ - (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \ - (defined(PSNIP_CLOCK_WALL_METHOD) && \ - (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \ - (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ - (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) +#if (defined(PSNIP_CLOCK_CPU_METHOD) && \ + (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \ + (defined(PSNIP_CLOCK_WALL_METHOD) && \ + (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \ + (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ + (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) PSNIP_CLOCK__FUNCTION psnip_uint32_t psnip_clock__clock_getres(clockid_t clk_id) { - struct timespec res; - int r; + struct timespec res; + int r; - r = clock_getres(clk_id, &res); - if (r != 0) - return 0; + r = clock_getres(clk_id, &res); + if (r != 0) + return 0; - return (psnip_uint32_t)(PSNIP_CLOCK_NSEC_PER_SEC / res.tv_nsec); + return (psnip_uint32_t)(PSNIP_CLOCK_NSEC_PER_SEC / res.tv_nsec); } PSNIP_CLOCK__FUNCTION int psnip_clock__clock_gettime(clockid_t clk_id, struct PsnipClockTimespec *res) { - struct timespec ts; + struct timespec ts; - if (clock_gettime(clk_id, &ts) != 0) - return -10; + if (clock_gettime(clk_id, &ts) != 0) + return -10; - res->seconds = (psnip_uint64_t)(ts.tv_sec); - res->nanoseconds = (psnip_uint64_t)(ts.tv_nsec); + res->seconds = (psnip_uint64_t)(ts.tv_sec); + res->nanoseconds = (psnip_uint64_t)(ts.tv_nsec); - return 0; + return 0; } #endif @@ -588,67 +586,67 @@ PSNIP_CLOCK__FUNCTION psnip_uint32_t psnip_clock_wall_get_precision(void) { #if !defined(PSNIP_CLOCK_WALL_METHOD) - return 0; + return 0; #elif defined(PSNIP_CLOCK_WALL_METHOD) && \ - PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME - return psnip_clock__clock_getres(PSNIP_CLOCK_CLOCK_GETTIME_WALL); + PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME + return psnip_clock__clock_getres(PSNIP_CLOCK_CLOCK_GETTIME_WALL); #elif defined(PSNIP_CLOCK_WALL_METHOD) && \ - PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY - return 1000000; + PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY + return 1000000; #elif defined(PSNIP_CLOCK_WALL_METHOD) && \ - PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_TIME - return 1; + PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_TIME + return 1; #else - return 0; + return 0; #endif } PSNIP_CLOCK__FUNCTION int psnip_clock_wall_get_time(struct PsnipClockTimespec *res) { - (void)res; + (void)res; #if !defined(PSNIP_CLOCK_WALL_METHOD) - return -2; + return -2; #elif defined(PSNIP_CLOCK_WALL_METHOD) && \ - PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME - return psnip_clock__clock_gettime(PSNIP_CLOCK_CLOCK_GETTIME_WALL, res); + PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME + return psnip_clock__clock_gettime(PSNIP_CLOCK_CLOCK_GETTIME_WALL, res); #elif defined(PSNIP_CLOCK_WALL_METHOD) && \ - PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_TIME - res->seconds = time(NULL); - res->nanoseconds = 0; + PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_TIME + res->seconds = time(NULL); + res->nanoseconds = 0; #elif defined(PSNIP_CLOCK_WALL_METHOD) && \ - PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY - struct timeval tv; + PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY + struct timeval tv; - if (gettimeofday(&tv, NULL) != 0) - return -6; + if (gettimeofday(&tv, NULL) != 0) + return -6; - res->seconds = tv.tv_sec; - res->nanoseconds = tv.tv_usec * 1000; + res->seconds = tv.tv_sec; + res->nanoseconds = tv.tv_usec * 1000; #else - return -2; + return -2; #endif - return 0; + return 0; } PSNIP_CLOCK__FUNCTION psnip_uint32_t psnip_clock_cpu_get_precision(void) { #if !defined(PSNIP_CLOCK_CPU_METHOD) - return 0; + return 0; #elif defined(PSNIP_CLOCK_CPU_METHOD) && \ - PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME - return psnip_clock__clock_getres(PSNIP_CLOCK_CLOCK_GETTIME_CPU); + PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME + return psnip_clock__clock_getres(PSNIP_CLOCK_CLOCK_GETTIME_CPU); #elif defined(PSNIP_CLOCK_CPU_METHOD) && \ - PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK - return CLOCKS_PER_SEC; + PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK + return CLOCKS_PER_SEC; #elif defined(PSNIP_CLOCK_CPU_METHOD) && \ - PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES - return PSNIP_CLOCK_NSEC_PER_SEC / 100; + PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES + return PSNIP_CLOCK_NSEC_PER_SEC / 100; #else - return 0; + return 0; #endif } @@ -656,80 +654,80 @@ PSNIP_CLOCK__FUNCTION int psnip_clock_cpu_get_time(struct PsnipClockTimespec *res) { #if !defined(PSNIP_CLOCK_CPU_METHOD) - (void)res; - return -2; + (void)res; + return -2; #elif defined(PSNIP_CLOCK_CPU_METHOD) && \ - PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME - return psnip_clock__clock_gettime(PSNIP_CLOCK_CLOCK_GETTIME_CPU, res); + PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME + return psnip_clock__clock_gettime(PSNIP_CLOCK_CLOCK_GETTIME_CPU, res); #elif defined(PSNIP_CLOCK_CPU_METHOD) && \ - PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK - clock_t t = clock(); - if (t == ((clock_t)-1)) - return -5; - res->seconds = t / CLOCKS_PER_SEC; - res->nanoseconds = (t % CLOCKS_PER_SEC) * - (PSNIP_CLOCK_NSEC_PER_SEC / CLOCKS_PER_SEC); + PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK + clock_t t = clock(); + if (t == ((clock_t)-1)) + return -5; + res->seconds = t / CLOCKS_PER_SEC; + res->nanoseconds = (t % CLOCKS_PER_SEC) * + (PSNIP_CLOCK_NSEC_PER_SEC / CLOCKS_PER_SEC); #elif defined(PSNIP_CLOCK_CPU_METHOD) && \ - PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES - FILETIME CreationTime, ExitTime, KernelTime, UserTime; - LARGE_INTEGER date, adjust; + PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES + FILETIME CreationTime, ExitTime, KernelTime, UserTime; + LARGE_INTEGER date, adjust; - if (!GetProcessTimes(GetCurrentProcess(), &CreationTime, &ExitTime, - &KernelTime, &UserTime)) - return -7; + if (!GetProcessTimes(GetCurrentProcess(), &CreationTime, &ExitTime, + &KernelTime, &UserTime)) + return -7; - /* http://www.frenk.com/2009/12/convert-filetime-to-unix-timestamp/ */ - date.HighPart = UserTime.dwHighDateTime; - date.LowPart = UserTime.dwLowDateTime; - adjust.QuadPart = 11644473600000 * 10000; - date.QuadPart -= adjust.QuadPart; + /* http://www.frenk.com/2009/12/convert-filetime-to-unix-timestamp/ */ + date.HighPart = UserTime.dwHighDateTime; + date.LowPart = UserTime.dwLowDateTime; + adjust.QuadPart = 11644473600000 * 10000; + date.QuadPart -= adjust.QuadPart; - res->seconds = date.QuadPart / 10000000; - res->nanoseconds = (date.QuadPart % 10000000) * - (PSNIP_CLOCK_NSEC_PER_SEC / 100); + res->seconds = date.QuadPart / 10000000; + res->nanoseconds = (date.QuadPart % 10000000) * + (PSNIP_CLOCK_NSEC_PER_SEC / 100); #elif PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE - struct rusage usage; - if (getrusage(RUSAGE_SELF, &usage) != 0) - return -8; + struct rusage usage; + if (getrusage(RUSAGE_SELF, &usage) != 0) + return -8; - res->seconds = usage.ru_utime.tv_sec; - res->nanoseconds = tv.tv_usec * 1000; + res->seconds = usage.ru_utime.tv_sec; + res->nanoseconds = tv.tv_usec * 1000; #else - (void)res; - return -2; + (void)res; + return -2; #endif - return 0; + return 0; } PSNIP_CLOCK__FUNCTION psnip_uint32_t psnip_clock_monotonic_get_precision(void) { #if !defined(PSNIP_CLOCK_MONOTONIC_METHOD) - return 0; + return 0; #elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ - PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME - return psnip_clock__clock_getres(PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC); + PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME + return psnip_clock__clock_getres(PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC); #elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ - PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME - static mach_timebase_info_data_t tbi = { - 0, - }; - if (tbi.denom == 0) - mach_timebase_info(&tbi); - return (psnip_uint32_t)(tbi.numer / tbi.denom); + PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME + static mach_timebase_info_data_t tbi = { + 0, + }; + if (tbi.denom == 0) + mach_timebase_info(&tbi); + return (psnip_uint32_t)(tbi.numer / tbi.denom); #elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ - PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64 - return 1000; + PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64 + return 1000; #elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ - PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER - LARGE_INTEGER Frequency; - QueryPerformanceFrequency(&Frequency); - return (psnip_uint32_t)((Frequency.QuadPart > PSNIP_CLOCK_NSEC_PER_SEC) ? - PSNIP_CLOCK_NSEC_PER_SEC : - Frequency.QuadPart); + PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER + LARGE_INTEGER Frequency; + QueryPerformanceFrequency(&Frequency); + return (psnip_uint32_t)((Frequency.QuadPart > PSNIP_CLOCK_NSEC_PER_SEC) ? + PSNIP_CLOCK_NSEC_PER_SEC : + Frequency.QuadPart); #else - return 0; + return 0; #endif } @@ -737,45 +735,45 @@ PSNIP_CLOCK__FUNCTION int psnip_clock_monotonic_get_time(struct PsnipClockTimespec *res) { #if !defined(PSNIP_CLOCK_MONOTONIC_METHOD) - (void)res; - return -2; + (void)res; + return -2; #elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ - PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME - return psnip_clock__clock_gettime(PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC, res); + PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME + return psnip_clock__clock_gettime(PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC, res); #elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ - PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME - psnip_uint64_t nsec = mach_absolute_time(); - static mach_timebase_info_data_t tbi = { - 0, - }; - if (tbi.denom == 0) - mach_timebase_info(&tbi); - nsec *= ((psnip_uint64_t)tbi.numer) / ((psnip_uint64_t)tbi.denom); - res->seconds = nsec / PSNIP_CLOCK_NSEC_PER_SEC; - res->nanoseconds = nsec % PSNIP_CLOCK_NSEC_PER_SEC; + PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME + psnip_uint64_t nsec = mach_absolute_time(); + static mach_timebase_info_data_t tbi = { + 0, + }; + if (tbi.denom == 0) + mach_timebase_info(&tbi); + nsec *= ((psnip_uint64_t)tbi.numer) / ((psnip_uint64_t)tbi.denom); + res->seconds = nsec / PSNIP_CLOCK_NSEC_PER_SEC; + res->nanoseconds = nsec % PSNIP_CLOCK_NSEC_PER_SEC; #elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ - PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER - LARGE_INTEGER t, f; - if (QueryPerformanceCounter(&t) == 0) - return -12; + PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER + LARGE_INTEGER t, f; + if (QueryPerformanceCounter(&t) == 0) + return -12; - QueryPerformanceFrequency(&f); - res->seconds = t.QuadPart / f.QuadPart; - res->nanoseconds = t.QuadPart % f.QuadPart; - if (f.QuadPart > PSNIP_CLOCK_NSEC_PER_SEC) - res->nanoseconds /= f.QuadPart / PSNIP_CLOCK_NSEC_PER_SEC; - else - res->nanoseconds *= PSNIP_CLOCK_NSEC_PER_SEC / f.QuadPart; + QueryPerformanceFrequency(&f); + res->seconds = t.QuadPart / f.QuadPart; + res->nanoseconds = t.QuadPart % f.QuadPart; + if (f.QuadPart > PSNIP_CLOCK_NSEC_PER_SEC) + res->nanoseconds /= f.QuadPart / PSNIP_CLOCK_NSEC_PER_SEC; + else + res->nanoseconds *= PSNIP_CLOCK_NSEC_PER_SEC / f.QuadPart; #elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && \ - PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64 - const ULONGLONG msec = GetTickCount64(); - res->seconds = msec / 1000; - res->nanoseconds = sec % 1000; + PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64 + const ULONGLONG msec = GetTickCount64(); + res->seconds = msec / 1000; + res->nanoseconds = sec % 1000; #else - return -2; + return -2; #endif - return 0; + return 0; } /* Returns the number of ticks per second for the specified clock. @@ -793,53 +791,52 @@ psnip_clock_monotonic_get_time(struct PsnipClockTimespec *res) PSNIP_CLOCK__FUNCTION psnip_uint32_t psnip_clock_get_precision(enum PsnipClockType clock_type) { - switch (clock_type) { - case PSNIP_CLOCK_TYPE_MONOTONIC: - return psnip_clock_monotonic_get_precision(); - case PSNIP_CLOCK_TYPE_CPU: - return psnip_clock_cpu_get_precision(); - case PSNIP_CLOCK_TYPE_WALL: - return psnip_clock_wall_get_precision(); - } + switch (clock_type) { + case PSNIP_CLOCK_TYPE_MONOTONIC: + return psnip_clock_monotonic_get_precision(); + case PSNIP_CLOCK_TYPE_CPU: + return psnip_clock_cpu_get_precision(); + case PSNIP_CLOCK_TYPE_WALL: + return psnip_clock_wall_get_precision(); + } - PSNIP_CLOCK_UNREACHABLE(); - return 0; + PSNIP_CLOCK_UNREACHABLE(); + return 0; } /* Set the provided timespec to the requested time. Returns 0 on * success, or a negative value on failure. */ PSNIP_CLOCK__FUNCTION int psnip_clock_get_time(enum PsnipClockType clock_type, - struct PsnipClockTimespec *res) + struct PsnipClockTimespec *res) { - assert(res != NULL); + assert(res != NULL); - switch (clock_type) { - case PSNIP_CLOCK_TYPE_MONOTONIC: - return psnip_clock_monotonic_get_time(res); - case PSNIP_CLOCK_TYPE_CPU: - return psnip_clock_cpu_get_time(res); - case PSNIP_CLOCK_TYPE_WALL: - return psnip_clock_wall_get_time(res); - } + switch (clock_type) { + case PSNIP_CLOCK_TYPE_MONOTONIC: + return psnip_clock_monotonic_get_time(res); + case PSNIP_CLOCK_TYPE_CPU: + return psnip_clock_cpu_get_time(res); + case PSNIP_CLOCK_TYPE_WALL: + return psnip_clock_wall_get_time(res); + } - return -1; + return -1; } #endif /* !defined(PSNIP_CLOCK_H) */ static psnip_uint64_t munit_clock_get_elapsed(struct PsnipClockTimespec *start, - struct PsnipClockTimespec *end) + struct PsnipClockTimespec *end) { - psnip_uint64_t r = (end->seconds - start->seconds) * - PSNIP_CLOCK_NSEC_PER_SEC; - if (end->nanoseconds < start->nanoseconds) { - r -= (start->nanoseconds - end->nanoseconds); - } else { - r += (end->nanoseconds - start->nanoseconds); - } - return r; + psnip_uint64_t r = (end->seconds - start->seconds) * PSNIP_CLOCK_NSEC_PER_SEC; + if (end->nanoseconds < start->nanoseconds) { + r -= (start->nanoseconds - end->nanoseconds); + } else { + r += (end->nanoseconds - start->nanoseconds); + } + return r; } #else @@ -860,9 +857,9 @@ munit_clock_get_elapsed(struct PsnipClockTimespec *start, * chance of being reproducible. */ #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ - !defined(__STDC_NO_ATOMICS__) && !defined(__EMSCRIPTEN__) && \ - (!defined(__GNUC_MINOR__) || (__GNUC__ > 4) || \ - (__GNUC__ == 4 && __GNUC_MINOR__ > 8)) + !defined(__STDC_NO_ATOMICS__) && !defined(__EMSCRIPTEN__) && \ + (!defined(__GNUC_MINOR__) || (__GNUC__ > 4) || \ + (__GNUC__ == 4 && __GNUC_MINOR__ > 8)) #define HAVE_STDATOMIC #elif defined(__clang__) #if __has_extension(c_atomic) @@ -903,89 +900,89 @@ static inline void munit_atomic_store(ATOMIC_UINT32_T *dest, ATOMIC_UINT32_T value) { #pragma omp critical(munit_atomics) - *dest = value; + *dest = value; } static inline uint32_t munit_atomic_load(ATOMIC_UINT32_T *src) { - int ret; + int ret; #pragma omp critical(munit_atomics) - ret = *src; - return ret; + ret = *src; + return ret; } static inline uint32_t munit_atomic_cas(ATOMIC_UINT32_T *dest, ATOMIC_UINT32_T *expected, - ATOMIC_UINT32_T desired) + ATOMIC_UINT32_T desired) { - munit_bool ret; + munit_bool ret; #pragma omp critical(munit_atomics) - { - if (*dest == *expected) { - *dest = desired; - ret = 1; - } else { - ret = 0; - } + { + if (*dest == *expected) { + *dest = desired; + ret = 1; + } else { + ret = 0; } + } - return ret; + return ret; } #elif defined(HAVE_STDATOMIC) #define munit_atomic_store(dest, value) atomic_store(dest, value) #define munit_atomic_load(src) atomic_load(src) #define munit_atomic_cas(dest, expected, value) \ - atomic_compare_exchange_weak(dest, expected, value) + atomic_compare_exchange_weak(dest, expected, value) #elif defined(HAVE_CLANG_ATOMICS) #define munit_atomic_store(dest, value) \ - __c11_atomic_store(dest, value, __ATOMIC_SEQ_CST) + __c11_atomic_store(dest, value, __ATOMIC_SEQ_CST) #define munit_atomic_load(src) __c11_atomic_load(src, __ATOMIC_SEQ_CST) -#define munit_atomic_cas(dest, expected, value) \ - __c11_atomic_compare_exchange_weak(dest, expected, value, \ - __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) +#define munit_atomic_cas(dest, expected, value) \ + __c11_atomic_compare_exchange_weak(dest, expected, value, __ATOMIC_SEQ_CST, \ + __ATOMIC_SEQ_CST) #elif defined(__GNUC__) && (__GNUC__ > 4) || \ - (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) + (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) #define munit_atomic_store(dest, value) \ - __atomic_store_n(dest, value, __ATOMIC_SEQ_CST) + __atomic_store_n(dest, value, __ATOMIC_SEQ_CST) #define munit_atomic_load(src) __atomic_load_n(src, __ATOMIC_SEQ_CST) -#define munit_atomic_cas(dest, expected, value) \ - __atomic_compare_exchange_n(dest, expected, value, 1, __ATOMIC_SEQ_CST, \ - __ATOMIC_SEQ_CST) +#define munit_atomic_cas(dest, expected, value) \ + __atomic_compare_exchange_n(dest, expected, value, 1, __ATOMIC_SEQ_CST, \ + __ATOMIC_SEQ_CST) #elif defined(__GNUC__) && (__GNUC__ >= 4) #define munit_atomic_store(dest, value) \ - do { \ - *(dest) = (value); \ - } while (0) + do { \ + *(dest) = (value); \ + } while (0) #define munit_atomic_load(src) (*(src)) #define munit_atomic_cas(dest, expected, value) \ - __sync_bool_compare_and_swap(dest, *expected, value) + __sync_bool_compare_and_swap(dest, *expected, value) #elif defined(_WIN32) /* Untested */ #define munit_atomic_store(dest, value) \ - do { \ - *(dest) = (value); \ - } while (0) + do { \ + *(dest) = (value); \ + } while (0) #define munit_atomic_load(src) (*(src)) #define munit_atomic_cas(dest, expected, value) \ - InterlockedCompareExchange((dest), (value), *(expected)) + InterlockedCompareExchange((dest), (value), *(expected)) #else #warning No atomic implementation, PRNG will not be thread-safe #define munit_atomic_store(dest, value) \ - do { \ - *(dest) = (value); \ - } while (0) + do { \ + *(dest) = (value); \ + } while (0) #define munit_atomic_load(src) (*(src)) static inline munit_bool munit_atomic_cas(ATOMIC_UINT32_T *dest, ATOMIC_UINT32_T *expected, - ATOMIC_UINT32_T desired) + ATOMIC_UINT32_T desired) { - if (*dest == *expected) { - *dest = desired; - return 1; - } else { - return 0; - } + if (*dest == *expected) { + *dest = desired; + return 1; + } else { + return 0; + } } #endif @@ -995,231 +992,229 @@ munit_atomic_cas(ATOMIC_UINT32_T *dest, ATOMIC_UINT32_T *expected, static munit_uint32_t munit_rand_next_state(munit_uint32_t state) { - return state * MUNIT_PRNG_MULTIPLIER + MUNIT_PRNG_INCREMENT; + return state * MUNIT_PRNG_MULTIPLIER + MUNIT_PRNG_INCREMENT; } static munit_uint32_t munit_rand_from_state(munit_uint32_t state) { - munit_uint32_t res = ((state >> ((state >> 28) + 4)) ^ state) * - (277803737U); - res ^= res >> 22; - return res; + munit_uint32_t res = ((state >> ((state >> 28) + 4)) ^ state) * (277803737U); + res ^= res >> 22; + return res; } void munit_rand_seed(munit_uint32_t seed) { - munit_uint32_t state = munit_rand_next_state(seed + MUNIT_PRNG_INCREMENT); - munit_atomic_store(&munit_rand_state, state); + munit_uint32_t state = munit_rand_next_state(seed + MUNIT_PRNG_INCREMENT); + munit_atomic_store(&munit_rand_state, state); } static munit_uint32_t munit_rand_generate_seed(void) { - munit_uint32_t seed, state; + munit_uint32_t seed, state; #if defined(MUNIT_ENABLE_TIMING) - struct PsnipClockTimespec wc = { - 0, - }; + struct PsnipClockTimespec wc = { + 0, + }; - psnip_clock_get_time(PSNIP_CLOCK_TYPE_WALL, &wc); - seed = (munit_uint32_t)wc.nanoseconds; + psnip_clock_get_time(PSNIP_CLOCK_TYPE_WALL, &wc); + seed = (munit_uint32_t)wc.nanoseconds; #else - seed = (munit_uint32_t)time(NULL); + seed = (munit_uint32_t)time(NULL); #endif - state = munit_rand_next_state(seed + MUNIT_PRNG_INCREMENT); - return munit_rand_from_state(state); + state = munit_rand_next_state(seed + MUNIT_PRNG_INCREMENT); + return munit_rand_from_state(state); } static munit_uint32_t munit_rand_state_uint32(munit_uint32_t *state) { - const munit_uint32_t old = *state; - *state = munit_rand_next_state(old); - return munit_rand_from_state(old); + const munit_uint32_t old = *state; + *state = munit_rand_next_state(old); + return munit_rand_from_state(old); } munit_uint32_t munit_rand_uint32(void) { - munit_uint32_t old, state; + munit_uint32_t old, state; - do { - old = munit_atomic_load(&munit_rand_state); - state = munit_rand_next_state(old); - } while (!munit_atomic_cas(&munit_rand_state, &old, state)); + do { + old = munit_atomic_load(&munit_rand_state); + state = munit_rand_next_state(old); + } while (!munit_atomic_cas(&munit_rand_state, &old, state)); - return munit_rand_from_state(old); + return munit_rand_from_state(old); } static void munit_rand_state_memory(munit_uint32_t *state, size_t size, - munit_uint8_t data[MUNIT_ARRAY_PARAM(size)]) + munit_uint8_t data[MUNIT_ARRAY_PARAM(size)]) { - size_t members_remaining = size / sizeof(munit_uint32_t); - size_t bytes_remaining = size % sizeof(munit_uint32_t); - munit_uint8_t *b = data; - munit_uint32_t rv; - while (members_remaining-- > 0) { - rv = munit_rand_state_uint32(state); - memcpy(b, &rv, sizeof(munit_uint32_t)); - b += sizeof(munit_uint32_t); - } - if (bytes_remaining != 0) { - rv = munit_rand_state_uint32(state); - memcpy(b, &rv, bytes_remaining); - } + size_t members_remaining = size / sizeof(munit_uint32_t); + size_t bytes_remaining = size % sizeof(munit_uint32_t); + munit_uint8_t *b = data; + munit_uint32_t rv; + while (members_remaining-- > 0) { + rv = munit_rand_state_uint32(state); + memcpy(b, &rv, sizeof(munit_uint32_t)); + b += sizeof(munit_uint32_t); + } + if (bytes_remaining != 0) { + rv = munit_rand_state_uint32(state); + memcpy(b, &rv, bytes_remaining); + } } void munit_rand_memory(size_t size, munit_uint8_t data[MUNIT_ARRAY_PARAM(size)]) { - munit_uint32_t old, state; + munit_uint32_t old, state; - do { - state = old = munit_atomic_load(&munit_rand_state); - munit_rand_state_memory(&state, size, data); - } while (!munit_atomic_cas(&munit_rand_state, &old, state)); + do { + state = old = munit_atomic_load(&munit_rand_state); + munit_rand_state_memory(&state, size, data); + } while (!munit_atomic_cas(&munit_rand_state, &old, state)); } static munit_uint32_t munit_rand_state_at_most(munit_uint32_t *state, munit_uint32_t salt, - munit_uint32_t max) + munit_uint32_t max) { - /* We want (UINT32_MAX + 1) % max, which in unsigned arithmetic is the same - * as (UINT32_MAX + 1 - max) % max = -max % max. We compute -max using not - * to avoid compiler warnings. - */ - const munit_uint32_t min = (~max + 1U) % max; - munit_uint32_t x; + /* We want (UINT32_MAX + 1) % max, which in unsigned arithmetic is the same + * as (UINT32_MAX + 1 - max) % max = -max % max. We compute -max using not + * to avoid compiler warnings. + */ + const munit_uint32_t min = (~max + 1U) % max; + munit_uint32_t x; - if (max == (~((munit_uint32_t)0U))) - return munit_rand_state_uint32(state) ^ salt; + if (max == (~((munit_uint32_t)0U))) + return munit_rand_state_uint32(state) ^ salt; - max++; + max++; - do { - x = munit_rand_state_uint32(state) ^ salt; - } while (x < min); + do { + x = munit_rand_state_uint32(state) ^ salt; + } while (x < min); - return x % max; + return x % max; } static munit_uint32_t munit_rand_at_most(munit_uint32_t salt, munit_uint32_t max) { - munit_uint32_t old, state; - munit_uint32_t retval; + munit_uint32_t old, state; + munit_uint32_t retval; - do { - state = old = munit_atomic_load(&munit_rand_state); - retval = munit_rand_state_at_most(&state, salt, max); - } while (!munit_atomic_cas(&munit_rand_state, &old, state)); + do { + state = old = munit_atomic_load(&munit_rand_state); + retval = munit_rand_state_at_most(&state, salt, max); + } while (!munit_atomic_cas(&munit_rand_state, &old, state)); - return retval; + return retval; } int munit_rand_int_range(int min, int max) { - munit_uint64_t range = (munit_uint64_t)max - (munit_uint64_t)min; + munit_uint64_t range = (munit_uint64_t)max - (munit_uint64_t)min; - if (min > max) - return munit_rand_int_range(max, min); + if (min > max) + return munit_rand_int_range(max, min); - if (range > (~((munit_uint32_t)0U))) - range = (~((munit_uint32_t)0U)); + if (range > (~((munit_uint32_t)0U))) + range = (~((munit_uint32_t)0U)); - return min + munit_rand_at_most(0, (munit_uint32_t)range); + return min + munit_rand_at_most(0, (munit_uint32_t)range); } double munit_rand_double(void) { - munit_uint32_t old, state; - double retval = 0.0; + munit_uint32_t old, state; + double retval = 0.0; - do { - state = old = munit_atomic_load(&munit_rand_state); + do { + state = old = munit_atomic_load(&munit_rand_state); - /* See http://mumble.net/~campbell/tmp/random_real.c for how to do - * this right. Patches welcome if you feel that this is too - * biased. */ - retval = munit_rand_state_uint32(&state) / - ((~((munit_uint32_t)0U)) + 1.0); - } while (!munit_atomic_cas(&munit_rand_state, &old, state)); + /* See http://mumble.net/~campbell/tmp/random_real.c for how to do + * this right. Patches welcome if you feel that this is too + * biased. */ + retval = munit_rand_state_uint32(&state) / ((~((munit_uint32_t)0U)) + 1.0); + } while (!munit_atomic_cas(&munit_rand_state, &old, state)); - return retval; + return retval; } /*** Test suite handling ***/ typedef struct { - unsigned int successful; - unsigned int skipped; - unsigned int failed; - unsigned int errored; + unsigned int successful; + unsigned int skipped; + unsigned int failed; + unsigned int errored; #if defined(MUNIT_ENABLE_TIMING) - munit_uint64_t cpu_clock; - munit_uint64_t wall_clock; + munit_uint64_t cpu_clock; + munit_uint64_t wall_clock; #endif } MunitReport; typedef struct { - const char *prefix; - const MunitSuite *suite; - const char **tests; - munit_uint32_t seed; - unsigned int iterations; - MunitParameter *parameters; - munit_bool single_parameter_mode; - void *user_data; - MunitReport report; - munit_bool colorize; - munit_bool fork; - munit_bool show_stderr; - munit_bool fatal_failures; + const char *prefix; + const MunitSuite *suite; + const char **tests; + munit_uint32_t seed; + unsigned int iterations; + MunitParameter *parameters; + munit_bool single_parameter_mode; + void *user_data; + MunitReport report; + munit_bool colorize; + munit_bool fork; + munit_bool show_stderr; + munit_bool fatal_failures; } MunitTestRunner; const char * munit_parameters_get(const MunitParameter params[], const char *key) { - const MunitParameter *param; + const MunitParameter *param; - for (param = params; param != NULL && param->name != NULL; param++) - if (strcmp(param->name, key) == 0) - return param->value; - return NULL; + for (param = params; param != NULL && param->name != NULL; param++) + if (strcmp(param->name, key) == 0) + return param->value; + return NULL; } #if defined(MUNIT_ENABLE_TIMING) static void munit_print_time(FILE *fp, munit_uint64_t nanoseconds) { - fprintf(fp, "%" MUNIT_TEST_TIME_FORMAT, - ((double)nanoseconds) / ((double)PSNIP_CLOCK_NSEC_PER_SEC)); + fprintf(fp, "%" MUNIT_TEST_TIME_FORMAT, + ((double)nanoseconds) / ((double)PSNIP_CLOCK_NSEC_PER_SEC)); } #endif /* Add a paramter to an array of parameters. */ static MunitResult munit_parameters_add(size_t *params_size, - MunitParameter *params[MUNIT_ARRAY_PARAM(*params_size)], char *name, - char *value) + MunitParameter *params[MUNIT_ARRAY_PARAM(*params_size)], char *name, + char *value) { - *params = realloc(*params, sizeof(MunitParameter) * (*params_size + 2)); - if (*params == NULL) - return MUNIT_ERROR; + *params = realloc(*params, sizeof(MunitParameter) * (*params_size + 2)); + if (*params == NULL) + return MUNIT_ERROR; - (*params)[*params_size].name = name; - (*params)[*params_size].value = value; - (*params_size)++; - (*params)[*params_size].name = NULL; - (*params)[*params_size].value = NULL; + (*params)[*params_size].name = name; + (*params)[*params_size].value = value; + (*params_size)++; + (*params)[*params_size].name = NULL; + (*params)[*params_size].value = NULL; - return MUNIT_OK; + return MUNIT_OK; } /* Concatenate two strings, but just return one of the components @@ -1227,150 +1222,149 @@ munit_parameters_add(size_t *params_size, static char * munit_maybe_concat(size_t *len, char *prefix, char *suffix) { - char *res; - size_t res_l; - const size_t prefix_l = prefix != NULL ? strlen(prefix) : 0; - const size_t suffix_l = suffix != NULL ? strlen(suffix) : 0; - if (prefix_l == 0 && suffix_l == 0) { - res = NULL; - res_l = 0; - } else if (prefix_l == 0 && suffix_l != 0) { - res = suffix; - res_l = suffix_l; - } else if (prefix_l != 0 && suffix_l == 0) { - res = prefix; - res_l = prefix_l; - } else { - res_l = prefix_l + suffix_l; - res = malloc(res_l + 1); - memcpy(res, prefix, prefix_l); - memcpy(res + prefix_l, suffix, suffix_l); - res[res_l] = 0; - } + char *res; + size_t res_l; + const size_t prefix_l = prefix != NULL ? strlen(prefix) : 0; + const size_t suffix_l = suffix != NULL ? strlen(suffix) : 0; + if (prefix_l == 0 && suffix_l == 0) { + res = NULL; + res_l = 0; + } else if (prefix_l == 0 && suffix_l != 0) { + res = suffix; + res_l = suffix_l; + } else if (prefix_l != 0 && suffix_l == 0) { + res = prefix; + res_l = prefix_l; + } else { + res_l = prefix_l + suffix_l; + res = malloc(res_l + 1); + memcpy(res, prefix, prefix_l); + memcpy(res + prefix_l, suffix, suffix_l); + res[res_l] = 0; + } - if (len != NULL) - *len = res_l; + if (len != NULL) + *len = res_l; - return res; + return res; } /* Possbily free a string returned by munit_maybe_concat. */ static void munit_maybe_free_concat(char *s, const char *prefix, const char *suffix) { - if (prefix != s && suffix != s) - free(s); + if (prefix != s && suffix != s) + free(s); } /* Cheap string hash function, just used to salt the PRNG. */ static munit_uint32_t munit_str_hash(const char *name) { - const char *p; - munit_uint32_t h = 5381U; + const char *p; + munit_uint32_t h = 5381U; - for (p = name; *p != '\0'; p++) - h = (h << 5) + h + *p; + for (p = name; *p != '\0'; p++) + h = (h << 5) + h + *p; - return h; + return h; } static void munit_splice(int from, int to) { - munit_uint8_t buf[1024]; + munit_uint8_t buf[1024]; #if !defined(_WIN32) - ssize_t len; - ssize_t bytes_written; - ssize_t write_res; + ssize_t len; + ssize_t bytes_written; + ssize_t write_res; #else - int len; - int bytes_written; - int write_res; + int len; + int bytes_written; + int write_res; #endif - do { - len = read(from, buf, sizeof(buf)); - if (len > 0) { - bytes_written = 0; - do { - write_res = write(to, buf + bytes_written, len - bytes_written); - if (write_res < 0) - break; - bytes_written += write_res; - } while (bytes_written < len); - } else - break; - } while (1); + do { + len = read(from, buf, sizeof(buf)); + if (len > 0) { + bytes_written = 0; + do { + write_res = write(to, buf + bytes_written, len - bytes_written); + if (write_res < 0) + break; + bytes_written += write_res; + } while (bytes_written < len); + } else + break; + } while (1); } /* This is the part that should be handled in the child process */ static MunitResult munit_test_runner_exec(MunitTestRunner *runner, const MunitTest *test, - const MunitParameter params[], MunitReport *report) + const MunitParameter params[], MunitReport *report) { - unsigned int iterations = runner->iterations; - MunitResult result = MUNIT_FAIL; + unsigned int iterations = runner->iterations; + MunitResult result = MUNIT_FAIL; #if defined(MUNIT_ENABLE_TIMING) - struct PsnipClockTimespec wall_clock_begin = { 0, }, wall_clock_end = { 0, }; - struct PsnipClockTimespec cpu_clock_begin = { 0, }, cpu_clock_end = { 0, }; + struct PsnipClockTimespec wall_clock_begin = { 0, }, wall_clock_end = { 0, }; + struct PsnipClockTimespec cpu_clock_begin = { 0, }, cpu_clock_end = { 0, }; #endif - unsigned int i = 0; + unsigned int i = 0; - if ((test->options & MUNIT_TEST_OPTION_SINGLE_ITERATION) == - MUNIT_TEST_OPTION_SINGLE_ITERATION) - iterations = 1; - else if (iterations == 0) - iterations = runner->suite->iterations; + if ((test->options & MUNIT_TEST_OPTION_SINGLE_ITERATION) == + MUNIT_TEST_OPTION_SINGLE_ITERATION) + iterations = 1; + else if (iterations == 0) + iterations = runner->suite->iterations; - munit_rand_seed(runner->seed); + munit_rand_seed(runner->seed); - do { - void *data = (test->setup == NULL) ? - runner->user_data : - test->setup(params, runner->user_data); + do { + void *data = (test->setup == NULL) ? runner->user_data : + test->setup(params, runner->user_data); #if defined(MUNIT_ENABLE_TIMING) - psnip_clock_get_time(PSNIP_CLOCK_TYPE_WALL, &wall_clock_begin); - psnip_clock_get_time(PSNIP_CLOCK_TYPE_CPU, &cpu_clock_begin); + psnip_clock_get_time(PSNIP_CLOCK_TYPE_WALL, &wall_clock_begin); + psnip_clock_get_time(PSNIP_CLOCK_TYPE_CPU, &cpu_clock_begin); #endif - result = test->test(params, data); + result = test->test(params, data); #if defined(MUNIT_ENABLE_TIMING) - psnip_clock_get_time(PSNIP_CLOCK_TYPE_WALL, &wall_clock_end); - psnip_clock_get_time(PSNIP_CLOCK_TYPE_CPU, &cpu_clock_end); + psnip_clock_get_time(PSNIP_CLOCK_TYPE_WALL, &wall_clock_end); + psnip_clock_get_time(PSNIP_CLOCK_TYPE_CPU, &cpu_clock_end); #endif - if (test->tear_down != NULL) - test->tear_down(data); + if (test->tear_down != NULL) + test->tear_down(data); - if (MUNIT_LIKELY(result == MUNIT_OK)) { - report->successful++; + if (MUNIT_LIKELY(result == MUNIT_OK)) { + report->successful++; #if defined(MUNIT_ENABLE_TIMING) - report->wall_clock += munit_clock_get_elapsed(&wall_clock_begin, - &wall_clock_end); - report->cpu_clock += munit_clock_get_elapsed(&cpu_clock_begin, - &cpu_clock_end); + report->wall_clock += munit_clock_get_elapsed(&wall_clock_begin, + &wall_clock_end); + report->cpu_clock += munit_clock_get_elapsed(&cpu_clock_begin, + &cpu_clock_end); #endif - } else { - switch ((int)result) { - case MUNIT_SKIP: - report->skipped++; - break; - case MUNIT_FAIL: - report->failed++; - break; - case MUNIT_ERROR: - report->errored++; - break; - default: - break; - } - break; - } - } while (++i < iterations); + } else { + switch ((int)result) { + case MUNIT_SKIP: + report->skipped++; + break; + case MUNIT_FAIL: + report->failed++; + break; + case MUNIT_ERROR: + report->errored++; + break; + default: + break; + } + break; + } + } while (++i < iterations); - return result; + return result; } #if defined(MUNIT_EMOTICON) @@ -1389,454 +1383,440 @@ munit_test_runner_exec(MunitTestRunner *runner, const MunitTest *test, static void munit_test_runner_print_color(const MunitTestRunner *runner, const char *string, - char color) + char color) { - if (runner->colorize) - fprintf(MUNIT_OUTPUT_FILE, "\x1b[3%cm%s\x1b[39m", color, string); - else - fputs(string, MUNIT_OUTPUT_FILE); + if (runner->colorize) + fprintf(MUNIT_OUTPUT_FILE, "\x1b[3%cm%s\x1b[39m", color, string); + else + fputs(string, MUNIT_OUTPUT_FILE); } #if !defined(MUNIT_NO_BUFFER) static int munit_replace_stderr(FILE *stderr_buf) { - if (stderr_buf != NULL) { - const int orig_stderr = dup(STDERR_FILENO); + if (stderr_buf != NULL) { + const int orig_stderr = dup(STDERR_FILENO); - int errfd = fileno(stderr_buf); - if (MUNIT_UNLIKELY(errfd == -1)) { - exit(EXIT_FAILURE); - } - - dup2(errfd, STDERR_FILENO); - - return orig_stderr; + int errfd = fileno(stderr_buf); + if (MUNIT_UNLIKELY(errfd == -1)) { + exit(EXIT_FAILURE); } - return -1; + dup2(errfd, STDERR_FILENO); + + return orig_stderr; + } + + return -1; } static void munit_restore_stderr(int orig_stderr) { - if (orig_stderr != -1) { - dup2(orig_stderr, STDERR_FILENO); - close(orig_stderr); - } + if (orig_stderr != -1) { + dup2(orig_stderr, STDERR_FILENO); + close(orig_stderr); + } } #endif /* !defined(MUNIT_NO_BUFFER) */ /* Run a test with the specified parameters. */ static void munit_test_runner_run_test_with_params(MunitTestRunner *runner, - const MunitTest *test, const MunitParameter params[]) + const MunitTest *test, const MunitParameter params[]) { - MunitResult result = MUNIT_OK; - MunitReport report = { - 0, - 0, - 0, - 0, + MunitResult result = MUNIT_OK; + MunitReport report = { + 0, + 0, + 0, + 0, #if defined(MUNIT_ENABLE_TIMING) - 0, - 0 + 0, + 0 #endif - }; - unsigned int output_l; - munit_bool first; - const MunitParameter *param; - FILE *stderr_buf; + }; + unsigned int output_l; + munit_bool first; + const MunitParameter *param; + FILE *stderr_buf; #if !defined(MUNIT_NO_FORK) - int pipefd[2]; - pid_t fork_pid; - int orig_stderr; - ssize_t bytes_written = 0; - ssize_t write_res; - ssize_t bytes_read = 0; - ssize_t read_res; - int status = 0; - pid_t changed_pid; + int pipefd[2]; + pid_t fork_pid; + int orig_stderr; + ssize_t bytes_written = 0; + ssize_t write_res; + ssize_t bytes_read = 0; + ssize_t read_res; + int status = 0; + pid_t changed_pid; #endif - if (params != NULL) { - output_l = 2; - fputs(" ", MUNIT_OUTPUT_FILE); - first = 1; - for (param = params; param != NULL && param->name != NULL; param++) { - if (!first) { - fputs(", ", MUNIT_OUTPUT_FILE); - output_l += 2; - } else { - first = 0; - } + if (params != NULL) { + output_l = 2; + fputs(" ", MUNIT_OUTPUT_FILE); + first = 1; + for (param = params; param != NULL && param->name != NULL; param++) { + if (!first) { + fputs(", ", MUNIT_OUTPUT_FILE); + output_l += 2; + } else { + first = 0; + } - output_l += fprintf(MUNIT_OUTPUT_FILE, "%s=%s", param->name, - param->value); - } - while (output_l++ < MUNIT_TEST_NAME_LEN) { - fputc(' ', MUNIT_OUTPUT_FILE); - } + output_l += fprintf(MUNIT_OUTPUT_FILE, "%s=%s", param->name, + param->value); } + while (output_l++ < MUNIT_TEST_NAME_LEN) { + fputc(' ', MUNIT_OUTPUT_FILE); + } + } - fflush(MUNIT_OUTPUT_FILE); + fflush(MUNIT_OUTPUT_FILE); - stderr_buf = NULL; + stderr_buf = NULL; #if !defined(_WIN32) || defined(__MINGW32__) - stderr_buf = tmpfile(); + stderr_buf = tmpfile(); #else - tmpfile_s(&stderr_buf); + tmpfile_s(&stderr_buf); #endif - if (stderr_buf == NULL) { - munit_log_errno(MUNIT_LOG_ERROR, stderr, - "unable to create buffer for stderr"); - result = MUNIT_ERROR; - goto print_result; - } + if (stderr_buf == NULL) { + munit_log_errno(MUNIT_LOG_ERROR, stderr, + "unable to create buffer for stderr"); + result = MUNIT_ERROR; + goto print_result; + } #if !defined(MUNIT_NO_FORK) - if (runner->fork) { - pipefd[0] = -1; - pipefd[1] = -1; - if (pipe(pipefd) != 0) { - munit_log_errno(MUNIT_LOG_ERROR, stderr, "unable to create pipe"); - result = MUNIT_ERROR; - goto print_result; + if (runner->fork) { + pipefd[0] = -1; + pipefd[1] = -1; + if (pipe(pipefd) != 0) { + munit_log_errno(MUNIT_LOG_ERROR, stderr, "unable to create pipe"); + result = MUNIT_ERROR; + goto print_result; + } + + fork_pid = fork(); + if (fork_pid == 0) { + close(pipefd[0]); + + orig_stderr = munit_replace_stderr(stderr_buf); + munit_test_runner_exec(runner, test, params, &report); + + /* Note that we don't restore stderr. This is so we can buffer + * things written to stderr later on (such as by + * asan/tsan/ubsan, valgrind, etc.) */ + close(orig_stderr); + + do { + write_res = write(pipefd[1], + ((munit_uint8_t *)(&report)) + bytes_written, + sizeof(report) - bytes_written); + if (write_res < 0) { + if (stderr_buf != NULL) { + munit_log_errno(MUNIT_LOG_ERROR, stderr, "unable to write to pipe"); + } + exit(EXIT_FAILURE); } + bytes_written += write_res; + } while ((size_t)bytes_written < sizeof(report)); - fork_pid = fork(); - if (fork_pid == 0) { - close(pipefd[0]); + if (stderr_buf != NULL) + fclose(stderr_buf); + close(pipefd[1]); - orig_stderr = munit_replace_stderr(stderr_buf); - munit_test_runner_exec(runner, test, params, &report); + exit(EXIT_SUCCESS); + } else if (fork_pid == -1) { + close(pipefd[0]); + close(pipefd[1]); + if (stderr_buf != NULL) { + munit_log_errno(MUNIT_LOG_ERROR, stderr, "unable to fork"); + } + report.errored++; + result = MUNIT_ERROR; + } else { + close(pipefd[1]); + do { + read_res = read(pipefd[0], ((munit_uint8_t *)(&report)) + bytes_read, + sizeof(report) - bytes_read); + if (read_res < 1) + break; + bytes_read += read_res; + } while (bytes_read < (ssize_t)sizeof(report)); - /* Note that we don't restore stderr. This is so we can buffer - * things written to stderr later on (such as by - * asan/tsan/ubsan, valgrind, etc.) */ - close(orig_stderr); + changed_pid = waitpid(fork_pid, &status, 0); - do { - write_res = write(pipefd[1], - ((munit_uint8_t *)(&report)) + bytes_written, - sizeof(report) - bytes_written); - if (write_res < 0) { - if (stderr_buf != NULL) { - munit_log_errno(MUNIT_LOG_ERROR, stderr, - "unable to write to pipe"); - } - exit(EXIT_FAILURE); - } - bytes_written += write_res; - } while ((size_t)bytes_written < sizeof(report)); - - if (stderr_buf != NULL) - fclose(stderr_buf); - close(pipefd[1]); - - exit(EXIT_SUCCESS); - } else if (fork_pid == -1) { - close(pipefd[0]); - close(pipefd[1]); - if (stderr_buf != NULL) { - munit_log_errno(MUNIT_LOG_ERROR, stderr, "unable to fork"); - } - report.errored++; - result = MUNIT_ERROR; - } else { - close(pipefd[1]); - do { - read_res = read(pipefd[0], - ((munit_uint8_t *)(&report)) + bytes_read, - sizeof(report) - bytes_read); - if (read_res < 1) - break; - bytes_read += read_res; - } while (bytes_read < (ssize_t)sizeof(report)); - - changed_pid = waitpid(fork_pid, &status, 0); - - if (MUNIT_LIKELY(changed_pid == fork_pid) && - MUNIT_LIKELY(WIFEXITED(status))) { - if (bytes_read != sizeof(report)) { - munit_logf_internal(MUNIT_LOG_ERROR, stderr_buf, - "child exited unexpectedly with status %d", - WEXITSTATUS(status)); - report.errored++; - } else if (WEXITSTATUS(status) != EXIT_SUCCESS) { - munit_logf_internal(MUNIT_LOG_ERROR, stderr_buf, - "child exited with status %d", WEXITSTATUS(status)); - report.errored++; - } - } else { - if (WIFSIGNALED(status)) { + if (MUNIT_LIKELY(changed_pid == fork_pid) && + MUNIT_LIKELY(WIFEXITED(status))) { + if (bytes_read != sizeof(report)) { + munit_logf_internal(MUNIT_LOG_ERROR, stderr_buf, + "child exited unexpectedly with status %d", WEXITSTATUS(status)); + report.errored++; + } else if (WEXITSTATUS(status) != EXIT_SUCCESS) { + munit_logf_internal(MUNIT_LOG_ERROR, stderr_buf, + "child exited with status %d", WEXITSTATUS(status)); + report.errored++; + } + } else { + if (WIFSIGNALED(status)) { #if defined(_XOPEN_VERSION) && (_XOPEN_VERSION >= 700) - munit_logf_internal(MUNIT_LOG_ERROR, stderr_buf, - "child killed by signal %d (%s)", WTERMSIG(status), - strsignal(WTERMSIG(status))); + munit_logf_internal(MUNIT_LOG_ERROR, stderr_buf, + "child killed by signal %d (%s)", WTERMSIG(status), + strsignal(WTERMSIG(status))); #else - munit_logf_internal(MUNIT_LOG_ERROR, stderr_buf, - "child killed by signal %d", WTERMSIG(status)); + munit_logf_internal(MUNIT_LOG_ERROR, stderr_buf, + "child killed by signal %d", WTERMSIG(status)); #endif - } else if (WIFSTOPPED(status)) { - munit_logf_internal(MUNIT_LOG_ERROR, stderr_buf, - "child stopped by signal %d", WSTOPSIG(status)); - } - report.errored++; - } - - close(pipefd[0]); - waitpid(fork_pid, NULL, 0); + } else if (WIFSTOPPED(status)) { + munit_logf_internal(MUNIT_LOG_ERROR, stderr_buf, + "child stopped by signal %d", WSTOPSIG(status)); } - } else + report.errored++; + } + + close(pipefd[0]); + waitpid(fork_pid, NULL, 0); + } + } else #endif - { + { #if !defined(MUNIT_NO_BUFFER) - const volatile int orig_stderr = munit_replace_stderr(stderr_buf); + const volatile int orig_stderr = munit_replace_stderr(stderr_buf); #endif #if defined(MUNIT_THREAD_LOCAL) - if (MUNIT_UNLIKELY(setjmp(munit_error_jmp_buf) != 0)) { - result = MUNIT_FAIL; - report.failed++; - } else { - munit_error_jmp_buf_valid = 1; - result = munit_test_runner_exec(runner, test, params, &report); - } + if (MUNIT_UNLIKELY(setjmp(munit_error_jmp_buf) != 0)) { + result = MUNIT_FAIL; + report.failed++; + } else { + munit_error_jmp_buf_valid = 1; + result = munit_test_runner_exec(runner, test, params, &report); + } #else - result = munit_test_runner_exec(runner, test, params, &report); + result = munit_test_runner_exec(runner, test, params, &report); #endif #if !defined(MUNIT_NO_BUFFER) - munit_restore_stderr(orig_stderr); + munit_restore_stderr(orig_stderr); #endif - /* Here just so that the label is used on Windows and we don't get - * a warning */ - goto print_result; - } + /* Here just so that the label is used on Windows and we don't get + * a warning */ + goto print_result; + } print_result: - fputs("[ ", MUNIT_OUTPUT_FILE); - if ((test->options & MUNIT_TEST_OPTION_TODO) == MUNIT_TEST_OPTION_TODO) { - if (report.failed != 0 || report.errored != 0 || report.skipped != 0) { - munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_TODO, - '3'); - result = MUNIT_OK; - } else { - munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_ERROR, - '1'); - if (MUNIT_LIKELY(stderr_buf != NULL)) - munit_log_internal(MUNIT_LOG_ERROR, stderr_buf, - "Test marked TODO, but was successful."); - runner->report.failed++; - result = MUNIT_ERROR; - } - } else if (report.failed > 0) { - munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_FAIL, '1'); - runner->report.failed++; - result = MUNIT_FAIL; - } else if (report.errored > 0) { - munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_ERROR, '1'); - runner->report.errored++; - result = MUNIT_ERROR; - } else if (report.skipped > 0) { - munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_SKIP, '3'); - runner->report.skipped++; - result = MUNIT_SKIP; - } else if (report.successful > 1) { - munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_OK, '2'); -#if defined(MUNIT_ENABLE_TIMING) - fputs(" ] [ ", MUNIT_OUTPUT_FILE); - munit_print_time(MUNIT_OUTPUT_FILE, - report.wall_clock / report.successful); - fputs(" / ", MUNIT_OUTPUT_FILE); - munit_print_time(MUNIT_OUTPUT_FILE, - report.cpu_clock / report.successful); - fprintf(MUNIT_OUTPUT_FILE, - " CPU ]\n %-" MUNIT_XSTRINGIFY(MUNIT_TEST_NAME_LEN) "s Total: [ ", - ""); - munit_print_time(MUNIT_OUTPUT_FILE, report.wall_clock); - fputs(" / ", MUNIT_OUTPUT_FILE); - munit_print_time(MUNIT_OUTPUT_FILE, report.cpu_clock); - fputs(" CPU", MUNIT_OUTPUT_FILE); -#endif - runner->report.successful++; - result = MUNIT_OK; - } else if (report.successful > 0) { - munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_OK, '2'); -#if defined(MUNIT_ENABLE_TIMING) - fputs(" ] [ ", MUNIT_OUTPUT_FILE); - munit_print_time(MUNIT_OUTPUT_FILE, report.wall_clock); - fputs(" / ", MUNIT_OUTPUT_FILE); - munit_print_time(MUNIT_OUTPUT_FILE, report.cpu_clock); - fputs(" CPU", MUNIT_OUTPUT_FILE); -#endif - runner->report.successful++; - result = MUNIT_OK; + fputs("[ ", MUNIT_OUTPUT_FILE); + if ((test->options & MUNIT_TEST_OPTION_TODO) == MUNIT_TEST_OPTION_TODO) { + if (report.failed != 0 || report.errored != 0 || report.skipped != 0) { + munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_TODO, '3'); + result = MUNIT_OK; + } else { + munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_ERROR, '1'); + if (MUNIT_LIKELY(stderr_buf != NULL)) + munit_log_internal(MUNIT_LOG_ERROR, stderr_buf, + "Test marked TODO, but was successful."); + runner->report.failed++; + result = MUNIT_ERROR; } - fputs(" ]\n", MUNIT_OUTPUT_FILE); + } else if (report.failed > 0) { + munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_FAIL, '1'); + runner->report.failed++; + result = MUNIT_FAIL; + } else if (report.errored > 0) { + munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_ERROR, '1'); + runner->report.errored++; + result = MUNIT_ERROR; + } else if (report.skipped > 0) { + munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_SKIP, '3'); + runner->report.skipped++; + result = MUNIT_SKIP; + } else if (report.successful > 1) { + munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_OK, '2'); +#if defined(MUNIT_ENABLE_TIMING) + fputs(" ] [ ", MUNIT_OUTPUT_FILE); + munit_print_time(MUNIT_OUTPUT_FILE, report.wall_clock / report.successful); + fputs(" / ", MUNIT_OUTPUT_FILE); + munit_print_time(MUNIT_OUTPUT_FILE, report.cpu_clock / report.successful); + fprintf(MUNIT_OUTPUT_FILE, + " CPU ]\n %-" MUNIT_XSTRINGIFY(MUNIT_TEST_NAME_LEN) "s Total: [ ", ""); + munit_print_time(MUNIT_OUTPUT_FILE, report.wall_clock); + fputs(" / ", MUNIT_OUTPUT_FILE); + munit_print_time(MUNIT_OUTPUT_FILE, report.cpu_clock); + fputs(" CPU", MUNIT_OUTPUT_FILE); +#endif + runner->report.successful++; + result = MUNIT_OK; + } else if (report.successful > 0) { + munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_OK, '2'); +#if defined(MUNIT_ENABLE_TIMING) + fputs(" ] [ ", MUNIT_OUTPUT_FILE); + munit_print_time(MUNIT_OUTPUT_FILE, report.wall_clock); + fputs(" / ", MUNIT_OUTPUT_FILE); + munit_print_time(MUNIT_OUTPUT_FILE, report.cpu_clock); + fputs(" CPU", MUNIT_OUTPUT_FILE); +#endif + runner->report.successful++; + result = MUNIT_OK; + } + fputs(" ]\n", MUNIT_OUTPUT_FILE); - if (stderr_buf != NULL) { - if (result == MUNIT_FAIL || result == MUNIT_ERROR || - runner->show_stderr) { - fflush(MUNIT_OUTPUT_FILE); + if (stderr_buf != NULL) { + if (result == MUNIT_FAIL || result == MUNIT_ERROR || runner->show_stderr) { + fflush(MUNIT_OUTPUT_FILE); - rewind(stderr_buf); - munit_splice(fileno(stderr_buf), STDERR_FILENO); + rewind(stderr_buf); + munit_splice(fileno(stderr_buf), STDERR_FILENO); - fflush(stderr); - } - - fclose(stderr_buf); + fflush(stderr); } + + fclose(stderr_buf); + } } static void munit_test_runner_run_test_wild(MunitTestRunner *runner, const MunitTest *test, - const char *test_name, MunitParameter *params, MunitParameter *p) + const char *test_name, MunitParameter *params, MunitParameter *p) { - const MunitParameterEnum *pe; - char **values; - MunitParameter *next; + const MunitParameterEnum *pe; + char **values; + MunitParameter *next; - for (pe = test->parameters; pe != NULL && pe->name != NULL; pe++) { - if (p->name == pe->name) - break; - } - - if (pe == NULL) - return; - - for (values = pe->values; *values != NULL; values++) { - next = p + 1; - p->value = *values; - if (next->name == NULL) { - munit_test_runner_run_test_with_params(runner, test, params); - } else { - munit_test_runner_run_test_wild(runner, test, test_name, params, - next); - } - if (runner->fatal_failures && - (runner->report.failed != 0 || runner->report.errored != 0)) - break; + for (pe = test->parameters; pe != NULL && pe->name != NULL; pe++) { + if (p->name == pe->name) + break; + } + + if (pe == NULL) + return; + + for (values = pe->values; *values != NULL; values++) { + next = p + 1; + p->value = *values; + if (next->name == NULL) { + munit_test_runner_run_test_with_params(runner, test, params); + } else { + munit_test_runner_run_test_wild(runner, test, test_name, params, next); } + if (runner->fatal_failures && + (runner->report.failed != 0 || runner->report.errored != 0)) + break; + } } /* Run a single test, with every combination of parameters * requested. */ static void munit_test_runner_run_test(MunitTestRunner *runner, const MunitTest *test, - const char *prefix) + const char *prefix) { - char *test_name = munit_maybe_concat(NULL, (char *)prefix, - (char *)test->name); - /* The array of parameters to pass to - * munit_test_runner_run_test_with_params */ - MunitParameter *params = NULL; - size_t params_l = 0; - /* Wildcard parameters are parameters which have possible values - * specified in the test, but no specific value was passed to the - * CLI. That means we want to run the test once for every - * possible combination of parameter values or, if --single was - * passed to the CLI, a single time with a random set of - * parameters. */ - MunitParameter *wild_params = NULL; - size_t wild_params_l = 0; - const MunitParameterEnum *pe; - const MunitParameter *cli_p; - munit_bool filled; - unsigned int possible; - char **vals; - size_t first_wild; - const MunitParameter *wp; - int pidx; + char *test_name = munit_maybe_concat(NULL, (char *)prefix, + (char *)test->name); + /* The array of parameters to pass to + * munit_test_runner_run_test_with_params */ + MunitParameter *params = NULL; + size_t params_l = 0; + /* Wildcard parameters are parameters which have possible values + * specified in the test, but no specific value was passed to the + * CLI. That means we want to run the test once for every + * possible combination of parameter values or, if --single was + * passed to the CLI, a single time with a random set of + * parameters. */ + MunitParameter *wild_params = NULL; + size_t wild_params_l = 0; + const MunitParameterEnum *pe; + const MunitParameter *cli_p; + munit_bool filled; + unsigned int possible; + char **vals; + size_t first_wild; + const MunitParameter *wp; + int pidx; - munit_rand_seed(runner->seed); + munit_rand_seed(runner->seed); - fprintf(MUNIT_OUTPUT_FILE, "%-" MUNIT_XSTRINGIFY(MUNIT_TEST_NAME_LEN) "s", - test_name); + fprintf(MUNIT_OUTPUT_FILE, "%-" MUNIT_XSTRINGIFY(MUNIT_TEST_NAME_LEN) "s", + test_name); - if (test->parameters == NULL) { - /* No parameters. Simple, nice. */ - munit_test_runner_run_test_with_params(runner, test, NULL); - } else { - fputc('\n', MUNIT_OUTPUT_FILE); + if (test->parameters == NULL) { + /* No parameters. Simple, nice. */ + munit_test_runner_run_test_with_params(runner, test, NULL); + } else { + fputc('\n', MUNIT_OUTPUT_FILE); - for (pe = test->parameters; pe != NULL && pe->name != NULL; pe++) { - /* Did we received a value for this parameter from the CLI? */ - filled = 0; - for (cli_p = runner->parameters; - cli_p != NULL && cli_p->name != NULL; cli_p++) { - if (strcmp(cli_p->name, pe->name) == 0) { - if (MUNIT_UNLIKELY(munit_parameters_add(¶ms_l, ¶ms, - pe->name, cli_p->value) != MUNIT_OK)) - goto cleanup; - filled = 1; - break; - } - } - if (filled) - continue; - - /* Nothing from CLI, is the enum NULL/empty? We're not a - * fuzzer… */ - if (pe->values == NULL || pe->values[0] == NULL) - continue; - - /* If --single was passed to the CLI, choose a value from the - * list of possibilities randomly. */ - if (runner->single_parameter_mode) { - possible = 0; - for (vals = pe->values; *vals != NULL; vals++) - possible++; - /* We want the tests to be reproducible, even if you're only - * running a single test, but we don't want every test with - * the same number of parameters to choose the same parameter - * number, so use the test name as a primitive salt. */ - pidx = munit_rand_at_most(munit_str_hash(test_name), - possible - 1); - if (MUNIT_UNLIKELY(munit_parameters_add(¶ms_l, ¶ms, - pe->name, pe->values[pidx]) != MUNIT_OK)) - goto cleanup; - } else { - /* We want to try every permutation. Put in a placeholder - * entry, we'll iterate through them later. */ - if (MUNIT_UNLIKELY( - munit_parameters_add(&wild_params_l, &wild_params, - pe->name, NULL) != MUNIT_OK)) - goto cleanup; - } + for (pe = test->parameters; pe != NULL && pe->name != NULL; pe++) { + /* Did we received a value for this parameter from the CLI? */ + filled = 0; + for (cli_p = runner->parameters; cli_p != NULL && cli_p->name != NULL; + cli_p++) { + if (strcmp(cli_p->name, pe->name) == 0) { + if (MUNIT_UNLIKELY(munit_parameters_add(¶ms_l, ¶ms, pe->name, + cli_p->value) != MUNIT_OK)) + goto cleanup; + filled = 1; + break; } + } + if (filled) + continue; - if (wild_params_l != 0) { - first_wild = params_l; - for (wp = wild_params; wp != NULL && wp->name != NULL; wp++) { - for (pe = test->parameters; - pe != NULL && pe->name != NULL && pe->values != NULL; - pe++) { - if (strcmp(wp->name, pe->name) == 0) { - if (MUNIT_UNLIKELY( - munit_parameters_add(¶ms_l, ¶ms, - pe->name, pe->values[0]) != MUNIT_OK)) - goto cleanup; - } - } - } + /* Nothing from CLI, is the enum NULL/empty? We're not a + * fuzzer… */ + if (pe->values == NULL || pe->values[0] == NULL) + continue; - munit_test_runner_run_test_wild(runner, test, test_name, params, - params + first_wild); - } else { - munit_test_runner_run_test_with_params(runner, test, params); - } - - cleanup: - free(params); - free(wild_params); + /* If --single was passed to the CLI, choose a value from the + * list of possibilities randomly. */ + if (runner->single_parameter_mode) { + possible = 0; + for (vals = pe->values; *vals != NULL; vals++) + possible++; + /* We want the tests to be reproducible, even if you're only + * running a single test, but we don't want every test with + * the same number of parameters to choose the same parameter + * number, so use the test name as a primitive salt. */ + pidx = munit_rand_at_most(munit_str_hash(test_name), possible - 1); + if (MUNIT_UNLIKELY(munit_parameters_add(¶ms_l, ¶ms, pe->name, + pe->values[pidx]) != MUNIT_OK)) + goto cleanup; + } else { + /* We want to try every permutation. Put in a placeholder + * entry, we'll iterate through them later. */ + if (MUNIT_UNLIKELY(munit_parameters_add(&wild_params_l, &wild_params, + pe->name, NULL) != MUNIT_OK)) + goto cleanup; + } } - munit_maybe_free_concat(test_name, prefix, test->name); + if (wild_params_l != 0) { + first_wild = params_l; + for (wp = wild_params; wp != NULL && wp->name != NULL; wp++) { + for (pe = test->parameters; + pe != NULL && pe->name != NULL && pe->values != NULL; pe++) { + if (strcmp(wp->name, pe->name) == 0) { + if (MUNIT_UNLIKELY(munit_parameters_add(¶ms_l, ¶ms, + pe->name, pe->values[0]) != MUNIT_OK)) + goto cleanup; + } + } + } + + munit_test_runner_run_test_wild(runner, test, test_name, params, + params + first_wild); + } else { + munit_test_runner_run_test_with_params(runner, test, params); + } + + cleanup: + free(params); + free(wild_params); + } + + munit_maybe_free_concat(test_name, prefix, test->name); } /* Recurse through the suite and run all the tests. If a list of @@ -1844,444 +1824,432 @@ munit_test_runner_run_test(MunitTestRunner *runner, const MunitTest *test, * tests. */ static void munit_test_runner_run_suite(MunitTestRunner *runner, const MunitSuite *suite, - const char *prefix) + const char *prefix) { - size_t pre_l; - char *pre = munit_maybe_concat(&pre_l, (char *)prefix, - (char *)suite->prefix); - const MunitTest *test; - const char **test_name; - const MunitSuite *child_suite; + size_t pre_l; + char *pre = munit_maybe_concat(&pre_l, (char *)prefix, (char *)suite->prefix); + const MunitTest *test; + const char **test_name; + const MunitSuite *child_suite; - /* Run the tests. */ - for (test = suite->tests; test != NULL && test->test != NULL; test++) { - if (runner->tests != - NULL) { /* Specific tests were requested on the CLI */ - for (test_name = runner->tests; - test_name != NULL && *test_name != NULL; test_name++) { - if ((pre_l == 0 || strncmp(pre, *test_name, pre_l) == 0) && - strncmp(test->name, *test_name + pre_l, - strlen(*test_name + pre_l)) == 0) { - munit_test_runner_run_test(runner, test, pre); - if (runner->fatal_failures && - (runner->report.failed != 0 || - runner->report.errored != 0)) - goto cleanup; - } - } - } else { /* Run all tests */ - munit_test_runner_run_test(runner, test, pre); + /* Run the tests. */ + for (test = suite->tests; test != NULL && test->test != NULL; test++) { + if (runner->tests != NULL) { /* Specific tests were requested on the CLI */ + for (test_name = runner->tests; test_name != NULL && *test_name != NULL; + test_name++) { + if ((pre_l == 0 || strncmp(pre, *test_name, pre_l) == 0) && + strncmp(test->name, *test_name + pre_l, strlen(*test_name + pre_l)) == + 0) { + munit_test_runner_run_test(runner, test, pre); + if (runner->fatal_failures && + (runner->report.failed != 0 || runner->report.errored != 0)) + goto cleanup; } + } + } else { /* Run all tests */ + munit_test_runner_run_test(runner, test, pre); } + } - if (runner->fatal_failures && - (runner->report.failed != 0 || runner->report.errored != 0)) - goto cleanup; + if (runner->fatal_failures && + (runner->report.failed != 0 || runner->report.errored != 0)) + goto cleanup; - /* Run any child suites. */ - for (child_suite = suite->suites; - child_suite != NULL && child_suite->prefix != NULL; child_suite++) { - munit_test_runner_run_suite(runner, child_suite, pre); - } + /* Run any child suites. */ + for (child_suite = suite->suites; + child_suite != NULL && child_suite->prefix != NULL; child_suite++) { + munit_test_runner_run_suite(runner, child_suite, pre); + } cleanup: - munit_maybe_free_concat(pre, prefix, suite->prefix); + munit_maybe_free_concat(pre, prefix, suite->prefix); } static void munit_test_runner_run(MunitTestRunner *runner) { - munit_test_runner_run_suite(runner, runner->suite, NULL); + munit_test_runner_run_suite(runner, runner->suite, NULL); } static void munit_print_help(int argc, char *const argv[MUNIT_ARRAY_PARAM(argc + 1)], - void *user_data, const MunitArgument arguments[]) + void *user_data, const MunitArgument arguments[]) { - const MunitArgument *arg; - (void)argc; + const MunitArgument *arg; + (void)argc; - printf("USAGE: %s [OPTIONS...] [TEST...]\n\n", argv[0]); - puts( - " --seed SEED\n" - " Value used to seed the PRNG. Must be a 32-bit integer in decimal\n" - " notation with no separators (commas, decimals, spaces, etc.), or\n" - " hexidecimal prefixed by \"0x\".\n" - " --iterations N\n" - " Run each test N times. 0 means the default number.\n" - " --param name value\n" - " A parameter key/value pair which will be passed to any test with\n" - " takes a parameter of that name. If not provided, the test will be\n" - " run once for each possible parameter value.\n" - " --list Write a list of all available tests.\n" - " --list-params\n" - " Write a list of all available tests and their possible parameters.\n" - " --single Run each parameterized test in a single configuration instead of\n" - " every possible combination\n" - " --log-visible debug|info|warning|error\n" - " --log-fatal debug|info|warning|error\n" - " Set the level at which messages of different severities are visible,\n" - " or cause the test to terminate.\n" + printf("USAGE: %s [OPTIONS...] [TEST...]\n\n", argv[0]); + puts( + " --seed SEED\n" + " Value used to seed the PRNG. Must be a 32-bit integer in decimal\n" + " notation with no separators (commas, decimals, spaces, etc.), or\n" + " hexidecimal prefixed by \"0x\".\n" + " --iterations N\n" + " Run each test N times. 0 means the default number.\n" + " --param name value\n" + " A parameter key/value pair which will be passed to any test with\n" + " takes a parameter of that name. If not provided, the test will be\n" + " run once for each possible parameter value.\n" + " --list Write a list of all available tests.\n" + " --list-params\n" + " Write a list of all available tests and their possible parameters.\n" + " --single Run each parameterized test in a single configuration instead of\n" + " every possible combination\n" + " --log-visible debug|info|warning|error\n" + " --log-fatal debug|info|warning|error\n" + " Set the level at which messages of different severities are visible,\n" + " or cause the test to terminate.\n" #if !defined(MUNIT_NO_FORK) - " --no-fork Do not execute tests in a child process. If this option is supplied\n" - " and a test crashes (including by failing an assertion), no further\n" - " tests will be performed.\n" + " --no-fork Do not execute tests in a child process. If this option is supplied\n" + " and a test crashes (including by failing an assertion), no further\n" + " tests will be performed.\n" #endif - " --fatal-failures\n" - " Stop executing tests as soon as a failure is found.\n" - " --show-stderr\n" - " Show data written to stderr by the tests, even if the test succeeds.\n" - " --color auto|always|never\n" - " Colorize (or don't) the output.\n" - /* 12345678901234567890123456789012345678901234567890123456789012345678901234567890 - */ - " --help Print this help message and exit.\n"); + " --fatal-failures\n" + " Stop executing tests as soon as a failure is found.\n" + " --show-stderr\n" + " Show data written to stderr by the tests, even if the test succeeds.\n" + " --color auto|always|never\n" + " Colorize (or don't) the output.\n" + /* 12345678901234567890123456789012345678901234567890123456789012345678901234567890 + */ + " --help Print this help message and exit.\n"); #if defined(MUNIT_NL_LANGINFO) - setlocale(LC_ALL, ""); - fputs((strcasecmp("UTF-8", nl_langinfo(CODESET)) == 0) ? "µnit" : "munit", - stdout); + setlocale(LC_ALL, ""); + fputs((strcasecmp("UTF-8", nl_langinfo(CODESET)) == 0) ? "µnit" : "munit", + stdout); #else - puts("munit"); + puts("munit"); #endif - printf(" %d.%d.%d\n" - "Full documentation at: https://nemequ.github.io/munit/\n", - (MUNIT_CURRENT_VERSION >> 16) & 0xff, - (MUNIT_CURRENT_VERSION >> 8) & 0xff, - (MUNIT_CURRENT_VERSION >> 0) & 0xff); - for (arg = arguments; arg != NULL && arg->name != NULL; arg++) - arg->write_help(arg, user_data); + printf(" %d.%d.%d\n" + "Full documentation at: https://nemequ.github.io/munit/\n", + (MUNIT_CURRENT_VERSION >> 16) & 0xff, (MUNIT_CURRENT_VERSION >> 8) & 0xff, + (MUNIT_CURRENT_VERSION >> 0) & 0xff); + for (arg = arguments; arg != NULL && arg->name != NULL; arg++) + arg->write_help(arg, user_data); } static const MunitArgument * munit_arguments_find(const MunitArgument arguments[], const char *name) { - const MunitArgument *arg; + const MunitArgument *arg; - for (arg = arguments; arg != NULL && arg->name != NULL; arg++) - if (strcmp(arg->name, name) == 0) - return arg; + for (arg = arguments; arg != NULL && arg->name != NULL; arg++) + if (strcmp(arg->name, name) == 0) + return arg; - return NULL; + return NULL; } static void munit_suite_list_tests(const MunitSuite *suite, munit_bool show_params, - const char *prefix) + const char *prefix) { - size_t pre_l; - char *pre = munit_maybe_concat(&pre_l, (char *)prefix, - (char *)suite->prefix); - const MunitTest *test; - const MunitParameterEnum *params; - munit_bool first; - char **val; - const MunitSuite *child_suite; + size_t pre_l; + char *pre = munit_maybe_concat(&pre_l, (char *)prefix, (char *)suite->prefix); + const MunitTest *test; + const MunitParameterEnum *params; + munit_bool first; + char **val; + const MunitSuite *child_suite; - for (test = suite->tests; test != NULL && test->name != NULL; test++) { - if (pre != NULL) - fputs(pre, stdout); - puts(test->name); + for (test = suite->tests; test != NULL && test->name != NULL; test++) { + if (pre != NULL) + fputs(pre, stdout); + puts(test->name); - if (show_params) { - for (params = test->parameters; - params != NULL && params->name != NULL; params++) { - fprintf(stdout, " - %s: ", params->name); - if (params->values == NULL) { - puts("Any"); - } else { - first = 1; - for (val = params->values; *val != NULL; val++) { - if (!first) { - fputs(", ", stdout); - } else { - first = 0; - } - fputs(*val, stdout); - } - putc('\n', stdout); - } + if (show_params) { + for (params = test->parameters; params != NULL && params->name != NULL; + params++) { + fprintf(stdout, " - %s: ", params->name); + if (params->values == NULL) { + puts("Any"); + } else { + first = 1; + for (val = params->values; *val != NULL; val++) { + if (!first) { + fputs(", ", stdout); + } else { + first = 0; } + fputs(*val, stdout); + } + putc('\n', stdout); } + } } + } - for (child_suite = suite->suites; - child_suite != NULL && child_suite->prefix != NULL; child_suite++) { - munit_suite_list_tests(child_suite, show_params, pre); - } + for (child_suite = suite->suites; + child_suite != NULL && child_suite->prefix != NULL; child_suite++) { + munit_suite_list_tests(child_suite, show_params, pre); + } - munit_maybe_free_concat(pre, prefix, suite->prefix); + munit_maybe_free_concat(pre, prefix, suite->prefix); } static munit_bool munit_stream_supports_ansi(FILE *stream) { #if !defined(_WIN32) - return isatty(fileno(stream)); + return isatty(fileno(stream)); #else #if !defined(__MINGW32__) - size_t ansicon_size = 0; + size_t ansicon_size = 0; #endif - if (isatty(fileno(stream))) { + if (isatty(fileno(stream))) { #if !defined(__MINGW32__) - getenv_s(&ansicon_size, NULL, 0, "ANSICON"); - return ansicon_size != 0; + getenv_s(&ansicon_size, NULL, 0, "ANSICON"); + return ansicon_size != 0; #else - return getenv("ANSICON") != NULL; + return getenv("ANSICON") != NULL; #endif - } - return 0; + } + return 0; #endif } int munit_suite_main_custom(const MunitSuite *suite, void *user_data, int argc, - char *const argv[MUNIT_ARRAY_PARAM(argc + 1)], - const MunitArgument arguments[]) + char *const argv[MUNIT_ARRAY_PARAM(argc + 1)], + const MunitArgument arguments[]) { - int result = EXIT_FAILURE; - MunitTestRunner runner; - size_t parameters_size = 0; - size_t tests_size = 0; - int arg; + int result = EXIT_FAILURE; + MunitTestRunner runner; + size_t parameters_size = 0; + size_t tests_size = 0; + int arg; - char *envptr; - unsigned long ts; - char *endptr; - unsigned long long iterations; - MunitLogLevel level; - const MunitArgument *argument; - const char **runner_tests; - unsigned int tests_run; - unsigned int tests_total; + char *envptr; + unsigned long ts; + char *endptr; + unsigned long long iterations; + MunitLogLevel level; + const MunitArgument *argument; + const char **runner_tests; + unsigned int tests_run; + unsigned int tests_total; - runner.prefix = NULL; - runner.suite = NULL; - runner.tests = NULL; - runner.seed = 0; - runner.iterations = 0; - runner.parameters = NULL; - runner.single_parameter_mode = 0; - runner.user_data = NULL; + runner.prefix = NULL; + runner.suite = NULL; + runner.tests = NULL; + runner.seed = 0; + runner.iterations = 0; + runner.parameters = NULL; + runner.single_parameter_mode = 0; + runner.user_data = NULL; - runner.report.successful = 0; - runner.report.skipped = 0; - runner.report.failed = 0; - runner.report.errored = 0; + runner.report.successful = 0; + runner.report.skipped = 0; + runner.report.failed = 0; + runner.report.errored = 0; #if defined(MUNIT_ENABLE_TIMING) - runner.report.cpu_clock = 0; - runner.report.wall_clock = 0; + runner.report.cpu_clock = 0; + runner.report.wall_clock = 0; #endif - runner.colorize = 0; + runner.colorize = 0; #if !defined(_WIN32) - runner.fork = 1; + runner.fork = 1; #else - runner.fork = 0; + runner.fork = 0; #endif - runner.show_stderr = 0; - runner.fatal_failures = 0; - runner.suite = suite; - runner.user_data = user_data; - runner.seed = munit_rand_generate_seed(); - runner.colorize = munit_stream_supports_ansi(MUNIT_OUTPUT_FILE); + runner.show_stderr = 0; + runner.fatal_failures = 0; + runner.suite = suite; + runner.user_data = user_data; + runner.seed = munit_rand_generate_seed(); + runner.colorize = munit_stream_supports_ansi(MUNIT_OUTPUT_FILE); - for (arg = 1; arg < argc; arg++) { - if (strncmp("--", argv[arg], 2) == 0) { - if (strcmp("seed", argv[arg] + 2) == 0) { - if (arg + 1 >= argc) { - munit_logf_internal(MUNIT_LOG_ERROR, stderr, - "%s requires an argument", argv[arg]); - goto cleanup; - } - - envptr = argv[arg + 1]; - ts = strtoul(argv[arg + 1], &envptr, 0); - if (*envptr != '\0' || ts > (~((munit_uint32_t)0U))) { - munit_logf_internal(MUNIT_LOG_ERROR, stderr, - "invalid value ('%s') passed to %s", argv[arg + 1], - argv[arg]); - goto cleanup; - } - runner.seed = (munit_uint32_t)ts; - - arg++; - } else if (strcmp("iterations", argv[arg] + 2) == 0) { - if (arg + 1 >= argc) { - munit_logf_internal(MUNIT_LOG_ERROR, stderr, - "%s requires an argument", argv[arg]); - goto cleanup; - } - - endptr = argv[arg + 1]; - iterations = strtoul(argv[arg + 1], &endptr, 0); - if (*endptr != '\0' || iterations > UINT_MAX) { - munit_logf_internal(MUNIT_LOG_ERROR, stderr, - "invalid value ('%s') passed to %s", argv[arg + 1], - argv[arg]); - goto cleanup; - } - - runner.iterations = (unsigned int)iterations; - - arg++; - } else if (strcmp("param", argv[arg] + 2) == 0) { - if (arg + 2 >= argc) { - munit_logf_internal(MUNIT_LOG_ERROR, stderr, - "%s requires two arguments", argv[arg]); - goto cleanup; - } - - runner.parameters = realloc(runner.parameters, - sizeof(MunitParameter) * (parameters_size + 2)); - if (runner.parameters == NULL) { - munit_log_internal(MUNIT_LOG_ERROR, stderr, - "failed to allocate memory"); - goto cleanup; - } - runner.parameters[parameters_size].name = (char *)argv[arg + 1]; - runner.parameters[parameters_size].value = (char *) - argv[arg + 2]; - parameters_size++; - runner.parameters[parameters_size].name = NULL; - runner.parameters[parameters_size].value = NULL; - arg += 2; - } else if (strcmp("color", argv[arg] + 2) == 0) { - if (arg + 1 >= argc) { - munit_logf_internal(MUNIT_LOG_ERROR, stderr, - "%s requires an argument", argv[arg]); - goto cleanup; - } - - if (strcmp(argv[arg + 1], "always") == 0) - runner.colorize = 1; - else if (strcmp(argv[arg + 1], "never") == 0) - runner.colorize = 0; - else if (strcmp(argv[arg + 1], "auto") == 0) - runner.colorize = munit_stream_supports_ansi( - MUNIT_OUTPUT_FILE); - else { - munit_logf_internal(MUNIT_LOG_ERROR, stderr, - "invalid value ('%s') passed to %s", argv[arg + 1], - argv[arg]); - goto cleanup; - } - - arg++; - } else if (strcmp("help", argv[arg] + 2) == 0) { - munit_print_help(argc, argv, user_data, arguments); - result = EXIT_SUCCESS; - goto cleanup; - } else if (strcmp("single", argv[arg] + 2) == 0) { - runner.single_parameter_mode = 1; - } else if (strcmp("show-stderr", argv[arg] + 2) == 0) { - runner.show_stderr = 1; -#if !defined(_WIN32) - } else if (strcmp("no-fork", argv[arg] + 2) == 0) { - runner.fork = 0; -#endif - } else if (strcmp("fatal-failures", argv[arg] + 2) == 0) { - runner.fatal_failures = 1; - } else if (strcmp("log-visible", argv[arg] + 2) == 0 || - strcmp("log-fatal", argv[arg] + 2) == 0) { - if (arg + 1 >= argc) { - munit_logf_internal(MUNIT_LOG_ERROR, stderr, - "%s requires an argument", argv[arg]); - goto cleanup; - } - - if (strcmp(argv[arg + 1], "debug") == 0) - level = MUNIT_LOG_DEBUG; - else if (strcmp(argv[arg + 1], "info") == 0) - level = MUNIT_LOG_INFO; - else if (strcmp(argv[arg + 1], "warning") == 0) - level = MUNIT_LOG_WARNING; - else if (strcmp(argv[arg + 1], "error") == 0) - level = MUNIT_LOG_ERROR; - else { - munit_logf_internal(MUNIT_LOG_ERROR, stderr, - "invalid value ('%s') passed to %s", argv[arg + 1], - argv[arg]); - goto cleanup; - } - - if (strcmp("log-visible", argv[arg] + 2) == 0) - munit_log_level_visible = level; - else - munit_log_level_fatal = level; - - arg++; - } else if (strcmp("list", argv[arg] + 2) == 0) { - munit_suite_list_tests(suite, 0, NULL); - result = EXIT_SUCCESS; - goto cleanup; - } else if (strcmp("list-params", argv[arg] + 2) == 0) { - munit_suite_list_tests(suite, 1, NULL); - result = EXIT_SUCCESS; - goto cleanup; - } else { - argument = munit_arguments_find(arguments, argv[arg] + 2); - if (argument == NULL) { - munit_logf_internal(MUNIT_LOG_ERROR, stderr, - "unknown argument ('%s')", argv[arg]); - goto cleanup; - } - - if (!argument->parse_argument(suite, user_data, &arg, argc, - argv)) - goto cleanup; - } - } else { - runner_tests = realloc((void *)runner.tests, - sizeof(char *) * (tests_size + 2)); - if (runner_tests == NULL) { - munit_log_internal(MUNIT_LOG_ERROR, stderr, - "failed to allocate memory"); - goto cleanup; - } - runner.tests = runner_tests; - runner.tests[tests_size++] = argv[arg]; - runner.tests[tests_size] = NULL; + for (arg = 1; arg < argc; arg++) { + if (strncmp("--", argv[arg], 2) == 0) { + if (strcmp("seed", argv[arg] + 2) == 0) { + if (arg + 1 >= argc) { + munit_logf_internal(MUNIT_LOG_ERROR, stderr, + "%s requires an argument", argv[arg]); + goto cleanup; } - } - fflush(stderr); - fprintf(MUNIT_OUTPUT_FILE, - "Running test suite with seed 0x%08" PRIx32 "...\n", runner.seed); + envptr = argv[arg + 1]; + ts = strtoul(argv[arg + 1], &envptr, 0); + if (*envptr != '\0' || ts > (~((munit_uint32_t)0U))) { + munit_logf_internal(MUNIT_LOG_ERROR, stderr, + "invalid value ('%s') passed to %s", argv[arg + 1], argv[arg]); + goto cleanup; + } + runner.seed = (munit_uint32_t)ts; - munit_test_runner_run(&runner); + arg++; + } else if (strcmp("iterations", argv[arg] + 2) == 0) { + if (arg + 1 >= argc) { + munit_logf_internal(MUNIT_LOG_ERROR, stderr, + "%s requires an argument", argv[arg]); + goto cleanup; + } - tests_run = runner.report.successful + runner.report.failed + - runner.report.errored; - tests_total = tests_run + runner.report.skipped; - if (tests_run == 0) { - fprintf(stderr, "No tests run, %d (100%%) skipped.\n", - runner.report.skipped); - } else { - fprintf(MUNIT_OUTPUT_FILE, - "%d of %d (%0.0f%%) tests successful, %d (%0.0f%%) test skipped.\n", - runner.report.successful, tests_run, - (((double)runner.report.successful) / ((double)tests_run)) * 100.0, - runner.report.skipped, - (((double)runner.report.skipped) / ((double)tests_total)) * 100.0); - } + endptr = argv[arg + 1]; + iterations = strtoul(argv[arg + 1], &endptr, 0); + if (*endptr != '\0' || iterations > UINT_MAX) { + munit_logf_internal(MUNIT_LOG_ERROR, stderr, + "invalid value ('%s') passed to %s", argv[arg + 1], argv[arg]); + goto cleanup; + } - if (runner.report.failed == 0 && runner.report.errored == 0) { + runner.iterations = (unsigned int)iterations; + + arg++; + } else if (strcmp("param", argv[arg] + 2) == 0) { + if (arg + 2 >= argc) { + munit_logf_internal(MUNIT_LOG_ERROR, stderr, + "%s requires two arguments", argv[arg]); + goto cleanup; + } + + runner.parameters = realloc(runner.parameters, + sizeof(MunitParameter) * (parameters_size + 2)); + if (runner.parameters == NULL) { + munit_log_internal(MUNIT_LOG_ERROR, stderr, + "failed to allocate memory"); + goto cleanup; + } + runner.parameters[parameters_size].name = (char *)argv[arg + 1]; + runner.parameters[parameters_size].value = (char *)argv[arg + 2]; + parameters_size++; + runner.parameters[parameters_size].name = NULL; + runner.parameters[parameters_size].value = NULL; + arg += 2; + } else if (strcmp("color", argv[arg] + 2) == 0) { + if (arg + 1 >= argc) { + munit_logf_internal(MUNIT_LOG_ERROR, stderr, + "%s requires an argument", argv[arg]); + goto cleanup; + } + + if (strcmp(argv[arg + 1], "always") == 0) + runner.colorize = 1; + else if (strcmp(argv[arg + 1], "never") == 0) + runner.colorize = 0; + else if (strcmp(argv[arg + 1], "auto") == 0) + runner.colorize = munit_stream_supports_ansi(MUNIT_OUTPUT_FILE); + else { + munit_logf_internal(MUNIT_LOG_ERROR, stderr, + "invalid value ('%s') passed to %s", argv[arg + 1], argv[arg]); + goto cleanup; + } + + arg++; + } else if (strcmp("help", argv[arg] + 2) == 0) { + munit_print_help(argc, argv, user_data, arguments); result = EXIT_SUCCESS; + goto cleanup; + } else if (strcmp("single", argv[arg] + 2) == 0) { + runner.single_parameter_mode = 1; + } else if (strcmp("show-stderr", argv[arg] + 2) == 0) { + runner.show_stderr = 1; +#if !defined(_WIN32) + } else if (strcmp("no-fork", argv[arg] + 2) == 0) { + runner.fork = 0; +#endif + } else if (strcmp("fatal-failures", argv[arg] + 2) == 0) { + runner.fatal_failures = 1; + } else if (strcmp("log-visible", argv[arg] + 2) == 0 || + strcmp("log-fatal", argv[arg] + 2) == 0) { + if (arg + 1 >= argc) { + munit_logf_internal(MUNIT_LOG_ERROR, stderr, + "%s requires an argument", argv[arg]); + goto cleanup; + } + + if (strcmp(argv[arg + 1], "debug") == 0) + level = MUNIT_LOG_DEBUG; + else if (strcmp(argv[arg + 1], "info") == 0) + level = MUNIT_LOG_INFO; + else if (strcmp(argv[arg + 1], "warning") == 0) + level = MUNIT_LOG_WARNING; + else if (strcmp(argv[arg + 1], "error") == 0) + level = MUNIT_LOG_ERROR; + else { + munit_logf_internal(MUNIT_LOG_ERROR, stderr, + "invalid value ('%s') passed to %s", argv[arg + 1], argv[arg]); + goto cleanup; + } + + if (strcmp("log-visible", argv[arg] + 2) == 0) + munit_log_level_visible = level; + else + munit_log_level_fatal = level; + + arg++; + } else if (strcmp("list", argv[arg] + 2) == 0) { + munit_suite_list_tests(suite, 0, NULL); + result = EXIT_SUCCESS; + goto cleanup; + } else if (strcmp("list-params", argv[arg] + 2) == 0) { + munit_suite_list_tests(suite, 1, NULL); + result = EXIT_SUCCESS; + goto cleanup; + } else { + argument = munit_arguments_find(arguments, argv[arg] + 2); + if (argument == NULL) { + munit_logf_internal(MUNIT_LOG_ERROR, stderr, + "unknown argument ('%s')", argv[arg]); + goto cleanup; + } + + if (!argument->parse_argument(suite, user_data, &arg, argc, argv)) + goto cleanup; + } + } else { + runner_tests = realloc((void *)runner.tests, + sizeof(char *) * (tests_size + 2)); + if (runner_tests == NULL) { + munit_log_internal(MUNIT_LOG_ERROR, stderr, + "failed to allocate memory"); + goto cleanup; + } + runner.tests = runner_tests; + runner.tests[tests_size++] = argv[arg]; + runner.tests[tests_size] = NULL; } + } + + fflush(stderr); + fprintf(MUNIT_OUTPUT_FILE, + "Running test suite with seed 0x%08" PRIx32 "...\n", runner.seed); + + munit_test_runner_run(&runner); + + tests_run = runner.report.successful + runner.report.failed + + runner.report.errored; + tests_total = tests_run + runner.report.skipped; + if (tests_run == 0) { + fprintf(stderr, "No tests run, %d (100%%) skipped.\n", + runner.report.skipped); + } else { + fprintf(MUNIT_OUTPUT_FILE, + "%d of %d (%0.0f%%) tests successful, %d (%0.0f%%) test skipped.\n", + runner.report.successful, tests_run, + (((double)runner.report.successful) / ((double)tests_run)) * 100.0, + runner.report.skipped, + (((double)runner.report.skipped) / ((double)tests_total)) * 100.0); + } + + if (runner.report.failed == 0 && runner.report.errored == 0) { + result = EXIT_SUCCESS; + } cleanup: - free(runner.parameters); - free((void *)runner.tests); + free(runner.parameters); + free((void *)runner.tests); - return result; + return result; } int munit_suite_main(const MunitSuite *suite, void *user_data, int argc, - char *const argv[MUNIT_ARRAY_PARAM(argc + 1)]) + char *const argv[MUNIT_ARRAY_PARAM(argc + 1)]) { - return munit_suite_main_custom(suite, user_data, argc, argv, NULL); + return munit_suite_main_custom(suite, user_data, argc, argv, NULL); } diff --git a/tests/munit.h b/tests/munit.h index 87c7615..28fbf8b 100644 --- a/tests/munit.h +++ b/tests/munit.h @@ -29,7 +29,7 @@ #include #define MUNIT_VERSION(major, minor, revision) \ - (((major) << 16) | ((minor) << 8) | (revision)) + (((major) << 16) | ((minor) << 8) | (revision)) #define MUNIT_CURRENT_VERSION MUNIT_VERSION(0, 4, 1) @@ -132,7 +132,7 @@ extern "C" { #endif #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ - !defined(__PGI) + !defined(__PGI) #define MUNIT_ARRAY_PARAM(name) name #else #define MUNIT_ARRAY_PARAM(name) @@ -164,7 +164,7 @@ extern "C" { #if defined(_MSC_VER) && (_MSC_VER >= 1500) #define MUNIT_PUSH_DISABLE_MSVC_C4127_ \ - __pragma(warning(push)) __pragma(warning(disable : 4127)) + __pragma(warning(push)) __pragma(warning(disable : 4127)) #define MUNIT_POP_DISABLE_MSVC_C4127_ __pragma(warning(pop)) #else #define MUNIT_PUSH_DISABLE_MSVC_C4127_ @@ -172,25 +172,25 @@ extern "C" { #endif typedef enum { - MUNIT_LOG_DEBUG, - MUNIT_LOG_INFO, - MUNIT_LOG_WARNING, - MUNIT_LOG_ERROR + MUNIT_LOG_DEBUG, + MUNIT_LOG_INFO, + MUNIT_LOG_WARNING, + MUNIT_LOG_ERROR } MunitLogLevel; #if defined(__GNUC__) && !defined(__MINGW32__) #define MUNIT_PRINTF(string_index, first_to_check) \ - __attribute__((format(printf, string_index, first_to_check))) + __attribute__((format(printf, string_index, first_to_check))) #else #define MUNIT_PRINTF(string_index, first_to_check) #endif MUNIT_PRINTF(4, 5) void munit_logf_ex(MunitLogLevel level, const char *filename, int line, - const char *format, ...); + const char *format, ...); #define munit_logf(level, format, ...) \ - munit_logf_ex(level, __FILE__, __LINE__, format, __VA_ARGS__) + munit_logf_ex(level, __FILE__, __LINE__, format, __VA_ARGS__) #define munit_log(level, msg) munit_logf(level, "%s", msg) @@ -199,170 +199,167 @@ MUNIT_PRINTF(3, 4) void munit_errorf_ex(const char *filename, int line, const char *format, ...); #define munit_errorf(format, ...) \ - munit_errorf_ex(__FILE__, __LINE__, format, __VA_ARGS__) + munit_errorf_ex(__FILE__, __LINE__, format, __VA_ARGS__) #define munit_error(msg) munit_errorf("%s", msg) -#define munit_assert(expr) \ - do { \ - if (!MUNIT_LIKELY(expr)) { \ - munit_error("assertion failed: " #expr); \ - } \ - MUNIT_PUSH_DISABLE_MSVC_C4127_ \ - } while (0) MUNIT_POP_DISABLE_MSVC_C4127_ +#define munit_assert(expr) \ + do { \ + if (!MUNIT_LIKELY(expr)) { \ + munit_error("assertion failed: " #expr); \ + } \ + MUNIT_PUSH_DISABLE_MSVC_C4127_ \ + } while (0) MUNIT_POP_DISABLE_MSVC_C4127_ -#define munit_assert_true(expr) \ - do { \ - if (!MUNIT_LIKELY(expr)) { \ - munit_error("assertion failed: " #expr " is not true"); \ - } \ - MUNIT_PUSH_DISABLE_MSVC_C4127_ \ - } while (0) MUNIT_POP_DISABLE_MSVC_C4127_ +#define munit_assert_true(expr) \ + do { \ + if (!MUNIT_LIKELY(expr)) { \ + munit_error("assertion failed: " #expr " is not true"); \ + } \ + MUNIT_PUSH_DISABLE_MSVC_C4127_ \ + } while (0) MUNIT_POP_DISABLE_MSVC_C4127_ -#define munit_assert_false(expr) \ - do { \ - if (!MUNIT_LIKELY(!(expr))) { \ - munit_error("assertion failed: " #expr " is not false"); \ - } \ - MUNIT_PUSH_DISABLE_MSVC_C4127_ \ - } while (0) MUNIT_POP_DISABLE_MSVC_C4127_ +#define munit_assert_false(expr) \ + do { \ + if (!MUNIT_LIKELY(!(expr))) { \ + munit_error("assertion failed: " #expr " is not false"); \ + } \ + MUNIT_PUSH_DISABLE_MSVC_C4127_ \ + } while (0) MUNIT_POP_DISABLE_MSVC_C4127_ -#define munit_assert_type_full(prefix, suffix, T, fmt, a, op, b) \ - do { \ - T munit_tmp_a_ = (a); \ - T munit_tmp_b_ = (b); \ - if (!(munit_tmp_a_ op munit_tmp_b_)) { \ - munit_errorf("assertion failed: %s %s %s (" prefix "%" fmt suffix \ - " %s " prefix "%" fmt suffix ")", \ - #a, #op, #b, munit_tmp_a_, #op, munit_tmp_b_); \ - } \ - MUNIT_PUSH_DISABLE_MSVC_C4127_ \ - } while (0) MUNIT_POP_DISABLE_MSVC_C4127_ +#define munit_assert_type_full(prefix, suffix, T, fmt, a, op, b) \ + do { \ + T munit_tmp_a_ = (a); \ + T munit_tmp_b_ = (b); \ + if (!(munit_tmp_a_ op munit_tmp_b_)) { \ + munit_errorf("assertion failed: %s %s %s (" prefix "%" fmt suffix \ + " %s " prefix "%" fmt suffix ")", \ + #a, #op, #b, munit_tmp_a_, #op, munit_tmp_b_); \ + } \ + MUNIT_PUSH_DISABLE_MSVC_C4127_ \ + } while (0) MUNIT_POP_DISABLE_MSVC_C4127_ #define munit_assert_type(T, fmt, a, op, b) \ - munit_assert_type_full("", "", T, fmt, a, op, b) + munit_assert_type_full("", "", T, fmt, a, op, b) -#define munit_assert_char(a, op, b) \ - munit_assert_type_full("'\\x", "'", char, "02" MUNIT_CHAR_MODIFIER "x", a, \ - op, b) -#define munit_assert_uchar(a, op, b) \ - munit_assert_type_full("'\\x", "'", unsigned char, \ - "02" MUNIT_CHAR_MODIFIER "x", a, op, b) +#define munit_assert_char(a, op, b) \ + munit_assert_type_full("'\\x", "'", char, "02" MUNIT_CHAR_MODIFIER "x", a, \ + op, b) +#define munit_assert_uchar(a, op, b) \ + munit_assert_type_full("'\\x", "'", unsigned char, \ + "02" MUNIT_CHAR_MODIFIER "x", a, op, b) #define munit_assert_short(a, op, b) \ - munit_assert_type(short, MUNIT_SHORT_MODIFIER "d", a, op, b) + munit_assert_type(short, MUNIT_SHORT_MODIFIER "d", a, op, b) #define munit_assert_ushort(a, op, b) \ - munit_assert_type(unsigned short, MUNIT_SHORT_MODIFIER "u", a, op, b) + munit_assert_type(unsigned short, MUNIT_SHORT_MODIFIER "u", a, op, b) #define munit_assert_int(a, op, b) munit_assert_type(int, "d", a, op, b) #define munit_assert_uint(a, op, b) \ - munit_assert_type(unsigned int, "u", a, op, b) + munit_assert_type(unsigned int, "u", a, op, b) #define munit_assert_long(a, op, b) munit_assert_type(long int, "ld", a, op, b) #define munit_assert_ulong(a, op, b) \ - munit_assert_type(unsigned long int, "lu", a, op, b) + munit_assert_type(unsigned long int, "lu", a, op, b) #define munit_assert_llong(a, op, b) \ - munit_assert_type(long long int, "lld", a, op, b) + munit_assert_type(long long int, "lld", a, op, b) #define munit_assert_ullong(a, op, b) \ - munit_assert_type(unsigned long long int, "llu", a, op, b) + munit_assert_type(unsigned long long int, "llu", a, op, b) #define munit_assert_size(a, op, b) \ - munit_assert_type(size_t, MUNIT_SIZE_MODIFIER "u", a, op, b) + munit_assert_type(size_t, MUNIT_SIZE_MODIFIER "u", a, op, b) #define munit_assert_float(a, op, b) munit_assert_type(float, "f", a, op, b) #define munit_assert_double(a, op, b) munit_assert_type(double, "g", a, op, b) #define munit_assert_ptr(a, op, b) \ - munit_assert_type(const void *, "p", a, op, b) + munit_assert_type(const void *, "p", a, op, b) #define munit_assert_int8(a, op, b) \ - munit_assert_type(munit_int8_t, PRIi8, a, op, b) + munit_assert_type(munit_int8_t, PRIi8, a, op, b) #define munit_assert_uint8(a, op, b) \ - munit_assert_type(munit_uint8_t, PRIu8, a, op, b) + munit_assert_type(munit_uint8_t, PRIu8, a, op, b) #define munit_assert_int16(a, op, b) \ - munit_assert_type(munit_int16_t, PRIi16, a, op, b) + munit_assert_type(munit_int16_t, PRIi16, a, op, b) #define munit_assert_uint16(a, op, b) \ - munit_assert_type(munit_uint16_t, PRIu16, a, op, b) + munit_assert_type(munit_uint16_t, PRIu16, a, op, b) #define munit_assert_int32(a, op, b) \ - munit_assert_type(munit_int32_t, PRIi32, a, op, b) + munit_assert_type(munit_int32_t, PRIi32, a, op, b) #define munit_assert_uint32(a, op, b) \ - munit_assert_type(munit_uint32_t, PRIu32, a, op, b) + munit_assert_type(munit_uint32_t, PRIu32, a, op, b) #define munit_assert_int64(a, op, b) \ - munit_assert_type(munit_int64_t, PRIi64, a, op, b) + munit_assert_type(munit_int64_t, PRIi64, a, op, b) #define munit_assert_uint64(a, op, b) \ - munit_assert_type(munit_uint64_t, PRIu64, a, op, b) + munit_assert_type(munit_uint64_t, PRIu64, a, op, b) -#define munit_assert_double_equal(a, b, precision) \ - do { \ - const double munit_tmp_a_ = (a); \ - const double munit_tmp_b_ = (b); \ - const double munit_tmp_diff_ = ((munit_tmp_a_ - munit_tmp_b_) < 0) ? \ - -(munit_tmp_a_ - munit_tmp_b_) : \ - (munit_tmp_a_ - munit_tmp_b_); \ - if (MUNIT_UNLIKELY(munit_tmp_diff_ > 1e-##precision)) { \ - munit_errorf("assertion failed: %s == %s (%0." #precision \ - "g == %0." #precision "g)", \ - #a, #b, munit_tmp_a_, munit_tmp_b_); \ - } \ - MUNIT_PUSH_DISABLE_MSVC_C4127_ \ - } while (0) MUNIT_POP_DISABLE_MSVC_C4127_ +#define munit_assert_double_equal(a, b, precision) \ + do { \ + const double munit_tmp_a_ = (a); \ + const double munit_tmp_b_ = (b); \ + const double munit_tmp_diff_ = ((munit_tmp_a_ - munit_tmp_b_) < 0) ? \ + -(munit_tmp_a_ - munit_tmp_b_) : \ + (munit_tmp_a_ - munit_tmp_b_); \ + if (MUNIT_UNLIKELY(munit_tmp_diff_ > 1e-##precision)) { \ + munit_errorf("assertion failed: %s == %s (%0." #precision \ + "g == %0." #precision "g)", \ + #a, #b, munit_tmp_a_, munit_tmp_b_); \ + } \ + MUNIT_PUSH_DISABLE_MSVC_C4127_ \ + } while (0) MUNIT_POP_DISABLE_MSVC_C4127_ #include -#define munit_assert_string_equal(a, b) \ - do { \ - const char *munit_tmp_a_ = a; \ - const char *munit_tmp_b_ = b; \ - if (MUNIT_UNLIKELY(strcmp(munit_tmp_a_, munit_tmp_b_) != 0)) { \ - munit_errorf( \ - "assertion failed: string %s == %s (\"%s\" == \"%s\")", #a, \ - #b, munit_tmp_a_, munit_tmp_b_); \ - } \ - MUNIT_PUSH_DISABLE_MSVC_C4127_ \ - } while (0) MUNIT_POP_DISABLE_MSVC_C4127_ +#define munit_assert_string_equal(a, b) \ + do { \ + const char *munit_tmp_a_ = a; \ + const char *munit_tmp_b_ = b; \ + if (MUNIT_UNLIKELY(strcmp(munit_tmp_a_, munit_tmp_b_) != 0)) { \ + munit_errorf("assertion failed: string %s == %s (\"%s\" == \"%s\")", #a, \ + #b, munit_tmp_a_, munit_tmp_b_); \ + } \ + MUNIT_PUSH_DISABLE_MSVC_C4127_ \ + } while (0) MUNIT_POP_DISABLE_MSVC_C4127_ -#define munit_assert_string_not_equal(a, b) \ - do { \ - const char *munit_tmp_a_ = a; \ - const char *munit_tmp_b_ = b; \ - if (MUNIT_UNLIKELY(strcmp(munit_tmp_a_, munit_tmp_b_) == 0)) { \ - munit_errorf( \ - "assertion failed: string %s != %s (\"%s\" == \"%s\")", #a, \ - #b, munit_tmp_a_, munit_tmp_b_); \ - } \ - MUNIT_PUSH_DISABLE_MSVC_C4127_ \ - } while (0) MUNIT_POP_DISABLE_MSVC_C4127_ +#define munit_assert_string_not_equal(a, b) \ + do { \ + const char *munit_tmp_a_ = a; \ + const char *munit_tmp_b_ = b; \ + if (MUNIT_UNLIKELY(strcmp(munit_tmp_a_, munit_tmp_b_) == 0)) { \ + munit_errorf("assertion failed: string %s != %s (\"%s\" == \"%s\")", #a, \ + #b, munit_tmp_a_, munit_tmp_b_); \ + } \ + MUNIT_PUSH_DISABLE_MSVC_C4127_ \ + } while (0) MUNIT_POP_DISABLE_MSVC_C4127_ -#define munit_assert_memory_equal(size, a, b) \ - do { \ - const unsigned char *munit_tmp_a_ = (const unsigned char *)(a); \ - const unsigned char *munit_tmp_b_ = (const unsigned char *)(b); \ - const size_t munit_tmp_size_ = (size); \ - if (MUNIT_UNLIKELY( \ - memcmp(munit_tmp_a_, munit_tmp_b_, munit_tmp_size_)) != 0) { \ - size_t munit_tmp_pos_; \ - for (munit_tmp_pos_ = 0; munit_tmp_pos_ < munit_tmp_size_; \ - munit_tmp_pos_++) { \ - if (munit_tmp_a_[munit_tmp_pos_] != \ - munit_tmp_b_[munit_tmp_pos_]) { \ - munit_errorf( \ - "assertion failed: memory %s == %s, at offset %" MUNIT_SIZE_MODIFIER \ - "u", \ - #a, #b, munit_tmp_pos_); \ - break; \ - } \ - } \ - } \ - MUNIT_PUSH_DISABLE_MSVC_C4127_ \ - } while (0) MUNIT_POP_DISABLE_MSVC_C4127_ +#define munit_assert_memory_equal(size, a, b) \ + do { \ + const unsigned char *munit_tmp_a_ = (const unsigned char *)(a); \ + const unsigned char *munit_tmp_b_ = (const unsigned char *)(b); \ + const size_t munit_tmp_size_ = (size); \ + if (MUNIT_UNLIKELY(memcmp(munit_tmp_a_, munit_tmp_b_, munit_tmp_size_)) != \ + 0) { \ + size_t munit_tmp_pos_; \ + for (munit_tmp_pos_ = 0; munit_tmp_pos_ < munit_tmp_size_; \ + munit_tmp_pos_++) { \ + if (munit_tmp_a_[munit_tmp_pos_] != munit_tmp_b_[munit_tmp_pos_]) { \ + munit_errorf( \ + "assertion failed: memory %s == %s, at offset %" MUNIT_SIZE_MODIFIER \ + "u", \ + #a, #b, munit_tmp_pos_); \ + break; \ + } \ + } \ + } \ + MUNIT_PUSH_DISABLE_MSVC_C4127_ \ + } while (0) MUNIT_POP_DISABLE_MSVC_C4127_ -#define munit_assert_memory_not_equal(size, a, b) \ - do { \ - const unsigned char *munit_tmp_a_ = (const unsigned char *)(a); \ - const unsigned char *munit_tmp_b_ = (const unsigned char *)(b); \ - const size_t munit_tmp_size_ = (size); \ - if (MUNIT_UNLIKELY( \ - memcmp(munit_tmp_a_, munit_tmp_b_, munit_tmp_size_)) == 0) { \ - munit_errorf("assertion failed: memory %s != %s (%zu bytes)", #a, \ - #b, munit_tmp_size_); \ - } \ - MUNIT_PUSH_DISABLE_MSVC_C4127_ \ - } while (0) MUNIT_POP_DISABLE_MSVC_C4127_ +#define munit_assert_memory_not_equal(size, a, b) \ + do { \ + const unsigned char *munit_tmp_a_ = (const unsigned char *)(a); \ + const unsigned char *munit_tmp_b_ = (const unsigned char *)(b); \ + const size_t munit_tmp_size_ = (size); \ + if (MUNIT_UNLIKELY(memcmp(munit_tmp_a_, munit_tmp_b_, munit_tmp_size_)) == \ + 0) { \ + munit_errorf("assertion failed: memory %s != %s (%zu bytes)", #a, #b, \ + munit_tmp_size_); \ + } \ + MUNIT_PUSH_DISABLE_MSVC_C4127_ \ + } while (0) MUNIT_POP_DISABLE_MSVC_C4127_ #define munit_assert_ptr_equal(a, b) munit_assert_ptr(a, ==, b) #define munit_assert_ptr_not_equal(a, b) munit_assert_ptr(a, !=, b) @@ -390,54 +387,54 @@ munit_uint32_t munit_rand_uint32(void); int munit_rand_int_range(int min, int max); double munit_rand_double(void); void munit_rand_memory(size_t size, - munit_uint8_t buffer[MUNIT_ARRAY_PARAM(size)]); + munit_uint8_t buffer[MUNIT_ARRAY_PARAM(size)]); /*** Tests and Suites ***/ typedef enum { - /* Test successful */ - MUNIT_OK, - /* Test failed */ - MUNIT_FAIL, - /* Test was skipped */ - MUNIT_SKIP, - /* Test failed due to circumstances not intended to be tested - * (things like network errors, invalid parameter value, failure to - * allocate memory in the test harness, etc.). */ - MUNIT_ERROR + /* Test successful */ + MUNIT_OK, + /* Test failed */ + MUNIT_FAIL, + /* Test was skipped */ + MUNIT_SKIP, + /* Test failed due to circumstances not intended to be tested + * (things like network errors, invalid parameter value, failure to + * allocate memory in the test harness, etc.). */ + MUNIT_ERROR } MunitResult; typedef struct { - char *name; - char **values; + char *name; + char **values; } MunitParameterEnum; typedef struct { - char *name; - char *value; + char *name; + char *value; } MunitParameter; const char *munit_parameters_get(const MunitParameter params[], - const char *key); + const char *key); typedef enum { - MUNIT_TEST_OPTION_NONE = 0, - MUNIT_TEST_OPTION_SINGLE_ITERATION = 1 << 0, - MUNIT_TEST_OPTION_TODO = 1 << 1 + MUNIT_TEST_OPTION_NONE = 0, + MUNIT_TEST_OPTION_SINGLE_ITERATION = 1 << 0, + MUNIT_TEST_OPTION_TODO = 1 << 1 } MunitTestOptions; typedef MunitResult ( - *MunitTestFunc)(const MunitParameter params[], void *user_data_or_fixture); + *MunitTestFunc)(const MunitParameter params[], void *user_data_or_fixture); typedef void *(*MunitTestSetup)(const MunitParameter params[], void *user_data); typedef void (*MunitTestTearDown)(void *fixture); typedef struct { - char *name; - MunitTestFunc test; - MunitTestSetup setup; - MunitTestTearDown tear_down; - MunitTestOptions options; - MunitParameterEnum *parameters; + char *name; + MunitTestFunc test; + MunitTestSetup setup; + MunitTestTearDown tear_down; + MunitTestOptions options; + MunitParameterEnum *parameters; } MunitTest; typedef enum { MUNIT_SUITE_OPTION_NONE = 0 } MunitSuiteOptions; @@ -445,15 +442,15 @@ typedef enum { MUNIT_SUITE_OPTION_NONE = 0 } MunitSuiteOptions; typedef struct MunitSuite_ MunitSuite; struct MunitSuite_ { - char *prefix; - MunitTest *tests; - MunitSuite *suites; - unsigned int iterations; - MunitSuiteOptions options; + char *prefix; + MunitTest *tests; + MunitSuite *suites; + unsigned int iterations; + MunitSuiteOptions options; }; int munit_suite_main(const MunitSuite *suite, void *user_data, int argc, - char *const argv[MUNIT_ARRAY_PARAM(argc + 1)]); + char *const argv[MUNIT_ARRAY_PARAM(argc + 1)]); /* Note: I'm not very happy with this API; it's likely to change if I * figure out something better. Suggestions welcome. */ @@ -461,15 +458,15 @@ int munit_suite_main(const MunitSuite *suite, void *user_data, int argc, typedef struct MunitArgument_ MunitArgument; struct MunitArgument_ { - char *name; - munit_bool (*parse_argument)(const MunitSuite *suite, void *user_data, - int *arg, int argc, char *const argv[MUNIT_ARRAY_PARAM(argc + 1)]); - void (*write_help)(const MunitArgument *argument, void *user_data); + char *name; + munit_bool (*parse_argument)(const MunitSuite *suite, void *user_data, + int *arg, int argc, char *const argv[MUNIT_ARRAY_PARAM(argc + 1)]); + void (*write_help)(const MunitArgument *argument, void *user_data); }; int munit_suite_main_custom(const MunitSuite *suite, void *user_data, int argc, - char *const argv[MUNIT_ARRAY_PARAM(argc + 1)], - const MunitArgument arguments[]); + char *const argv[MUNIT_ARRAY_PARAM(argc + 1)], + const MunitArgument arguments[]); #if defined(MUNIT_ENABLE_ASSERT_ALIASES) @@ -500,12 +497,12 @@ int munit_suite_main_custom(const MunitSuite *suite, void *user_data, int argc, #define assert_uint64(a, op, b) munit_assert_uint64(a, op, b) #define assert_double_equal(a, b, precision) \ - munit_assert_double_equal(a, b, precision) + munit_assert_double_equal(a, b, precision) #define assert_string_equal(a, b) munit_assert_string_equal(a, b) #define assert_string_not_equal(a, b) munit_assert_string_not_equal(a, b) #define assert_memory_equal(size, a, b) munit_assert_memory_equal(size, a, b) #define assert_memory_not_equal(size, a, b) \ - munit_assert_memory_not_equal(size, a, b) + munit_assert_memory_not_equal(size, a, b) #define assert_ptr_equal(a, b) munit_assert_ptr_equal(a, b) #define assert_ptr_not_equal(a, b) munit_assert_ptr_not_equal(a, b) #define assert_ptr_null(ptr) munit_assert_null_equal(ptr) diff --git a/tests/test.c b/tests/test.c index 4962c8c..eff568e 100644 --- a/tests/test.c +++ b/tests/test.c @@ -29,55 +29,55 @@ #define __SLD_P(...) printf(__VA_ARGS__) #elif __SL_DEBUG >= 3 typedef struct dbg_node { - sl_node snode; - int value; + sl_node snode; + int value; } dbg_node_t; inline void __sld_rt_ins(int error_code, sl_node *node, int top_layer, int cur_layer) { - dbg_node_t *ddd = sl_get_entry(node, dbg_node_t, snode); - printf("[INS] retry (code %d) " - "%p (top %d, cur %d) %d\n", - error_code, node, top_layer, cur_layer, ddd->value); + dbg_node_t *ddd = sl_get_entry(node, dbg_node_t, snode); + printf("[INS] retry (code %d) " + "%p (top %d, cur %d) %d\n", + error_code, node, top_layer, cur_layer, ddd->value); } inline void __sld_nc_ins(sl_node *node, sl_node *next_node, int top_layer, int cur_layer) { - dbg_node_t *ddd = sl_get_entry(node, dbg_node_t, snode); - dbg_node_t *ddd_next = sl_get_entry(next_node, dbg_node_t, snode); + dbg_node_t *ddd = sl_get_entry(node, dbg_node_t, snode); + dbg_node_t *ddd_next = sl_get_entry(next_node, dbg_node_t, snode); - printf("[INS] next node changed, " - "%p %p (top %d, cur %d) %d %d\n", - node, next_node, top_layer, cur_layer, ddd->value, ddd_next->value); + printf("[INS] next node changed, " + "%p %p (top %d, cur %d) %d %d\n", + node, next_node, top_layer, cur_layer, ddd->value, ddd_next->value); } inline void __sld_rt_rmv(int error_code, sl_node *node, int top_layer, int cur_layer) { - dbg_node_t *ddd = sl_get_entry(node, dbg_node_t, snode); - printf("[RMV] retry (code %d) " - "%p (top %d, cur %d) %d\n", - error_code, node, top_layer, cur_layer, ddd->value); + dbg_node_t *ddd = sl_get_entry(node, dbg_node_t, snode); + printf("[RMV] retry (code %d) " + "%p (top %d, cur %d) %d\n", + error_code, node, top_layer, cur_layer, ddd->value); } inline void __sld_nc_rmv(sl_node *node, sl_node *next_node, int top_layer, int cur_layer) { - dbg_node_t *ddd = sl_get_entry(node, dbg_node_t, snode); - dbg_node_t *ddd_next = sl_get_entry(next_node, dbg_node_t, snode); + dbg_node_t *ddd = sl_get_entry(node, dbg_node_t, snode); + dbg_node_t *ddd_next = sl_get_entry(next_node, dbg_node_t, snode); - printf("[RMV] next node changed, " - "%p %p (top %d, cur %d) %d %d\n", - node, next_node, top_layer, cur_layer, ddd->value, ddd_next->value); + printf("[RMV] next node changed, " + "%p %p (top %d, cur %d) %d %d\n", + node, next_node, top_layer, cur_layer, ddd->value, ddd_next->value); } inline void __sld_bm(sl_node *node) { - dbg_node_t *ddd = sl_get_entry(node, dbg_node_t, snode); - printf("[RMV] node is being modified %d\n", ddd->value); + dbg_node_t *ddd = sl_get_entry(node, dbg_node_t, snode); + printf("[RMV] node is being modified %d\n", ddd->value); } #define __SLD_RT_INS(e, n, t, c) __sld_rt_ins(e, n, t, c) @@ -95,13 +95,13 @@ __sld_bm(sl_node *node) #endif struct user_data { - size_t n_ele; + size_t n_ele; }; typedef struct ex_node { - sl_node snode; - uint32_t key; - uint32_t value; + sl_node snode; + uint32_t key; + uint32_t value; } ex_node_t; typedef sl_raw ex_sl_t; @@ -109,340 +109,341 @@ typedef sl_raw ex_sl_t; static int uint32_key_cmp(sl_node *a, sl_node *b, void *aux) { - ex_node_t *aa, *bb; - (void)aux; - aa = sl_get_entry(a, ex_node_t, snode); - bb = sl_get_entry(b, ex_node_t, snode); + ex_node_t *aa, *bb; + (void)aux; + aa = sl_get_entry(a, ex_node_t, snode); + bb = sl_get_entry(b, ex_node_t, snode); - if (aa->key < bb->key) - return -1; - if (aa->key > bb->key) - return 1; - return 0; + if (aa->key < bb->key) + return -1; + if (aa->key > bb->key) + return 1; + return 0; } static size_t -__populate_slist(ex_sl_t *slist){ - size_t inserted = 0; - uint32_t n, key; - ex_node_t *node; +__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; + 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) { - struct test_info *info = (struct test_info *)user_data; - (void)info; - (void)params; + struct test_info *info = (struct test_info *)user_data; + (void)info; + (void)params; - ex_sl_t *slist = calloc(sizeof(ex_sl_t), 1); - if (slist == NULL) - return NULL; - sl_init(slist, uint32_key_cmp); - return (void *)(uintptr_t)slist; + ex_sl_t *slist = calloc(sizeof(ex_sl_t), 1); + if (slist == NULL) + return NULL; + sl_init(slist, uint32_key_cmp); + return (void *)(uintptr_t)slist; } static void test_api_tear_down(void *fixture) { - ex_sl_t *slist = (ex_sl_t *)fixture; - assert_ptr_not_null(slist); - sl_node *cursor = sl_begin(slist); - while (cursor) { - assert_ptr_not_null(cursor); - ex_node_t *entry = sl_get_entry(cursor, ex_node_t, snode); - assert_ptr_not_null(entry); - assert_uint32(entry->key, ==, entry->value); - cursor = sl_next(slist, cursor); - sl_erase_node(slist, &entry->snode); - sl_release_node(&entry->snode); - sl_wait_for_free(&entry->snode); - sl_free_node(&entry->snode); - free(entry); - } - sl_free(slist); - free(fixture); + ex_sl_t *slist = (ex_sl_t *)fixture; + assert_ptr_not_null(slist); + sl_node *cursor = sl_begin(slist); + while (cursor) { + assert_ptr_not_null(cursor); + ex_node_t *entry = sl_get_entry(cursor, ex_node_t, snode); + assert_ptr_not_null(entry); + assert_uint32(entry->key, ==, entry->value); + cursor = sl_next(slist, cursor); + sl_erase_node(slist, &entry->snode); + sl_release_node(&entry->snode); + sl_wait_for_free(&entry->snode); + sl_free_node(&entry->snode); + free(entry); + } + sl_free(slist); + free(fixture); } static void * test_api_insert_setup(const MunitParameter params[], void *user_data) { - return test_api_setup(params, user_data); + return test_api_setup(params, user_data); } static void test_api_insert_tear_down(void *fixture) { - test_api_tear_down(fixture); + test_api_tear_down(fixture); } static MunitResult test_api_insert(const MunitParameter params[], void *data) { - int ret; - size_t inserted = 0; - uint32_t n, key; - sl_raw *slist = (sl_raw *)data; - ex_node_t *node; - (void)params; + int ret; + size_t inserted = 0; + uint32_t n, key; + sl_raw *slist = (sl_raw *)data; + ex_node_t *node; + (void)params; - assert_ptr_not_null(slist); - n = munit_rand_int_range(4096, 8192); - 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 ((ret = sl_insert_nodup(slist, &node->snode)) == -1) - continue; /* a random duplicate appeared */ - else { - assert_int(ret, ==, 0); - inserted++; - } + assert_ptr_not_null(slist); + n = munit_rand_int_range(4096, 8192); + 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 ((ret = sl_insert_nodup(slist, &node->snode)) == -1) + continue; /* a random duplicate appeared */ + else { + assert_int(ret, ==, 0); + inserted++; } - assert_size(inserted, ==, sl_get_size(slist)); - return MUNIT_OK; + } + assert_size(inserted, ==, sl_get_size(slist)); + return MUNIT_OK; } static void * test_api_remove_setup(const MunitParameter params[], void *user_data) { - sl_raw *slist = (sl_raw *)test_api_setup(params, user_data); - __populate_slist(slist); - return (void *)slist; + sl_raw *slist = (sl_raw *)test_api_setup(params, user_data); + __populate_slist(slist); + return (void *)slist; } static void test_api_remove_tear_down(void *fixture) { - test_api_tear_down(fixture); + test_api_tear_down(fixture); } static MunitResult test_api_remove(const MunitParameter params[], void *data) { - uint32_t key; - sl_raw *slist = (sl_raw *)data; - ex_node_t *node; - (void)params; + uint32_t key; + sl_raw *slist = (sl_raw *)data; + ex_node_t *node; + (void)params; - assert_ptr_not_null(slist); - key = munit_rand_int_range((((uint32_t)0-1) / 10) + 1, ((uint32_t)0-1)); - 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) - return MUNIT_ERROR; - else { - ex_node_t query; - query.key = key; - 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); - sl_erase_node(slist, &entry->snode); - sl_release_node(&entry->snode); - sl_wait_for_free(&entry->snode); - sl_free_node(&entry->snode); - free(entry); - } - return MUNIT_OK; + assert_ptr_not_null(slist); + key = munit_rand_int_range((((uint32_t)0 - 1) / 10) + 1, ((uint32_t)0 - 1)); + 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) + return MUNIT_ERROR; + else { + ex_node_t query; + query.key = key; + 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); + sl_erase_node(slist, &entry->snode); + sl_release_node(&entry->snode); + sl_wait_for_free(&entry->snode); + sl_free_node(&entry->snode); + free(entry); + } + return MUNIT_OK; } static void * test_api_find_setup(const MunitParameter params[], void *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; + 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) { - test_api_tear_down(fixture); + test_api_tear_down(fixture); } static MunitResult test_api_find(const MunitParameter params[], void *data) { - sl_raw *slist = (sl_raw *)data; - (void)params; + sl_raw *slist = (sl_raw *)data; + (void)params; - /* 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); - } + /* 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; + /* */ + return MUNIT_OK; } static void * test_api_update_setup(const MunitParameter params[], void *user_data) { - return test_api_setup(params, user_data); + return test_api_setup(params, user_data); } static void test_api_update_tear_down(void *fixture) { - test_api_tear_down(fixture); + test_api_tear_down(fixture); } static MunitResult test_api_update(const MunitParameter params[], void *data) { - sl_raw *slist = (sl_raw *)data; - (void)params; - (void)slist; - return MUNIT_OK; + sl_raw *slist = (sl_raw *)data; + (void)params; + (void)slist; + return MUNIT_OK; } static void * test_api_delete_setup(const MunitParameter params[], void *user_data) { - return test_api_setup(params, user_data); + return test_api_setup(params, user_data); } static void test_api_delete_tear_down(void *fixture) { - test_api_tear_down(fixture); + test_api_tear_down(fixture); } static MunitResult test_api_delete(const MunitParameter params[], void *data) { - sl_raw *slist = (sl_raw *)data; - (void)params; - (void)slist; - return MUNIT_OK; + sl_raw *slist = (sl_raw *)data; + (void)params; + (void)slist; + return MUNIT_OK; } static void * test_api_duplicates_setup(const MunitParameter params[], void *user_data) { - return test_api_setup(params, user_data); + return test_api_setup(params, user_data); } static void test_api_duplicates_tear_down(void *fixture) { - test_api_tear_down(fixture); + test_api_tear_down(fixture); } static MunitResult test_api_duplicates(const MunitParameter params[], void *data) { - sl_raw *slist = (sl_raw *)data; - (void)params; - (void)slist; - return MUNIT_OK; + sl_raw *slist = (sl_raw *)data; + (void)params; + (void)slist; + return MUNIT_OK; } static void * test_api_size_setup(const MunitParameter params[], void *user_data) { - return test_api_setup(params, user_data); + return test_api_setup(params, user_data); } static void test_api_size_tear_down(void *fixture) { - test_api_tear_down(fixture); + test_api_tear_down(fixture); } static MunitResult test_api_size(const MunitParameter params[], void *data) { - sl_raw *slist = (sl_raw *)data; - (void)params; - (void)slist; - return MUNIT_OK; + sl_raw *slist = (sl_raw *)data; + (void)params; + (void)slist; + return MUNIT_OK; } static void * test_api_iterators_setup(const MunitParameter params[], void *user_data) { - return test_api_setup(params, user_data); + return test_api_setup(params, user_data); } static void test_api_iterators_tear_down(void *fixture) { - test_api_tear_down(fixture); + test_api_tear_down(fixture); } static MunitResult test_api_iterators(const MunitParameter params[], void *data) { - sl_raw *slist = (sl_raw *)data; - (void)params; - (void)slist; - return MUNIT_OK; + sl_raw *slist = (sl_raw *)data; + (void)params; + (void)slist; + return MUNIT_OK; } static MunitTest api_test_suite[] = { - { (char *)"/api/insert", test_api_insert, test_api_insert_setup, - test_api_insert_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, - { (char *)"/api/remove", test_api_remove, test_api_remove_setup, - test_api_remove_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, - { (char *)"/api/find", test_api_find, test_api_find_setup, - test_api_find_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, - { (char *)"/api/update", test_api_update, test_api_update_setup, - test_api_update_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, - { (char *)"/api/delete", test_api_delete, test_api_delete_setup, - test_api_delete_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, - { (char *)"/api/duplicates", test_api_duplicates, test_api_duplicates_setup, - test_api_duplicates_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, - { (char *)"/api/size", test_api_size, test_api_size_setup, - test_api_size_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, - { (char *)"/api/iterators", test_api_iterators, test_api_iterators_setup, - test_api_iterators_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, - { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL } + { (char *)"/api/insert", test_api_insert, test_api_insert_setup, + test_api_insert_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { (char *)"/api/remove", test_api_remove, test_api_remove_setup, + test_api_remove_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { (char *)"/api/find", test_api_find, test_api_find_setup, + test_api_find_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { (char *)"/api/update", test_api_update, test_api_update_setup, + test_api_update_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { (char *)"/api/delete", test_api_delete, test_api_delete_setup, + test_api_delete_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { (char *)"/api/duplicates", test_api_duplicates, test_api_duplicates_setup, + test_api_duplicates_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { (char *)"/api/size", test_api_size, test_api_size_setup, + test_api_size_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { (char *)"/api/iterators", test_api_iterators, test_api_iterators_setup, + test_api_iterators_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL } }; static MunitTest mt_tests[] = { { NULL, NULL, NULL, NULL, - MUNIT_TEST_OPTION_NONE, NULL } }; + MUNIT_TEST_OPTION_NONE, NULL } }; static MunitTest scale_tests[] = { { NULL, NULL, NULL, NULL, - MUNIT_TEST_OPTION_NONE, NULL } }; + MUNIT_TEST_OPTION_NONE, NULL } }; static MunitSuite other_test_suite[] = { { "/mt", mt_tests, NULL, 1, - MUNIT_SUITE_OPTION_NONE }, - { "/scale", scale_tests, NULL, 1, MUNIT_SUITE_OPTION_NONE }, - { NULL, NULL, NULL, 0, MUNIT_SUITE_OPTION_NONE } }; + MUNIT_SUITE_OPTION_NONE }, + { "/scale", scale_tests, NULL, 1, MUNIT_SUITE_OPTION_NONE }, + { NULL, NULL, NULL, 0, MUNIT_SUITE_OPTION_NONE } }; static const MunitSuite main_test_suite = { (char *)"/api", api_test_suite, - other_test_suite, 1, MUNIT_SUITE_OPTION_NONE }; + other_test_suite, 1, MUNIT_SUITE_OPTION_NONE }; int 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); + struct user_data info; + return munit_suite_main(&main_test_suite, (void *)&info, argc, argv); } /* ARGS: --no-fork --seed 8675309 */