From a4dfda9304edaf7b08670f4bd70a387b734a8d51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Sat, 23 Aug 2014 20:29:54 +0200 Subject: [PATCH] core: add functions to manage array lists --- po/POTFILES.in | 2 + po/srcfiles.cmake | 2 + src/core/CMakeLists.txt | 1 + src/core/Makefile.am | 2 + src/core/wee-arraylist.c | 610 +++++++++++++++++++++++++++++++++++++++ src/core/wee-arraylist.h | 64 ++++ 6 files changed, 681 insertions(+) create mode 100644 src/core/wee-arraylist.c create mode 100644 src/core/wee-arraylist.h diff --git a/po/POTFILES.in b/po/POTFILES.in index e89ebdad6..c9681d5f4 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,4 +1,6 @@ ./doc/docgen.py +./src/core/wee-arraylist.c +./src/core/wee-arraylist.h ./src/core/wee-backtrace.c ./src/core/wee-backtrace.h ./src/core/weechat.c diff --git a/po/srcfiles.cmake b/po/srcfiles.cmake index 810fd3a31..a7d10d667 100644 --- a/po/srcfiles.cmake +++ b/po/srcfiles.cmake @@ -1,5 +1,7 @@ SET(WEECHAT_SOURCES ./doc/docgen.py +./src/core/wee-arraylist.c +./src/core/wee-arraylist.h ./src/core/wee-backtrace.c ./src/core/wee-backtrace.h ./src/core/weechat.c diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index ff2bc13b5..1ae0eaf37 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -21,6 +21,7 @@ set(LIB_CORE_SRC weechat.c weechat.h +wee-arraylist.c wee-arraylist.h wee-backtrace.c wee-backtrace.h wee-command.c wee-command.h wee-completion.c wee-completion.h diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 65b199263..c7276d8fe 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -23,6 +23,8 @@ noinst_LIBRARIES = lib_weechat_core.a lib_weechat_core_a_SOURCES = weechat.c \ weechat.h \ + wee-arraylist.c \ + wee-arraylist.h \ wee-backtrace.c \ wee-backtrace.h \ wee-command.c \ diff --git a/src/core/wee-arraylist.c b/src/core/wee-arraylist.c new file mode 100644 index 000000000..83b65c23c --- /dev/null +++ b/src/core/wee-arraylist.c @@ -0,0 +1,610 @@ +/* + * wee-arraylist.c - array lists management + * + * Copyright (C) 2014 Sébastien Helleu + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see . + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "weechat.h" +#include "wee-arraylist.h" +#include "wee-log.h" +#include "wee-string.h" + + +/* + * Creates a new arraylist. + * + * Returns pointer to arraylist, NULL if error. + */ + +struct t_arraylist * +arraylist_new (int initial_size, + int sorted, + int allow_duplicates, + t_arraylist_cmp *callback_cmp, void *callback_cmp_data, + t_arraylist_free *callback_free, void *callback_free_data) +{ + struct t_arraylist *new_arraylist; + + /* check arguments */ + if ((initial_size < 0) || !callback_cmp) + return NULL; + + new_arraylist = malloc (sizeof (*new_arraylist)); + if (!new_arraylist) + return NULL; + + new_arraylist->size = 0; + if (initial_size > 0) + { + new_arraylist->size_alloc = initial_size; + new_arraylist->size_alloc_min = initial_size; + new_arraylist->data = calloc (initial_size, + sizeof (*new_arraylist->data)); + if (!new_arraylist->data) + { + free (new_arraylist); + return NULL; + } + } + else + { + new_arraylist->size_alloc = 0; + new_arraylist->size_alloc_min = 0; + new_arraylist->data = NULL; + } + new_arraylist->sorted = sorted; + new_arraylist->allow_duplicates = allow_duplicates; + new_arraylist->callback_cmp = callback_cmp; + new_arraylist->callback_cmp_data = callback_cmp_data; + new_arraylist->callback_free = callback_free; + new_arraylist->callback_free_data = callback_free_data; + + return new_arraylist; +} + +/* + * Returns the size of an arraylist (number of elements). + */ + +int +arraylist_size (struct t_arraylist *arraylist) +{ + if (!arraylist) + return 0; + + return arraylist->size; +} + +/* + * Returns the pointer to an arraylist element, by index. + */ + +void * +arraylist_get (struct t_arraylist *arraylist, int index) +{ + if (!arraylist || (index < 0) || (index >= arraylist->size)) + return NULL; + + return arraylist->data[index]; +} + +/* + * Adjusts the allocated size of arraylist to add one element (if needed), + * so that the list has enough allocated data to store (current_size + 1) + * elements. + * + * Returns: + * 1: OK + * 0: error + */ + +int +arraylist_grow (struct t_arraylist *arraylist) +{ + int new_size_alloc; + void **data; + + /* if we have enough space allocated, do nothing */ + if (arraylist->size + 1 <= arraylist->size_alloc) + return 1; + + new_size_alloc = (arraylist->size_alloc < 2) ? + 2 : arraylist->size_alloc + (arraylist->size_alloc / 2); + + data = realloc (arraylist->data, + new_size_alloc * sizeof (*arraylist->data)); + if (!data) + return 0; + arraylist->data = data; + memset (&arraylist->data[arraylist->size_alloc], + 0, + (new_size_alloc - arraylist->size_alloc) * + sizeof (*arraylist->data)); + arraylist->size_alloc = new_size_alloc; + + return 1; +} + +/* + * Adjusts the allocated size of arraylist to remove one element (if needed), + * so that the list has enough allocated data to store (current size - 1) + * elements. + * + * Returns: + * 1: OK + * 0: error + */ + +int +arraylist_shrink (struct t_arraylist *arraylist) +{ + int new_size_alloc; + void **data; + + /* we don't shrink if we are below the min allocated size */ + if ((arraylist->size_alloc == 0) + || (arraylist->size_alloc <= arraylist->size_alloc_min)) + { + return 1; + } + + /* clear the arraylist if current allocated size is 1 */ + if (arraylist->size_alloc == 1) + { + free (arraylist->data); + arraylist->data = NULL; + arraylist->size_alloc = 0; + return 1; + } + + new_size_alloc = arraylist->size_alloc - (arraylist->size_alloc / 2); + + if (arraylist->size - 1 >= new_size_alloc) + return 1; + + data = realloc (arraylist->data, + new_size_alloc * sizeof (*arraylist->data)); + if (!data) + return 0; + arraylist->data = data; + arraylist->size_alloc = new_size_alloc; + + return 1; +} + +/* + * Performs a binary search in the arraylist to find an element + * (this function must be called only if the arraylist is sorted). + * + * If "index" is not NULL, it is set with the index of element found (or -1 if + * element was not found). + * + * If "index_insert" is not NULL, it is set with the index that must be used to + * insert the element in the arraylist (to keep arraylist sorted). + * + * Returns pointer to element found, NULL if not found. + */ + +void * +arraylist_binary_search (struct t_arraylist *arraylist, void *pointer, + int *index, int *index_insert) +{ + int ret_index, ret_index_insert, start, end, middle, rc; + void *ret_pointer; + + ret_index = -1; + ret_index_insert = -1; + ret_pointer = NULL; + + start = 0; + end = arraylist->size - 1; + + /* + * statistically we often add at the end, or before first element, so + * first check these cases (for performance), before doing the binary + * search + */ + rc = (arraylist->callback_cmp) (arraylist->callback_cmp_data, + arraylist, + pointer, + arraylist->data[end]); + if (rc == 0) + { + ret_index = end; + /* by convention, add an element with same value after the last one */ + ret_index_insert = -1; + ret_pointer = arraylist->data[end]; + goto end; + } + if (rc > 0) + { + ret_index = -1; + ret_index_insert = -1; + ret_pointer = NULL; + goto end; + } + if (arraylist->size == 1) + { + ret_index = -1; + ret_index_insert = 0; + ret_pointer = NULL; + goto end; + } + + rc = (arraylist->callback_cmp) (arraylist->callback_cmp_data, + arraylist, + pointer, + arraylist->data[start]); + if (rc == 0) + { + ret_index = start; + ret_index_insert = start; + ret_pointer = arraylist->data[start]; + goto end; + } + if (rc < 0) + { + ret_index = -1; + ret_index_insert = start; + ret_pointer = NULL; + goto end; + } + if (arraylist->size == 2) + { + ret_index = -1; + ret_index_insert = end; + ret_pointer = NULL; + goto end; + } + + start++; + end--; + + /* perform a binary search to find the index */ + while (start <= end) + { + middle = (start + end) / 2; + + rc = (arraylist->callback_cmp) (arraylist->callback_cmp_data, + arraylist, + pointer, + arraylist->data[middle]); + if (rc == 0) + { + ret_index = middle; + ret_index_insert = middle; + ret_pointer = arraylist->data[middle]; + goto end; + } + + if (rc < 0) + end = middle - 1; + else + start = middle + 1; + + if (start > end) + { + ret_index = -1; + ret_index_insert = (rc < 0) ? middle : middle + 1; + ret_pointer = NULL; + } + } + +end: + if (index) + *index = ret_index; + if (index_insert) + *index_insert = ret_index_insert; + return ret_pointer; +} + +/* + * Performs a standard search in the arraylist to find an element + * (this function must be called only if the arraylist is NOT sorted). + * + * If "index" is not NULL, it is set with the index of element found (or -1 if + * element was not found). + * + * If "index_insert" is not NULL, it is set to -1 (elements are always added + * at the end of list when it is not sorted). + * + * Returns pointer to element found, NULL if not found. + */ + +void * +arraylist_standard_search (struct t_arraylist *arraylist, void *pointer, + int *index, int *index_insert) +{ + int i; + + for (i = 0; i < arraylist->size; i++) + { + if ((arraylist->callback_cmp) (arraylist->callback_cmp_data, + arraylist, arraylist->data[i], + pointer) == 0) + { + if (index) + *index = i; + if (index_insert) + *index_insert = -1; + return arraylist->data[i]; + } + } + + if (index) + *index = -1; + if (index_insert) + *index_insert = -1; + return NULL; +} + +/* + * Searches an element in the arraylist. + * + * If "index" is not NULL, it is set with the index of element found (or -1 if + * element was not found). + * + * If "index_insert" is not NULL, it is set with the index that must be used to + * insert the element in the arraylist (to keep arraylist sorted). + * + * Returns pointer to element found, NULL if not found. + */ + +void * +arraylist_search (struct t_arraylist *arraylist, void *pointer, + int *index, int *index_insert) +{ + if (index) + *index = -1; + if (index_insert) + *index_insert = -1; + + if (arraylist->size == 0) + return NULL; + + if (arraylist->sorted) + { + return arraylist_binary_search (arraylist, pointer, + index, index_insert); + } + else + { + return arraylist_standard_search (arraylist, pointer, + index, index_insert); + } +} + +/* + * Inserts an element at a given index (and shifts next elements by one + * position), or at automatic index if the arraylist is sorted. + * + * If the index is negative and that the arraylist is not sorted, the element + * is added at the end of arraylist. + * + * Returns the index of the new element (>= 0) or -1 if error. + */ + +int +arraylist_insert (struct t_arraylist *arraylist, int index, void *pointer) +{ + int index_insert, i; + + if (arraylist->sorted) + { + (void) arraylist_search (arraylist, pointer, &index, &index_insert); + if ((index >= 0) && !arraylist->allow_duplicates) + { + while ((index < arraylist->size) + && (((arraylist->callback_cmp) (arraylist->callback_cmp_data, + arraylist, arraylist->data[index], + pointer)) == 0)) + { + if (arraylist->callback_free) + { + (arraylist->callback_free) (arraylist->callback_free_data, + arraylist, + arraylist->data[index]); + } + arraylist_remove (arraylist, index); + } + } + else + index = index_insert; + } + else if (!arraylist->allow_duplicates) + { + /* + * arraylist is not sorted and does not allow duplicates, then we + * remove any element with the same value + */ + i = 0; + while (i < arraylist->size) + { + if ((arraylist->callback_cmp) (arraylist->callback_cmp_data, + arraylist, arraylist->data[i], + pointer) == 0) + { + if (arraylist->callback_free) + { + (arraylist->callback_free) (arraylist->callback_free_data, + arraylist, + arraylist->data[i]); + } + arraylist_remove (arraylist, i); + } + else + i++; + } + } + + /* if index is negative or too big, add at the end */ + if ((index < 0) || (index > arraylist->size)) + index = arraylist->size; + + if (!arraylist_grow (arraylist)) + return -1; + + /* shift next elements by one position */ + if (index < arraylist->size) + { + memmove (&arraylist->data[index + 1], + &arraylist->data[index], + (arraylist->size - index) * sizeof (*arraylist->data)); + } + + /* set element */ + arraylist->data[index] = pointer; + + (arraylist->size)++; + + return index; +} + +/* + * Adds an element at the end of arraylist (or in the middle if the arraylist + * is sorted). + * + * Returns the index of the new element (>= 0) or -1 if error. + */ + +int +arraylist_add (struct t_arraylist *arraylist, void *pointer) +{ + return arraylist_insert (arraylist, -1, pointer); +} + +/* + * Removes one element from the arraylist. + * + * Returns the index removed or -1 if error. + */ + +int +arraylist_remove (struct t_arraylist *arraylist, int index) +{ + if ((index < 0) || (index >= arraylist->size)) + return -1; + + if (index < arraylist->size - 1) + { + memmove (&arraylist->data[index], + &arraylist->data[index + 1], + (arraylist->size - index - 1) * sizeof (*arraylist->data)); + memset (&arraylist->data[arraylist->size - 1], 0, + sizeof (*arraylist->data)); + } + else + { + memset (&arraylist->data[index], 0, sizeof (*arraylist->data)); + } + + arraylist_shrink (arraylist); + + (arraylist->size)--; + + return index; +} + +/* + * Removes all elements in the arraylist. + * + * Returns: + * 1: OK + * 0: error + */ + +int +arraylist_clear (struct t_arraylist *arraylist) +{ + void **data; + + if (arraylist->data + && (arraylist->size_alloc != arraylist->size_alloc_min)) + { + if (arraylist->size_alloc_min == 0) + { + free (arraylist->data); + arraylist->data = NULL; + } + else + { + data = realloc ( + arraylist->data, + arraylist->size_alloc_min * sizeof (*arraylist->data)); + if (!data) + return 0; + arraylist->data = data; + } + arraylist->size_alloc = arraylist->size_alloc_min; + } + + arraylist->size = 0; + + return 1; +} + +/* + * Frees an arraylist. + */ + +void +arraylist_free (struct t_arraylist *arraylist) +{ + if (arraylist->data) + free (arraylist->data); + + free (arraylist); +} + +/* + * Prints an arraylist in WeeChat log file (usually for crash dump). + */ + +void +arraylist_print_log (struct t_arraylist *arraylist, const char *name) +{ + int i; + + log_printf (""); + log_printf ("[arraylist %s (addr:0x%lx)]", name, arraylist); + log_printf (" size . . . . . . . . . : %d", arraylist->size); + log_printf (" size_alloc . . . . . . : %d", arraylist->size_alloc); + log_printf (" size_alloc_min . . . . : %d", arraylist->size_alloc_min); + log_printf (" sorted . . . . . . . . : %d", arraylist->sorted); + log_printf (" allow_duplicates . . . : %d", arraylist->allow_duplicates); + log_printf (" data . . . . . . . . . : 0x%lx", arraylist->data); + if (arraylist->data) + { + for (i = 0; i < arraylist->size_alloc; i++) + { + log_printf (" data[%08d] . . . : 0x%lx", + i, arraylist->data[i]); + } + } + log_printf (" callback_cmp . . . . . : 0x%lx", arraylist->callback_cmp); + log_printf (" callback_cmp_data. . . : 0x%lx", arraylist->callback_cmp_data); + log_printf (" callback_free. . . . . : 0x%lx", arraylist->callback_free); + log_printf (" callback_free_data . . : 0x%lx", arraylist->callback_free_data); +} diff --git a/src/core/wee-arraylist.h b/src/core/wee-arraylist.h new file mode 100644 index 000000000..9f3ac481b --- /dev/null +++ b/src/core/wee-arraylist.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2014 Sébastien Helleu + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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. + * + * WeeChat 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 WeeChat. If not, see . + */ + +#ifndef WEECHAT_ARRAYLIST_H +#define WEECHAT_ARRAYLIST_H 1 + +struct t_arraylist; + +typedef int (t_arraylist_cmp)(void *data, struct t_arraylist *arraylist, + void *pointer1, void *pointer2); +typedef void (t_arraylist_free)(void *data, struct t_arraylist *arraylist, + void *pointer); + +struct t_arraylist +{ + int size; /* number of items in data */ + int size_alloc; /* number of allocated items */ + int size_alloc_min; /* min number of allocated items */ + int sorted; /* 1 if the arraylist is sorted */ + int allow_duplicates; /* 1 if duplicates are allowed */ + void **data; /* pointers to data */ + t_arraylist_cmp *callback_cmp; /* compare two elements */ + void *callback_cmp_data; /* data for compare callback */ + t_arraylist_free *callback_free; /* free an element */ + void *callback_free_data; /* data for free callback */ +}; + +extern struct t_arraylist *arraylist_new (int initial_size, + int sorted, + int allow_duplicates, + t_arraylist_cmp *callback_cmp, + void *callback_cmp_data, + t_arraylist_free *callback_free, + void *callback_free_data); +extern int arraylist_size (struct t_arraylist *arraylist); +extern void *arraylist_get (struct t_arraylist *arraylist, int index); +extern void *arraylist_search (struct t_arraylist *arraylist, void *pointer, + int *index, int *index_insert); +extern int arraylist_insert (struct t_arraylist *arraylist, int index, + void *pointer); +extern int arraylist_add (struct t_arraylist *arraylist, void *pointer); +extern int arraylist_remove (struct t_arraylist *arraylist, int index); +extern int arraylist_clear (struct t_arraylist *arraylist); +extern void arraylist_free (struct t_arraylist *arraylist); +extern void arraylist_print_log (struct t_arraylist *arraylist, + const char *name); + +#endif /* WEECHAT_ARRAYLIST_H */