submodule
This commit is contained in:
parent
6c6a5be4ed
commit
79a8c00636
13 changed files with 1352 additions and 1 deletions
1
src/list
1
src/list
|
@ -1 +0,0 @@
|
|||
Subproject commit 23faa20d03b67d752e1f882f0832c941a942c75b
|
6
src/list/.gitignore
vendored
Normal file
6
src/list/.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
.DS_Store
|
||||
bin
|
||||
.pomo
|
||||
*.o
|
||||
build
|
||||
deps
|
74
src/list/History.md
Normal file
74
src/list/History.md
Normal file
|
@ -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
|
22
src/list/LICENSE
Normal file
22
src/list/LICENSE
Normal file
|
@ -0,0 +1,22 @@
|
|||
(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.
|
72
src/list/Makefile
Normal file
72
src/list/Makefile
Normal file
|
@ -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
|
185
src/list/Readme.md
Normal file
185
src/list/Readme.md
Normal file
|
@ -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.
|
146
src/list/benchmark.c.bak
Normal file
146
src/list/benchmark.c.bak
Normal file
|
@ -0,0 +1,146 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#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;
|
||||
}
|
17
src/list/clib.json
Normal file
17
src/list/clib.json
Normal file
|
@ -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": "*"
|
||||
}
|
||||
}
|
215
src/list/src/list.c
Normal file
215
src/list/src/list.c
Normal file
|
@ -0,0 +1,215 @@
|
|||
|
||||
//
|
||||
// list.c
|
||||
//
|
||||
// Copyright (c) 2010 TJ Holowaychuk <tj@vision-media.ca>
|
||||
//
|
||||
|
||||
#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;
|
||||
}
|
130
src/list/src/list.h
Normal file
130
src/list/src/list.h
Normal file
|
@ -0,0 +1,130 @@
|
|||
|
||||
//
|
||||
// list.h
|
||||
//
|
||||
// Copyright (c) 2010 TJ Holowaychuk <tj@vision-media.ca>
|
||||
//
|
||||
|
||||
#ifndef __CLIBS_LIST_H__
|
||||
#define __CLIBS_LIST_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
// 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__ */
|
62
src/list/src/list_iterator.c
Normal file
62
src/list/src/list_iterator.c
Normal file
|
@ -0,0 +1,62 @@
|
|||
|
||||
//
|
||||
// iterator.c
|
||||
//
|
||||
// Copyright (c) 2010 TJ Holowaychuk <tj@vision-media.ca>
|
||||
//
|
||||
|
||||
#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;
|
||||
}
|
23
src/list/src/list_node.c
Normal file
23
src/list/src/list_node.c
Normal file
|
@ -0,0 +1,23 @@
|
|||
|
||||
//
|
||||
// node.c
|
||||
//
|
||||
// Copyright (c) 2010 TJ Holowaychuk <tj@vision-media.ca>
|
||||
//
|
||||
|
||||
#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;
|
||||
}
|
400
src/list/test.c.bak
Normal file
400
src/list/test.c.bak
Normal file
|
@ -0,0 +1,400 @@
|
|||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#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;
|
||||
}
|
Loading…
Reference in a new issue