diff --git a/src/list b/src/list deleted file mode 160000 index 23faa20..0000000 --- a/src/list +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 23faa20d03b67d752e1f882f0832c941a942c75b diff --git a/src/list/.gitignore b/src/list/.gitignore new file mode 100644 index 0000000..bf1a22c --- /dev/null +++ b/src/list/.gitignore @@ -0,0 +1,6 @@ +.DS_Store +bin +.pomo +*.o +build +deps diff --git a/src/list/History.md b/src/list/History.md new file mode 100644 index 0000000..7e99b99 --- /dev/null +++ b/src/list/History.md @@ -0,0 +1,74 @@ + +0.4.1 / 2023-05-05 +================== + + * Minor fixes for debian packaging (#43) + +0.1.0 / 2020-05-15 +================== + + * Update list.h to allow configuration header set up LIST macros (#27) + * fix typo in README (#19) + +0.0.8 / 2015-01-30 +================== + + * fix `list_at` unsigned compare error (bolasblack, #13) + * travis: Fail the build if any memory is leaked + * travis: Update before installing deps + * test: Fix memory leaks + +0.0.7 / 2014-06-02 +================== + + * ci: Remove benchmarks + * add travis + * test: Fix 'unused parameter' warning + * readme: Update benchmarks + * benchmark: Fix 'unused parameter' warning + * benchmark: Use clibs/bench for CPU time + * readme: Correct types + * readme: Add syntax highlighting + * iterator: set to `NULL` after freeing + +0.0.6 / 2014-05-06 +================== + + * list.h: Fix header guards + * Add “list” prefix to sources + +0.0.5 / 2013-10-20 +================== + + * add package.json + * fix free in link_remove(). Closes #3 + +0.0.4 / 2011-04-15 +================== + + * pop -> rpop + * shift -> lpop + * push -> rpush + * unshift -> lpush + +0.0.3 / 2010-11-27 +================== + + * Added `make install` + * Added `list_shift()` + * Added `list_pop()` + * Lowercased function names and typedefs + * Wrap with extern "C" when \_\_cplusplus + * Installing `list.h` + +0.0.2 / 2010-08-21 +================== + + * Added `ListNode *List_at(List *self, int index)` + * Added `void List_remove(List *self, ListNode *node)` + * Added `make liblist.a` + +0.0.1 / 2010-08-13 +================== + + * Initial release diff --git a/src/list/LICENSE b/src/list/LICENSE new file mode 100644 index 0000000..8dc55f9 --- /dev/null +++ b/src/list/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2009-2010 TJ Holowaychuk + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/list/Makefile b/src/list/Makefile new file mode 100644 index 0000000..f33573e --- /dev/null +++ b/src/list/Makefile @@ -0,0 +1,72 @@ + +AR ?= ar +CC ?= gcc +STRIP ?= strip +PREFIX ?= /usr/local +LIBDIR ?= $(PREFIX)/lib +INCLUDEDIR ?= $(PREFIX)/include +INCLUDESUBDIR ?= /clibs +DESTDIR ?= + +CFLAGS = -O3 -std=c99 -Wall -Wextra -Ideps +LDFLAGS ?= -Wl,-z,now + +SRCS = src/list.c \ + src/list_node.c \ + src/list_iterator.c + +OBJS = $(SRCS:.c=.o) + +MAJOR_VERSION = 0 +MINOR_VERSION = 4 +PATCH_VERSION = 1 + +all: build/libclibs_list.a build/libclibs_list.so.$(MAJOR_VERSION).$(MINOR_VERSION).$(PATCH_VERSION) + +install: all + test -d $(DESTDIR)$(LIBDIR) || -mkdir $(DESTDIR)$(LIBDIR) + cp -f build/libclibs_list.a $(DESTDIR)$(LIBDIR)/libclibs_list.a + cp -f build/libclibs_list.so.$(MAJOR_VERSION).$(MINOR_VERSION).$(PATCH_VERSION) $(DESTDIR)$(LIBDIR)/libclibs_list.so.$(MAJOR_VERSION).$(MINOR_VERSION).$(PATCH_VERSION) + ln -sf libclibs_list.so.$(MAJOR_VERSION).$(MINOR_VERSION).$(PATCH_VERSION) $(DESTDIR)$(LIBDIR)/libclibs_list.so.$(MAJOR_VERSION) + ln -sf libclibs_list.so.$(MAJOR_VERSION) $(DESTDIR)$(LIBDIR)/libclibs_list.so + test -d $(DESTDIR)$(INCLUDEDIR)$(INCLUDESUBDIR) || -mkdir $(DESTDIR)$(INCLUDEDIR)$(INCLUDESUBDIR)/ + cp -f src/list.h $(DESTDIR)$(INCLUDEDIR)$(INCLUDESUBDIR)/list.h + +uninstall: + rm -f $(DESTDIR)$(LIBDIR)/libclibs_list.a + rm -f $(DESTDIR)$(LIBDIR)/libclibs_list.so + rm -f $(DESTDIR)$(LIBDIR)/libclibs_list.so.$(MAJOR_VERSION) + rm -f $(DESTDIR)$(LIBDIR)/libclibs_list.so.$(MAJOR_VERSION).$(MINOR_VERSION) + rm -f $(DESTDIR)$(LIBDIR)/libclibs_list.so.$(MAJOR_VERSION).$(MINOR_VERSION).$(PATCH_VERSION) + rm -f $(DESTDIR)$(INCLUDEDIR)$(INCLUDESUBDIR)/list.h + +build/libclibs_list.a: $(OBJS) + -mkdir build + $(AR) rcs $@ $^ + +build/libclibs_list.so.$(MAJOR_VERSION).$(MINOR_VERSION).$(PATCH_VERSION): $(OBJS) + -mkdir build + $(CC) $(LDFLAGS) -shared -lc -Wl,-soname,`basename $@ | sed s/\.${MINOR_VERSION}.${PATCH_VERSION}//` src/*.o -o $@ + $(STRIP) --strip-unneeded --remove-section=.comment --remove-section=.note $@ + +bin/test: test.o $(OBJS) + -mkdir bin + $(CC) $^ -o $@ + +bin/benchmark: benchmark.o $(OBJS) + -mkdir bin + $(CC) $^ -o $@ + +%.o: %.c + $(CC) $< $(CFLAGS) -c -o $@ + +clean: + rm -fr bin build *.o src/*.o + +test: bin/test + @./$< + +benchmark: bin/benchmark + @./$< + +.PHONY: test benchmark clean install uninstall diff --git a/src/list/Readme.md b/src/list/Readme.md new file mode 100644 index 0000000..4eeab16 --- /dev/null +++ b/src/list/Readme.md @@ -0,0 +1,185 @@ + +# list + + C doubly linked list implementation. + +## API + + Below is the public api currently provided by "list". + +## list_t *list_new(); + + Allocate and initialize a `list`. + + list_t *mylist = list_new(); + +## list_node_t \*list_node_new(void *val) + + Allocate and initialize a `list_node_t` with the given _val_. + +```c +list_node_t *node = list_node_new("my value"); +node->val; // "my value" +``` + +## list_node_t \* list_rpush(list_t \*self, list_node_t *node) + + Append _node_ to _self_, returning _node_. + +```c +list_rpush(list, list_node_new("value")); +list->tail->val; // "value" +``` + +## list_node_t \* list_rpop(list_t \*self) + + Return / detach node from the end of the list, or __NULL__. + +```c +list_node_t *last = list_rpop(names); +``` + +## list_node_t \*list_lpush(list_t \*self, list_node_t *node) + + Prepend _node_ to _self_, returning _node_. + +```c +list_lpush(list, list_node_new("value")); +list->head->val; // "value" +``` + +## list_node_t \*list_find(list_t \*self, void *val) + + Return the `list_node_t` containing _val_ or __NULL__. + +```c +list_node_t *node = list_find(list, "some value"); +``` + +## list_node_t \*list_at(list_t *self, int index) + + Return the `list_node_t` at the given _index_, where _index_ + may also be a negative integer indicating an index from the + list _tail_. + +```c +list_at(list, 0); // first +list_at(list, 1); // second +list_at(list, -1); // last +list_at(list, -3); // third last +``` + +## void list_remove(list_t \*self, list_node_t *node) + + Remove _node_ from the list, freeing it and it's value. + +```c +list_remove(list, node); +``` + +## void list_destroy(list_t *self) + + Free the list and all nodes. + +```c +list_destroy(list); +``` + +## list_iterator_t \*list_iterator_new(list_t *list, list_direction_t direction) + + Allocate and initialize a `list_iterator_t` with the given _direction_, + where _direction_ may be __LIST_HEAD__ or __LIST_TAIL__. + +```c +list_node_t *node; +list_iterator_t *it = list_iterator_new(list, LIST_HEAD); +while ((node = list_iterator_next(it))) { + puts(node->val); +} +``` + +## list_node_t \*list_iterator_next(list_iterator_t *self) + + Return the next `list_node_t` or __NULL__. + +## void list_iterator_destroy(list_iterator_t *self); + + Free the iterator only. + +```c +list_iterator_destroy(it); +``` + +## Examples + +list iteration: + +```c +list_t *langs = list_new(); + +list_node_t *c = list_rpush(langs, list_node_new("c")); +list_node_t *js = list_rpush(langs, list_node_new("js")); +list_node_t *ruby = list_rpush(langs, list_node_new("ruby")); + +list_node_t *node; +list_iterator_t *it = list_iterator_new(langs, LIST_HEAD); +while ((node = list_iterator_next(it))) { + puts(node->val); +} + +list_iterator_destroy(it); +list_destroy(langs); +``` + +stdout: + + c + js + ruby + +## Benchmarks + + $ make benchmark + + 10,000,000 nodes + + lpush: 0.5000s + rpush: 0.5000s + lpop: 0.0312s + rpop: 0.0312s + find (last node): 0.0312s + iterate: 0.0625s + at(100,000): 0.0000s + at(1,000,000): 0.0000s + at(-100,000): 0.0000s + + + +## Testing + + $ make test + +## License + +(The MIT License) + +Copyright (c) 2009-2010 TJ Holowaychuk <tj@vision-media.ca> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/list/benchmark.c.bak b/src/list/benchmark.c.bak new file mode 100644 index 0000000..b71f90f --- /dev/null +++ b/src/list/benchmark.c.bak @@ -0,0 +1,146 @@ + +#include +#include "bench/bench.h" +#include "src/list.h" + +static void +bm(char *label, void (*fn)()) { + printf(" %18s", label); + fflush(stdout); + fn(); +} + +static int nnodes = 10000000; +static float startTime; + + +static void +start() { + startTime = cpu(); +} + +static void +stop() { + float duration = cpu() - startTime; + printf(": \x1b[32m%.4f\x1b[0ms\n", duration); +} + +static void +bm_rpush() { + start(); + int n = nnodes; + list_t *list = list_new(); + while (n--) { + list_rpush(list, list_node_new("foo")); + } + stop(); +} + +static void +bm_lpush() { + start(); + int n = nnodes; + list_t *list = list_new(); + while (n--) { + list_lpush(list, list_node_new("foo")); + } + stop(); +} + +static void +bm_find() { + int n = nnodes; + list_t *list = list_new(); + while (n--) { + list_lpush(list, list_node_new("foo")); + } + list_rpush(list, list_node_new("bar")); + start(); + list_find(list, "bar"); + stop(); +} + +static void +bm_iterate() { + int n = nnodes; + list_t *list = list_new(); + while (n--) { + list_lpush(list, list_node_new("foo")); + } + list_iterator_t *it = list_iterator_new(list, LIST_HEAD); + list_node_t *node; + start(); + while ((node = list_iterator_next(it))) + ; + stop(); +} + +static void +bm_rpop() { + int n = nnodes; + list_t *list = list_new(); + while (n--) { + list_lpush(list, list_node_new("foo")); + } + list_node_t *node; + start(); + while ((node = list_rpop(list))) + ; + stop(); +} + +static void +bm_lpop() { + int n = nnodes; + list_t *list = list_new(); + while (n--) { + list_lpush(list, list_node_new("foo")); + } + list_node_t *node; + start(); + while ((node = list_lpop(list))) + ; + stop(); +} + +static list_t *list; + +static void +bm_at() { + start(); + list_at(list, 100000); + stop(); +} + +static void +bm_at2() { + start(); + list_at(list, 1000000); + stop(); +} + +static void +bm_at3() { + start(); + list_at(list, -100000); + stop(); +} + +int +main(void){ + int n = nnodes; + list = list_new(); + while (n--) list_lpush(list, list_node_new("foo")); + puts("\n 10,000,000 nodes\n"); + bm("lpush", bm_lpush); + bm("rpush", bm_rpush); + bm("lpop", bm_lpop); + bm("rpop", bm_rpop); + bm("find (last node)", bm_find); + bm("iterate", bm_iterate); + bm("at(100,000)", bm_at); + bm("at(1,000,000)", bm_at2); + bm("at(-100,000)", bm_at3); + puts(""); + return 0; +} diff --git a/src/list/clib.json b/src/list/clib.json new file mode 100644 index 0000000..65cb4d7 --- /dev/null +++ b/src/list/clib.json @@ -0,0 +1,17 @@ +{ + "name": "list", + "version": "0.4.1", + "repo": "clibs/list", + "description": "Simple linked list", + "keywords": ["list", "structure"], + "license": "MIT", + "src": [ + "src/list_iterator.c", + "src/list.c", + "src/list_node.c", + "src/list.h" + ], + "development": { + "bench": "*" + } +} diff --git a/src/list/src/list.c b/src/list/src/list.c new file mode 100644 index 0000000..ac71b8b --- /dev/null +++ b/src/list/src/list.c @@ -0,0 +1,215 @@ + +// +// list.c +// +// Copyright (c) 2010 TJ Holowaychuk +// + +#include "list.h" + +/* + * Allocate a new list_t. NULL on failure. + */ + +list_t * +list_new(void) { + list_t *self; + if (!(self = LIST_MALLOC(sizeof(list_t)))) + return NULL; + self->head = NULL; + self->tail = NULL; + self->free = NULL; + self->match = NULL; + self->len = 0; + return self; +} + +/* + * Free the list. + * @self: Pointer to the list + */ + +void +list_destroy(list_t *self) { + unsigned int len = self->len; + list_node_t *next; + list_node_t *curr = self->head; + + while (len--) { + next = curr->next; + if (self->free) self->free(curr->val); + LIST_FREE(curr); + curr = next; + } + + LIST_FREE(self); +} + +/* + * Append the given node to the list + * and return the node, NULL on failure. + * @self: Pointer to the list for popping node + * @node: the node to push + */ + +list_node_t * +list_rpush(list_t *self, list_node_t *node) { + if (!node) return NULL; + + if (self->len) { + node->prev = self->tail; + node->next = NULL; + self->tail->next = node; + self->tail = node; + } else { + self->head = self->tail = node; + node->prev = node->next = NULL; + } + + ++self->len; + return node; +} + +/* + * Return / detach the last node in the list, or NULL. + * @self: Pointer to the list for popping node + */ + +list_node_t * +list_rpop(list_t *self) { + if (!self->len) return NULL; + + list_node_t *node = self->tail; + + if (--self->len) { + (self->tail = node->prev)->next = NULL; + } else { + self->tail = self->head = NULL; + } + + node->next = node->prev = NULL; + return node; +} + +/* + * Return / detach the first node in the list, or NULL. + * @self: Pointer to the list for popping node + */ + +list_node_t * +list_lpop(list_t *self) { + if (!self->len) return NULL; + + list_node_t *node = self->head; + + if (--self->len) { + (self->head = node->next)->prev = NULL; + } else { + self->head = self->tail = NULL; + } + + node->next = node->prev = NULL; + return node; +} + +/* + * Prepend the given node to the list + * and return the node, NULL on failure. + * @self: Pointer to the list for pushing node + * @node: the node to push + */ + +list_node_t * +list_lpush(list_t *self, list_node_t *node) { + if (!node) return NULL; + + if (self->len) { + node->next = self->head; + node->prev = NULL; + self->head->prev = node; + self->head = node; + } else { + self->head = self->tail = node; + node->prev = node->next = NULL; + } + + ++self->len; + return node; +} + +/* + * Return the node associated to val or NULL. + * @self: Pointer to the list for finding given value + * @val: Value to find + */ + +list_node_t * +list_find(list_t *self, void *val) { + list_iterator_t *it = list_iterator_new(self, LIST_HEAD); + list_node_t *node; + + while ((node = list_iterator_next(it))) { + if (self->match) { + if (self->match(val, node->val)) { + list_iterator_destroy(it); + return node; + } + } else { + if (val == node->val) { + list_iterator_destroy(it); + return node; + } + } + } + + list_iterator_destroy(it); + return NULL; +} + +/* + * Return the node at the given index or NULL. + * @self: Pointer to the list for finding given index + * @index: the index of node in the list + */ + +list_node_t * +list_at(list_t *self, int index) { + list_direction_t direction = LIST_HEAD; + + if (index < 0) { + direction = LIST_TAIL; + index = ~index; + } + + if ((unsigned)index < self->len) { + list_iterator_t *it = list_iterator_new(self, direction); + list_node_t *node = list_iterator_next(it); + while (index--) node = list_iterator_next(it); + list_iterator_destroy(it); + return node; + } + + return NULL; +} + +/* + * Remove the given node from the list, freeing it and it's value. + * @self: Pointer to the list to delete a node + * @node: Pointer the node to be deleted + */ + +void +list_remove(list_t *self, list_node_t *node) { + node->prev + ? (node->prev->next = node->next) + : (self->head = node->next); + + node->next + ? (node->next->prev = node->prev) + : (self->tail = node->prev); + + if (self->free) self->free(node->val); + + LIST_FREE(node); + --self->len; +} diff --git a/src/list/src/list.h b/src/list/src/list.h new file mode 100644 index 0000000..c0075dd --- /dev/null +++ b/src/list/src/list.h @@ -0,0 +1,130 @@ + +// +// list.h +// +// Copyright (c) 2010 TJ Holowaychuk +// + +#ifndef __CLIBS_LIST_H__ +#define __CLIBS_LIST_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +// Library version + +#define LIST_VERSION "0.4.1" + +// Memory management macros +#ifdef LIST_CONFIG_H +#define _STR(x) #x +#define STR(x) _STR(x) +#include STR(LIST_CONFIG_H) +#undef _STR +#undef STR +#endif + +#ifndef LIST_MALLOC +#define LIST_MALLOC malloc +#endif + +#ifndef LIST_FREE +#define LIST_FREE free +#endif + +/* + * list_t iterator direction. + */ + +typedef enum { + LIST_HEAD + , LIST_TAIL +} list_direction_t; + +/* + * list_t node struct. + */ + +typedef struct list_node { + struct list_node *prev; + struct list_node *next; + void *val; +} list_node_t; + +/* + * list_t struct. + */ + +typedef struct { + list_node_t *head; + list_node_t *tail; + unsigned int len; + void (*free)(void *val); + int (*match)(void *a, void *b); +} list_t; + +/* + * list_t iterator struct. + */ + +typedef struct { + list_node_t *next; + list_direction_t direction; +} list_iterator_t; + +// Node prototypes. + +list_node_t * +list_node_new(void *val); + +// list_t prototypes. + +list_t * +list_new(void); + +list_node_t * +list_rpush(list_t *self, list_node_t *node); + +list_node_t * +list_lpush(list_t *self, list_node_t *node); + +list_node_t * +list_find(list_t *self, void *val); + +list_node_t * +list_at(list_t *self, int index); + +list_node_t * +list_rpop(list_t *self); + +list_node_t * +list_lpop(list_t *self); + +void +list_remove(list_t *self, list_node_t *node); + +void +list_destroy(list_t *self); + +// list_t iterator prototypes. + +list_iterator_t * +list_iterator_new(list_t *list, list_direction_t direction); + +list_iterator_t * +list_iterator_new_from_node(list_node_t *node, list_direction_t direction); + +list_node_t * +list_iterator_next(list_iterator_t *self); + +void +list_iterator_destroy(list_iterator_t *self); + +#ifdef __cplusplus +} +#endif + +#endif /* __CLIBS_LIST_H__ */ diff --git a/src/list/src/list_iterator.c b/src/list/src/list_iterator.c new file mode 100644 index 0000000..f1363b2 --- /dev/null +++ b/src/list/src/list_iterator.c @@ -0,0 +1,62 @@ + +// +// iterator.c +// +// Copyright (c) 2010 TJ Holowaychuk +// + +#include "list.h" + +/* + * Allocate a new list_iterator_t. NULL on failure. + * Accepts a direction, which may be LIST_HEAD or LIST_TAIL. + */ + +list_iterator_t * +list_iterator_new(list_t *list, list_direction_t direction) { + list_node_t *node = direction == LIST_HEAD + ? list->head + : list->tail; + return list_iterator_new_from_node(node, direction); +} + +/* + * Allocate a new list_iterator_t with the given start + * node. NULL on failure. + */ + +list_iterator_t * +list_iterator_new_from_node(list_node_t *node, list_direction_t direction) { + list_iterator_t *self; + if (!(self = LIST_MALLOC(sizeof(list_iterator_t)))) + return NULL; + self->next = node; + self->direction = direction; + return self; +} + +/* + * Return the next list_node_t or NULL when no more + * nodes remain in the list. + */ + +list_node_t * +list_iterator_next(list_iterator_t *self) { + list_node_t *curr = self->next; + if (curr) { + self->next = self->direction == LIST_HEAD + ? curr->next + : curr->prev; + } + return curr; +} + +/* + * Free the list iterator. + */ + +void +list_iterator_destroy(list_iterator_t *self) { + LIST_FREE(self); + self = NULL; +} diff --git a/src/list/src/list_node.c b/src/list/src/list_node.c new file mode 100644 index 0000000..3457856 --- /dev/null +++ b/src/list/src/list_node.c @@ -0,0 +1,23 @@ + +// +// node.c +// +// Copyright (c) 2010 TJ Holowaychuk +// + +#include "list.h" + +/* + * Allocates a new list_node_t. NULL on failure. + */ + +list_node_t * +list_node_new(void *val) { + list_node_t *self; + if (!(self = LIST_MALLOC(sizeof(list_node_t)))) + return NULL; + self->prev = NULL; + self->next = NULL; + self->val = val; + return self; +} \ No newline at end of file diff --git a/src/list/test.c.bak b/src/list/test.c.bak new file mode 100644 index 0000000..4f242ab --- /dev/null +++ b/src/list/test.c.bak @@ -0,0 +1,400 @@ + +#include +#include +#include +#include +#include "src/list.h" + +// Helpers + +#define test(fn) \ + puts("... " # fn); \ + test_##fn(); //Call the test function + +// For counting destroyed time +static int freeProxyCalls = 0; + +void +freeProxy(void *val) { + ++freeProxyCalls; + free(val); +} + +typedef struct { + char *name; +} User; + +static int +User_equal(void *a1, void *b1) { + User *a = a1; + User *b = b1; + return 0 == strcmp(a->name, b->name); +} + +// Tests + +static void +test_list_node_new() { + char *val = "some value"; + list_node_t *node = list_node_new(val); + assert(node->val == val); + free(node); +} + +static void +test_list_rpush() { + // Setup + list_t *list = list_new(); + list_node_t *a = list_node_new("a"); + list_node_t *b = list_node_new("b"); + list_node_t *c = list_node_new("c"); + + // a b c + list_rpush(list, a); + list_rpush(list, b); + list_rpush(list, c); + + // Assertions + assert(a == list->head); + assert(c == list->tail); + assert(3 == list->len); + assert(b == a->next); + assert(NULL == a->prev); + assert(c == b->next); + assert(a == b->prev); + assert(NULL == c->next); + assert(b == c->prev); + + list_destroy(list); +} + +static void +test_list_lpush() { + // Setup + list_t *list = list_new(); + list_node_t *a = list_node_new("a"); + list_node_t *b = list_node_new("b"); + list_node_t *c = list_node_new("c"); + + // c b a + list_rpush(list, a); + list_lpush(list, b); + list_lpush(list, c); + + // Assertions + assert(c == list->head); + assert(a == list->tail); + assert(3 == list->len); + assert(NULL == a->next); + assert(b == a->prev); + assert(a == b->next); + assert(c == b->prev); + assert(b == c->next); + assert(NULL == c->prev); + + list_destroy(list); +} + +static void +test_list_at() { + // Setup + list_t *list = list_new(); + list_node_t *a = list_node_new("a"); + list_node_t *b = list_node_new("b"); + list_node_t *c = list_node_new("c"); + + // a b c + list_rpush(list, a); + list_rpush(list, b); + list_rpush(list, c); + + // Assertions + assert(a == list_at(list, 0)); + assert(b == list_at(list, 1)); + assert(c == list_at(list, 2)); + assert(NULL == list_at(list, 3)); + + assert(c == list_at(list, -1)); + assert(b == list_at(list, -2)); + assert(a == list_at(list, -3)); + assert(NULL == list_at(list, -4)); + + list_destroy(list); +} + +static void +test_list_destroy() { + // Setup + list_t *a = list_new(); + list_destroy(a); + + // a b c + list_t *b = list_new(); + list_rpush(b, list_node_new("a")); + list_rpush(b, list_node_new("b")); + list_rpush(b, list_node_new("c")); + list_destroy(b); + + // Assertions + list_t *c = list_new(); + c->free = freeProxy; + list_rpush(c, list_node_new(list_node_new("a"))); + list_rpush(c, list_node_new(list_node_new("b"))); + list_rpush(c, list_node_new(list_node_new("c"))); + list_destroy(c); + assert(3 == freeProxyCalls); + freeProxyCalls=0; +} + +static void +test_list_destroy_complexver() { + // Setup + list_t *a = list_new(); + list_destroy(a); + + // a b c + list_t *b = list_new(); + list_rpush(b, list_node_new("a")); + list_rpush(b, list_node_new("b")); + list_rpush(b, list_node_new("c")); + list_destroy(b); + + // Assertions + list_t *c = list_new(); + c->free = freeProxy; + list_rpush(c, list_node_new(list_node_new("a"))); + list_rpush(c, list_node_new(list_node_new("b"))); + list_rpush(c, list_node_new(list_node_new("c"))); + list_destroy(c); + assert(3 == freeProxyCalls); + freeProxyCalls=0; + list_t *d = list_new(); + d->free = freeProxy; + list_rpush(d, list_node_new(list_node_new("a"))); + list_rpush(d, list_node_new(list_node_new("b"))); + list_rpush(d, list_node_new(list_node_new("c"))); + list_destroy(d); + assert(3 == freeProxyCalls); + freeProxyCalls=0; +} +static void +test_list_empty_list_destroy() { + list_t *list = list_new(); + list_destroy(list); + freeProxyCalls=0; +} +static void +test_list_find() { + // Setup + list_t *langs = list_new(); + list_node_t *js = list_rpush(langs, list_node_new("js")); + list_node_t *ruby = list_rpush(langs, list_node_new("ruby")); + + list_t *users = list_new(); + users->match = User_equal; + User userTJ = { "tj" }; + User userSimon = { "simon" }; + User userTaylor = { "taylor" }; + list_node_t *tj = list_rpush(users, list_node_new(&userTJ)); + list_node_t *simon = list_rpush(users, list_node_new(&userSimon)); + + // Assertions + list_node_t *a = list_find(langs, "js"); + list_node_t *b = list_find(langs, "ruby"); + list_node_t *c = list_find(langs, "foo"); + assert(js == a); + assert(ruby == b); + assert(NULL == c); + + list_destroy(langs); + + a = list_find(users, &userTJ); + b = list_find(users, &userSimon); + c = list_find(users, &userTaylor); + assert(tj == a); + assert(simon == b); + assert(NULL == c); + + list_destroy(users); +} + +static void +test_list_remove() { + // Setup + list_t *list = list_new(); + list_node_t *a = list_rpush(list, list_node_new("a")); + list_node_t *b = list_rpush(list, list_node_new("b")); + list_node_t *c = list_rpush(list, list_node_new("c")); + + // Assertions + assert(3 == list->len); + + list_remove(list, b); + assert(2 == list->len); + assert(a == list->head); + assert(c == list->tail); + assert(c == a->next); + assert(NULL == a->prev); + assert(NULL == c->next); + assert(a == c->prev); + + list_remove(list, a); + assert(1 == list->len); + assert(c == list->head); + assert(c == list->tail); + assert(NULL == c->next); + assert(NULL == c->prev); + + list_remove(list, c); + assert(0 == list->len); + assert(NULL == list->head); + assert(NULL == list->tail); + + list_destroy(list); +} + +static void +test_list_rpop() { + // Setup + list_t *list = list_new(); + list_node_t *a = list_rpush(list, list_node_new("a")); + list_node_t *b = list_rpush(list, list_node_new("b")); + list_node_t *c = list_rpush(list, list_node_new("c")); + + // Assertions + assert(3 == list->len); + + assert(c == list_rpop(list)); + assert(2 == list->len); + assert(a == list->head); + assert(b == list->tail); + assert(a == b->prev); + assert(NULL == list->tail->next && "new tail node next is not NULL"); + assert(NULL == c->prev && "detached node prev is not NULL"); + assert(NULL == c->next && "detached node next is not NULL"); + + free(c); + + assert(b == list_rpop(list)); + assert(1 == list->len); + assert(a == list->head); + assert(a == list->tail); + + free(b); + + assert(a == list_rpop(list)); + assert(0 == list->len); + assert(NULL == list->head); + assert(NULL == list->tail); + + free(a); + + assert(NULL == list_rpop(list)); + assert(0 == list->len); + + list_destroy(list); +} + +static void +test_list_lpop() { + // Setup + list_t *list = list_new(); + list_node_t *a = list_rpush(list, list_node_new("a")); + list_node_t *b = list_rpush(list, list_node_new("b")); + list_node_t *c = list_rpush(list, list_node_new("c")); + + // Assertions + assert(3 == list->len); + + assert(a == list_lpop(list)); + assert(2 == list->len); + assert(b == list->head); + assert(NULL == list->head->prev && "new head node prev is not NULL"); + assert(NULL == a->prev && "detached node prev is not NULL"); + assert(NULL == a->next && "detached node next is not NULL"); + + free(a); + + assert(b == list_lpop(list)); + assert(1 == list->len); + + free(b); + + assert(c == list_lpop(list)); + assert(0 == list->len); + assert(NULL == list->head); + assert(NULL == list->tail); + + free(c); + + assert(NULL == list_lpop(list)); + assert(0 == list->len); + + list_destroy(list); +} + +static void +test_list_iterator_t() { + // Setup + list_t *list = list_new(); + list_node_t *tj = list_node_new("tj"); + list_node_t *taylor = list_node_new("taylor"); + list_node_t *simon = list_node_new("simon"); + + // tj taylor simon + list_rpush(list, tj); + list_rpush(list, taylor); + list_rpush(list, simon); + + // Assertions + + // From head + list_iterator_t *it = list_iterator_new(list, LIST_HEAD); + list_node_t *a = list_iterator_next(it); + list_node_t *b = list_iterator_next(it); + list_node_t *c = list_iterator_next(it); + list_node_t *d = list_iterator_next(it); + + assert(a == tj); + assert(b == taylor); + assert(c == simon); + assert(NULL == d); + + list_iterator_destroy(it); + + // From tail + it = list_iterator_new(list, LIST_TAIL); + list_node_t *a2 = list_iterator_next(it); + list_node_t *b2 = list_iterator_next(it); + list_node_t *c2 = list_iterator_next(it); + list_node_t *d2 = list_iterator_next(it); + + assert(a2 == simon); + assert(b2 == taylor); + assert(c2 == tj); + assert(NULL == d2); + list_iterator_destroy(it); + + list_destroy(list); +} + +int +main(void){ + printf("\nlist_t: %ld\n", sizeof(list_t)); + printf("list_node_t: %ld\n", sizeof(list_node_t)); + printf("list_iterator_t: %ld\n\n", sizeof(list_iterator_t)); + test(list_node_new); + test(list_rpush); + test(list_lpush); + test(list_find); + test(list_at); + test(list_remove); + test(list_rpop); + test(list_lpop); + test(list_destroy); + test(list_empty_list_destroy) + test(list_destroy_complexver) + test(list_iterator_t); + puts("... 100%"); + return 0; +}