From: Gustavo Sverzut Barbieri <barbi...@profusion.mobi> Array with configurable member size and optimized operations such as binary search and insert sorted. --- .gitignore | 1 + Makefile.am | 11 +- ell/array.c | 758 +++++++++++++++++++++++++++++++++++++++++++++++++++++ ell/array.h | 92 +++++++ ell/ell.h | 1 + unit/test-array.c | 262 ++++++++++++++++++ 6 files changed, 1122 insertions(+), 3 deletions(-) create mode 100644 ell/array.c create mode 100644 ell/array.h create mode 100644 unit/test-array.c
diff --git a/.gitignore b/.gitignore index 3d88ef7..81c83c7 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,7 @@ unit/test-settings unit/test-netlink unit/test-dbus unit/test-dbus-message +unit/test-array doc/*.bak doc/*.stamp diff --git a/Makefile.am b/Makefile.am index ffac42e..e822cd8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -18,7 +18,8 @@ pkginclude_HEADERS = ell/ell.h ell/util.h \ ell/checksum.h \ ell/netlink.h \ ell/dbus.h \ - ell/idle.h + ell/idle.h \ + ell/array.h lib_LTLIBRARIES = ell/libell.la @@ -38,7 +39,8 @@ ell_libell_la_SOURCES = $(include_HEADERS) ell/private.h \ ell/checksum.c \ ell/netlink.c \ ell/dbus.c \ - ell/idle.c + ell/idle.c \ + ell/array.c ell_libell_la_LDFLAGS = -version-info 0:0:0 @@ -58,7 +60,8 @@ noinst_PROGRAMS = unit/test-unit unit/test-queue \ unit/test-main unit/test-io \ unit/test-plugin unit/test-checksum \ unit/test-settings unit/test-netlink \ - unit/test-dbus unit/test-dbus-message + unit/test-dbus unit/test-dbus-message \ + unit/test-array unit_test_unit_LDADD = ell/libell-private.la @@ -86,6 +89,8 @@ unit_test_dbus_LDADD = ell/libell-private.la unit_test_dbus_message_LDADD = ell/libell-private.la +unit_test_array_LDADD = ell/libell-private.la + noinst_LTLIBRARIES += unit/example-plugin.la unit_example_plugin_la_LDFLAGS = -no-undefined -module -avoid-version \ diff --git a/ell/array.c b/ell/array.c new file mode 100644 index 0000000..53c7517 --- /dev/null +++ b/ell/array.c @@ -0,0 +1,758 @@ +/* + * + * Embedded Linux library + * + * Copyright (C) 2012 ProFUSION embedded systems. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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 <config.h> +#endif + +#include "util.h" +#include "array.h" +#include "private.h" + +/** + * SECTION:array + * @short_description: Array support + * + * Array support + */ + +static void init_array(struct l_array *array, unsigned int member_size, unsigned int step) +{ + array->member_size = member_size; + array->len = 0; + array->max = 0; + array->step = (step > 0) ? step : 32; + array->members = NULL; +} + +static void clear_array(struct l_array *array, l_array_destroy_func_t destroy) +{ + if (destroy) { + uint8_t *itr, *itr_end; + + itr = array->members; + itr_end = itr + array->len * array->member_size; + for (; itr < itr_end; itr += array->member_size) + destroy(itr); + } + + l_free(array->members); +} + +static bool resize_array(struct l_array *array, unsigned int new_size) +{ + unsigned int new_max; + + if (new_size < array->max) + return true; + + if (new_size % array->step == 0) + new_max = new_size; + else + new_max = ((new_size / array->step) + 1) * array->step; + + array->members = l_realloc(array->members, + new_max * array->member_size); + array->max = new_max; + return true; +} + +static inline void __attribute__ ((always_inline)) array_swap(struct l_array *array, void *a, void *b, void *tmp) +{ + if (a == b) + return; + memcpy(tmp, a, array->member_size); + memcpy(a, b, array->member_size); + memcpy(b, tmp, array->member_size); +} + +static inline void *__attribute__ ((always_inline))get_member(const struct l_array *array, unsigned int position) +{ + unsigned int offset = position * array->member_size; + return (uint8_t *)array->members + offset; +} + +static int lookup(const struct l_array *array, const void *data, + l_array_compare_func_t function, void *user_data) +{ + const uint8_t *itr, *itr_end; + unsigned int sz; + + sz = array->member_size; + itr = array->members; + itr_end = itr + array->len * sz; + for (; itr < itr_end; itr += sz) { + if (function(data, itr, user_data) == 0) { + unsigned int offset = itr - (uint8_t*)array->members; + return offset / sz; + } + } + + return -1; +} + +static unsigned int lookup_sorted_near(const struct l_array *array, + const void *data, + l_array_compare_func_t function, + void *user_data, int *cmp) +{ + unsigned int start, last, middle; + + if (array->len == 0) { + *cmp = -1; + return 0; + } else if (array->len == 1) { + *cmp = function(data, array->members, user_data); + return 0; + } + + start = 0; + last = array->len - 1; /* inclusive */ + do { + void *p; + middle = start + (last - start) / 2; /* avoid overflow */ + p = get_member(array, middle); + *cmp = function(data, p, user_data); + if (*cmp == 0) + return middle; + else if (*cmp > 0) + start = middle + 1; + else if (middle > 0) + last = middle - 1; + else + break; + } while (start <= last); + return middle; +} + +/** + * l_array_new: + * @member_size: size of each member in the array. + * @step: when resizing the array, do this using the following extra amount. + * + * Create a new array. If the @step is 0, then a safe default is chosen. + * + * No error handling is needed since. In case of real memory allocation + * problems abort() will be called. + * + * Returns: a newly allocated #l_array object + */ +LIB_EXPORT struct l_array *l_array_new(unsigned int member_size, unsigned int step) +{ + struct l_array *ret; + + if (member_size == 0) + return NULL; + + ret = l_new(struct l_array, 1); + init_array(ret, member_size, step); + return ret; +} + +/** + * l_array_destroy: + * @array: array object + * @destroy: destroy function + * + * Free array and call @destory on all remaining members. + * + * The data given to @destory is the member memory, just free its + * members as the member itself is managed by the array. + */ +LIB_EXPORT void l_array_destroy(struct l_array *array, + l_array_destroy_func_t destroy) +{ + if (unlikely(!array)) + return; + + clear_array(array, destroy); + l_free(array); +} + +/** + * l_array_init: + * @array: array object to initialize. + * @member_size: size of each member in the array. + * @step: when resizing the array, do this using the following extra amount. + * + * Initialize array. If the @step is 0, then a safe default is chosen. + * + * This is useful for arrays inlined into other structures or + * allocated at stack. + */ +LIB_EXPORT void l_array_init(struct l_array *array, + unsigned int member_size, unsigned int step) +{ + if (unlikely(!array)) + return; + if (member_size == 0) + return; + init_array(array, member_size, step); +} + +/** + * l_array_clear: + * @array: array object + * @destroy: destroy function + * + * Free members and call @destory on all remaining entries. + **/ +LIB_EXPORT void l_array_clear(struct l_array *array, + l_array_destroy_func_t destroy) +{ + if (unlikely(!array)) + return; + clear_array(array, destroy); + array->len = 0; + array->max = 0; + array->members = NULL; +} + +/** + * l_array_append: + * @array: array object + * @data: data to be copied at the end + * + * Copies the given pointer contents at the end of the array. The + * pointer is not referenced, instead it's contents is copied to the + * members array using the previously defined #member_size. + * + * See also l_array_insert_at(). + * + * Returns: the index of the new member or -1 on errors. + **/ +LIB_EXPORT int l_array_append(struct l_array *array, const void *data) +{ + void *p; + + if (unlikely(!array)) + return -1; + + if (!resize_array(array, array->len + 1)) + return -1; + + p = get_member(array, array->len); + memcpy(p, data, array->member_size); + + array->len++; + return array->len - 1; +} + +/** + * l_array_insert: + * @array: array object + * @data: data to be copied + * @function: compare function + * @user_data: user data given to compare function + * + * Copies the given pointer contents at the array position defined by + * given compare @function. The pointer is not referenced, instead + * it's contents is copied to the members array using the previously + * defined #member_size. + * + * The data given to @function are the pointer to member memory + * itself, do no change it. + * + * If you're keeping a sorted array see l_array_insert_sorted(). + * + * Returns: the index of the new member or -1 on errors. + **/ +LIB_EXPORT int l_array_insert(struct l_array *array, const void *data, + l_array_compare_func_t function, void *user_data) +{ + const uint8_t *itr, *itr_end; + unsigned int sz; + + if (unlikely(!array)) + return -1; + + sz = array->member_size; + itr = array->members; + itr_end = itr + array->len * sz; + for (; itr < itr_end; itr += sz) { + unsigned int offset, position; + int cmp = function(itr, data, user_data); + if (cmp <= 0) + continue; + + offset = itr - (uint8_t *)array->members; + position = offset / sz; + if (!l_array_insert_at(array, position, data)) + return -1; + return position; + } + return l_array_append(array, data); +} + +/** + * l_array_insert_sorted: + * @array: array object + * @data: data to be copied + * @function: compare function + * @user_data: user data given to compare function + * + * Copies the given pointer contents at the array position defined by + * given compare @function. The pointer is not referenced, instead + * it's contents is copied to the members array using the previously + * defined #member_size. + * + * The data given to @function are the pointer to member memory + * itself, do no change it. + * + * This variation will optimize insertion position assuming the array + * is already sorted by doing binary search. + * + * Returns: the index of the new member or -1 on errors. + **/ +LIB_EXPORT int l_array_insert_sorted(struct l_array *array, const void *data, + l_array_compare_func_t function, void *user_data) +{ + unsigned int pos; + int cmp; + + if (unlikely(!array)) + return -1; + + pos = lookup_sorted_near(array, data, function, user_data, &cmp); + if (cmp > 0) + pos++; + + if (!l_array_insert_at(array, pos, data)) + return -1; + return pos; +} + +/** + * l_array_remove: + * @array: array object + * @data: data to be found and removed + * + * Find data in the array and remove it. Data may be an existing + * member of array (then optimized) or the contents will be matched + * using memcmp(). + * + * See also l_array_pop() and l_array_remove_at(). + * + * Returns: the index of the removed member or -1 on errors. + **/ +LIB_EXPORT int l_array_remove(struct l_array *array, const void *data) +{ + const uint8_t *itr, *itr_end; + unsigned int position, sz; + + if (unlikely(!array)) + return -1; + + sz = array->member_size; + if (data >= array->members && data < get_member(array, array->len)) { + unsigned int offset = (uint8_t*)data - (uint8_t*)array->members; + position = offset / sz; + goto found; + } + + itr = array->members; + itr_end = itr + array->len * sz; + for (; itr < itr_end; itr += sz) { + if (memcmp(data, itr, sz) == 0) { + unsigned int offset = itr - (uint8_t*)array->members; + position = offset / sz; + goto found; + } + } + return -1; + +found: + if (!l_array_remove_at(array, position)) + return -1; + return position; +} + +/** + * l_array_pop: + * @array: array object + * + * Removes the last member of the array. + * + * Returns: the index of the removed member or -1 on errors. + **/ +LIB_EXPORT int l_array_pop(struct l_array *array) +{ + if (unlikely(!array)) + return -1; + if (array->len == 0) + return -1; + if (!resize_array(array, array->len - 1)) + return -1; + array->len--; + return array->len + 1; +} + +/** + * l_array_get_at: + * @array: array object + * @position: member position + * + * Gets the member given its position in the array. It is a pointer to + * its current memory, then it can be invalidated with functions that + * changes the array such as l_array_append(), l_array_insert_at() or + * l_array_remove_at() or variants. + * + * See also l_array_lookup() and l_array_lookup_sorted(). + * + * Returns: pointer to current member memory. + */ +LIB_EXPORT void *l_array_get_at(const struct l_array *array, unsigned int position) +{ + if (unlikely(!array)) + return NULL; + if (unlikely(position >= array->len)) + return NULL; + return get_member(array, position); +} + +/** + * l_array_insert_at: + * @array: array object + * @position: where to insert the member + * @data: data to be copied at position + * + * Copies the given pointer contents at the given @position in the + * array. The pointer is not referenced, instead it's contents is + * copied to the members array using the previously defined + * #member_size. + * + * All the members from @position to the end of the array are shifted + * to the end. + * + * If @position is equal to the end of the array (equals to + * l_array_length()), then the member is appended. + * + * If @position is bigger than the array length, it will fail. + * + * Returns: the index of the new member or -1 on errors. + **/ +LIB_EXPORT bool l_array_insert_at(struct l_array *array, + unsigned int position, const void *data) +{ + unsigned int sz; + uint8_t *p; + + if (unlikely(!array)) + return false; + if (unlikely(position > array->len)) + return false; + + if (!resize_array(array, array->len + 1)) + return false; + + p = get_member(array, position); + + sz = array->member_size; + if (array->len > position) + memmove(p + sz, p, (array->len - position) * sz); + memcpy(p, data, sz); + + array->len++; + return true; +} + +/** + * l_array_remove: + * @array: array object + * @position: position to be removed. + * + * The member is removed from array and any members after it are moved + * towards the array head. + * + * See also l_array_pop() and l_array_remove(). + * + * Returns: #true when member has been removed and #false in case of failure. + **/ +LIB_EXPORT bool l_array_remove_at(struct l_array *array, unsigned int position) +{ + if (unlikely(!array)) + return false; + if (unlikely(position >= array->len)) + return false; + + if (position + 1 < array->len) { + unsigned int sz = array->member_size; + uint8_t *p = get_member(array, position); + memmove(p, p + sz, (array->len - position - 1) * sz); + } + + resize_array(array, array->len - 1); + array->len--; + return true; +} + +/** + * l_array_reverse: + * @array: array object + * + * Reverse members in the array. + */ +LIB_EXPORT void l_array_reverse(struct l_array *array) +{ + size_t sz; + uint8_t *fwd, *rev, *fwd_end; + void *tmp; + + if (unlikely(!array)) + return; + + if (array->len < 2) + return; + + sz = array->member_size; + + tmp = l_malloc(sz); + + fwd = array->members; + fwd_end = fwd + (array->len / 2) * sz; + + rev = fwd + (array->len - 1) * sz; + + for (; fwd < fwd_end; fwd += sz, rev -= sz) + array_swap(array, fwd, rev, tmp); + + l_free(tmp); +} + +static unsigned int array_partition(struct l_array *array, + unsigned int start, unsigned int last, + unsigned int pivot, + l_array_compare_func_t function, + void *user_data, + void *buf) +{ + const unsigned sz = array->member_size; + uint8_t *itr, *itr_end, *p; + void *pivot_value = buf; + void *swap_tmp = (uint8_t *)buf + sz; + + p = get_member(array, pivot); + + memcpy(pivot_value, p, sz); + array_swap(array, p, get_member(array, last), swap_tmp); + + p = get_member(array, start); + itr = get_member(array, start); + itr_end = itr + (last - start) * sz; + for (; itr < itr_end; itr += sz) { + if (function(itr, pivot_value, user_data) < 0) { + array_swap(array, itr, p, swap_tmp); + p += sz; + } + } + array_swap(array, p, get_member(array, last), swap_tmp); + + return (p - (uint8_t *)array->members) / sz; +} + +static void array_sort(struct l_array *array, + unsigned int start, unsigned int last, + l_array_compare_func_t function, void *user_data, + void *buf) +{ + unsigned int pivot, new_pivot; + + if (last <= start) + return; + + pivot = start + (last - start) / 2; /* avoid overflow */ + new_pivot = array_partition(array, start, last, pivot, + function, user_data, buf); + + if (start + 1 < new_pivot) + array_sort(array, start, new_pivot - 1, + function, user_data, buf); + + if (new_pivot + 1 < last) + array_sort(array, new_pivot + 1, last, + function, user_data, buf); +} + +/** + * l_array_sort: + * @array: array object + * @function: compare function + * @user_data: user data given to compare function + * + * Applies quick sort to the @array. + * + * The data given to @function are the pointer to member memory + * itself, do no change it. + */ +LIB_EXPORT void l_array_sort(struct l_array *array, + l_array_compare_func_t function, void *user_data) +{ + unsigned int start, last; + void *buf; + + if (unlikely(!array)) + return; + + start = 0; + last = array->len - 1; /* inclusive */ + + buf = l_malloc(array->member_size * 2); + array_sort(array, start, last, function, user_data, buf); + l_free(buf); +} + +/** + * l_array_lookup: + * @array: array object + * @function: compare function + * @user_data: user data given to compare function + * + * Walks array linearly looking for given data as compared by @function. + * + * The data given to @function are the pointer to member memory + * itself, do no change it. + * + * See also l_array_lookup_sorted(). + * + * Returns: the member index or -1 if not found. + */ +LIB_EXPORT int l_array_lookup(const struct l_array *array, const void *data, + l_array_compare_func_t function, void *user_data) +{ + if (unlikely(!array)) + return -1; + return lookup(array, data, function, user_data); +} + +/** + * l_array_lookup_sorted: + * @array: array object + * @function: compare function + * @user_data: user data given to compare function + * + * Uses binary search for given data as compared by @function. + * + * The data given to @function are the pointer to member memory + * itself, do no change it. + * + * Returns: the member index or -1 if not found. + */ +LIB_EXPORT int l_array_lookup_sorted(const struct l_array *array, const void *data, + l_array_compare_func_t function, void *user_data) +{ + unsigned int pos; + int cmp; + + if (unlikely(!array)) + return -1; + + pos = lookup_sorted_near(array, data, function, user_data, &cmp); + if (cmp == 0) + return pos; + return -1; +} + +/** + * l_array_foreach: + * @array: array object + * @function: callback function + * @user_data: user data given to callback function + * + * Call @function for every given data in @array. + * + * The data given to @function are the pointer to member memory + * itself. + */ +LIB_EXPORT void l_array_foreach(const struct l_array *array, + l_array_foreach_func_t function, void *user_data) +{ + uint8_t *itr, *itr_end; + + if (unlikely(!array)) + return; + if (unlikely(!function)) + return; + + itr = array->members; + itr_end = itr + array->len * array->member_size; + for (; itr < itr_end; itr += array->member_size) + function(itr, user_data); +} + +/** + * l_array_foreach: + * @array: array object + * @function: callback function + * @user_data: user data given to callback function + * + * Remove all entries in the @array where @function returns #true. + * + * Returns: number of removed entries + */ +LIB_EXPORT unsigned int l_array_foreach_remove(struct l_array *array, + l_array_remove_func_t function, void *user_data) +{ + unsigned int i = 0, count = 0; + + if (unlikely(!array)) + return 0; + + while (i < array->len) { + void *p = get_member(array, i); + if (function(p, user_data) == false) { + i++; + continue; + } + + l_array_remove_at(array, i); + count++; + } + + return count; +} + +/** + * l_array_length: + * @array: array object + * + * Returns: entries of the array + **/ +LIB_EXPORT unsigned int l_array_length(const struct l_array *array) +{ + if (unlikely(!array)) + return 0; + + return array->len; +} + +/** + * l_array_isempty: + * @array: array object + * + * Returns: #true if @array is empty and #false is not + **/ +LIB_EXPORT bool l_array_isempty(const struct l_array *array) +{ + if (unlikely(!array)) + return true; + + return array->len == 0; +} diff --git a/ell/array.h b/ell/array.h new file mode 100644 index 0000000..f18d9fe --- /dev/null +++ b/ell/array.h @@ -0,0 +1,92 @@ +/* + * + * Embedded Linux library + * + * Copyright (C) 2012 ProFUSION embedded systems. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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 __ELL_ARRAY_H +#define __ELL_ARRAY_H + +#include <stdbool.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*l_array_foreach_func_t) (void *value, void *user_data); +typedef void (*l_array_destroy_func_t) (void *value); +typedef int (*l_array_compare_func_t) (const void *a, const void *b, + void *user_data); +typedef bool (*l_array_remove_func_t) (void *data, void *user_data); + +struct l_array { + unsigned int member_size; + unsigned int len; + unsigned int max; + unsigned int step; + void *members; +}; + +struct l_array *l_array_new(unsigned int member_size, unsigned int step); +void l_array_destroy(struct l_array *array, + l_array_destroy_func_t destroy); + +void l_array_init(struct l_array *array, + unsigned int member_size, unsigned int step); +void l_array_clear(struct l_array *array, + l_array_destroy_func_t destroy); + +int l_array_append(struct l_array *array, const void *data); +int l_array_insert(struct l_array *array, const void *data, + l_array_compare_func_t function, void *user_data); +int l_array_insert_sorted(struct l_array *array, const void *data, + l_array_compare_func_t function, void *user_data); +int l_array_remove(struct l_array *array, const void *data); +int l_array_pop(struct l_array *array); + +void *l_array_get_at(const struct l_array *array, unsigned int position); +bool l_array_insert_at(struct l_array *array, + unsigned int position, const void *data); +bool l_array_remove_at(struct l_array *array, unsigned int position); + +void l_array_reverse(struct l_array *array); +void l_array_sort(struct l_array *array, + l_array_compare_func_t function, void *user_data); +int l_array_lookup(const struct l_array *array, const void *data, + l_array_compare_func_t function, void *user_data); +int l_array_lookup_sorted(const struct l_array *array, const void *data, + l_array_compare_func_t function, void *user_data); + +void l_array_foreach(const struct l_array *array, + l_array_foreach_func_t function, void *user_data); +unsigned int l_array_foreach_remove(struct l_array *array, + l_array_remove_func_t function, void *user_data); + +unsigned int l_array_length(const struct l_array *array); +bool l_array_isempty(const struct l_array *array); + +#define L_ARRAY_FOREACH(array, itr) \ + for (itr = array->members; \ + itr < ((typeof(*itr)*)array->members) + array->len; \ + itr++) + +#ifdef __cplusplus +} +#endif + +#endif /* __ELL_ARRAY_H */ diff --git a/ell/ell.h b/ell/ell.h index f62c554..1ac8904 100644 --- a/ell/ell.h +++ b/ell/ell.h @@ -22,6 +22,7 @@ #include <ell/util.h> #include <ell/test.h> #include <ell/queue.h> +#include <ell/array.h> #include <ell/hashmap.h> #include <ell/string.h> #include <ell/main.h> diff --git a/unit/test-array.c b/unit/test-array.c new file mode 100644 index 0000000..24d9e07 --- /dev/null +++ b/unit/test-array.c @@ -0,0 +1,262 @@ +/* + * + * Embedded Linux library + * + * Copyright (C) 2012 ProFUSION embedded systems. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <assert.h> + +#include <ell/ell.h> + +static void test_basic(const void *test_data) +{ + const int test_members = 5; + struct l_array *array; + int i, pos, *member; + const struct spec { + int pos, value; + } *s, specs[] = { + {test_members, 1234}, + {5, 0x1337}, + {0, 0xbeef}, + {-1, -1} + }; + + array = l_array_new(sizeof(int), 2); + assert(array); + + for (i = 0; i < test_members; i++) { + pos = l_array_append(array, &i); + assert(pos == i); + } + assert(l_array_length(array) == (unsigned)test_members); + + for (i = 0; i < test_members; i++) { + member = l_array_get_at(array, i); + assert(*member == i); + } + + for (s = specs; s->pos >= 0; s++) { + assert(l_array_insert_at(array, s->pos, &s->value)); + + for (i = 0; i < s->pos; i++) { + member = l_array_get_at(array, i); + assert(*member == i); + } + member = l_array_get_at(array, s->pos); + assert(*member == s->value); + for (i = s->pos + 1; i < test_members + 1; i++) { + member = l_array_get_at(array, i); + assert(*member == i - 1); + } + + assert(l_array_remove_at(array, s->pos)); + for (i = 0; i < test_members; i++) { + member = l_array_get_at(array, i); + assert(*member == i); + } + } + + l_array_destroy(array, NULL); +} + +static const short rand_numbers[] = { + 9, 0, 2, 3, 6, 5, 4, 7, 8, 1, 10 +}; +static const int numbers_count = sizeof(rand_numbers)/sizeof(rand_numbers[0]); + +static void show_sort_array(const struct l_array *array) +{ + int i, len = l_array_length(array); + printf("array with %d members:\n", len); + for (i = 0; i < len; i++) { + short *member = l_array_get_at(array, i); + printf("\tarray[%2d]=%5hd\n", i, *member); + } +} + +static bool check_short_sorted(const struct l_array *array) +{ + int i; + for (i = 0; i < numbers_count; i++) { + short *member = l_array_get_at(array, i); + if (*member != (short)i) { + show_sort_array(array); + fprintf(stderr, "not sorted at %d: %hd\n", i, *member); + return false; + } + } + return true; +} + +static int short_cmp(const void *pa, const void *pb, void *user_data) +{ + const short *a = pa, *b = pb; + return *a - *b; +} + +static void test_insert_sort(const void *test_data) +{ + struct l_array *array; + int i, pos; + + array = l_array_new(sizeof(short), 1); + assert(array); + + /* insert sorted and already sorted sequence */ + for (i = 0; i < numbers_count; i++) { + short val = i; + pos = l_array_insert(array, &val, short_cmp, NULL); + assert(pos == (int)val); + } + assert(check_short_sorted(array)); + l_array_clear(array, NULL); + + for (i = 0; i < numbers_count; i++) { + short val = i; + pos = l_array_insert_sorted(array, &val, short_cmp, NULL); + assert(pos == (int)val); + } + assert(check_short_sorted(array)); + l_array_clear(array, NULL); + + /* insert sorted the reverse sequence */ + for (i = 0; i < numbers_count; i++) { + short val = numbers_count - i - 1; + pos = l_array_insert(array, &val, short_cmp, NULL); + assert(pos == 0); + } + assert(check_short_sorted(array)); + l_array_clear(array, NULL); + + for (i = 0; i < numbers_count; i++) { + short val = numbers_count - i - 1; + pos = l_array_insert_sorted(array, &val, short_cmp, NULL); + assert(pos == 0); + } + assert(check_short_sorted(array)); + l_array_clear(array, NULL); + + /* insert sorted random numbers */ + for (i = 0; i < numbers_count; i++) { + short val = rand_numbers[i]; + l_array_insert(array, &val, short_cmp, NULL); + } + assert(check_short_sorted(array)); + l_array_clear(array, NULL); + + for (i = 0; i < numbers_count; i++) { + short val = rand_numbers[i]; + l_array_insert_sorted(array, &val, short_cmp, NULL); + } + assert(check_short_sorted(array)); + l_array_clear(array, NULL); + + l_array_destroy(array, NULL); +} + +static void test_sort(const void *test_data) +{ + struct l_array *array; + int i; + + array = l_array_new(sizeof(short), 1); + assert(array); + + for (i = 0; i < numbers_count; i++) { + short val = rand_numbers[i]; + l_array_append(array, &val); + } + l_array_sort(array, short_cmp, NULL); + assert(check_short_sorted(array)); + l_array_destroy(array, NULL); +} + +static void test_reverse(const void *test_data) +{ + struct l_array *array; + int i; + + array = l_array_new(sizeof(short), 1); + assert(array); + + for (i = 0; i < numbers_count; i++) { + short val = i; + l_array_append(array, &val); + } + l_array_reverse(array); + + for (i = 0; i < numbers_count; i++) { + short *member = l_array_get_at(array, i); + assert(*member == (numbers_count - i - 1)); + } + + l_array_destroy(array, NULL); +} + +static void array_foreach(void *p, void *user_data) +{ + short *member = p; + int *i = user_data; + assert(*i == *member); + (*i)++; +} + +static void test_itr(const void *test_data) +{ + struct l_array *array; + short *member; + int i; + + array = l_array_new(sizeof(short), 1); + assert(array); + + for (i = 0; i < numbers_count; i++) { + short val = i; + l_array_append(array, &val); + } + i = 0; + L_ARRAY_FOREACH(array, member) { + assert(*member == i); + i++; + } + assert(i == numbers_count); + + i = 0; + l_array_foreach(array, array_foreach, &i); + assert(i == numbers_count); + + l_array_destroy(array, NULL); +} + +int main(int argc, char *argv[]) +{ + l_test_init(&argc, &argv); + + l_test_add("Basic Test", test_basic, NULL); + l_test_add("Insert Sorted Test", test_insert_sort, NULL); + l_test_add("Reverse Test", test_reverse, NULL); + l_test_add("Sort Test", test_sort, NULL); + l_test_add("Iterators", test_itr, NULL); + return l_test_run(); +} -- 1.7.10.2 _______________________________________________ connman mailing list connman@connman.net http://lists.connman.net/listinfo/connman