├── .gitignore ├── test ├── ptest.h ├── test_skiplist.c └── ptest.c ├── Makefile ├── README.md └── skiplist.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.core 2 | *.o 3 | *.profraw 4 | *.profdata 5 | .DS_Store 6 | /test_skiplist 7 | /skiplist.coverage.html 8 | -------------------------------------------------------------------------------- /test/ptest.h: -------------------------------------------------------------------------------- 1 | #ifndef ptest_h 2 | #define ptest_h 3 | 4 | #include 5 | 6 | #define PT_SUITE(name) void name(void) 7 | 8 | #define PT_FUNC(name) static void name(void) 9 | #define PT_REG(name) pt_add_test(name, #name, __func__) 10 | #define PT_TEST(name) auto void name(void); PT_REG(name); void name(void) 11 | 12 | #define PT_ASSERT(expr) pt_assert_run((int)(expr), #expr, __func__, __FILE__, __LINE__) 13 | #define PT_ASSERT_STR_EQ(fst, snd) pt_assert_run(strcmp(fst, snd) == 0, "strcmp( " #fst ", " #snd " ) == 0", __func__, __FILE__, __LINE__) 14 | 15 | void pt_assert_run(int result, const char* expr, const char* func, const char* file, int line); 16 | 17 | void pt_add_test(void (*func)(void), const char* name, const char* suite); 18 | void pt_add_suite(void (*func)(void)); 19 | int pt_run(void); 20 | 21 | #endif -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ifdef COVERAGE 2 | CFLAGS=-DDEBUG -g -O -std=c99 -Wall -Wextra -pedantic -fprofile-instr-generate=test_skiplist.profraw -fcoverage-mapping 3 | LDFLAGS=-fprofile-instr-generate=test_skiplist.profraw 4 | else 5 | CFLAGS=-DDEBUG -g -O -std=c99 -Wall -Wextra -pedantic 6 | endif 7 | 8 | ifeq ($(shell uname -s),Darwin) 9 | LLVM_PREFIX=xcrun 10 | endif 11 | 12 | SL_HEADER=skiplist.h 13 | SRCS=test/test_skiplist.c test/ptest.c 14 | OBJS=$(SRCS:.c=.o) 15 | TEST_OUT=test_skiplist 16 | 17 | all: build test 18 | 19 | build: $(SL_HEADER) $(SRCS) $(TEST_OUT) 20 | 21 | .PHONY: test 22 | test: $(TEST_OUT) 23 | ./$(TEST_OUT) 24 | 25 | $(TEST_OUT): $(OBJS) 26 | $(CC) $(LDFLAGS) $(OBJS) -o $@ 27 | 28 | $(OBJS): $(SL_HEADER) 29 | 30 | .c.o: 31 | $(CC) $(CFLAGS) -c $< -o $@ 32 | 33 | ifdef COVERAGE 34 | skiplist.coverage.html: $(TEST_OUT) test_skiplist.profdata 35 | $(LLVM_PREFIX) llvm-cov show $(TEST_OUT) \ 36 | -format=html \ 37 | -show-line-counts-or-regions \ 38 | -instr-profile=test_skiplist.profdata \ 39 | $(SL_HEADER) > $@ 40 | 41 | test_skiplist.profraw: $(TEST_OUT) 42 | ./$(TEST_OUT) 43 | 44 | test_skiplist.profdata: test_skiplist.profraw 45 | $(LLVM_PREFIX) llvm-profdata merge $^ -output $@ 46 | endif 47 | 48 | .PHONY: clean 49 | clean: 50 | rm -f test/*.o $(TEST_OUT) *.profdata *.profraw skiplist.coverage.html 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | skiplist.h 2 | ========== 3 | 4 | Single-header-file, public domain, type-generic C89 skip list implementation. 5 | ----------------------------------------------------------------------------- 6 | 7 | Implements a sorted dictionary or set with a skiplist. Duplicate elements (i.e. 8 | a sorted linked list) are not currently supported. 9 | 10 | 1. Download [skiplist.h](https://raw.githubusercontent.com/zacharyvoase/skiplist.h/master/skiplist.h). 11 | That's the only file you need. 12 | 2. Create a guarded header that defines SKIPLIST_KEY and SKIPLIST_VALUE 13 | and includes skiplist.h. Optionally define SKIPLIST_NAMESPACE. 14 | Define SKIPLIST_IMPLEMENTATION somewhere in your program above 15 | your custom header include. 16 | 3. Repeat for any different key/value pair types you need. Be sure to 17 | define different SKIPLIST_NAMESPACE values and define SKIPLIST_IMPLEMENTATION 18 | once for each key/value type pair. 19 | 20 | Other options: 21 | 22 | - SKIPLIST_MAX_LEVELS - 33 by default. 23 | - SKIPLIST_MALLOC & SKIPLIST_FREE - wrappers for stdlib malloc/free by default. 24 | Both are passed a void \* data pointer (for memory pool, gc context, etc). 25 | - SKIPLIST_HEIGHT - a macro function taking `(SKIPLIST_KEY)` which should 26 | produce an unsigned int 0 <= height < SKIPLIST_MAX_LEVELS. By default this 27 | is implemented using `arc4random_uniform` in the C standard library, but you 28 | might want to replace it with a key-dependent function. 29 | - SKIPLIST_STATIC - if defined, declare all public functions static 30 | (make skiplist local to the file it's included from). 31 | - SKIPLIST_EXTERN - 'extern' by default; define to change calling convention 32 | or linkage etc. 33 | 34 | skiplist.h has no dependencies. By default it uses some functions from the C 35 | standard library, but that dependency can be replaced by defining the 36 | SKIPLIST_MALLOC, SKIPLIST_FREE, and SKIPLIST_HEIGHT macros. 37 | 38 | Tests 39 | ----- 40 | 41 | Clone this repository and run `make`. The default Makefile builds and runs 42 | the test suite. 43 | 44 | Documentation 45 | ------------- 46 | 47 | The documentation is built with [cldoc](https://github.com/jessevdk/cldoc) and 48 | should be viewed in a web browser. To build the doc yourself, install cldoc and 49 | run `make doc` from this repository's root. 50 | 51 | Example Usage 52 | ------------- 53 | 54 | ```c 55 | /* skiplist_str_int.h */ 56 | /* This header should be guarded, as below */ 57 | #ifndef SKIPLIST_STR_INT_H 58 | #define SKIPLIST_STR_INT_H 59 | 60 | #define SKIPLIST_KEY const char * 61 | #define SKIPLIST_VALUE int 62 | #define SKIPLIST_NAMESPACE sl_strint_ 63 | #include "skiplist.h" 64 | 65 | #endif 66 | 67 | /* program.c */ 68 | #include 69 | #include 70 | #include 71 | /* You should only define this once. If it helps, 72 | you can simply make a skiplist_str_int.c file 73 | with the following 2 lines and link to it. */ 74 | #define SKIPLIST_IMPLEMENTATION 75 | #include "skiplist_str_int.h" 76 | 77 | int cmp(const char *a, const char *b, void *userdata) { 78 | return strcmp(a, b); 79 | } 80 | 81 | int iter(const char *key, int val, void *userdata) { 82 | printf("%s = %d\n", key, val); 83 | return 0; 84 | } 85 | 86 | int main(int argc, const char **argv) { 87 | sl_strint_skiplist list; 88 | int err = sl_strint_init(&list, cmp, NULL, NULL); 89 | assert(err == 0); 90 | 91 | sl_strint_insert(&list, "a", 1, NULL); 92 | sl_strint_insert(&list, "c", 3, NULL); 93 | sl_strint_insert(&list, "b", 2, NULL); 94 | 95 | short has_b = sl_strint_find(&list, "b", NULL), // => 1 96 | has_d = sl_strint_find(&list, "d", NULL); // => 0 97 | 98 | int a_val; 99 | short exists = sl_strint_find(&list, "a", &a_val); 100 | if (exists) 101 | printf("a = %d\n", a_val); 102 | else 103 | puts("a does not exist"); 104 | 105 | int default_val = 10; 106 | int d_val = sl_strint_get(&list, "d", default_val); // => 10 107 | 108 | sl_strint_iter(&list, iter, NULL); 109 | // Prints a = 1, b = 2, c = 3 110 | 111 | int b_val; 112 | short existed = sl_strint_remove(&list, "b", &b_val); 113 | if (existed) 114 | print("b used to be %d, but now it is no more\n", b_val); 115 | else 116 | puts("b was only an illusion, a fleeting glimpse of a dream"); 117 | 118 | sl_strint_free(&list); 119 | return 0; 120 | } 121 | ``` 122 | 123 | 124 | Unlicense 125 | --------- 126 | 127 | This is free and unencumbered software released into the public domain. 128 | 129 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 130 | software, either in source code form or as a compiled binary, for any purpose, 131 | commercial or non-commercial, and by any means. 132 | 133 | In jurisdictions that recognize copyright laws, the author or authors of this 134 | software dedicate any and all copyright interest in the software to the public 135 | domain. We make this dedication for the benefit of the public at large and to 136 | the detriment of our heirs and successors. We intend this dedication to be an 137 | overt act of relinquishment in perpetuity of all present and future rights to 138 | this software under copyright law. 139 | 140 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 141 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 142 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 143 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 144 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 145 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 146 | 147 | For more information, please refer to 148 | -------------------------------------------------------------------------------- /test/test_skiplist.c: -------------------------------------------------------------------------------- 1 | #include "ptest.h" 2 | 3 | #define SKIPLIST_KEY int 4 | #define SKIPLIST_VALUE int 5 | #define SKIPLIST_IMPLEMENTATION 6 | #define SKIPLIST_MAX_LEVELS 5 7 | #include "../skiplist.h" 8 | 9 | int int_cmp(int a, int b, void *_udata) { 10 | return a - b; 11 | } 12 | 13 | #define SETUP sl_skiplist sl; sl_init(&sl, int_cmp, NULL, NULL); 14 | #define TEARDOWN sl_free(&sl); 15 | 16 | #define TEST(name) void test_ ## name(void) { SETUP 17 | #define END(name) TEARDOWN } 18 | 19 | TEST(insert) 20 | int old; 21 | PT_ASSERT(sl_insert(&sl, 3, 3, NULL) == 0); 22 | PT_ASSERT(sl_insert(&sl, 1, 3, NULL) == 0); 23 | PT_ASSERT(sl_insert(&sl, 3, 7, &old) == 1); 24 | PT_ASSERT(old == 3); 25 | PT_ASSERT(sl_insert(&sl, 5, 5, &old) == 0); 26 | PT_ASSERT(sl_insert(&sl, 3, 1, &old) == 1); 27 | PT_ASSERT(old == 7); 28 | END(insert) 29 | 30 | TEST(find) 31 | int val; 32 | PT_ASSERT(sl_find(&sl, 10, NULL) == 0); 33 | sl_insert(&sl, 10, 4, NULL); 34 | sl_insert(&sl, 4, 10, NULL); 35 | sl_insert(&sl, 8, 8, NULL); 36 | PT_ASSERT(sl_find(&sl, 10, &val) == 1); 37 | PT_ASSERT(val == 4); 38 | sl_insert(&sl, 8, 2, NULL); 39 | PT_ASSERT(sl_find(&sl, 8, &val) == 1); 40 | PT_ASSERT(val == 2); 41 | END(find) 42 | 43 | TEST(get) 44 | PT_ASSERT(sl_get(&sl, 2, 10) == 10); 45 | sl_insert(&sl, 4, 4, NULL); 46 | PT_ASSERT(sl_get(&sl, 4, 10) == 4); 47 | END(get) 48 | 49 | TEST(size) 50 | PT_ASSERT(sl_size(&sl) == 0); 51 | sl_insert(&sl, 1, 2, NULL); 52 | PT_ASSERT(sl_size(&sl) == 1); 53 | sl_insert(&sl, 2, 2, NULL); 54 | sl_insert(&sl, 1, 4, NULL); 55 | PT_ASSERT(sl_size(&sl) == 2); 56 | END(size) 57 | 58 | struct iter_data { 59 | int cnt; 60 | int keys[5]; 61 | int vals[5]; 62 | }; 63 | 64 | int int_iter(int k, int v, void *data) { 65 | struct iter_data *id = (struct iter_data *)data; 66 | id->keys[id->cnt] = k; 67 | id->vals[id->cnt++] = v; 68 | return 0; 69 | } 70 | 71 | TEST(iter) 72 | struct iter_data id; 73 | id.cnt = 0; 74 | PT_ASSERT(sl_iter(&sl, int_iter, &id) == 0); 75 | PT_ASSERT(id.cnt == 0); 76 | 77 | sl_insert(&sl, 2, 10, NULL); 78 | sl_insert(&sl, 3, 3, NULL); 79 | sl_insert(&sl, 1, 5, NULL); 80 | sl_insert(&sl, 2, 4, NULL); 81 | sl_insert(&sl, 5, 1, NULL); 82 | sl_insert(&sl, 4, 2, NULL); 83 | 84 | PT_ASSERT(sl_iter(&sl, int_iter, &id) == 0); 85 | PT_ASSERT(id.cnt == 5); 86 | for (int i = 0; i < 5; ++i) { 87 | PT_ASSERT(id.keys[i] == i+1); 88 | PT_ASSERT(id.vals[i] == 5-i); 89 | } 90 | END(iter) 91 | 92 | int int_iter_stop(int k, int v, void *data) { 93 | struct iter_data *id = (struct iter_data *)data; 94 | id->keys[id->cnt] = k; 95 | id->vals[id->cnt++] = v; 96 | return k == 3; 97 | } 98 | 99 | TEST(iter_stop) 100 | struct iter_data id; 101 | id.cnt = 0; 102 | PT_ASSERT(sl_iter(&sl, int_iter_stop, &id) == 0); 103 | PT_ASSERT(id.cnt == 0); 104 | 105 | sl_insert(&sl, 2, 10, NULL); 106 | sl_insert(&sl, 3, 3, NULL); 107 | sl_insert(&sl, 1, 5, NULL); 108 | sl_insert(&sl, 2, 4, NULL); 109 | sl_insert(&sl, 5, 1, NULL); 110 | sl_insert(&sl, 4, 2, NULL); 111 | 112 | PT_ASSERT(sl_iter(&sl, int_iter_stop, &id) == 1); 113 | PT_ASSERT(id.cnt == 3); 114 | for (int i = 0; i < 3; ++i) { 115 | PT_ASSERT(id.keys[i] == i+1); 116 | PT_ASSERT(id.vals[i] == 5-i); 117 | } 118 | END(iter_stop) 119 | 120 | TEST(remove) 121 | int rm; 122 | int val; 123 | PT_ASSERT(sl_remove(&sl, 2, &rm) == 0); 124 | sl_insert(&sl, 2, 4, NULL); 125 | sl_insert(&sl, 7, 6, NULL); 126 | sl_insert(&sl, 2, 5, NULL); 127 | sl_insert(&sl, 1, 1, NULL); 128 | PT_ASSERT(sl_remove(&sl, 2, &rm) == 1); 129 | PT_ASSERT(rm == 5); 130 | PT_ASSERT(sl_remove(&sl, 2, &rm) == 0); 131 | PT_ASSERT(sl_find(&sl, 2, NULL) == 0); 132 | PT_ASSERT(sl_size(&sl) == 2); 133 | PT_ASSERT(sl_find(&sl, 7, &val) == 1); 134 | PT_ASSERT(val == 6); 135 | PT_ASSERT(sl_find(&sl, 1, &val) == 1); 136 | PT_ASSERT(val == 1); 137 | END(remove) 138 | 139 | TEST(min) 140 | int min, val; 141 | PT_ASSERT(sl_min(&sl, &min, &val) == 0); 142 | sl_insert(&sl, 5, 11, NULL); 143 | sl_insert(&sl, 3, 11, NULL); 144 | sl_insert(&sl, 7, 10, NULL); 145 | sl_insert(&sl, 1, 13, NULL); 146 | sl_insert(&sl, 9, 12, NULL); 147 | sl_insert(&sl, 1, 17, NULL); 148 | PT_ASSERT(sl_min(&sl, &min, &val) == 1); 149 | PT_ASSERT(min == 1); 150 | PT_ASSERT(val == 17); 151 | PT_ASSERT(sl_size(&sl) == 5); 152 | END(min) 153 | 154 | TEST(max) 155 | int max, val; 156 | PT_ASSERT(sl_max(&sl, &max, &val) == 0); 157 | sl_insert(&sl, 5, 11, NULL); 158 | sl_insert(&sl, 3, 11, NULL); 159 | sl_insert(&sl, 7, 10, NULL); 160 | sl_insert(&sl, 1, 13, NULL); 161 | sl_insert(&sl, 9, 12, NULL); 162 | sl_insert(&sl, 1, 17, NULL); 163 | PT_ASSERT(sl_max(&sl, &max, &val) == 1); 164 | PT_ASSERT(max == 9); 165 | PT_ASSERT(val == 12); 166 | PT_ASSERT(sl_size(&sl) == 5); 167 | END(max) 168 | 169 | TEST(pop) 170 | int min, val; 171 | PT_ASSERT(sl_pop(&sl, &min, &val) == 0); 172 | 173 | sl_insert(&sl, 2, 10, NULL); 174 | sl_insert(&sl, 3, 3, NULL); 175 | sl_insert(&sl, 1, 5, NULL); 176 | sl_insert(&sl, 2, 4, NULL); 177 | sl_insert(&sl, 5, 1, NULL); 178 | sl_insert(&sl, 4, 2, NULL); 179 | 180 | for (int i = 0; i < 5; ++i) { 181 | PT_ASSERT(sl_pop(&sl, &min, &val) == 1); 182 | PT_ASSERT(min == i + 1); 183 | PT_ASSERT(val == 5 - i); 184 | } 185 | 186 | PT_ASSERT(sl_size(&sl) == 0); 187 | END(pop) 188 | 189 | TEST(shift) 190 | int max, val; 191 | PT_ASSERT(sl_shift(&sl, &max, &val) == 0); 192 | 193 | sl_insert(&sl, 2, 10, NULL); 194 | sl_insert(&sl, 3, 3, NULL); 195 | sl_insert(&sl, 1, 5, NULL); 196 | sl_insert(&sl, 2, 4, NULL); 197 | sl_insert(&sl, 5, 1, NULL); 198 | sl_insert(&sl, 4, 2, NULL); 199 | 200 | for (int i = 0; i < 5; ++i) { 201 | PT_ASSERT(sl_shift(&sl, &max, &val) == 1); 202 | PT_ASSERT(max == 5 - i); 203 | PT_ASSERT(val == i + 1); 204 | } 205 | 206 | PT_ASSERT(sl_size(&sl) == 0); 207 | END(shift) 208 | 209 | void suite_skiplist(void) { 210 | pt_add_test(test_insert, "Should insert key/value pairs", "skiplist"); 211 | pt_add_test(test_find, "Should find values that exist", "skiplist"); 212 | pt_add_test(test_get, "Should be able to return a default value for keys that don't exist", "skiplist"); 213 | pt_add_test(test_size, "Should keep track of its size", "skiplist"); 214 | pt_add_test(test_iter, "Should iterate over keys in order", "skiplist"); 215 | pt_add_test(test_iter_stop, "Should be able to stop iteration from the callback", "skiplist"); 216 | pt_add_test(test_remove, "Should be able to remove items", "skiplist"); 217 | pt_add_test(test_min, "Should find the minimum key", "skiplist"); 218 | pt_add_test(test_max, "Should find the maximum key", "skiplist"); 219 | pt_add_test(test_pop, "Should remove the minimum key", "skiplist"); 220 | pt_add_test(test_shift, "Should remove the maximum key", "skiplist"); 221 | } 222 | 223 | int main(int argc, const char **argv) { 224 | pt_add_suite(suite_skiplist); 225 | return pt_run(); 226 | } 227 | -------------------------------------------------------------------------------- /test/ptest.c: -------------------------------------------------------------------------------- 1 | #include "ptest.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /* Globals */ 10 | 11 | enum { 12 | MAX_NAME = 512 13 | }; 14 | 15 | enum { 16 | MAX_ERROR = 2048 17 | }; 18 | 19 | enum { 20 | MAX_TESTS = 2048 21 | }; 22 | 23 | static int test_passing = 0; 24 | static int suite_passing = 0; 25 | 26 | /* Colors */ 27 | 28 | enum { 29 | BLACK = 0, 30 | BLUE = 1, 31 | GREEN = 2, 32 | AQUA = 3, 33 | RED = 4, 34 | PURPLE = 5, 35 | YELLOW = 6, 36 | WHITE = 7, 37 | GRAY = 8, 38 | 39 | LIGHT_BLUE = 9, 40 | LIGHT_GREEN = 10, 41 | LIGHT_AQUA = 11, 42 | LIGHT_RED = 12, 43 | LIGHT_PURPLE = 13, 44 | LIGHT_YELLOW = 14, 45 | LIGHT_WHITE = 15, 46 | 47 | DEFAULT = 16, 48 | }; 49 | 50 | #ifdef _WIN32 51 | 52 | #include 53 | 54 | static WORD defaults; 55 | static int defaults_loaded = 0; 56 | 57 | static void pt_color(int color) { 58 | 59 | HANDLE cnsl = GetStdHandle(STD_OUTPUT_HANDLE); 60 | 61 | if (!defaults_loaded) { 62 | CONSOLE_SCREEN_BUFFER_INFO info; 63 | GetConsoleScreenBufferInfo(cnsl, &info); 64 | defaults = info.wAttributes; 65 | defaults_loaded = 1; 66 | } 67 | 68 | SetConsoleTextAttribute(cnsl, color == DEFAULT ? defaults : color); 69 | } 70 | 71 | #else 72 | 73 | static const char* colors[] = { 74 | "\x1B[0m", 75 | "\x1B[34m", 76 | "\x1B[32m", 77 | "\x1B[36m", 78 | "\x1B[31m", 79 | "\x1B[35m", 80 | "\x1B[33m", 81 | "\x1B[37m", 82 | "", 83 | "\x1B[34m", 84 | "\x1B[32m", 85 | "\x1B[36m", 86 | "\x1B[31m", 87 | "\x1B[35m", 88 | "\x1B[33m", 89 | "\x1B[37m", 90 | "\x1B[39m", 91 | }; 92 | 93 | static void pt_color(int color) { 94 | printf("%s", colors[color]); 95 | } 96 | 97 | #endif 98 | 99 | /* Asserts */ 100 | 101 | static int num_asserts = 0; 102 | static int num_assert_passes = 0; 103 | static int num_assert_fails = 0; 104 | 105 | static char assert_err[MAX_ERROR]; 106 | static char assert_err_buff[MAX_ERROR]; 107 | static int assert_err_num = 0; 108 | 109 | void pt_assert_run( 110 | int result, const char* expr, const char* func, const char* file, int line) { 111 | 112 | num_asserts++; 113 | test_passing = test_passing && result; 114 | 115 | if (result) { 116 | num_assert_passes++; 117 | } else { 118 | sprintf(assert_err_buff, 119 | " %i. Assert [ %s ] (%s:%i)\n", 120 | assert_err_num+1, expr, file, line ); 121 | strcat(assert_err, assert_err_buff); 122 | assert_err_num++; 123 | num_assert_fails++; 124 | } 125 | 126 | } 127 | 128 | static void ptest_signal(int sig) { 129 | 130 | test_passing = 0; 131 | 132 | switch( sig ) { 133 | case SIGFPE: sprintf(assert_err_buff, 134 | " %i. Division by Zero\n", assert_err_num+1); 135 | break; 136 | case SIGILL: sprintf(assert_err_buff, 137 | " %i. Illegal Instruction\n", assert_err_num+1); 138 | break; 139 | case SIGSEGV: sprintf(assert_err_buff, 140 | " %i. Segmentation Fault\n", assert_err_num+1); 141 | break; 142 | } 143 | 144 | assert_err_num++; 145 | strcat(assert_err, assert_err_buff); 146 | 147 | pt_color(RED); 148 | printf("Failed! \n\n%s\n", assert_err); 149 | pt_color(DEFAULT); 150 | 151 | puts(" | Stopping Execution."); 152 | fflush(stdout); 153 | exit(0); 154 | 155 | } 156 | 157 | /* Tests */ 158 | 159 | static void pt_title_case(char* output, const char* input) { 160 | 161 | int space = 1; 162 | unsigned int i; 163 | 164 | strcpy(output, input); 165 | 166 | for(i = 0; i < strlen(output); i++) { 167 | 168 | if (output[i] == '_' || output[i] == ' ') { 169 | space = 1; 170 | output[i] = ' '; 171 | continue; 172 | } 173 | 174 | if (space && output[i] >= 'a' && output[i] <= 'z') { 175 | space = 0; 176 | output[i] = output[i] - 32; 177 | continue; 178 | } 179 | 180 | space = 0; 181 | 182 | } 183 | 184 | } 185 | 186 | typedef struct { 187 | void (*func)(void); 188 | char name[MAX_NAME]; 189 | char suite[MAX_NAME]; 190 | } test_t; 191 | 192 | static test_t tests[MAX_TESTS]; 193 | 194 | static int num_tests = 0; 195 | static int num_tests_passes = 0; 196 | static int num_tests_fails = 0; 197 | 198 | void pt_add_test(void (*func)(void), const char* name, const char* suite) { 199 | 200 | test_t test; 201 | 202 | if (num_tests == MAX_TESTS) { 203 | printf("ERROR: Exceeded maximum test count of %i!\n", 204 | MAX_TESTS); abort(); 205 | } 206 | 207 | if (strlen(name) >= MAX_NAME) { 208 | printf("ERROR: Test name '%s' too long (Maximum is %i characters)\n", 209 | name, MAX_NAME); abort(); 210 | } 211 | 212 | if (strlen(suite) >= MAX_NAME) { 213 | printf("ERROR: Test suite '%s' too long (Maximum is %i characters)\n", 214 | suite, MAX_NAME); abort(); 215 | } 216 | 217 | test.func = func; 218 | pt_title_case(test.name, name); 219 | pt_title_case(test.suite, suite); 220 | 221 | tests[num_tests] = test; 222 | num_tests++; 223 | } 224 | 225 | /* Suites */ 226 | 227 | static int num_suites = 0; 228 | static int num_suites_passes = 0; 229 | static int num_suites_fails = 0; 230 | 231 | void pt_add_suite(void (*func)(void)) { 232 | num_suites++; 233 | func(); 234 | } 235 | 236 | /* Running */ 237 | 238 | static clock_t start, end; 239 | static char current_suite[MAX_NAME]; 240 | 241 | int pt_run(void) { 242 | 243 | unsigned int i; 244 | double total; 245 | test_t test; 246 | 247 | puts(""); 248 | puts(" +-------------------------------------------+"); 249 | puts(" | ptest MicroTesting Magic for C |"); 250 | puts(" | |"); 251 | puts(" | http://github.com/orangeduck/ptest |"); 252 | puts(" | |"); 253 | puts(" | Daniel Holden (contact@theorangeduck.com) |"); 254 | puts(" +-------------------------------------------+"); 255 | 256 | signal(SIGFPE, ptest_signal); 257 | signal(SIGILL, ptest_signal); 258 | signal(SIGSEGV, ptest_signal); 259 | 260 | start = clock(); 261 | strcpy(current_suite, ""); 262 | 263 | for(i = 0; i < num_tests; i++) { 264 | 265 | test = tests[i]; 266 | 267 | /* Check for transition to a new suite */ 268 | if (strcmp(test.suite, current_suite)) { 269 | 270 | /* Don't increment any counter for first entrance */ 271 | if (strcmp(current_suite, "")) { 272 | if (suite_passing) { 273 | num_suites_passes++; 274 | } else { 275 | num_suites_fails++; 276 | } 277 | } 278 | 279 | suite_passing = 1; 280 | strcpy(current_suite, test.suite); 281 | printf("\n\n ===== %s =====\n\n", current_suite); 282 | } 283 | 284 | /* Run Test */ 285 | 286 | test_passing = 1; 287 | strcpy(assert_err, ""); 288 | strcpy(assert_err_buff, ""); 289 | assert_err_num = 0; 290 | printf(" | %s ... ", test.name); 291 | fflush(stdout); 292 | 293 | test.func(); 294 | 295 | suite_passing = suite_passing && test_passing; 296 | 297 | if (test_passing) { 298 | num_tests_passes++; 299 | pt_color(GREEN); 300 | puts("Passed!"); 301 | pt_color(DEFAULT); 302 | } else { 303 | num_tests_fails++; 304 | pt_color(RED); 305 | printf("Failed! \n\n%s\n", assert_err); 306 | pt_color(DEFAULT); 307 | } 308 | 309 | } 310 | 311 | if (suite_passing) { 312 | num_suites_passes++; 313 | } else { 314 | num_suites_fails++; 315 | } 316 | 317 | end = clock(); 318 | 319 | puts(""); 320 | puts(" +---------------------------------------------------+"); 321 | puts(" | Summary |"); 322 | puts(" +---------++------------+-------------+-------------+"); 323 | 324 | printf(" | Suites ||"); 325 | pt_color(YELLOW); printf(" Total %4d ", num_suites); 326 | pt_color(DEFAULT); putchar('|'); 327 | pt_color(GREEN); printf(" Passed %4d ", num_suites_passes); 328 | pt_color(DEFAULT); putchar('|'); 329 | pt_color(RED); printf(" Failed %4d ", num_suites_fails); 330 | pt_color(DEFAULT); puts("|"); 331 | 332 | printf(" | Tests ||"); 333 | pt_color(YELLOW); printf(" Total %4d ", num_tests); 334 | pt_color(DEFAULT); putchar('|'); 335 | pt_color(GREEN); printf(" Passed %4d ", num_tests_passes); 336 | pt_color(DEFAULT); putchar('|'); 337 | pt_color(RED); printf(" Failed %4d ", num_tests_fails); 338 | pt_color(DEFAULT); puts("|"); 339 | 340 | printf(" | Asserts ||"); 341 | pt_color(YELLOW); printf(" Total %4d ", num_asserts); 342 | pt_color(DEFAULT); putchar('|'); 343 | pt_color(GREEN); printf(" Passed %4d ", num_assert_passes); 344 | pt_color(DEFAULT); putchar('|'); 345 | pt_color(RED); printf(" Failed %4d ", num_assert_fails); 346 | pt_color(DEFAULT); puts("|"); 347 | 348 | puts(" +---------++------------+-------------+-------------+"); 349 | puts(""); 350 | 351 | total = (double)(end - start) / CLOCKS_PER_SEC; 352 | 353 | printf(" Total Running Time: %0.3fs\n\n", total); 354 | 355 | if (num_suites_fails > 0) { return 1; } else { return 0; } 356 | } 357 | -------------------------------------------------------------------------------- /skiplist.h: -------------------------------------------------------------------------------- 1 | /* vi: set expandttab softtabstop=4 tabstop=4 shiftwidth=4 */ 2 | 3 | /** 4 | * This is free and unencumbered software released into the public domain. 5 | * 6 | * Anyone is free to copy, modify, publish, use, compile, sell, or 7 | * distribute this software, either in source code form or as a compiled 8 | * binary, for any purpose, commercial or non-commercial, and by any 9 | * means. 10 | * 11 | * In jurisdictions that recognize copyright laws, the author or authors 12 | * of this software dedicate any and all copyright interest in the 13 | * software to the public domain. We make this dedication for the benefit 14 | * of the public at large and to the detriment of our heirs and 15 | * successors. We intend this dedication to be an overt act of 16 | * relinquishment in perpetuity of all present and future rights to this 17 | * software under copyright law. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 23 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 24 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | * OTHER DEALINGS IN THE SOFTWARE. 26 | * 27 | * For more information, please refer to 28 | */ 29 | 30 | /** 31 | * 1. Create a guarded header that defines SKIPLIST_KEY and SKIPLIST_VALUE 32 | * and includes skiplist.h. Optionally define SKIPLIST_NAMESPACE. 33 | * 2. Define SKIPLIST_IMPLEMENTATION somewhere in your program above 34 | * your custom header include. 35 | * 3. Repeat for any different key/value pair types you need. Be sure to 36 | * define different SKIPLIST_NAMESPACE values and define SKIPLIST_IMPLEMENTATION 37 | * once for each key/value type pair. 38 | * 4. Other options: 39 | * - SKIPLIST_MAX_LEVELS - 33 by default. 40 | * - SKIPLIST_MALLOC & SKIPLIST_FREE - wrappers for stdlib malloc/free by default. 41 | * Both are passed a void \* data pointer (for memory pool, gc context, etc). 42 | * - SKIPLIST_HEIGHT - a macro function taking `(SKIPLIST_KEY)` which should 43 | * produce an unsigned int 0 <= height < SKIPLIST_MAX_LEVELS. By default this 44 | * is implemented using `arc4random_uniform` in the C standard library, but you 45 | * might want to replace it with a key-dependent function. 46 | * - SKIPLIST_STATIC - if defined, declare all public functions static 47 | * (make skiplist local to the file it's included from). 48 | * - SKIPLIST_EXTERN - 'extern' by default; define to change calling convention 49 | * or linkage etc. 50 | * 51 | * Example: 52 | * 53 | * // skiplist_str_int.h 54 | * // Include this file wherever you need a string -> int skiplist 55 | * #ifndef SKIPLIST_STR_INT_H 56 | * #define SKIPLIST_STR_INT_H 57 | * 58 | * #define SKIPLIST_KEY const char * 59 | * #define SKIPLIST_VALUE int 60 | * #define SKIPLIST_NAMESPACE sl_strint_ 61 | * #include "skiplist.h" 62 | * 63 | * #endif 64 | * 65 | * // program.c 66 | * // short test drive program 67 | * #include 68 | * #include 69 | * #include 70 | * #define SKIPLIST_IMPLEMENTATION 71 | * #include "skiplist_str_int.h" 72 | * 73 | * int cmp(const char *a, const char *b, void *userdata) { 74 | * return strcmp(a, b); 75 | * } 76 | * 77 | * int iter(const char *key, int val, void *userdata) { 78 | * printf("%s = %d\n", key, val); 79 | * return 0; 80 | * } 81 | * 82 | * int main(int argc, const char **argv) { 83 | * sl_strint_skiplist list; 84 | * int err = sl_strint_init(&list, cmp, NULL); 85 | * assert(err == 0); 86 | * 87 | * sl_strint_insert(&list, "a", 1, NULL); 88 | * sl_strint_insert(&list, "c", 3, NULL); 89 | * sl_strint_insert(&list, "b", 2, NULL); 90 | * 91 | * short has_b = sl_strint_find(&list, "b", NULL), // => 1 92 | * has_d = sl_strint_find(&list, "d", NULL); // => 0 93 | * 94 | * int a_val; 95 | * short exists = sl_strint_find(&list, "a", &a_val); 96 | * if (exists) 97 | * printf("a = %d\n", a_val); 98 | * else 99 | * puts("a does not exist"); 100 | * 101 | * int default_val = 10; 102 | * int d_val = sl_strint_get(&list, "d", default_val); // => 10 103 | * 104 | * sl_strint_iter(&list, iter, NULL); 105 | * // Prints a = 1, b = 2, c = 3 106 | * 107 | * int b_val; 108 | * short existed = sl_strint_remove(&list, "b", &b_val); 109 | * if (existed) 110 | * print("b used to be %d, but now it is no more\n", b_val); 111 | * else 112 | * puts("b was only an illusion, a fleeting glimpse of a dream"); 113 | * 114 | * sl_strint_free(&list); 115 | * return 0; 116 | * } 117 | */ 118 | 119 | #ifndef SKIPLIST_MALLOC 120 | #ifdef SKIPLIST_IMPLEMENTATION 121 | #include 122 | #endif 123 | #define SKIPLIST_MALLOC(udata, sz) malloc((sz)) 124 | #define SKIPLIST_FREE(udata, ptr) free((ptr)) 125 | #endif 126 | 127 | #ifndef SKIPLIST_HEIGHT 128 | #ifdef SKIPLIST_IMPLEMENTATION 129 | #include 130 | #endif 131 | #define SKIPLIST_HEIGHT(key) (unsigned int)arc4random_uniform(SKIPLIST_MAX_LEVELS); 132 | #endif 133 | 134 | #if !defined(SKIPLIST_KEY) || !defined(SKIPLIST_VALUE) 135 | #error Please define SKIPLIST_KEY and SKIPLIST_VALUE before including \ 136 | this file. See the comments at the top for usage instructions. 137 | #endif 138 | 139 | #ifndef SKIPLIST_NAMESPACE 140 | #define SKIPLIST_NAMESPACE sl_ 141 | #endif 142 | 143 | #if !defined(SKIPLIST_EXTERN) && !defined(SKIPLIST_STATIC) 144 | #define SKIPLIST_EXTERN extern 145 | #elif defined(SKIPLIST_STATIC) 146 | #define SKIPLIST_EXTERN static 147 | #endif 148 | 149 | #ifndef SKIPLIST_MAX_LEVELS 150 | #define SKIPLIST_MAX_LEVELS 33 151 | #endif 152 | 153 | #define SL_PASTE_(x,y) x ## y 154 | #define SL_CAT_(x,y) SL_PASTE_(x,y) 155 | #define SKIPLIST_NAME(name) SL_CAT_(SKIPLIST_NAMESPACE,name) 156 | 157 | #define SL_NODE SKIPLIST_NAME(node) 158 | #define SL_LIST SKIPLIST_NAME(skiplist) 159 | #define SL_CMP_FN SKIPLIST_NAME(cmp_fn) 160 | #define SL_ITER_FN SKIPLIST_NAME(iter_fn) 161 | #define SL_KEY SKIPLIST_KEY 162 | #define SL_VAL SKIPLIST_VALUE 163 | 164 | 165 | typedef int (* SL_CMP_FN)(SL_KEY, SL_KEY, void *); 166 | typedef int (* SL_ITER_FN)(SL_KEY, SL_VAL, void *); 167 | 168 | typedef struct SKIPLIST_NAME(_node) { 169 | unsigned int height; 170 | SL_KEY key; 171 | SL_VAL val; 172 | struct SKIPLIST_NAME(_node) *prev; 173 | struct SKIPLIST_NAME(_node) *next[SKIPLIST_MAX_LEVELS]; 174 | } SL_NODE; 175 | 176 | typedef struct { 177 | unsigned long size; 178 | unsigned int highest; 179 | SL_CMP_FN cmp; 180 | void *cmp_udata; 181 | void *mem_udata; 182 | SKIPLIST_NAME(node) *head; 183 | } SL_LIST; 184 | 185 | /* Must be called prior to using any other functions on a skiplist. 186 | * @list a pointer to the skiplist to initialize 187 | * @cmp the comparator function to use to order nodes 188 | * @cmp_udata Opaque pointer to pass to cmp 189 | * @mem_udata Opaque pointer to pass to the SKIPLIST_MALLOC and 190 | * SKIPLIST_FREE macros. Unused by default, but custom 191 | * memory allocators may use it. 192 | * 193 | * @return 0 if successful and nonzero if something failed 194 | */ 195 | SKIPLIST_EXTERN 196 | int SKIPLIST_NAME(init)(SL_LIST *list, SL_CMP_FN cmp, void *cmp_udata, void *mem_udata); 197 | 198 | /* Free memory used by a skiplist. 199 | * @list a pointer to the skiplist to be freed 200 | */ 201 | SKIPLIST_EXTERN 202 | void SKIPLIST_NAME(free)(SL_LIST *list); 203 | 204 | /* Sets a value in the skiplist. 205 | * @list An initialized skiplist 206 | * @key Associate the value with this key 207 | * @val Value 208 | * @prior If non-NULL, put the prior value at this location. Not touched if 209 | * there was no prior value associated with this key. 210 | * 211 | * If a value already exists at that key, 212 | * overwrite it and stick the prior value in this function's out parameter. 213 | * 214 | * @return 0 if no value was at this key, 1 if a value did exist and was 215 | * overwritten. 216 | */ 217 | SKIPLIST_EXTERN 218 | short SKIPLIST_NAME(insert)(SL_LIST *list, SL_KEY key, SL_VAL val, SL_VAL *prior); 219 | 220 | /* Gets a value associated with a key. 221 | * @list An initialized skiplist 222 | * @key Get the value associated with this key 223 | * @out If a value exists, store it at this location. 224 | * If this parameter is NULL, nothing is stored. 225 | * 226 | * @return 0 if the key does not exist, 1 if it does 227 | */ 228 | SKIPLIST_EXTERN 229 | short SKIPLIST_NAME(find)(SL_LIST *list, SL_KEY key, SL_VAL *out); 230 | 231 | /* Gets a value associated with a key, or a default value. 232 | * @list An initialized skiplist 233 | * @key Get the value associated with this key 234 | * @default_val If the key does not exist in this list, 235 | * return this value instead. 236 | * 237 | * @return The value associated with the key or default_val if the 238 | * key is not set. 239 | */ 240 | SKIPLIST_EXTERN 241 | SL_VAL SKIPLIST_NAME(get)(SL_LIST *list, SL_KEY key, SL_VAL default_val); 242 | 243 | /* Removes a key/value pair from this list. 244 | * @list An initialized skiplist 245 | * @key Key indicating the key/value pair to remove 246 | * @out If non-NULL and the key existed, store the old value at this location 247 | * 248 | * @return 1 if the key used to be in the list (and was thus removed), 249 | * 0 if it was never there 250 | */ 251 | SKIPLIST_EXTERN 252 | short SKIPLIST_NAME(remove)(SL_LIST *list, SL_KEY key, SL_VAL *out); 253 | 254 | /* Iterates through all key/value pairs in order. 255 | * @list An initialized skiplist 256 | * @iter An iterator function to call for each key/value pair 257 | * @userdata An opaque pointer to pass to `iter`. 258 | * 259 | * If `iter` returns a non-zero value, stop the iteration and return 260 | * that value. 261 | * 262 | * @return The first non-zero result of `iter` or 0 if `iter` always 263 | * returned 0. 264 | */ 265 | SKIPLIST_EXTERN 266 | int SKIPLIST_NAME(iter)(SL_LIST *list, SL_ITER_FN iter, void *userdata); 267 | 268 | /* Returns the number of items in the skiplist 269 | * @list An initialized skiplist 270 | * 271 | * @return The number of key/value pairs in the skiplist 272 | */ 273 | SKIPLIST_EXTERN 274 | unsigned long SKIPLIST_NAME(size)(SL_LIST *list); 275 | 276 | /* Returns the minimum key and value in this list. 277 | * @list An initalized skiplist 278 | * @key_out Set to the smallest key if non-NULL and the list is not empty 279 | * @val_out Set to the value associated with the smallest key if non-NULL 280 | * and the list is not empty. 281 | * 282 | * @return 0 if the list is empty and 1 if it is not 283 | */ 284 | SKIPLIST_EXTERN 285 | short SKIPLIST_NAME(min)(SL_LIST *list, SL_KEY *key_out, SL_VAL *val_out); 286 | 287 | /* Returns the maximum key and value in this list. 288 | * @list An initalized skiplist 289 | * @key_out Set to the largest key if non-NULL and the list is not empty 290 | * @val_out Set to the value associated with the largest key if non-NULL 291 | * and the list is not empty. 292 | * 293 | * @return 0 if the list is empty and 1 if it is not 294 | */ 295 | SKIPLIST_EXTERN 296 | short SKIPLIST_NAME(max)(SL_LIST *list, SL_KEY *key_out, SL_VAL *val_out); 297 | 298 | /* Removes and returns the minimum key/value pair from a list. 299 | * @list An initialized skiplist 300 | * @key_out Set to the smallest key if non-NULL and the list is not empty 301 | * @val_out Set to the value associated with the smallest key if non-NULL 302 | * and the list is not empty. 303 | * 304 | * @return 0 if the list was already empty and 1 if it was not 305 | */ 306 | SKIPLIST_EXTERN 307 | short SKIPLIST_NAME(pop)(SL_LIST *list, SL_KEY *key_out, SL_VAL *val_out); 308 | 309 | /* Removes and returns the maximum key/value pair from a list. 310 | * @list An initialized skiplist 311 | * @key_out Set to the largest key if non-NULL and the list is not empty 312 | * @val_out Set to the value associated with the largest key if non-NULL 313 | * and the list is not empty. 314 | * 315 | * @return 0 if the list was already empty and 1 if it was not 316 | */ 317 | SKIPLIST_EXTERN 318 | short SKIPLIST_NAME(shift)(SL_LIST *list, SL_KEY *key_out, SL_VAL *val_out); 319 | 320 | #ifdef SKIPLIST_IMPLEMENTATION 321 | 322 | #define CMP_EQ(list, a, b) (list->cmp(a, b, list->cmp_udata) == 0) 323 | #define CMP_LT(list, a, b) (list->cmp(a, b, list->cmp_udata) < 0) 324 | 325 | SKIPLIST_EXTERN 326 | int SKIPLIST_NAME(init)(SL_LIST *list, SL_CMP_FN cmp, void *cmp_udata, void *mem_udata) { 327 | list->cmp = cmp; 328 | list->cmp_udata = cmp_udata; 329 | list->mem_udata = mem_udata; 330 | list->highest = 0; 331 | list->size = 0; 332 | list->head = (SL_NODE *)SKIPLIST_MALLOC(mem_udata, sizeof(SL_NODE)); 333 | list->head->height = SKIPLIST_MAX_LEVELS; 334 | memset(list->head->next, 0, SKIPLIST_MAX_LEVELS * sizeof(SL_NODE *)); 335 | return 0; 336 | } 337 | 338 | SKIPLIST_EXTERN 339 | void SKIPLIST_NAME(free)(SL_LIST *list) { 340 | SL_NODE *n, *next; 341 | n = list->head; 342 | while (n) { 343 | next = n->next[0]; 344 | SKIPLIST_FREE(list->mem_udata, n); 345 | n = next; 346 | } 347 | } 348 | 349 | SKIPLIST_EXTERN 350 | short SKIPLIST_NAME(insert)(SL_LIST *list, SL_KEY key, SL_VAL val, SL_VAL *prior) { 351 | SL_NODE *n, *nn, *update[SKIPLIST_MAX_LEVELS]; 352 | unsigned int i; 353 | short replaced; 354 | 355 | n = list->head; 356 | nn = (SL_NODE *)SKIPLIST_MALLOC(list->mem_udata, sizeof(SL_NODE)); 357 | nn->key = key; 358 | nn->val = val; 359 | memset(nn->next, 0, SKIPLIST_MAX_LEVELS * sizeof(SL_NODE *)); 360 | 361 | nn->height = SKIPLIST_HEIGHT(key); 362 | 363 | i = list->highest; 364 | while (i --> 0) { 365 | while (n->next[i] && CMP_LT(list, n->next[i]->key, key)) 366 | n = n->next[i]; 367 | update[i] = n; 368 | } 369 | n = n->next[0]; 370 | 371 | replaced = n != NULL && CMP_EQ(list, key, n->key); 372 | if (replaced) { 373 | if (prior) 374 | *prior = n->val; 375 | n->val = val; 376 | SKIPLIST_FREE(list->mem_udata, nn); 377 | } 378 | else { 379 | i = nn->height; 380 | while (nn->height >= list->highest) 381 | update[list->highest++] = list->head; 382 | 383 | for (i = 0; i <= nn->height; i++) { 384 | nn->next[i] = update[i]->next[i]; 385 | update[i]->next[i] = nn; 386 | } 387 | } 388 | 389 | list->size += !replaced; 390 | 391 | return replaced; 392 | } 393 | 394 | SKIPLIST_EXTERN 395 | short SKIPLIST_NAME(find)(SL_LIST *list, SL_KEY key, SL_VAL *out) { 396 | SL_NODE *n; 397 | unsigned int i; 398 | n = list->head; 399 | i = list->highest; 400 | 401 | while (i --> 0) { 402 | while (n->next[i] && CMP_LT(list, n->next[i]->key, key)) { 403 | n = n->next[i]; 404 | } 405 | } 406 | n = n->next[0]; 407 | 408 | if (n && CMP_EQ(list, key, n->key)) { 409 | if (out) 410 | *out = n->val; 411 | return 1; 412 | } 413 | return 0; 414 | } 415 | 416 | SKIPLIST_EXTERN 417 | SL_VAL SKIPLIST_NAME(get)(SL_LIST *list, SL_KEY key, SL_VAL default_val) { 418 | SL_VAL v; 419 | if (SKIPLIST_NAME(find)(list, key, &v)) 420 | return v; 421 | return default_val; 422 | } 423 | 424 | SKIPLIST_EXTERN 425 | short SKIPLIST_NAME(remove)(SL_LIST *list, SL_KEY key, SL_VAL *out) { 426 | SL_NODE *n; 427 | SL_NODE *update[SKIPLIST_MAX_LEVELS]; 428 | unsigned int i; 429 | 430 | if (list->size == 0) { 431 | return 0; 432 | } 433 | 434 | n = list->head; 435 | i = list->highest - 1; 436 | do { 437 | while (n->next[i] && CMP_LT(list, n->next[i]->key, key)) { 438 | n = n->next[i]; 439 | } 440 | update[i] = n; 441 | } while (i --> 0); 442 | 443 | n = n->next[0]; 444 | if (n && CMP_EQ(list, n->key, key)) { 445 | if (out) *out = n->val; 446 | i = 0; 447 | while (i < list->highest) { 448 | if (update[i]->next[i] != n) break; 449 | update[i]->next[i] = n->next[i]; 450 | i++; 451 | } 452 | SKIPLIST_FREE(list->mem_udata, n); 453 | while (list->highest > 0 && list->head->next[list->highest - 1] == NULL) { 454 | --list->highest; 455 | } 456 | --list->size; 457 | return 1; 458 | } 459 | return 0; 460 | } 461 | 462 | SKIPLIST_EXTERN 463 | int SKIPLIST_NAME(iter)(SL_LIST *list, SL_ITER_FN iter, void *userdata) { 464 | SL_NODE *n; 465 | int stop; 466 | n = list->head; 467 | while (n->next[0]) { 468 | if ((stop = iter(n->next[0]->key, n->next[0]->val, userdata))) 469 | return stop; 470 | n = n->next[0]; 471 | } 472 | return 0; 473 | } 474 | 475 | SKIPLIST_EXTERN 476 | unsigned long SKIPLIST_NAME(size)(SL_LIST *list) { 477 | return list->size; 478 | } 479 | 480 | SKIPLIST_EXTERN 481 | short SKIPLIST_NAME(min)(SL_LIST *list, SL_KEY *key_out, SL_VAL *val_out) { 482 | if (list->size == 0) 483 | return 0; 484 | if (key_out) 485 | *key_out = list->head->next[0]->key; 486 | if (val_out) 487 | *val_out = list->head->next[0]->val; 488 | return 1; 489 | } 490 | 491 | SKIPLIST_EXTERN 492 | short SKIPLIST_NAME(max)(SL_LIST *list, SL_KEY *key_out, SL_VAL *val_out) { 493 | int i; 494 | SL_NODE *n; 495 | if (list->size == 0) 496 | return 0; 497 | /* TODO store the biggest */ 498 | n = list->head; 499 | for (i = 0; i < list->size; ++i) 500 | n = n->next[0]; 501 | if (key_out) 502 | *key_out = n->key; 503 | if (val_out) 504 | *val_out = n->val; 505 | return 1; 506 | } 507 | 508 | SKIPLIST_EXTERN 509 | short SKIPLIST_NAME(pop)(SL_LIST *list, SL_KEY *key_out, SL_VAL *val_out) { 510 | int i; 511 | SL_NODE *first; 512 | 513 | if (list->size == 0) 514 | return 0; 515 | 516 | first = list->head->next[0]; 517 | i = first->height; 518 | do { 519 | if (first->next[i] == NULL) 520 | list->highest--; 521 | list->head->next[i] = first->next[i]; 522 | } while (i --> 0); 523 | 524 | if (key_out) 525 | *key_out = first->key; 526 | if (val_out) 527 | *val_out = first->val; 528 | SKIPLIST_FREE(list->mem_udata, first); 529 | --list->size; 530 | return 1; 531 | } 532 | 533 | SKIPLIST_EXTERN 534 | short SKIPLIST_NAME(shift)(SL_LIST *list, SL_KEY *key_out, SL_VAL *val_out) { 535 | int i; 536 | SL_NODE *penultimate, *last; 537 | if (list->size == 0) 538 | return 0; 539 | penultimate = list->head; 540 | for (i = 0; i < list->size - 1; ++i) 541 | penultimate = penultimate->next[0]; 542 | last = penultimate->next[0]; 543 | if (key_out) 544 | *key_out = last->key; 545 | if (val_out) 546 | *val_out = last->val; 547 | i = penultimate->height; 548 | while (i --> 0) 549 | penultimate->next[i] = NULL; 550 | SKIPLIST_FREE(list->mem_udata, last); 551 | --list->size; 552 | return 1; 553 | } 554 | 555 | #undef CMP_EQ 556 | #undef CMP_LT 557 | 558 | #endif 559 | 560 | #undef SL_PASTE_ 561 | #undef SL_CAT_ 562 | 563 | #undef SL_NODE 564 | #undef SL_LIST 565 | #undef SL_CMP_FN 566 | #undef SL_ITER_FN 567 | #undef SL_KEY 568 | #undef SL_VAL 569 | --------------------------------------------------------------------------------