diff --git a/Makefile.am b/Makefile.am index f1b5ce1..99dfb14 100644 --- a/Makefile.am +++ b/Makefile.am @@ -20,12 +20,14 @@ pkginclude_HEADERS = \ include/igloo/ro.h \ include/igloo/types.h \ include/igloo/typedef.h \ - include/igloo/buffer.h + include/igloo/buffer.h \ + include/igloo/list.h libigloo_la_SOURCES = \ src/libigloo.c \ src/ro.c \ - src/buffer.c + src/buffer.c \ + src/list.c libigloo_la_LIBADD = \ avl/libiceavl.la \ httpp/libicehttpp.la \ diff --git a/include/igloo/list.h b/include/igloo/list.h new file mode 100644 index 0000000..7a1e2ab --- /dev/null +++ b/include/igloo/list.h @@ -0,0 +1,112 @@ +/* Copyright (C) 2018 Philipp "ph3-der-loewe" Schafft + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _LIBIGLOO__LIST_H_ +#define _LIBIGLOO__LIST_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* About thread safety: + * This set of functions is intentinally not thread safe. + */ + +typedef struct igloo_list_iterator_tag igloo_list_iterator_t; +typedef struct igloo_list_iterator_tag igloo_list_iterator_storage_t; + +igloo_RO_FORWARD_TYPE(igloo_list_t); + +/* ---[ PRIVATE ]--- */ +/* + * Those types are defined here as they must be known to the compiler. + * Nobody should ever try to access them directly. + */ +struct igloo_list_iterator_tag { + igloo_list_t *list; + size_t idx; +}; +/* ---[ END PRIVATE ]--- */ + +/* To create lists use: igloo_ro_new(igloo_list_t) + */ + +/* Clear a list (remove all elements). */ +int igloo_list_clear(igloo_list_t *list); +/* Preallocate space for later mass-adding of elements. */ +void igloo_list_preallocate(igloo_list_t *list, size_t request); +/* Limit elements to those of the given type. */ +int igloo_list_set_type__real(igloo_list_t *list, const igloo_ro_type_t *type); +#define igloo_list_set_type(list,type) igloo_list_set_type__real((list),(igloo_ro__type__ ## type)) +/* Add an element at the end of the list. */ +int igloo_list_push(igloo_list_t *list, igloo_ro_t element); +/* Add an element at the begin of a list. */ +int igloo_list_unshift(igloo_list_t *list, igloo_ro_t element); +/* Get and remove the first element from the list. */ +igloo_ro_t igloo_list_shift(igloo_list_t *list); +/* Get and remove the last element from the list. */ +igloo_ro_t igloo_list_pop(igloo_list_t *list); + +/* Merge the content of the list elements into the list list. The list elements is not changed. */ +int igloo_list_merge(igloo_list_t *list, igloo_list_t *elements); + +/* Creates a new iterator that can be used to walk the list. + * The memory pointed to by storage of size storage_length is used to store the iterator's internal + * values. It must be allocated (e.g. on stack) untill igloo_list_iterator_end() is called. + * igloo_list_iterator_storage_t is provided to be used as storage object. + * Example: + * igloo_list_iterator_storage_t storage; + * igloo_list_iterator_t *iterator = igloo_list_iterator_start(list, &storage, sizeof(storage)); + */ +igloo_list_iterator_t * igloo_list_iterator_start(igloo_list_t *list, void *storage, size_t storage_length); +/* Get next element from iterator. */ +igloo_ro_t igloo_list_iterator_next(igloo_list_iterator_t *iterator); +/* Destory iterator. */ +void igloo_list_iterator_end(igloo_list_iterator_t *iterator); +/* Rewind iterator. The next call to igloo_list_iterator_next() will return the first element again. */ +int igloo_list_iterator_rewind(igloo_list_iterator_t *iterator); + +/* Go thru all elements in the list. + * Parameters: + * list: the list to use. + * type: the type of elements in the list + * var: the variable to store the current element in. + * code: the code block to be used in the loop. + * + * Note: This can only be used on lists that contain only one type of objects. + * See also: igloo_list_set_type() + */ +#define igloo_list_foreach(list,type,var,code) \ +do { \ + igloo_list_iterator_storage_t __igloo_list_iterator_storage; \ + igloo_list_iterator_t *__igloo_list_iterator = igloo_list_iterator_start((list), &__igloo_list_iterator_storage, sizeof(__igloo_list_iterator_storage)); \ + type * var; \ + for (; !igloo_RO_IS_NULL((var) = igloo_RO_TO_TYPE(igloo_list_iterator_next(__igloo_list_iterator),type));) { \ + code; \ + igloo_ro_unref((var)); \ + } \ + igloo_list_iterator_end(__igloo_list_iterator); \ +} while (0); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/igloo/ro.h b/include/igloo/ro.h index ae429bf..fbed872 100644 --- a/include/igloo/ro.h +++ b/include/igloo/ro.h @@ -102,6 +102,7 @@ int igloo_ro_new__return_zero(igloo_ro_t self, const igloo_ro_type_t *type, va_l #define igloo_RO_GET_TYPE(x) (igloo_RO__GETBASE((x)) == NULL ? NULL : igloo_RO__GETBASE((x))->type) #define igloo_RO_GET_TYPENAME(x) (igloo_RO_GET_TYPE((x)) == NULL ? NULL : igloo_RO_GET_TYPE((x))->type_name) #define igloo_RO_IS_VALID(x,type) (!igloo_RO_IS_NULL((x)) && igloo_RO_GET_TYPE((x)) == (igloo_ro__type__ ## type)) +#define igloo_RO_HAS_TYPE(x,type) (!igloo_RO_IS_NULL((x)) && igloo_RO_GET_TYPE((x)) == (type)) /* Create a new refobject * The type argument gives the type for the new object, diff --git a/include/igloo/types.h b/include/igloo/types.h index ace9b20..d9c4494 100644 --- a/include/igloo/types.h +++ b/include/igloo/types.h @@ -33,6 +33,7 @@ extern "C" { #include "typedef.h" typedef struct igloo_buffer_tag igloo_buffer_t; +typedef struct igloo_list_tag igloo_list_t; /* * This header includes forward declarations for several basic types. @@ -47,6 +48,7 @@ typedef union __attribute__ ((__transparent_union__)) { /* Those are libigloo's own types */ igloo_RO_TYPE(igloo_ro_base_t) igloo_RO_TYPE(igloo_buffer_t) + igloo_RO_TYPE(igloo_list_t) /* Now we add the current compilation unit's private types if any */ #ifdef igloo_RO_PRIVATETYPES diff --git a/src/list.c b/src/list.c new file mode 100644 index 0000000..c76cd8b --- /dev/null +++ b/src/list.c @@ -0,0 +1,319 @@ +/* Copyright (C) 2018 Philipp "ph3-der-loewe" Schafft + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include + +struct igloo_list_tag { + igloo_ro_base_t __base; + size_t offset; + size_t fill; + size_t length; + igloo_ro_t *elements; + const igloo_ro_type_t *type; +}; + +static void __free(igloo_ro_t self); + +igloo_RO_PUBLIC_TYPE(igloo_list_t, + igloo_RO_TYPEDECL_FREE(__free), + igloo_RO_TYPEDECL_NEW_NOOP() + ); + +static void __free(igloo_ro_t self) +{ + igloo_list_t *list = igloo_RO_TO_TYPE(self, igloo_list_t); + igloo_list_clear(list); +} + +int igloo_list_clear(igloo_list_t *list) +{ + size_t i; + + if (!igloo_RO_IS_VALID(list, igloo_list_t)) + return -1; + + for (i = list->offset; i < list->fill; i++) { + igloo_ro_unref(list->elements[i]); + } + + list->offset = 0; + list->fill = 0; + + return 0; +} + +static inline void igloo_list_preallocate__realign(igloo_list_t *list) +{ + size_t new_len; + + if (!list->offset) + return; + + new_len = list->fill - list->offset; + memmove(list->elements, list->elements + list->offset, new_len*sizeof(*list->elements)); + list->offset = 0; + list->fill = new_len; +} + +void igloo_list_preallocate(igloo_list_t *list, size_t request) +{ + size_t new_len; + + if (!igloo_RO_IS_VALID(list, igloo_list_t)) + return; + + new_len = (list->fill - list->offset) + request; + + if (new_len > list->length || (new_len + 32) > list->length) { + igloo_ro_t *n; + + igloo_list_preallocate__realign(list); + + n = realloc(list->elements, new_len*sizeof(*list->elements)); + if (!n) + return; + + list->elements = n; + list->length = new_len; + } + + if (list->offset > 16 || ((list->length - list->fill) < request)) + igloo_list_preallocate__realign(list); +} + +int igloo_list_set_type__real(igloo_list_t *list, const igloo_ro_type_t *type) +{ + size_t i; + + if (!igloo_RO_IS_VALID(list, igloo_list_t)) + return -1; + + for (i = list->offset; i < list->fill; i++) { + if (!igloo_RO_HAS_TYPE(list->elements[i], type)) { + return -1; + } + } + + list->type = type; + return 0; +} + +int igloo_list_push(igloo_list_t *list, igloo_ro_t element) +{ + if (!igloo_RO_IS_VALID(list, igloo_list_t)) + return -1; + + if ((list->type && !igloo_RO_HAS_TYPE(element, list->type)) || igloo_RO_IS_NULL(element)) + return -1; + + igloo_list_preallocate(list, 1); + + if (list->fill == list->length) + return -1; + + if (igloo_ro_ref(element) != 0) + return -1; + + list->elements[list->fill++] = element; + + return 0; +} +int igloo_list_unshift(igloo_list_t *list, igloo_ro_t element) +{ + if (!igloo_RO_IS_VALID(list, igloo_list_t)) + return -1; + + if (list->offset == list->fill) + return igloo_list_push(list, element); + + if ((list->type && !igloo_RO_HAS_TYPE(element, list->type)) || igloo_RO_IS_NULL(element)) + return -1; + + if (!list->offset) { + igloo_list_preallocate(list, 1); + + if (list->fill == list->length) + return -1; + + memmove(list->elements + 1, list->elements, sizeof(*list->elements)*list->fill); + list->offset++; + list->fill++; + } + + if (!list->offset) + return -1; + + if (igloo_ro_ref(element) != 0) + return -1; + + list->elements[--list->offset] = element; + + return 0; +} + +igloo_ro_t igloo_list_shift(igloo_list_t *list) +{ + igloo_ro_t ret; + + if (!igloo_RO_IS_VALID(list, igloo_list_t)) + return igloo_RO_NULL; + + if (list->offset == list->fill) + return igloo_RO_NULL; + + ret = list->elements[list->offset++]; + + igloo_list_preallocate(list, 0); + + return ret; +} + +igloo_ro_t igloo_list_pop(igloo_list_t *list) +{ + igloo_ro_t ret; + + if (!igloo_RO_IS_VALID(list, igloo_list_t)) + return igloo_RO_NULL; + + if (list->offset == list->fill) + return igloo_RO_NULL; + + ret = list->elements[--list->fill]; + + igloo_list_preallocate(list, 0); + + return ret; +} + +static inline int igloo_list_copy_elements(igloo_list_t *list, igloo_list_t *elements) +{ + size_t i; + + for (i = elements->offset; i < elements->fill; i++) { + if (list->fill == list->length) { + igloo_list_preallocate(list, 1); + + if (list->fill == list->length) + return -1; + } + + if (igloo_ro_ref(elements->elements[i]) != 0) + return -1; + + list->elements[list->fill++] = elements->elements[i]; + } + + return 0; +} + +int igloo_list_merge(igloo_list_t *list, igloo_list_t *elements) +{ + size_t old_fill; + + if (!igloo_RO_IS_VALID(list, igloo_list_t) || !igloo_RO_IS_VALID(elements, igloo_list_t)) + return -1; + + if (list->type) { + size_t i; + + for (i = elements->offset; i < elements->fill; i++) { + if (!igloo_RO_HAS_TYPE(elements->elements[i], list->type)) + return -1; + } + } + + igloo_list_preallocate(list, elements->fill - elements->offset); + + old_fill = list->fill; + if (igloo_list_copy_elements(list, elements) != 0) { + size_t i; + + for (i = old_fill; i < list->fill; i++) { + igloo_ro_unref(list->elements[i]); + } + + list->fill = old_fill; + + return -1; + } + + return 0; +} + +igloo_list_iterator_t * igloo_list_iterator_start(igloo_list_t *list, void *storage, size_t storage_length) +{ + igloo_list_iterator_t *iterator; + + if (!igloo_RO_IS_VALID(list, igloo_list_t)) + return NULL; + + if (!storage || storage_length != sizeof(igloo_list_iterator_t)) + return NULL; + + iterator = storage; + memset(iterator, 0, sizeof(*iterator)); + + if (igloo_ro_ref(list) != 0) + return NULL; + + iterator->list = list; + + igloo_list_iterator_rewind(iterator); + + return iterator; +} + +igloo_ro_t igloo_list_iterator_next(igloo_list_iterator_t *iterator) +{ + size_t physical; + + physical = iterator->idx + iterator->list->offset; + + if (physical >= iterator->list->fill) + return igloo_RO_NULL; + + if (igloo_ro_ref(iterator->list->elements[physical]) != 0) + return igloo_RO_NULL; + + iterator->idx++; + + return iterator->list->elements[physical]; +} +void igloo_list_iterator_end(igloo_list_iterator_t *iterator) +{ + if (!iterator) + return; + + igloo_ro_unref(iterator->list); +} +int igloo_list_iterator_rewind(igloo_list_iterator_t *iterator) +{ + if (!iterator) + return -1; + + iterator->idx = 0; + + return 0; +} diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am index deb6469..db53be0 100644 --- a/src/tests/Makefile.am +++ b/src/tests/Makefile.am @@ -39,6 +39,13 @@ ctest_buffer_test_LDADD = libice_ctest.la \ src/buffer.o check_PROGRAMS += ctest_buffer.test +ctest_list_test_SOURCES = %reldir%/ctest_list.c +ctest_list_test_LDADD = libice_ctest.la \ + thread/libicethread.la \ + avl/libiceavl.la \ + src/ro.o \ + src/list.o +check_PROGRAMS += ctest_list.test # Add all programs to TESTS TESTS = $(check_PROGRAMS) diff --git a/src/tests/ctest_list.c b/src/tests/ctest_list.c new file mode 100644 index 0000000..655575d --- /dev/null +++ b/src/tests/ctest_list.c @@ -0,0 +1,419 @@ +/* Icecast + * + * This program is distributed under the GNU General Public License, version 2. + * A copy of this license is included with this source. + * + * Copyright 2018, Philipp "ph3-der-loewe" Schafft , + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "ctest_lib.h" + +#include +#include + +static void test_create_ref_unref(void) +{ + igloo_list_t *a; + + a = igloo_ro_new(igloo_list_t); + ctest_test("list created", !igloo_RO_IS_NULL(a)); + + ctest_test("un-referenced", igloo_ro_unref(a) == 0); +} + +static void test__create_push_unshift(igloo_list_t **list, igloo_ro_base_t **a, igloo_ro_base_t **b) +{ + *list = NULL; + *a = NULL; + *b = NULL; + + *list = igloo_ro_new(igloo_list_t); + + ctest_test("list created", !igloo_RO_IS_NULL(*list)); + + *a = igloo_ro_new(igloo_ro_base_t); + ctest_test("test object a created", !igloo_RO_IS_NULL(*a)); + + *b = igloo_ro_new(igloo_ro_base_t); + ctest_test("test object b created", !igloo_RO_IS_NULL(*b)); + + ctest_test("test object a pushed", igloo_list_push(*list, *a) == 0); + ctest_test("test object b unshifted", igloo_list_unshift(*list, *b) == 0); +} + +static void test_list_push_unshift(void) +{ + igloo_list_t *list; + igloo_ro_base_t *a; + igloo_ro_base_t *b; + + test__create_push_unshift(&list, &a, &b); + + ctest_test("un-referenced a", igloo_ro_unref(a) == 0); + ctest_test("un-referenced b", igloo_ro_unref(b) == 0); + ctest_test("un-referenced list", igloo_ro_unref(list) == 0); +} + +static void test_list_push_unshift_pop(void) +{ + igloo_list_t *list; + igloo_ro_base_t *a; + igloo_ro_base_t *b; + igloo_ro_t ret; + + test__create_push_unshift(&list, &a, &b); + + ret = igloo_list_pop(list); + ctest_test("popped element", !igloo_RO_IS_NULL(ret)); + ctest_test("popped element matches a", igloo_RO_TO_TYPE(ret, igloo_ro_base_t) == a); + ctest_test("un-referenced popped element", igloo_ro_unref(ret) == 0); + + ctest_test("un-referenced a", igloo_ro_unref(a) == 0); + ctest_test("un-referenced b", igloo_ro_unref(b) == 0); + ctest_test("un-referenced list", igloo_ro_unref(list) == 0); +} + +static void test_list_push_unshift_pop_pop(void) +{ + igloo_list_t *list; + igloo_ro_base_t *a; + igloo_ro_base_t *b; + igloo_ro_t ret; + + test__create_push_unshift(&list, &a, &b); + + ret = igloo_list_pop(list); + ctest_test("popped element", !igloo_RO_IS_NULL(ret)); + ctest_test("popped element matches a", igloo_RO_TO_TYPE(ret, igloo_ro_base_t) == a); + ctest_test("un-referenced popped element", igloo_ro_unref(ret) == 0); + + ret = igloo_list_pop(list); + ctest_test("popped element", !igloo_RO_IS_NULL(ret)); + ctest_test("popped element matches b", igloo_RO_TO_TYPE(ret, igloo_ro_base_t) == b); + ctest_test("un-referenced popped element", igloo_ro_unref(ret) == 0); + + ret = igloo_list_pop(list); + ctest_test("popped no element", igloo_RO_IS_NULL(ret)); + igloo_ro_unref(ret); /* just in case we got an element */ + + ctest_test("un-referenced a", igloo_ro_unref(a) == 0); + ctest_test("un-referenced b", igloo_ro_unref(b) == 0); + ctest_test("un-referenced list", igloo_ro_unref(list) == 0); +} + +static void test_list_push_unshift_shift(void) +{ + igloo_list_t *list; + igloo_ro_base_t *a; + igloo_ro_base_t *b; + igloo_ro_t ret; + + test__create_push_unshift(&list, &a, &b); + + ret = igloo_list_shift(list); + ctest_test("shifted element", !igloo_RO_IS_NULL(ret)); + ctest_test("shifted element matches b", igloo_RO_TO_TYPE(ret, igloo_ro_base_t) == b); + ctest_test("un-referenced shifted element", igloo_ro_unref(ret) == 0); + + ctest_test("un-referenced a", igloo_ro_unref(a) == 0); + ctest_test("un-referenced b", igloo_ro_unref(b) == 0); + ctest_test("un-referenced list", igloo_ro_unref(list) == 0); +} + +static void test_list_push_unshift_shift_shift(void) +{ + igloo_list_t *list; + igloo_ro_base_t *a; + igloo_ro_base_t *b; + igloo_ro_t ret; + + test__create_push_unshift(&list, &a, &b); + + ret = igloo_list_shift(list); + ctest_test("shifted element", !igloo_RO_IS_NULL(ret)); + ctest_test("shifted element matches b", igloo_RO_TO_TYPE(ret, igloo_ro_base_t) == b); + ctest_test("un-referenced shifted element", igloo_ro_unref(ret) == 0); + + ret = igloo_list_shift(list); + ctest_test("shifted element", !igloo_RO_IS_NULL(ret)); + ctest_test("shifted element matches a", igloo_RO_TO_TYPE(ret, igloo_ro_base_t) == a); + ctest_test("un-referenced shifted element", igloo_ro_unref(ret) == 0); + + ret = igloo_list_shift(list); + ctest_test("shifted no element", igloo_RO_IS_NULL(ret)); + igloo_ro_unref(ret); /* just in case we got an element */ + + ctest_test("un-referenced a", igloo_ro_unref(a) == 0); + ctest_test("un-referenced b", igloo_ro_unref(b) == 0); + ctest_test("un-referenced list", igloo_ro_unref(list) == 0); +} + + +static void test_list_push_unshift_pop_shift(void) +{ + igloo_list_t *list; + igloo_ro_base_t *a; + igloo_ro_base_t *b; + igloo_ro_t ret; + + test__create_push_unshift(&list, &a, &b); + + ret = igloo_list_pop(list); + ctest_test("popped element", !igloo_RO_IS_NULL(ret)); + ctest_test("popped element matches a", igloo_RO_TO_TYPE(ret, igloo_ro_base_t) == a); + ctest_test("un-referenced popped element", igloo_ro_unref(ret) == 0); + + ret = igloo_list_shift(list); + ctest_test("shifted element", !igloo_RO_IS_NULL(ret)); + ctest_test("shifted element matches b", igloo_RO_TO_TYPE(ret, igloo_ro_base_t) == b); + ctest_test("un-referenced shifted element", igloo_ro_unref(ret) == 0); + + ctest_test("un-referenced a", igloo_ro_unref(a) == 0); + ctest_test("un-referenced b", igloo_ro_unref(b) == 0); + ctest_test("un-referenced list", igloo_ro_unref(list) == 0); +} +static void test_list_push_unshift_shift_pop(void) +{ + igloo_list_t *list; + igloo_ro_base_t *a; + igloo_ro_base_t *b; + igloo_ro_t ret; + + test__create_push_unshift(&list, &a, &b); + + ret = igloo_list_shift(list); + ctest_test("shifted element", !igloo_RO_IS_NULL(ret)); + ctest_test("shifted element matches b", igloo_RO_TO_TYPE(ret, igloo_ro_base_t) == b); + ctest_test("un-referenced shifted element", igloo_ro_unref(ret) == 0); + + ret = igloo_list_pop(list); + ctest_test("popped element", !igloo_RO_IS_NULL(ret)); + ctest_test("popped element matches a", igloo_RO_TO_TYPE(ret, igloo_ro_base_t) == a); + ctest_test("un-referenced popped element", igloo_ro_unref(ret) == 0); + + ctest_test("un-referenced a", igloo_ro_unref(a) == 0); + ctest_test("un-referenced b", igloo_ro_unref(b) == 0); + ctest_test("un-referenced list", igloo_ro_unref(list) == 0); +} + +static void test_list_merge(void) +{ + igloo_list_t *list_a; + igloo_list_t *list_b; + igloo_ro_base_t *a; + igloo_ro_base_t *b; + igloo_ro_t ret; + + list_a = igloo_ro_new(igloo_list_t); + ctest_test("list a created", !igloo_RO_IS_NULL(list_a)); + + list_b = igloo_ro_new(igloo_list_t); + ctest_test("list a created", !igloo_RO_IS_NULL(list_b)); + + a = igloo_ro_new(igloo_ro_base_t); + ctest_test("test object a created", !igloo_RO_IS_NULL(a)); + + b = igloo_ro_new(igloo_ro_base_t); + ctest_test("test object b created", !igloo_RO_IS_NULL(b)); + + ctest_test("test object a pushed to list a", igloo_list_push(list_a, a) == 0); + ctest_test("test object b pushed to list b", igloo_list_push(list_b, b) == 0); + + ctest_test("merged list b into list a", igloo_list_merge(list_a, list_b) == 0); + + ret = igloo_list_shift(list_a); + ctest_test("shifted element", !igloo_RO_IS_NULL(ret)); + ctest_test("shifted element matches a", igloo_RO_TO_TYPE(ret, igloo_ro_base_t) == a); + ctest_test("un-referenced shifted element", igloo_ro_unref(ret) == 0); + + ret = igloo_list_shift(list_a); + ctest_test("shifted element", !igloo_RO_IS_NULL(ret)); + ctest_test("shifted element matches b", igloo_RO_TO_TYPE(ret, igloo_ro_base_t) == b); + ctest_test("un-referenced shifted element", igloo_ro_unref(ret) == 0); + + ret = igloo_list_shift(list_a); + ctest_test("shifted no element", igloo_RO_IS_NULL(ret)); + igloo_ro_unref(ret); /* just in case we got an element */ + + ret = igloo_list_shift(list_b); + ctest_test("shifted element", !igloo_RO_IS_NULL(ret)); + ctest_test("shifted element matches b", igloo_RO_TO_TYPE(ret, igloo_ro_base_t) == b); + ctest_test("un-referenced shifted element", igloo_ro_unref(ret) == 0); + + ret = igloo_list_shift(list_b); + ctest_test("shifted no element", igloo_RO_IS_NULL(ret)); + igloo_ro_unref(ret); /* just in case we got an element */ + + ctest_test("un-referenced a", igloo_ro_unref(a) == 0); + ctest_test("un-referenced b", igloo_ro_unref(b) == 0); + ctest_test("un-referenced list a", igloo_ro_unref(list_a) == 0); + ctest_test("un-referenced list b", igloo_ro_unref(list_b) == 0); +} + +static void test_list_set_type(void) +{ + igloo_list_t *list; + igloo_ro_base_t *a; + igloo_list_t *b; + igloo_ro_t ret; + + list = igloo_ro_new(igloo_list_t); + ctest_test("list created", !igloo_RO_IS_NULL(list)); + + a = igloo_ro_new(igloo_ro_base_t); + ctest_test("test object a created", !igloo_RO_IS_NULL(a)); + + b = igloo_ro_new(igloo_list_t); + ctest_test("test object b created", !igloo_RO_IS_NULL(b)); + + ctest_test("pinned list to type igloo_ro_base_t", igloo_list_set_type(list, igloo_ro_base_t) == 0); + ctest_test("pinned list to type igloo_list_t", igloo_list_set_type(list, igloo_list_t) == 0); + ctest_test("pinned list to type igloo_ro_base_t", igloo_list_set_type(list, igloo_ro_base_t) == 0); + + ctest_test("test object a pushed to list", igloo_list_push(list, a) == 0); + + ctest_test("pinned list to type igloo_ro_base_t", igloo_list_set_type(list, igloo_ro_base_t) == 0); + ctest_test("can not pin list to type igloo_list_t", igloo_list_set_type(list, igloo_list_t) != 0); + ctest_test("pinned list to type igloo_ro_base_t", igloo_list_set_type(list, igloo_ro_base_t) == 0); + + ctest_test("test object b failed to pushed to list", igloo_list_push(list, b) != 0); + + ctest_test("list cleared", igloo_list_clear(list) == 0); + + ctest_test("pinned list to type igloo_ro_base_t", igloo_list_set_type(list, igloo_ro_base_t) == 0); + ctest_test("can not pin list to type igloo_list_t", igloo_list_set_type(list, igloo_list_t) == 0); + ctest_test("pinned list to type igloo_ro_base_t", igloo_list_set_type(list, igloo_ro_base_t) == 0); + + ctest_test("un-referenced list a", igloo_ro_unref(list) == 0); + list = igloo_ro_new(igloo_list_t); + ctest_test("list created", !igloo_RO_IS_NULL(list)); + + ctest_test("test object a pushed to list", igloo_list_push(list, a) == 0); + ctest_test("test object b pushed to list", igloo_list_push(list, b) == 0); + + ctest_test("pinned list to type igloo_ro_base_t", igloo_list_set_type(list, igloo_ro_base_t) != 0); + ctest_test("pinned list to type igloo_list_t", igloo_list_set_type(list, igloo_list_t) != 0); + ctest_test("pinned list to type igloo_ro_base_t", igloo_list_set_type(list, igloo_ro_base_t) != 0); + + ret = igloo_list_shift(list); + ctest_test("shifted element", !igloo_RO_IS_NULL(ret)); + ctest_test("shifted element matches a", igloo_RO_TO_TYPE(ret, igloo_ro_base_t) == a); + ctest_test("un-referenced shifted element", igloo_ro_unref(ret) == 0); + + ctest_test("pinned list to type igloo_ro_base_t", igloo_list_set_type(list, igloo_ro_base_t) != 0); + ctest_test("can not pin list to type igloo_list_t", igloo_list_set_type(list, igloo_list_t) == 0); + ctest_test("pinned list to type igloo_ro_base_t", igloo_list_set_type(list, igloo_ro_base_t) != 0); + + ctest_test("un-referenced a", igloo_ro_unref(a) == 0); + ctest_test("un-referenced b", igloo_ro_unref(b) == 0); + ctest_test("un-referenced list a", igloo_ro_unref(list) == 0); +} + +static void test_list_iterator(void) +{ + igloo_list_t *list; + igloo_ro_base_t * elements[3] = {NULL, NULL, NULL}; + igloo_list_iterator_storage_t storage; + igloo_list_iterator_t *iterator; + size_t i; + + test__create_push_unshift(&list, &(elements[1]), &(elements[0])); + + iterator = igloo_list_iterator_start(list, &storage, sizeof(storage)); + ctest_test("iterator created", iterator != NULL); + + for (i = 0; i < (sizeof(elements)/sizeof(*elements)); i++) { + igloo_ro_t ret = igloo_list_iterator_next(iterator); + ctest_test("shifted element matches corresponding element in list", igloo_RO_TO_TYPE(ret, igloo_ro_base_t) == elements[i]); + if (!igloo_RO_IS_NULL(ret)) { + ctest_test("un-referenced element returned by iterator", igloo_ro_unref(ret) == 0); + } + } + + ctest_test("rewinded iterator", igloo_list_iterator_rewind(iterator) == 0); + + for (i = 0; i < (sizeof(elements)/sizeof(*elements)); i++) { + igloo_ro_t ret = igloo_list_iterator_next(iterator); + ctest_test("shifted element matches corresponding element in list", igloo_RO_TO_TYPE(ret, igloo_ro_base_t) == elements[i]); + if (!igloo_RO_IS_NULL(ret)) { + ctest_test("un-referenced element returned by iterator", igloo_ro_unref(ret) == 0); + } + } + + ctest_test("rewinded iterator", igloo_list_iterator_rewind(iterator) == 0); + + for (i = 0; i < 1; i++) { + igloo_ro_t ret = igloo_list_iterator_next(iterator); + ctest_test("shifted element matches corresponding element in list", igloo_RO_TO_TYPE(ret, igloo_ro_base_t) == elements[i]); + if (!igloo_RO_IS_NULL(ret)) { + ctest_test("un-referenced element returned by iterator", igloo_ro_unref(ret) == 0); + } + } + + ctest_test("rewinded iterator", igloo_list_iterator_rewind(iterator) == 0); + + for (i = 0; i < (sizeof(elements)/sizeof(*elements)); i++) { + igloo_ro_t ret = igloo_list_iterator_next(iterator); + ctest_test("shifted element matches corresponding element in list", igloo_RO_TO_TYPE(ret, igloo_ro_base_t) == elements[i]); + if (!igloo_RO_IS_NULL(ret)) { + ctest_test("un-referenced element returned by iterator", igloo_ro_unref(ret) == 0); + } + } + + igloo_list_iterator_end(iterator); + + ctest_test("un-referenced a", igloo_ro_unref(elements[0]) == 0); + ctest_test("un-referenced b", igloo_ro_unref(elements[1]) == 0); + ctest_test("un-referenced list", igloo_ro_unref(list) == 0); +} + +static void test_list_foreach(void) +{ + igloo_list_t *list; + igloo_ro_base_t * elements[3] = {NULL, NULL, NULL}; + size_t i = 0; + + test__create_push_unshift(&list, &(elements[1]), &(elements[0])); + + igloo_list_foreach(list, igloo_ro_base_t, ret, { + ctest_test("foreach returned element matches corresponding element in list", ret == elements[i]); + i++; + }); + + ctest_test("un-referenced a", igloo_ro_unref(elements[0]) == 0); + ctest_test("un-referenced b", igloo_ro_unref(elements[1]) == 0); + ctest_test("un-referenced list", igloo_ro_unref(list) == 0); +} + +int main (void) +{ + ctest_init(); + + test_create_ref_unref(); + + test_list_push_unshift(); + test_list_push_unshift_pop(); + test_list_push_unshift_pop_pop(); + test_list_push_unshift_shift(); + test_list_push_unshift_shift_shift(); + test_list_push_unshift_pop_shift(); + test_list_push_unshift_shift_pop(); + + test_list_merge(); + + test_list_set_type(); + + test_list_iterator(); + + test_list_foreach(); + + ctest_fin(); + + return 0; +}