├── .github └── workflows │ └── tests.yml ├── .gitignore ├── History.md ├── LICENSE ├── Makefile ├── Readme.md ├── benchmark.c ├── clib.json ├── src ├── list.c ├── list.h ├── list_iterator.c └── list_node.c └── test.c /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v3 10 | - run: sudo apt install -y valgrind 11 | - run: make bin/test 12 | - run: valgrind --leak-check=full --error-exitcode=5 ./bin/test 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | bin 3 | .pomo 4 | *.o 5 | build 6 | deps 7 | -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | 2 | 0.4.1 / 2023-05-05 3 | ================== 4 | 5 | * Minor fixes for debian packaging (#43) 6 | 7 | 0.1.0 / 2020-05-15 8 | ================== 9 | 10 | * Update list.h to allow configuration header set up LIST macros (#27) 11 | * fix typo in README (#19) 12 | 13 | 0.0.8 / 2015-01-30 14 | ================== 15 | 16 | * fix `list_at` unsigned compare error (bolasblack, #13) 17 | * travis: Fail the build if any memory is leaked 18 | * travis: Update before installing deps 19 | * test: Fix memory leaks 20 | 21 | 0.0.7 / 2014-06-02 22 | ================== 23 | 24 | * ci: Remove benchmarks 25 | * add travis 26 | * test: Fix 'unused parameter' warning 27 | * readme: Update benchmarks 28 | * benchmark: Fix 'unused parameter' warning 29 | * benchmark: Use clibs/bench for CPU time 30 | * readme: Correct types 31 | * readme: Add syntax highlighting 32 | * iterator: set to `NULL` after freeing 33 | 34 | 0.0.6 / 2014-05-06 35 | ================== 36 | 37 | * list.h: Fix header guards 38 | * Add “list” prefix to sources 39 | 40 | 0.0.5 / 2013-10-20 41 | ================== 42 | 43 | * add package.json 44 | * fix free in link_remove(). Closes #3 45 | 46 | 0.0.4 / 2011-04-15 47 | ================== 48 | 49 | * pop -> rpop 50 | * shift -> lpop 51 | * push -> rpush 52 | * unshift -> lpush 53 | 54 | 0.0.3 / 2010-11-27 55 | ================== 56 | 57 | * Added `make install` 58 | * Added `list_shift()` 59 | * Added `list_pop()` 60 | * Lowercased function names and typedefs 61 | * Wrap with extern "C" when \_\_cplusplus 62 | * Installing `list.h` 63 | 64 | 0.0.2 / 2010-08-21 65 | ================== 66 | 67 | * Added `ListNode *List_at(List *self, int index)` 68 | * Added `void List_remove(List *self, ListNode *node)` 69 | * Added `make liblist.a` 70 | 71 | 0.0.1 / 2010-08-13 72 | ================== 73 | 74 | * Initial release 75 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2009-2010 TJ Holowaychuk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | AR ?= ar 3 | CC ?= gcc 4 | STRIP ?= strip 5 | PREFIX ?= /usr/local 6 | LIBDIR ?= $(PREFIX)/lib 7 | INCLUDEDIR ?= $(PREFIX)/include 8 | INCLUDESUBDIR ?= /clibs 9 | DESTDIR ?= 10 | 11 | CFLAGS = -O3 -std=c99 -Wall -Wextra -Ideps 12 | LDFLAGS ?= -Wl,-z,now 13 | 14 | SRCS = src/list.c \ 15 | src/list_node.c \ 16 | src/list_iterator.c 17 | 18 | OBJS = $(SRCS:.c=.o) 19 | 20 | MAJOR_VERSION = 0 21 | MINOR_VERSION = 4 22 | PATCH_VERSION = 1 23 | 24 | all: build/libclibs_list.a build/libclibs_list.so.$(MAJOR_VERSION).$(MINOR_VERSION).$(PATCH_VERSION) 25 | 26 | install: all 27 | test -d $(DESTDIR)$(LIBDIR) || mkdir -p $(DESTDIR)$(LIBDIR) 28 | cp -f build/libclibs_list.a $(DESTDIR)$(LIBDIR)/libclibs_list.a 29 | cp -f build/libclibs_list.so.$(MAJOR_VERSION).$(MINOR_VERSION).$(PATCH_VERSION) $(DESTDIR)$(LIBDIR)/libclibs_list.so.$(MAJOR_VERSION).$(MINOR_VERSION).$(PATCH_VERSION) 30 | ln -sf libclibs_list.so.$(MAJOR_VERSION).$(MINOR_VERSION).$(PATCH_VERSION) $(DESTDIR)$(LIBDIR)/libclibs_list.so.$(MAJOR_VERSION) 31 | ln -sf libclibs_list.so.$(MAJOR_VERSION) $(DESTDIR)$(LIBDIR)/libclibs_list.so 32 | test -d $(DESTDIR)$(INCLUDEDIR)$(INCLUDESUBDIR) || mkdir -p $(DESTDIR)$(INCLUDEDIR)$(INCLUDESUBDIR)/ 33 | cp -f src/list.h $(DESTDIR)$(INCLUDEDIR)$(INCLUDESUBDIR)/list.h 34 | 35 | uninstall: 36 | rm -f $(DESTDIR)$(LIBDIR)/libclibs_list.a 37 | rm -f $(DESTDIR)$(LIBDIR)/libclibs_list.so 38 | rm -f $(DESTDIR)$(LIBDIR)/libclibs_list.so.$(MAJOR_VERSION) 39 | rm -f $(DESTDIR)$(LIBDIR)/libclibs_list.so.$(MAJOR_VERSION).$(MINOR_VERSION) 40 | rm -f $(DESTDIR)$(LIBDIR)/libclibs_list.so.$(MAJOR_VERSION).$(MINOR_VERSION).$(PATCH_VERSION) 41 | rm -f $(DESTDIR)$(INCLUDEDIR)$(INCLUDESUBDIR)/list.h 42 | 43 | build/libclibs_list.a: $(OBJS) 44 | @mkdir -p build 45 | $(AR) rcs $@ $^ 46 | 47 | build/libclibs_list.so.$(MAJOR_VERSION).$(MINOR_VERSION).$(PATCH_VERSION): $(OBJS) 48 | @mkdir -p build 49 | $(CC) $(LDFLAGS) -shared -lc -Wl,-soname,`basename $@ | sed s/\.${MINOR_VERSION}.${PATCH_VERSION}//` src/*.o -o $@ 50 | $(STRIP) --strip-unneeded --remove-section=.comment --remove-section=.note $@ 51 | 52 | bin/test: test.o $(OBJS) 53 | @mkdir -p bin 54 | $(CC) $^ -o $@ 55 | 56 | bin/benchmark: benchmark.o $(OBJS) 57 | @mkdir -p bin 58 | $(CC) $^ -o $@ 59 | 60 | %.o: %.c 61 | $(CC) $< $(CFLAGS) -c -o $@ 62 | 63 | clean: 64 | rm -fr bin build *.o src/*.o 65 | 66 | test: bin/test 67 | @./$< 68 | 69 | benchmark: bin/benchmark 70 | @./$< 71 | 72 | .PHONY: test benchmark clean install uninstall 73 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | 2 | # list 3 | 4 | C doubly linked list implementation. 5 | 6 | ## API 7 | 8 | Below is the public api currently provided by "list". 9 | 10 | ## list_t *list_new(); 11 | 12 | Allocate and initialize a `list`. 13 | 14 | list_t *mylist = list_new(); 15 | 16 | ## list_node_t \*list_node_new(void *val) 17 | 18 | Allocate and initialize a `list_node_t` with the given _val_. 19 | 20 | ```c 21 | list_node_t *node = list_node_new("my value"); 22 | node->val; // "my value" 23 | ``` 24 | 25 | ## list_node_t \* list_rpush(list_t \*self, list_node_t *node) 26 | 27 | Append _node_ to _self_, returning _node_. 28 | 29 | ```c 30 | list_rpush(list, list_node_new("value")); 31 | list->tail->val; // "value" 32 | ``` 33 | 34 | ## list_node_t \* list_rpop(list_t \*self) 35 | 36 | Return / detach node from the end of the list, or __NULL__. 37 | 38 | ```c 39 | list_node_t *last = list_rpop(names); 40 | ``` 41 | 42 | ## list_node_t \*list_lpush(list_t \*self, list_node_t *node) 43 | 44 | Prepend _node_ to _self_, returning _node_. 45 | 46 | ```c 47 | list_lpush(list, list_node_new("value")); 48 | list->head->val; // "value" 49 | ``` 50 | 51 | ## list_node_t \*list_find(list_t \*self, void *val) 52 | 53 | Return the `list_node_t` containing _val_ or __NULL__. 54 | 55 | ```c 56 | list_node_t *node = list_find(list, "some value"); 57 | ``` 58 | 59 | ## list_node_t \*list_at(list_t *self, int index) 60 | 61 | Return the `list_node_t` at the given _index_, where _index_ 62 | may also be a negative integer indicating an index from the 63 | list _tail_. 64 | 65 | ```c 66 | list_at(list, 0); // first 67 | list_at(list, 1); // second 68 | list_at(list, -1); // last 69 | list_at(list, -3); // third last 70 | ``` 71 | 72 | ## void list_remove(list_t \*self, list_node_t *node) 73 | 74 | Remove _node_ from the list, freeing it and it's value. 75 | 76 | ```c 77 | list_remove(list, node); 78 | ``` 79 | 80 | ## void list_destroy(list_t *self) 81 | 82 | Free the list and all nodes. 83 | 84 | ```c 85 | list_destroy(list); 86 | ``` 87 | 88 | ## list_iterator_t \*list_iterator_new(list_t *list, list_direction_t direction) 89 | 90 | Allocate and initialize a `list_iterator_t` with the given _direction_, 91 | where _direction_ may be __LIST_HEAD__ or __LIST_TAIL__. 92 | 93 | ```c 94 | list_node_t *node; 95 | list_iterator_t *it = list_iterator_new(list, LIST_HEAD); 96 | while ((node = list_iterator_next(it))) { 97 | puts(node->val); 98 | } 99 | ``` 100 | 101 | ## list_node_t \*list_iterator_next(list_iterator_t *self) 102 | 103 | Return the next `list_node_t` or __NULL__. 104 | 105 | ## void list_iterator_destroy(list_iterator_t *self); 106 | 107 | Free the iterator only. 108 | 109 | ```c 110 | list_iterator_destroy(it); 111 | ``` 112 | 113 | ## Examples 114 | 115 | list iteration: 116 | 117 | ```c 118 | list_t *langs = list_new(); 119 | 120 | list_node_t *c = list_rpush(langs, list_node_new("c")); 121 | list_node_t *js = list_rpush(langs, list_node_new("js")); 122 | list_node_t *ruby = list_rpush(langs, list_node_new("ruby")); 123 | 124 | list_node_t *node; 125 | list_iterator_t *it = list_iterator_new(langs, LIST_HEAD); 126 | while ((node = list_iterator_next(it))) { 127 | puts(node->val); 128 | } 129 | 130 | list_iterator_destroy(it); 131 | list_destroy(langs); 132 | ``` 133 | 134 | stdout: 135 | 136 | c 137 | js 138 | ruby 139 | 140 | ## Benchmarks 141 | 142 | $ make benchmark 143 | 144 | 10,000,000 nodes 145 | 146 | lpush: 0.5000s 147 | rpush: 0.5000s 148 | lpop: 0.0312s 149 | rpop: 0.0312s 150 | find (last node): 0.0312s 151 | iterate: 0.0625s 152 | at(100,000): 0.0000s 153 | at(1,000,000): 0.0000s 154 | at(-100,000): 0.0000s 155 | 156 | 157 | 158 | ## Testing 159 | 160 | $ make test 161 | 162 | ## License 163 | 164 | (The MIT License) 165 | 166 | Copyright (c) 2009-2010 TJ Holowaychuk <tj@vision-media.ca> 167 | 168 | Permission is hereby granted, free of charge, to any person obtaining 169 | a copy of this software and associated documentation files (the 170 | 'Software'), to deal in the Software without restriction, including 171 | without limitation the rights to use, copy, modify, merge, publish, 172 | distribute, sublicense, and/or sell copies of the Software, and to 173 | permit persons to whom the Software is furnished to do so, subject to 174 | the following conditions: 175 | 176 | The above copyright notice and this permission notice shall be 177 | included in all copies or substantial portions of the Software. 178 | 179 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 180 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 181 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 182 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 183 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 184 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 185 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 186 | -------------------------------------------------------------------------------- /benchmark.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "bench/bench.h" 4 | #include "src/list.h" 5 | 6 | static void 7 | bm(char *label, void (*fn)()) { 8 | printf(" %18s", label); 9 | fflush(stdout); 10 | fn(); 11 | } 12 | 13 | static int nnodes = 10000000; 14 | static float startTime; 15 | 16 | 17 | static void 18 | start() { 19 | startTime = cpu(); 20 | } 21 | 22 | static void 23 | stop() { 24 | float duration = cpu() - startTime; 25 | printf(": \x1b[32m%.4f\x1b[0ms\n", duration); 26 | } 27 | 28 | static void 29 | bm_rpush() { 30 | start(); 31 | int n = nnodes; 32 | list_t *list = list_new(); 33 | while (n--) { 34 | list_rpush(list, list_node_new("foo")); 35 | } 36 | stop(); 37 | } 38 | 39 | static void 40 | bm_lpush() { 41 | start(); 42 | int n = nnodes; 43 | list_t *list = list_new(); 44 | while (n--) { 45 | list_lpush(list, list_node_new("foo")); 46 | } 47 | stop(); 48 | } 49 | 50 | static void 51 | bm_find() { 52 | int n = nnodes; 53 | list_t *list = list_new(); 54 | while (n--) { 55 | list_lpush(list, list_node_new("foo")); 56 | } 57 | list_rpush(list, list_node_new("bar")); 58 | start(); 59 | list_find(list, "bar"); 60 | stop(); 61 | } 62 | 63 | static void 64 | bm_iterate() { 65 | int n = nnodes; 66 | list_t *list = list_new(); 67 | while (n--) { 68 | list_lpush(list, list_node_new("foo")); 69 | } 70 | list_iterator_t *it = list_iterator_new(list, LIST_HEAD); 71 | list_node_t *node; 72 | start(); 73 | while ((node = list_iterator_next(it))) 74 | ; 75 | stop(); 76 | } 77 | 78 | static void 79 | bm_rpop() { 80 | int n = nnodes; 81 | list_t *list = list_new(); 82 | while (n--) { 83 | list_lpush(list, list_node_new("foo")); 84 | } 85 | list_node_t *node; 86 | start(); 87 | while ((node = list_rpop(list))) 88 | ; 89 | stop(); 90 | } 91 | 92 | static void 93 | bm_lpop() { 94 | int n = nnodes; 95 | list_t *list = list_new(); 96 | while (n--) { 97 | list_lpush(list, list_node_new("foo")); 98 | } 99 | list_node_t *node; 100 | start(); 101 | while ((node = list_lpop(list))) 102 | ; 103 | stop(); 104 | } 105 | 106 | static list_t *list; 107 | 108 | static void 109 | bm_at() { 110 | start(); 111 | list_at(list, 100000); 112 | stop(); 113 | } 114 | 115 | static void 116 | bm_at2() { 117 | start(); 118 | list_at(list, 1000000); 119 | stop(); 120 | } 121 | 122 | static void 123 | bm_at3() { 124 | start(); 125 | list_at(list, -100000); 126 | stop(); 127 | } 128 | 129 | int 130 | main(void){ 131 | int n = nnodes; 132 | list = list_new(); 133 | while (n--) list_lpush(list, list_node_new("foo")); 134 | puts("\n 10,000,000 nodes\n"); 135 | bm("lpush", bm_lpush); 136 | bm("rpush", bm_rpush); 137 | bm("lpop", bm_lpop); 138 | bm("rpop", bm_rpop); 139 | bm("find (last node)", bm_find); 140 | bm("iterate", bm_iterate); 141 | bm("at(100,000)", bm_at); 142 | bm("at(1,000,000)", bm_at2); 143 | bm("at(-100,000)", bm_at3); 144 | puts(""); 145 | return 0; 146 | } 147 | -------------------------------------------------------------------------------- /clib.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "list", 3 | "version": "0.4.1", 4 | "repo": "clibs/list", 5 | "description": "Simple linked list", 6 | "keywords": ["list", "structure"], 7 | "license": "MIT", 8 | "src": [ 9 | "src/list_iterator.c", 10 | "src/list.c", 11 | "src/list_node.c", 12 | "src/list.h" 13 | ], 14 | "development": { 15 | "bench": "*" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/list.c: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // list.c 4 | // 5 | // Copyright (c) 2010 TJ Holowaychuk 6 | // 7 | 8 | #include "list.h" 9 | 10 | /* 11 | * Allocate a new list_t. NULL on failure. 12 | */ 13 | 14 | list_t * 15 | list_new(void) { 16 | list_t *self; 17 | if (!(self = LIST_MALLOC(sizeof(list_t)))) 18 | return NULL; 19 | self->head = NULL; 20 | self->tail = NULL; 21 | self->free = NULL; 22 | self->match = NULL; 23 | self->len = 0; 24 | return self; 25 | } 26 | 27 | /* 28 | * Free the list. 29 | * @self: Pointer to the list 30 | */ 31 | 32 | void 33 | list_destroy(list_t *self) { 34 | unsigned int len = self->len; 35 | list_node_t *next; 36 | list_node_t *curr = self->head; 37 | 38 | while (len--) { 39 | next = curr->next; 40 | if (self->free) self->free(curr->val); 41 | LIST_FREE(curr); 42 | curr = next; 43 | } 44 | 45 | LIST_FREE(self); 46 | } 47 | 48 | /* 49 | * Append the given node to the list 50 | * and return the node, NULL on failure. 51 | * @self: Pointer to the list for popping node 52 | * @node: the node to push 53 | */ 54 | 55 | list_node_t * 56 | list_rpush(list_t *self, list_node_t *node) { 57 | if (!node) return NULL; 58 | 59 | if (self->len) { 60 | node->prev = self->tail; 61 | node->next = NULL; 62 | self->tail->next = node; 63 | self->tail = node; 64 | } else { 65 | self->head = self->tail = node; 66 | node->prev = node->next = NULL; 67 | } 68 | 69 | ++self->len; 70 | return node; 71 | } 72 | 73 | /* 74 | * Return / detach the last node in the list, or NULL. 75 | * @self: Pointer to the list for popping node 76 | */ 77 | 78 | list_node_t * 79 | list_rpop(list_t *self) { 80 | if (!self->len) return NULL; 81 | 82 | list_node_t *node = self->tail; 83 | 84 | if (--self->len) { 85 | (self->tail = node->prev)->next = NULL; 86 | } else { 87 | self->tail = self->head = NULL; 88 | } 89 | 90 | node->next = node->prev = NULL; 91 | return node; 92 | } 93 | 94 | /* 95 | * Return / detach the first node in the list, or NULL. 96 | * @self: Pointer to the list for popping node 97 | */ 98 | 99 | list_node_t * 100 | list_lpop(list_t *self) { 101 | if (!self->len) return NULL; 102 | 103 | list_node_t *node = self->head; 104 | 105 | if (--self->len) { 106 | (self->head = node->next)->prev = NULL; 107 | } else { 108 | self->head = self->tail = NULL; 109 | } 110 | 111 | node->next = node->prev = NULL; 112 | return node; 113 | } 114 | 115 | /* 116 | * Prepend the given node to the list 117 | * and return the node, NULL on failure. 118 | * @self: Pointer to the list for pushing node 119 | * @node: the node to push 120 | */ 121 | 122 | list_node_t * 123 | list_lpush(list_t *self, list_node_t *node) { 124 | if (!node) return NULL; 125 | 126 | if (self->len) { 127 | node->next = self->head; 128 | node->prev = NULL; 129 | self->head->prev = node; 130 | self->head = node; 131 | } else { 132 | self->head = self->tail = node; 133 | node->prev = node->next = NULL; 134 | } 135 | 136 | ++self->len; 137 | return node; 138 | } 139 | 140 | /* 141 | * Return the node associated to val or NULL. 142 | * @self: Pointer to the list for finding given value 143 | * @val: Value to find 144 | */ 145 | 146 | list_node_t * 147 | list_find(list_t *self, void *val) { 148 | list_iterator_t *it = list_iterator_new(self, LIST_HEAD); 149 | list_node_t *node; 150 | 151 | while ((node = list_iterator_next(it))) { 152 | if (self->match) { 153 | if (self->match(val, node->val)) { 154 | list_iterator_destroy(it); 155 | return node; 156 | } 157 | } else { 158 | if (val == node->val) { 159 | list_iterator_destroy(it); 160 | return node; 161 | } 162 | } 163 | } 164 | 165 | list_iterator_destroy(it); 166 | return NULL; 167 | } 168 | 169 | /* 170 | * Return the node at the given index or NULL. 171 | * @self: Pointer to the list for finding given index 172 | * @index: the index of node in the list 173 | */ 174 | 175 | list_node_t * 176 | list_at(list_t *self, int index) { 177 | list_direction_t direction = LIST_HEAD; 178 | 179 | if (index < 0) { 180 | direction = LIST_TAIL; 181 | index = ~index; 182 | } 183 | 184 | if ((unsigned)index < self->len) { 185 | list_iterator_t *it = list_iterator_new(self, direction); 186 | list_node_t *node = list_iterator_next(it); 187 | while (index--) node = list_iterator_next(it); 188 | list_iterator_destroy(it); 189 | return node; 190 | } 191 | 192 | return NULL; 193 | } 194 | 195 | /* 196 | * Remove the given node from the list, freeing it and it's value. 197 | * @self: Pointer to the list to delete a node 198 | * @node: Pointer the node to be deleted 199 | */ 200 | 201 | void 202 | list_remove(list_t *self, list_node_t *node) { 203 | node->prev 204 | ? (node->prev->next = node->next) 205 | : (self->head = node->next); 206 | 207 | node->next 208 | ? (node->next->prev = node->prev) 209 | : (self->tail = node->prev); 210 | 211 | if (self->free) self->free(node->val); 212 | 213 | LIST_FREE(node); 214 | --self->len; 215 | } 216 | -------------------------------------------------------------------------------- /src/list.h: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // list.h 4 | // 5 | // Copyright (c) 2010 TJ Holowaychuk 6 | // 7 | 8 | #ifndef __CLIBS_LIST_H__ 9 | #define __CLIBS_LIST_H__ 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | #include 16 | 17 | // Library version 18 | 19 | #define LIST_VERSION "0.4.1" 20 | 21 | // Memory management macros 22 | #ifdef LIST_CONFIG_H 23 | #define _STR(x) #x 24 | #define STR(x) _STR(x) 25 | #include STR(LIST_CONFIG_H) 26 | #undef _STR 27 | #undef STR 28 | #endif 29 | 30 | #ifndef LIST_MALLOC 31 | #define LIST_MALLOC malloc 32 | #endif 33 | 34 | #ifndef LIST_FREE 35 | #define LIST_FREE free 36 | #endif 37 | 38 | /* 39 | * list_t iterator direction. 40 | */ 41 | 42 | typedef enum { 43 | LIST_HEAD 44 | , LIST_TAIL 45 | } list_direction_t; 46 | 47 | /* 48 | * list_t node struct. 49 | */ 50 | 51 | typedef struct list_node { 52 | struct list_node *prev; 53 | struct list_node *next; 54 | void *val; 55 | } list_node_t; 56 | 57 | /* 58 | * list_t struct. 59 | */ 60 | 61 | typedef struct { 62 | list_node_t *head; 63 | list_node_t *tail; 64 | unsigned int len; 65 | void (*free)(void *val); 66 | int (*match)(void *a, void *b); 67 | } list_t; 68 | 69 | /* 70 | * list_t iterator struct. 71 | */ 72 | 73 | typedef struct { 74 | list_node_t *next; 75 | list_direction_t direction; 76 | } list_iterator_t; 77 | 78 | // Node prototypes. 79 | 80 | list_node_t * 81 | list_node_new(void *val); 82 | 83 | // list_t prototypes. 84 | 85 | list_t * 86 | list_new(void); 87 | 88 | list_node_t * 89 | list_rpush(list_t *self, list_node_t *node); 90 | 91 | list_node_t * 92 | list_lpush(list_t *self, list_node_t *node); 93 | 94 | list_node_t * 95 | list_find(list_t *self, void *val); 96 | 97 | list_node_t * 98 | list_at(list_t *self, int index); 99 | 100 | list_node_t * 101 | list_rpop(list_t *self); 102 | 103 | list_node_t * 104 | list_lpop(list_t *self); 105 | 106 | void 107 | list_remove(list_t *self, list_node_t *node); 108 | 109 | void 110 | list_destroy(list_t *self); 111 | 112 | // list_t iterator prototypes. 113 | 114 | list_iterator_t * 115 | list_iterator_new(list_t *list, list_direction_t direction); 116 | 117 | list_iterator_t * 118 | list_iterator_new_from_node(list_node_t *node, list_direction_t direction); 119 | 120 | list_node_t * 121 | list_iterator_next(list_iterator_t *self); 122 | 123 | void 124 | list_iterator_destroy(list_iterator_t *self); 125 | 126 | #ifdef __cplusplus 127 | } 128 | #endif 129 | 130 | #endif /* __CLIBS_LIST_H__ */ 131 | -------------------------------------------------------------------------------- /src/list_iterator.c: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // iterator.c 4 | // 5 | // Copyright (c) 2010 TJ Holowaychuk 6 | // 7 | 8 | #include "list.h" 9 | 10 | /* 11 | * Allocate a new list_iterator_t. NULL on failure. 12 | * Accepts a direction, which may be LIST_HEAD or LIST_TAIL. 13 | */ 14 | 15 | list_iterator_t * 16 | list_iterator_new(list_t *list, list_direction_t direction) { 17 | list_node_t *node = direction == LIST_HEAD 18 | ? list->head 19 | : list->tail; 20 | return list_iterator_new_from_node(node, direction); 21 | } 22 | 23 | /* 24 | * Allocate a new list_iterator_t with the given start 25 | * node. NULL on failure. 26 | */ 27 | 28 | list_iterator_t * 29 | list_iterator_new_from_node(list_node_t *node, list_direction_t direction) { 30 | list_iterator_t *self; 31 | if (!(self = LIST_MALLOC(sizeof(list_iterator_t)))) 32 | return NULL; 33 | self->next = node; 34 | self->direction = direction; 35 | return self; 36 | } 37 | 38 | /* 39 | * Return the next list_node_t or NULL when no more 40 | * nodes remain in the list. 41 | */ 42 | 43 | list_node_t * 44 | list_iterator_next(list_iterator_t *self) { 45 | list_node_t *curr = self->next; 46 | if (curr) { 47 | self->next = self->direction == LIST_HEAD 48 | ? curr->next 49 | : curr->prev; 50 | } 51 | return curr; 52 | } 53 | 54 | /* 55 | * Free the list iterator. 56 | */ 57 | 58 | void 59 | list_iterator_destroy(list_iterator_t *self) { 60 | LIST_FREE(self); 61 | self = NULL; 62 | } 63 | -------------------------------------------------------------------------------- /src/list_node.c: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // node.c 4 | // 5 | // Copyright (c) 2010 TJ Holowaychuk 6 | // 7 | 8 | #include "list.h" 9 | 10 | /* 11 | * Allocates a new list_node_t. NULL on failure. 12 | */ 13 | 14 | list_node_t * 15 | list_node_new(void *val) { 16 | list_node_t *self; 17 | if (!(self = LIST_MALLOC(sizeof(list_node_t)))) 18 | return NULL; 19 | self->prev = NULL; 20 | self->next = NULL; 21 | self->val = val; 22 | return self; 23 | } -------------------------------------------------------------------------------- /test.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "src/list.h" 7 | 8 | // Helpers 9 | 10 | #define test(fn) \ 11 | puts("... \x1b[33m" # fn "\x1b[0m"); \ 12 | test_##fn(); //Call the test function 13 | 14 | // For counting destroyed time 15 | static int freeProxyCalls = 0; 16 | 17 | void 18 | freeProxy(void *val) { 19 | ++freeProxyCalls; 20 | free(val); 21 | } 22 | 23 | typedef struct { 24 | char *name; 25 | } User; 26 | 27 | static int 28 | User_equal(void *a1, void *b1) { 29 | User *a = a1; 30 | User *b = b1; 31 | return 0 == strcmp(a->name, b->name); 32 | } 33 | 34 | // Tests 35 | 36 | static void 37 | test_list_node_new() { 38 | char *val = "some value"; 39 | list_node_t *node = list_node_new(val); 40 | assert(node->val == val); 41 | free(node); 42 | } 43 | 44 | static void 45 | test_list_rpush() { 46 | // Setup 47 | list_t *list = list_new(); 48 | list_node_t *a = list_node_new("a"); 49 | list_node_t *b = list_node_new("b"); 50 | list_node_t *c = list_node_new("c"); 51 | 52 | // a b c 53 | list_rpush(list, a); 54 | list_rpush(list, b); 55 | list_rpush(list, c); 56 | 57 | // Assertions 58 | assert(a == list->head); 59 | assert(c == list->tail); 60 | assert(3 == list->len); 61 | assert(b == a->next); 62 | assert(NULL == a->prev); 63 | assert(c == b->next); 64 | assert(a == b->prev); 65 | assert(NULL == c->next); 66 | assert(b == c->prev); 67 | 68 | list_destroy(list); 69 | } 70 | 71 | static void 72 | test_list_lpush() { 73 | // Setup 74 | list_t *list = list_new(); 75 | list_node_t *a = list_node_new("a"); 76 | list_node_t *b = list_node_new("b"); 77 | list_node_t *c = list_node_new("c"); 78 | 79 | // c b a 80 | list_rpush(list, a); 81 | list_lpush(list, b); 82 | list_lpush(list, c); 83 | 84 | // Assertions 85 | assert(c == list->head); 86 | assert(a == list->tail); 87 | assert(3 == list->len); 88 | assert(NULL == a->next); 89 | assert(b == a->prev); 90 | assert(a == b->next); 91 | assert(c == b->prev); 92 | assert(b == c->next); 93 | assert(NULL == c->prev); 94 | 95 | list_destroy(list); 96 | } 97 | 98 | static void 99 | test_list_at() { 100 | // Setup 101 | list_t *list = list_new(); 102 | list_node_t *a = list_node_new("a"); 103 | list_node_t *b = list_node_new("b"); 104 | list_node_t *c = list_node_new("c"); 105 | 106 | // a b c 107 | list_rpush(list, a); 108 | list_rpush(list, b); 109 | list_rpush(list, c); 110 | 111 | // Assertions 112 | assert(a == list_at(list, 0)); 113 | assert(b == list_at(list, 1)); 114 | assert(c == list_at(list, 2)); 115 | assert(NULL == list_at(list, 3)); 116 | 117 | assert(c == list_at(list, -1)); 118 | assert(b == list_at(list, -2)); 119 | assert(a == list_at(list, -3)); 120 | assert(NULL == list_at(list, -4)); 121 | 122 | list_destroy(list); 123 | } 124 | 125 | static void 126 | test_list_destroy() { 127 | // Setup 128 | list_t *a = list_new(); 129 | list_destroy(a); 130 | 131 | // a b c 132 | list_t *b = list_new(); 133 | list_rpush(b, list_node_new("a")); 134 | list_rpush(b, list_node_new("b")); 135 | list_rpush(b, list_node_new("c")); 136 | list_destroy(b); 137 | 138 | // Assertions 139 | list_t *c = list_new(); 140 | c->free = freeProxy; 141 | list_rpush(c, list_node_new(list_node_new("a"))); 142 | list_rpush(c, list_node_new(list_node_new("b"))); 143 | list_rpush(c, list_node_new(list_node_new("c"))); 144 | list_destroy(c); 145 | assert(3 == freeProxyCalls); 146 | freeProxyCalls=0; 147 | } 148 | 149 | static void 150 | test_list_destroy_complexver() { 151 | // Setup 152 | list_t *a = list_new(); 153 | list_destroy(a); 154 | 155 | // a b c 156 | list_t *b = list_new(); 157 | list_rpush(b, list_node_new("a")); 158 | list_rpush(b, list_node_new("b")); 159 | list_rpush(b, list_node_new("c")); 160 | list_destroy(b); 161 | 162 | // Assertions 163 | list_t *c = list_new(); 164 | c->free = freeProxy; 165 | list_rpush(c, list_node_new(list_node_new("a"))); 166 | list_rpush(c, list_node_new(list_node_new("b"))); 167 | list_rpush(c, list_node_new(list_node_new("c"))); 168 | list_destroy(c); 169 | assert(3 == freeProxyCalls); 170 | freeProxyCalls=0; 171 | list_t *d = list_new(); 172 | d->free = freeProxy; 173 | list_rpush(d, list_node_new(list_node_new("a"))); 174 | list_rpush(d, list_node_new(list_node_new("b"))); 175 | list_rpush(d, list_node_new(list_node_new("c"))); 176 | list_destroy(d); 177 | assert(3 == freeProxyCalls); 178 | freeProxyCalls=0; 179 | } 180 | static void 181 | test_list_empty_list_destroy() { 182 | list_t *list = list_new(); 183 | list_destroy(list); 184 | freeProxyCalls=0; 185 | } 186 | static void 187 | test_list_find() { 188 | // Setup 189 | list_t *langs = list_new(); 190 | list_node_t *js = list_rpush(langs, list_node_new("js")); 191 | list_node_t *ruby = list_rpush(langs, list_node_new("ruby")); 192 | 193 | list_t *users = list_new(); 194 | users->match = User_equal; 195 | User userTJ = { "tj" }; 196 | User userSimon = { "simon" }; 197 | User userTaylor = { "taylor" }; 198 | list_node_t *tj = list_rpush(users, list_node_new(&userTJ)); 199 | list_node_t *simon = list_rpush(users, list_node_new(&userSimon)); 200 | 201 | // Assertions 202 | list_node_t *a = list_find(langs, "js"); 203 | list_node_t *b = list_find(langs, "ruby"); 204 | list_node_t *c = list_find(langs, "foo"); 205 | assert(js == a); 206 | assert(ruby == b); 207 | assert(NULL == c); 208 | 209 | list_destroy(langs); 210 | 211 | a = list_find(users, &userTJ); 212 | b = list_find(users, &userSimon); 213 | c = list_find(users, &userTaylor); 214 | assert(tj == a); 215 | assert(simon == b); 216 | assert(NULL == c); 217 | 218 | list_destroy(users); 219 | } 220 | 221 | static void 222 | test_list_remove() { 223 | // Setup 224 | list_t *list = list_new(); 225 | list_node_t *a = list_rpush(list, list_node_new("a")); 226 | list_node_t *b = list_rpush(list, list_node_new("b")); 227 | list_node_t *c = list_rpush(list, list_node_new("c")); 228 | 229 | // Assertions 230 | assert(3 == list->len); 231 | 232 | list_remove(list, b); 233 | assert(2 == list->len); 234 | assert(a == list->head); 235 | assert(c == list->tail); 236 | assert(c == a->next); 237 | assert(NULL == a->prev); 238 | assert(NULL == c->next); 239 | assert(a == c->prev); 240 | 241 | list_remove(list, a); 242 | assert(1 == list->len); 243 | assert(c == list->head); 244 | assert(c == list->tail); 245 | assert(NULL == c->next); 246 | assert(NULL == c->prev); 247 | 248 | list_remove(list, c); 249 | assert(0 == list->len); 250 | assert(NULL == list->head); 251 | assert(NULL == list->tail); 252 | 253 | list_destroy(list); 254 | } 255 | 256 | static void 257 | test_list_rpop() { 258 | // Setup 259 | list_t *list = list_new(); 260 | list_node_t *a = list_rpush(list, list_node_new("a")); 261 | list_node_t *b = list_rpush(list, list_node_new("b")); 262 | list_node_t *c = list_rpush(list, list_node_new("c")); 263 | 264 | // Assertions 265 | assert(3 == list->len); 266 | 267 | assert(c == list_rpop(list)); 268 | assert(2 == list->len); 269 | assert(a == list->head); 270 | assert(b == list->tail); 271 | assert(a == b->prev); 272 | assert(NULL == list->tail->next && "new tail node next is not NULL"); 273 | assert(NULL == c->prev && "detached node prev is not NULL"); 274 | assert(NULL == c->next && "detached node next is not NULL"); 275 | 276 | free(c); 277 | 278 | assert(b == list_rpop(list)); 279 | assert(1 == list->len); 280 | assert(a == list->head); 281 | assert(a == list->tail); 282 | 283 | free(b); 284 | 285 | assert(a == list_rpop(list)); 286 | assert(0 == list->len); 287 | assert(NULL == list->head); 288 | assert(NULL == list->tail); 289 | 290 | free(a); 291 | 292 | assert(NULL == list_rpop(list)); 293 | assert(0 == list->len); 294 | 295 | list_destroy(list); 296 | } 297 | 298 | static void 299 | test_list_lpop() { 300 | // Setup 301 | list_t *list = list_new(); 302 | list_node_t *a = list_rpush(list, list_node_new("a")); 303 | list_node_t *b = list_rpush(list, list_node_new("b")); 304 | list_node_t *c = list_rpush(list, list_node_new("c")); 305 | 306 | // Assertions 307 | assert(3 == list->len); 308 | 309 | assert(a == list_lpop(list)); 310 | assert(2 == list->len); 311 | assert(b == list->head); 312 | assert(NULL == list->head->prev && "new head node prev is not NULL"); 313 | assert(NULL == a->prev && "detached node prev is not NULL"); 314 | assert(NULL == a->next && "detached node next is not NULL"); 315 | 316 | free(a); 317 | 318 | assert(b == list_lpop(list)); 319 | assert(1 == list->len); 320 | 321 | free(b); 322 | 323 | assert(c == list_lpop(list)); 324 | assert(0 == list->len); 325 | assert(NULL == list->head); 326 | assert(NULL == list->tail); 327 | 328 | free(c); 329 | 330 | assert(NULL == list_lpop(list)); 331 | assert(0 == list->len); 332 | 333 | list_destroy(list); 334 | } 335 | 336 | static void 337 | test_list_iterator_t() { 338 | // Setup 339 | list_t *list = list_new(); 340 | list_node_t *tj = list_node_new("tj"); 341 | list_node_t *taylor = list_node_new("taylor"); 342 | list_node_t *simon = list_node_new("simon"); 343 | 344 | // tj taylor simon 345 | list_rpush(list, tj); 346 | list_rpush(list, taylor); 347 | list_rpush(list, simon); 348 | 349 | // Assertions 350 | 351 | // From head 352 | list_iterator_t *it = list_iterator_new(list, LIST_HEAD); 353 | list_node_t *a = list_iterator_next(it); 354 | list_node_t *b = list_iterator_next(it); 355 | list_node_t *c = list_iterator_next(it); 356 | list_node_t *d = list_iterator_next(it); 357 | 358 | assert(a == tj); 359 | assert(b == taylor); 360 | assert(c == simon); 361 | assert(NULL == d); 362 | 363 | list_iterator_destroy(it); 364 | 365 | // From tail 366 | it = list_iterator_new(list, LIST_TAIL); 367 | list_node_t *a2 = list_iterator_next(it); 368 | list_node_t *b2 = list_iterator_next(it); 369 | list_node_t *c2 = list_iterator_next(it); 370 | list_node_t *d2 = list_iterator_next(it); 371 | 372 | assert(a2 == simon); 373 | assert(b2 == taylor); 374 | assert(c2 == tj); 375 | assert(NULL == d2); 376 | list_iterator_destroy(it); 377 | 378 | list_destroy(list); 379 | } 380 | 381 | int 382 | main(void){ 383 | printf("\nlist_t: %ld\n", sizeof(list_t)); 384 | printf("list_node_t: %ld\n", sizeof(list_node_t)); 385 | printf("list_iterator_t: %ld\n\n", sizeof(list_iterator_t)); 386 | test(list_node_new); 387 | test(list_rpush); 388 | test(list_lpush); 389 | test(list_find); 390 | test(list_at); 391 | test(list_remove); 392 | test(list_rpop); 393 | test(list_lpop); 394 | test(list_destroy); 395 | test(list_empty_list_destroy) 396 | test(list_destroy_complexver) 397 | test(list_iterator_t); 398 | puts("... \x1b[32m100%\x1b[0m\n"); 399 | return 0; 400 | } 401 | --------------------------------------------------------------------------------