diff --git a/Makefile.include b/Makefile.include index f5b0435..ceca2f2 100644 --- a/Makefile.include +++ b/Makefile.include @@ -19,6 +19,7 @@ ifdef NO_MEMRCHR obj-compat = src/compat/memrchr.o endif obj-xalloc = src/xalloc.o src/die.o +obj-str-list = src/str-list.o $(obj-xalloc) obj-strbuf = src/strbuf.o $(obj-xalloc) obj-path = src/path.o $(obj-strbuf) obj-rbtree = src/rbtree.o $(obj-xalloc) diff --git a/src/str-list.c b/src/str-list.c new file mode 100644 index 0000000..4616724 --- /dev/null +++ b/src/str-list.c @@ -0,0 +1,171 @@ +/* str-list.c + * + * (C) Copyright 2010 Henrik Hautakoski + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ + +#include + +#include "xalloc.h" +#include "str-list.h" + +static void resize(struct str_list *list) { + + if (!list->nr) { + xfree(list->items); + str_list_init(list); + return; + } + list->items = xrealloc(list->items, sizeof(list->items) * list->nr); +} + +static int get_index(struct str_list *list, const char *str, int *match) { + + int min = -1, max = list->nr; + + while(min + 1 < max) { + int mid = min + (max - min)/2; + int cmp = strcmp(list->items[mid], str); + if (cmp < 0) { + min = mid; + } else if (cmp > 0) { + max = mid; + } else { + *match = 1; + return mid; + } + } + + *match = 0; + return max; +} + +void str_list_init(struct str_list *list) { + + list->items = NULL; + list->nr = 0; +} + +struct str_list* str_list_create(void) { + + struct str_list *list = xmalloc(sizeof(struct str_list)); + list->items = NULL; + list->nr = 0; + return list; +} + +int str_list_destroy(struct str_list *list) { + + if (str_list_isempty(list)) { + xfree(list); + return 1; + } + return 0; +} + +void str_list_clear(struct str_list *list) { + + str_list_clear_fn(list, xfree); +} + +void str_list_clear_fn(struct str_list *list, void (*fn)(void *)) { + + if (list->items) { + if (fn) { + int i; + for(i=0; i < list->nr; i++) + fn(list->items[i]); + } + xfree(list->items); + } + list->items = NULL; + list->nr = 0; +} + +int str_list_insert(struct str_list *list, const char *str) { + + int match, index = get_index(list, str, &match); + + if (match) + return -1; + + list->items = xrealloc(list->items, sizeof(list->items) * (list->nr + 1)); + if (index < list->nr) { + memmove(list->items + index + 1, list->items + index, + sizeof(list->items) * (list->nr - index)); + } + + list->items[index] = (char *) str; + list->nr++; + + return index; +} + +char* str_list_remove(struct str_list *list, const char *str) { + + char *item = NULL; + + if (list && str) { + int match, index = get_index(list, str, &match); + if (match && index < list->nr) { + item = list->items[index]; + if (index < --list->nr) { + memmove(list->items + index, list->items + index + 1, + sizeof(list->items) * (list->nr - index)); + } + resize(list); + } + } + return item; +} + +char* str_list_reduce(struct str_list *list) { + + char *item = NULL; + + if (list && list->nr) { + item = list->items[--list->nr]; + resize(list); + } + return item; +} + +int str_list_indexof(struct str_list *list, const char *str) { + + int match, index = get_index(list, str, &match); + return match ? index : -1; +} + +char* str_list_lookup(struct str_list *list, const char *str) { + + int match, index = get_index(list, str, &match); + + if (!match) + return NULL; + return list->items[index]; +} + +int str_list_has(struct str_list *list, const char *str) { + + int rc; + get_index(list, str, &rc); + return rc; +} + +char** str_list_export(struct str_list *list) { + + int i; + char **out = xmalloc(sizeof(char*) * (list->nr + 1)); + + for(i=0; i < list->nr; i++) + out[i] = list->items[i]; + out[list->nr] = NULL; + + return out; +} + + diff --git a/src/str-list.h b/src/str-list.h new file mode 100644 index 0000000..d2f2b05 --- /dev/null +++ b/src/str-list.h @@ -0,0 +1,50 @@ +/* list.h + * + * (C) Copyright 2010 Henrik Hautakoski + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ + +#ifndef __STR_LIST_H +#define __STR_LIST_H + +#include + +struct str_list { + char **items; + size_t nr; +}; + +void str_list_init(struct str_list *list); + +struct str_list* str_list_create(void); + +int str_list_destroy(struct str_list *list); + +void str_list_clear(struct str_list *list); + +void str_list_clear_fn(struct str_list *list, void (*fn)(void *)); + +int str_list_insert(struct str_list *list, const char *str); + +char* str_list_remove(struct str_list *list, const char *str); + +char* str_list_reduce(struct str_list *list); + +int str_list_indexof(struct str_list *list, const char *str); + +char* str_list_lookup(struct str_list *list, const char *str); + +int str_list_has(struct str_list *list, const char *str); + +#define str_list_foreach(i, l) \ + for(i = (l)->items; i < (l)->items + (l)->nr; ++i) + +#define str_list_size(l) ((l) ? (l)->nr : 0) + +#define str_list_isempty(l) (!(l) || (l)->nr == 0) + +#endif /* __STR_LIST_H */ diff --git a/test/Makefile b/test/Makefile index 6441fdd..e5e736d 100644 --- a/test/Makefile +++ b/test/Makefile @@ -20,6 +20,7 @@ path : unit.c $(ROOT)src/path.o $(addprefix $(ROOT),$(obj-strbuf)) fscrawl : $(ROOT)src/fscrawl.o $(addprefix $(ROOT),$(obj-fscrawl)) notify : $(addprefix $(ROOT),$(obj-notify)) log : $(addprefix $(ROOT),$(obj-log)) +str-list : unit.c $(addprefix $(ROOT),$(obj-str-list)) clean : $(RM) $(patsubst t_%.c,test_%,$(wildcard t_*.c)) diff --git a/test/t_str-list.c b/test/t_str-list.c new file mode 100644 index 0000000..4cff082 --- /dev/null +++ b/test/t_str-list.c @@ -0,0 +1,155 @@ + +#include "../src/str-list.h" +#include "unit.h" +#include +#include +#include + +void test_insert() { + + int i; + char *ref[4] = { "a", "b", "c", "d" }; + struct str_list *l = str_list_create(); + + str_list_insert(l, "a"); + str_list_insert(l, "c"); + str_list_insert(l, "b"); + str_list_insert(l, "d"); + + for(i=0; i < 4; i++) + assert(strcmp(l->items[i], ref[i]) == 0); + + str_list_clear_fn(l, NULL); + str_list_destroy(l); +} + +void test_remove() { + + char *ref[2] = { "a", "c" }; + struct str_list *l = str_list_create(); + + str_list_insert(l, "a"); + str_list_insert(l, "c"); + str_list_insert(l, "b"); + str_list_insert(l, "d"); + + str_list_remove(l, "d"); + str_list_remove(l, "b"); + + assert(str_list_size(l) == 2); + assert(strcmp(l->items[0], ref[0]) == 0); + assert(strcmp(l->items[1], ref[1]) == 0); + + str_list_remove(l, "c"); + str_list_remove(l, "a"); + + assert(str_list_size(l) == 0); + str_list_destroy(l); +} + +void test_isempty() { + + struct str_list *l = NULL; + + assert(str_list_isempty(l)); + l = str_list_create(); + assert(str_list_isempty(l)); + str_list_insert(l, "string"); + assert(str_list_isempty(l) == 0); + + str_list_clear_fn(l, NULL); + str_list_destroy(l); +} + +void test_size() { + + struct str_list *l = NULL; + + assert(str_list_size(l) == 0); + l = str_list_create(); + assert(str_list_size(l) == 0); + str_list_insert(l, "a"); + str_list_insert(l, "b"); + assert(str_list_size(l) == 2); + + str_list_clear_fn(l, NULL); + str_list_destroy(l); +} + +void test_indexof() { + + struct str_list *l = str_list_create(); + + str_list_insert(l, "a"); + str_list_insert(l, "b"); + str_list_insert(l, "c"); + + assert(str_list_indexof(l, "a") == 0); + assert(str_list_indexof(l, "b") == 1); + assert(str_list_indexof(l, "c") == 2); + + str_list_clear_fn(l, NULL); + str_list_destroy(l); +} + +void test_foreach() { + + int i = 0; + char **item, *ref[4] = { "a", "b", "c", "d" }; + struct str_list *l = str_list_create(); + + str_list_insert(l, "a"); + str_list_insert(l, "c"); + str_list_insert(l, "b"); + str_list_insert(l, "d"); + + str_list_foreach(item, l) { + assert_string(ref[i], *item); + i++; + } + + str_list_clear_fn(l, NULL); + str_list_destroy(l); +} + +void test_has() { + + struct str_list *l = str_list_create(); + + str_list_insert(l, "something"); + str_list_insert(l, "somethingelse"); + assert(str_list_has(l, l->items[1])); + assert(str_list_has(l, "don't exists") == 0); + + str_list_clear_fn(l, NULL); + str_list_destroy(l); +} + +void test_lookup() { + + struct str_list *l = str_list_create(); + + str_list_insert(l, "something"); + str_list_insert(l, "find me"); + str_list_insert(l, "random"); + + assert(strcmp(str_list_lookup(l, "find me"), "find me") == 0); + assert(str_list_lookup(l, "don't exists") == NULL); + + str_list_clear_fn(l, NULL); + str_list_destroy(l); +} + +int main() { + + test_insert(); + test_remove(); + test_isempty(); + test_size(); + test_indexof(); + test_foreach(); + test_has(); + test_lookup(); + + return 0; +}