diff options
author | Sébastien Helleu <flashcode@flashtux.org> | 2014-10-05 08:35:17 +0200 |
---|---|---|
committer | Sébastien Helleu <flashcode@flashtux.org> | 2014-10-05 08:35:17 +0200 |
commit | f53baf628ec99fa511053a8ad171aa19b61f9cd2 (patch) | |
tree | ad67400bfa27698fbd53cd5dc3486419705afe53 | |
parent | c6eb5e6b12c120c720ea8129a3d80e7e349dde7f (diff) | |
parent | 7f4d9de2552a78e1d8f4f500c529b3aa6277b1ef (diff) | |
download | weechat-f53baf628ec99fa511053a8ad171aa19b61f9cd2.zip |
Merge branch 'arraylist'
-rw-r--r-- | po/POTFILES.in | 2 | ||||
-rw-r--r-- | po/srcfiles.cmake | 2 | ||||
-rw-r--r-- | src/core/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/core/Makefile.am | 2 | ||||
-rw-r--r-- | src/core/wee-arraylist.c | 632 | ||||
-rw-r--r-- | src/core/wee-arraylist.h | 64 | ||||
-rw-r--r-- | src/core/wee-completion.c | 5 | ||||
-rw-r--r-- | src/gui/gui-bar-item.c | 27 | ||||
-rw-r--r-- | src/gui/gui-completion.c | 419 | ||||
-rw-r--r-- | src/gui/gui-completion.h | 13 | ||||
-rw-r--r-- | src/plugins/plugin-api.c | 2 | ||||
-rw-r--r-- | tests/CMakeLists.txt | 3 | ||||
-rw-r--r-- | tests/Makefile.am | 7 | ||||
-rw-r--r-- | tests/tests.cpp | 1 | ||||
-rw-r--r-- | tests/unit/core/test-arraylist.cpp | 418 |
15 files changed, 1388 insertions, 210 deletions
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..fbbbbef7d --- /dev/null +++ b/src/core/wee-arraylist.c @@ -0,0 +1,632 @@ +/* + * wee-arraylist.c - array lists management + * + * Copyright (C) 2014 Sébastien Helleu <flashcode@flashtux.org> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> + +#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 (!arraylist) + return 0; + + /* 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; + + if (!arraylist) + return 0; + + /* 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; + + if (!arraylist) + goto end; + + 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; + + if (!arraylist) + goto end; + + 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]; + } + } + +end: + 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 || (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. + * + * If the arraylist is sorted, the argument "index" is ignored (the element + * will be inserted at appropriate position, to keep arraylist sorted). + * + * 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) + return -1; + + 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) +{ + if (!arraylist) + return -1; + + 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 (!arraylist || (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) +{ + if (!arraylist) + return 0; + + if (arraylist->data + && (arraylist->size_alloc != arraylist->size_alloc_min)) + { + free (arraylist->data); + arraylist->data = NULL; + arraylist->size_alloc = 0; + if (arraylist->size_alloc_min > 0) + { + arraylist->data = calloc(arraylist->size_alloc_min, + sizeof (*arraylist->data)); + if (!arraylist->data) + return 0; + arraylist->size_alloc = arraylist->size_alloc_min; + } + } + + arraylist->size = 0; + + return 1; +} + +/* + * Frees an arraylist. + */ + +void +arraylist_free (struct t_arraylist *arraylist) +{ + if (!arraylist) + return; + + 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 <flashcode@flashtux.org> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#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 */ diff --git a/src/core/wee-completion.c b/src/core/wee-completion.c index d4b77127d..e97c2d823 100644 --- a/src/core/wee-completion.c +++ b/src/core/wee-completion.c @@ -34,6 +34,7 @@ #include <unistd.h> #include "weechat.h" +#include "wee-arraylist.h" #include "wee-config.h" #include "wee-hashtable.h" #include "wee-hook.h" @@ -625,12 +626,12 @@ completion_list_add_nicks_cb (void *data, (void) completion_item; (void) buffer; - count_before = weelist_size (completion->completion_list); + count_before = completion->list->size; hook_completion_exec (completion->buffer->plugin, "nick", completion->buffer, completion); - if (weelist_size (completion->completion_list) == count_before) + if (completion->list->size == count_before) { /* * no plugin overrides nick completion => use default nick diff --git a/src/gui/gui-bar-item.c b/src/gui/gui-bar-item.c index 9c3d6129a..9fddb9658 100644 --- a/src/gui/gui-bar-item.c +++ b/src/gui/gui-bar-item.c @@ -29,6 +29,7 @@ #include <time.h> #include "../core/weechat.h" +#include "../core/wee-arraylist.h" #include "../core/wee-config.h" #include "../core/wee-hashtable.h" #include "../core/wee-hdata.h" @@ -1484,9 +1485,9 @@ gui_bar_item_default_completion (void *data, struct t_gui_bar_item *item, struct t_gui_buffer *buffer, struct t_hashtable *extra_info) { - int length; + int length, i; char *buf, str_number[64]; - struct t_gui_completion_partial *ptr_item; + struct t_gui_completion_word *ptr_completion_word; /* make C compiler happy */ (void) data; @@ -1495,37 +1496,39 @@ gui_bar_item_default_completion (void *data, struct t_gui_bar_item *item, (void) extra_info; if (!buffer || !buffer->completion - || !buffer->completion->partial_completion_list) + || (buffer->completion->partial_list->size == 0)) { return NULL; } length = 1; - for (ptr_item = buffer->completion->partial_completion_list; - ptr_item; ptr_item = ptr_item->next_item) + for (i = 0; i < buffer->completion->partial_list->size; i++) { - length += strlen (ptr_item->word) + 32; + ptr_completion_word = + (struct t_gui_completion_word *)(buffer->completion->partial_list->data[i]); + length += strlen (ptr_completion_word->word) + 32; } buf = malloc (length); if (buf) { buf[0] = '\0'; - for (ptr_item = buffer->completion->partial_completion_list; - ptr_item; ptr_item = ptr_item->next_item) + for (i = 0; i < buffer->completion->partial_list->size; i++) { + ptr_completion_word = + (struct t_gui_completion_word *)(buffer->completion->partial_list->data[i]); strcat (buf, GUI_COLOR_CUSTOM_BAR_FG); - strcat (buf, ptr_item->word); - if (ptr_item->count > 0) + strcat (buf, ptr_completion_word->word); + if (ptr_completion_word->count > 0) { strcat (buf, GUI_COLOR_CUSTOM_BAR_DELIM); strcat (buf, "("); snprintf (str_number, sizeof (str_number), - "%d", ptr_item->count); + "%d", ptr_completion_word->count); strcat (buf, str_number); strcat (buf, ")"); } - if (ptr_item->next_item) + if (i < buffer->completion->partial_list->size - 1) strcat (buf, " "); } } diff --git a/src/gui/gui-completion.c b/src/gui/gui-completion.c index eee784bca..7a416eee3 100644 --- a/src/gui/gui-completion.c +++ b/src/gui/gui-completion.c @@ -33,6 +33,7 @@ #include <unistd.h> #include "../core/weechat.h" +#include "../core/wee-arraylist.h" #include "../core/wee-completion.h" #include "../core/wee-config.h" #include "../core/wee-hdata.h" @@ -51,6 +52,48 @@ int gui_completion_freeze = 0; /* 1 to freeze completions (do not */ /* + * Compares two words in completion list. + */ + +int +gui_completion_word_compare_cb (void *data, struct t_arraylist *arraylist, + void *pointer1, void *pointer2) +{ + struct t_gui_completion_word *completion_word1, *completion_word2; + + /* make C compiler happy */ + (void) data; + (void) arraylist; + + completion_word1 = (struct t_gui_completion_word *)pointer1; + completion_word2 = (struct t_gui_completion_word *)pointer2; + + return string_strcasecmp (completion_word1->word, completion_word2->word); +} + +/* + * Frees a word in completion list. + */ + +void +gui_completion_word_free_cb (void *data, struct t_arraylist *arraylist, + void *pointer) +{ + struct t_gui_completion_word *completion_word; + + /* make C compiler happy */ + (void) data; + (void) arraylist; + + completion_word = (struct t_gui_completion_word *)pointer; + + if (completion_word->word) + free (completion_word->word); + + free (completion_word); +} + +/* * Initializes completion for a buffer. */ @@ -70,7 +113,9 @@ gui_completion_buffer_init (struct t_gui_completion *completion, completion->add_space = 1; completion->force_partial_completion = 0; - completion->completion_list = weelist_new (); + completion->list = arraylist_new (32, 1, 0, + &gui_completion_word_compare_cb, NULL, + &gui_completion_word_free_cb, NULL); completion->word_found = NULL; completion->word_found_is_nick = 0; @@ -78,8 +123,10 @@ gui_completion_buffer_init (struct t_gui_completion *completion, completion->diff_size = 0; completion->diff_length = 0; - completion->partial_completion_list = NULL; - completion->last_partial_completion = NULL; + completion->partial_list = arraylist_new ( + 0, 0, 0, + &gui_completion_word_compare_cb, NULL, + &gui_completion_word_free_cb, NULL); } /* @@ -88,66 +135,23 @@ gui_completion_buffer_init (struct t_gui_completion *completion, * Returns pointer to new item, NULL if error. */ -struct t_gui_completion_partial * +struct t_gui_completion_word * gui_completion_partial_list_add (struct t_gui_completion *completion, const char *word, int count) { - struct t_gui_completion_partial *new_item; + struct t_gui_completion_word *new_completion_word; - new_item = malloc (sizeof (*new_item)); - if (new_item) + new_completion_word = malloc (sizeof (*new_completion_word)); + if (new_completion_word) { - new_item->word = strdup (word); - new_item->count = count; + new_completion_word->word = strdup (word); + new_completion_word->nick_completion = 0; + new_completion_word->count = count; - new_item->prev_item = completion->last_partial_completion; - if (completion->partial_completion_list) - (completion->last_partial_completion)->next_item = new_item; - else - completion->partial_completion_list = new_item; - completion->last_partial_completion = new_item; - new_item->next_item = NULL; + arraylist_add (completion->partial_list, new_completion_word); } - return new_item; -} - -/* - * Removes an item from partial completion list. - */ - -void -gui_completion_partial_list_free (struct t_gui_completion *completion, - struct t_gui_completion_partial *item) -{ - /* remove partial completion item from list */ - if (item->prev_item) - (item->prev_item)->next_item = item->next_item; - if (item->next_item) - (item->next_item)->prev_item = item->prev_item; - if (completion->partial_completion_list == item) - completion->partial_completion_list = item->next_item; - if (completion->last_partial_completion == item) - completion->last_partial_completion = item->prev_item; - - /* free data */ - if (item->word) - free (item->word); - - free (item); -} - -/* - * Removes partial completion list. - */ -void -gui_completion_partial_list_free_all (struct t_gui_completion *completion) -{ - while (completion->partial_completion_list) - { - gui_completion_partial_list_free (completion, - completion->partial_completion_list); - } + return new_completion_word; } /* @@ -169,17 +173,17 @@ gui_completion_free_data (struct t_gui_completion *completion) free (completion->args); completion->args = NULL; - if (completion->completion_list) + if (completion->list) { - weelist_free (completion->completion_list); - completion->completion_list = NULL; + arraylist_free (completion->list); + completion->list = NULL; } if (completion->word_found) free (completion->word_found); completion->word_found = NULL; - gui_completion_partial_list_free_all (completion); + arraylist_clear (completion->partial_list); } /* @@ -206,9 +210,9 @@ gui_completion_stop (struct t_gui_completion *completion) completion->context = GUI_COMPLETION_NULL; completion->position = -1; - if (completion->partial_completion_list) + if (completion->partial_list->size > 0) { - gui_completion_partial_list_free_all (completion); + arraylist_clear (completion->partial_list); (void) hook_signal_send ("partial_completion", WEECHAT_HOOK_SIGNAL_STRING, NULL); } @@ -369,7 +373,9 @@ void gui_completion_list_add (struct t_gui_completion *completion, const char *word, int nick_completion, const char *where) { + struct t_gui_completion_word *completion_word; char buffer[512]; + int index; if (!word || !word[0]) return; @@ -380,17 +386,37 @@ gui_completion_list_add (struct t_gui_completion *completion, const char *word, || (!nick_completion && (string_strncasecmp (completion->base_word, word, utf8_strlen (completion->base_word)) == 0))) { - if (nick_completion && (completion->base_word_pos == 0)) - { - snprintf (buffer, sizeof (buffer), "%s%s", - word, CONFIG_STRING(config_completion_nick_completer)); - weelist_add (completion->completion_list, buffer, where, - (nick_completion) ? (void *)1 : (void *)0); - } - else + completion_word = malloc (sizeof (*completion_word)); + if (completion_word) { - weelist_add (completion->completion_list, word, where, - (nick_completion) ? (void *)1 : (void *)0); + completion_word->nick_completion = nick_completion; + completion_word->count = 0; + + index = -1; + if (strcmp (where, WEECHAT_LIST_POS_BEGINNING) == 0) + { + completion->list->sorted = 0; + index = 0; + } + else if (strcmp (where, WEECHAT_LIST_POS_END) == 0) + { + completion->list->sorted = 0; + index = -1; + } + + if (nick_completion && (completion->base_word_pos == 0)) + { + snprintf (buffer, sizeof (buffer), "%s%s", + word, + CONFIG_STRING(config_completion_nick_completer)); + completion_word->word = strdup (buffer); + arraylist_insert (completion->list, index, completion_word); + } + else + { + completion_word->word = strdup (word); + arraylist_insert (completion->list, index, completion_word); + } } } } @@ -842,28 +868,32 @@ gui_completion_find_context (struct t_gui_completion *completion, */ int -gui_completion_common_prefix_size (struct t_weelist *list, +gui_completion_common_prefix_size (struct t_arraylist *list, const char *utf_char) { - struct t_weelist_item *ptr_item; char *ptr_first_item, *ptr_char, *next_char; + struct t_gui_completion_word *ptr_completion_word; + int i; - ptr_first_item = list->items->data; + ptr_first_item = ((struct t_gui_completion_word *)(list->data[0]))->word; ptr_char = ptr_first_item; while (ptr_char && ptr_char[0]) { next_char = utf8_next_char (ptr_char); - for (ptr_item = list->items->next_item; ptr_item; - ptr_item = ptr_item->next_item) + for (i = 1; i < list->size; i++) { + ptr_completion_word = + (struct t_gui_completion_word *)(list->data[i]); if (!utf_char - || (utf8_charcasecmp (utf_char, ptr_item->data) == 0)) + || (utf8_charcasecmp (utf_char, + ptr_completion_word->word) == 0)) { - if ((ptr_item->data[ptr_char - ptr_first_item] == '\0') - || (utf8_charcasecmp (ptr_char, - ptr_item->data + (ptr_char - ptr_first_item)) != 0)) + if ((ptr_completion_word->word[ptr_char - ptr_first_item] == '\0') + || (utf8_charcasecmp ( + ptr_char, + ptr_completion_word->word + (ptr_char - ptr_first_item)) != 0)) { return ptr_char - ptr_first_item; } @@ -883,65 +913,78 @@ void gui_completion_partial_build_list (struct t_gui_completion *completion, int common_prefix_size) { - int char_size, items_count; + int i, char_size, items_count, index; char utf_char[16], *word; - struct t_weelist *weelist_temp; - struct t_weelist_item *ptr_item, *next_item; + struct t_gui_completion_word *ptr_completion_word, *new_completion_word; + struct t_arraylist *list_temp; - gui_completion_partial_list_free_all (completion); + arraylist_clear (completion->partial_list); - if (!completion->completion_list || !completion->completion_list->items) + if (!completion->list || (completion->list->size == 0)) return; - weelist_temp = weelist_new (); - if (!weelist_temp) + list_temp = arraylist_new (completion->list->size, 1, 0, + &gui_completion_word_compare_cb, NULL, + &gui_completion_word_free_cb, NULL); + if (!list_temp) return; - for (ptr_item = completion->completion_list->items; ptr_item; - ptr_item = ptr_item->next_item) + for (i = 0; i < completion->list->size; i++) { - weelist_add (weelist_temp, ptr_item->data + common_prefix_size, - WEECHAT_LIST_POS_END, NULL); + ptr_completion_word = + (struct t_gui_completion_word *)completion->list->data[i]; + new_completion_word = malloc (sizeof (*new_completion_word)); + if (new_completion_word) + { + new_completion_word->word = strdup ( + ptr_completion_word->word + common_prefix_size); + new_completion_word->nick_completion = 0; + new_completion_word->count = 0; + arraylist_add (list_temp, new_completion_word); + } } - while (weelist_temp->items) + while (list_temp->size > 0) { - char_size = utf8_char_size (weelist_temp->items->data); - memcpy (utf_char, weelist_temp->items->data, char_size); + ptr_completion_word = + (struct t_gui_completion_word *)list_temp->data[0]; + char_size = utf8_char_size (ptr_completion_word->word); + memcpy (utf_char, ptr_completion_word->word, char_size); utf_char[char_size] = '\0'; word = NULL; - common_prefix_size = gui_completion_common_prefix_size (weelist_temp, + common_prefix_size = gui_completion_common_prefix_size (list_temp, utf_char); if (common_prefix_size > 0) { - word = string_strndup (weelist_temp->items->data, + word = string_strndup (ptr_completion_word->word, common_prefix_size); } items_count = 0; - ptr_item = weelist_temp->items; - while (ptr_item) + index = 0; + while (index < list_temp->size) { - next_item = ptr_item->next_item; - - if (utf8_charcasecmp (utf_char, ptr_item->data) == 0) + ptr_completion_word = + (struct t_gui_completion_word *)list_temp->data[index]; + if (utf8_charcasecmp (utf_char, ptr_completion_word->word) == 0) { - weelist_remove (weelist_temp, ptr_item); + arraylist_remove (list_temp, index); items_count++; } - - ptr_item = next_item; + else + index++; } if (word) { - gui_completion_partial_list_add (completion, - word, - CONFIG_BOOLEAN(config_completion_partial_completion_count) ? - items_count : -1); + gui_completion_partial_list_add ( + completion, + word, + CONFIG_BOOLEAN(config_completion_partial_completion_count) ? + items_count : -1); free (word); } } - weelist_free (weelist_temp); + arraylist_free (list_temp); } /* @@ -952,8 +995,8 @@ void gui_completion_complete (struct t_gui_completion *completion) { int length, word_found_seen, other_completion, partial_completion; - int common_prefix_size, item_is_nick; - struct t_weelist_item *ptr_item, *ptr_item2; + int common_prefix_size, index, index2; + struct t_gui_completion_word *ptr_completion_word, *ptr_completion_word2; length = utf8_strlen (completion->base_word); word_found_seen = 0; @@ -977,21 +1020,12 @@ gui_completion_complete (struct t_gui_completion *completion) common_prefix_size = 0; if (partial_completion - && completion->completion_list && completion->completion_list->items) + && completion->list && (completion->list->size > 0)) { - common_prefix_size = gui_completion_common_prefix_size (completion->completion_list, + common_prefix_size = gui_completion_common_prefix_size (completion->list, NULL); } - ptr_item = NULL; - if (completion->completion_list) - { - if (completion->direction < 0) - ptr_item = completion->completion_list->last_item; - else - ptr_item = completion->completion_list->items; - } - if (partial_completion && completion->word_found && (utf8_strlen (completion->word_found) >= common_prefix_size)) @@ -999,59 +1033,68 @@ gui_completion_complete (struct t_gui_completion *completion) return; } - while (ptr_item) + index = -1; + if (completion->list) + { + if (completion->direction < 0) + index = completion->list->size - 1; + else + index = 0; + } + + while ((index >= 0) && (index < completion->list->size)) { - item_is_nick = ((long)(ptr_item->user_data) == 1); - if ((item_is_nick - && (gui_completion_nickncmp (completion->base_word, ptr_item->data, + ptr_completion_word = + (struct t_gui_completion_word *)(completion->list->data[index]); + if ((ptr_completion_word->nick_completion + && (gui_completion_nickncmp (completion->base_word, + ptr_completion_word->word, length) == 0)) - || ((!item_is_nick) - && (string_strncasecmp (completion->base_word, ptr_item->data, + || (!ptr_completion_word->nick_completion + && (string_strncasecmp (completion->base_word, + ptr_completion_word->word, length) == 0))) { if ((!completion->word_found) || word_found_seen) { if (completion->word_found) free (completion->word_found); - completion->word_found = strdup (ptr_item->data); - completion->word_found_is_nick = item_is_nick; - if (item_is_nick + completion->word_found = strdup (ptr_completion_word->word); + completion->word_found_is_nick = + ptr_completion_word->nick_completion; + if (ptr_completion_word->nick_completion && !CONFIG_BOOLEAN(config_completion_nick_add_space)) { completion->add_space = 0; } /* stop after first nick if user asked that */ - if (item_is_nick + if (ptr_completion_word->nick_completion && CONFIG_BOOLEAN(config_completion_nick_first_only)) { gui_completion_stop (completion); return; } - if (completion->direction < 0) - ptr_item2 = ptr_item->prev_item; - else - ptr_item2 = ptr_item->next_item; - - while (ptr_item2) + index2 = (completion->direction < 0) ? index - 1 : index + 1; + while ((index2 >= 0) && (index2 < completion->list->size)) { - if ((item_is_nick + ptr_completion_word2 = + (struct t_gui_completion_word *)(completion->list->data[index2]); + if ((ptr_completion_word->nick_completion && (gui_completion_nickncmp (completion->base_word, - ptr_item2->data, + ptr_completion_word2->word, length) == 0)) - || ((!item_is_nick) + || (!ptr_completion_word->nick_completion && (string_strncasecmp (completion->base_word, - ptr_item2->data, + ptr_completion_word2->word, length) == 0))) { other_completion++; } - if (completion->direction < 0) - ptr_item2 = ptr_item2->prev_item; - else - ptr_item2 = ptr_item2->next_item; + index2 = (completion->direction < 0) ? + index2 - 1 : index2 + 1; } if (other_completion == 0) @@ -1086,20 +1129,17 @@ gui_completion_complete (struct t_gui_completion *completion) return; } - gui_completion_partial_list_free_all (completion); + arraylist_clear (completion->partial_list); return; } other_completion++; } if (completion->word_found && - (strcmp (ptr_item->data, completion->word_found) == 0)) + (strcmp (ptr_completion_word->word, completion->word_found) == 0)) word_found_seen = 1; - if (completion->direction < 0) - ptr_item = ptr_item->prev_item; - else - ptr_item = ptr_item->next_item; + index = (completion->direction < 0) ? index - 1 : index + 1; } /* @@ -1124,7 +1164,7 @@ gui_completion_command (struct t_gui_completion *completion) { struct t_hook *ptr_hook; - if (!completion->completion_list->items) + if (completion->list->size == 0) { for (ptr_hook = weechat_hooks[HOOK_TYPE_COMMAND]; ptr_hook; ptr_hook = ptr_hook->next_hook) @@ -1154,18 +1194,19 @@ gui_completion_auto (struct t_gui_completion *completion) if ((completion->base_word[0] == '/') || (completion->base_word[0] == '~')) { - if (!completion->completion_list->items) + if (completion->list->size == 0) completion_list_add_filename_cb (NULL, NULL, NULL, completion); gui_completion_complete (completion); return; } /* use default template completion */ - if (!completion->completion_list->items) + if (completion->list->size == 0) { - gui_completion_build_list_template (completion, - CONFIG_STRING(config_completion_default_template), - NULL); + gui_completion_build_list_template ( + completion, + CONFIG_STRING(config_completion_default_template), + NULL); } gui_completion_complete (completion); } @@ -1205,7 +1246,7 @@ gui_completion_search (struct t_gui_completion *completion, int direction, gui_completion_command (completion); break; case GUI_COMPLETION_COMMAND_ARG: - if (completion->completion_list->items) + if (completion->list->size > 0) gui_completion_complete (completion); else { @@ -1286,24 +1327,23 @@ gui_completion_hdata_completion_cb (void *data, const char *hdata_name) HDATA_VAR(struct t_gui_completion, direction, INTEGER, 0, NULL, NULL); HDATA_VAR(struct t_gui_completion, add_space, INTEGER, 0, NULL, NULL); HDATA_VAR(struct t_gui_completion, force_partial_completion, INTEGER, 0, NULL, NULL); - HDATA_VAR(struct t_gui_completion, completion_list, POINTER, 0, NULL, NULL); + HDATA_VAR(struct t_gui_completion, list, POINTER, 0, NULL, NULL); HDATA_VAR(struct t_gui_completion, word_found, STRING, 0, NULL, NULL); HDATA_VAR(struct t_gui_completion, word_found_is_nick, INTEGER, 0, NULL, NULL); HDATA_VAR(struct t_gui_completion, position_replace, INTEGER, 0, NULL, NULL); HDATA_VAR(struct t_gui_completion, diff_size, INTEGER, 0, NULL, NULL); HDATA_VAR(struct t_gui_completion, diff_length, INTEGER, 0, NULL, NULL); - HDATA_VAR(struct t_gui_completion, partial_completion_list, POINTER, 0, NULL, "completion_partial"); - HDATA_VAR(struct t_gui_completion, last_partial_completion, POINTER, 0, NULL, "completion_partial"); + HDATA_VAR(struct t_gui_completion, partial_list, POINTER, 0, NULL, NULL); } return hdata; } /* - * Returns hdata for partial completion. + * Returns hdata for completion word. */ struct t_hdata * -gui_completion_hdata_completion_partial_cb (void *data, const char *hdata_name) +gui_completion_hdata_completion_word_cb (void *data, const char *hdata_name) { struct t_hdata *hdata; @@ -1314,23 +1354,41 @@ gui_completion_hdata_completion_partial_cb (void *data, const char *hdata_name) 0, 0, NULL, NULL); if (hdata) { - HDATA_VAR(struct t_gui_completion_partial, word, STRING, 0, NULL, NULL); - HDATA_VAR(struct t_gui_completion_partial, count, INTEGER, 0, NULL, NULL); - HDATA_VAR(struct t_gui_completion_partial, prev_item, POINTER, 0, NULL, hdata_name); - HDATA_VAR(struct t_gui_completion_partial, next_item, POINTER, 0, NULL, hdata_name); + HDATA_VAR(struct t_gui_completion_word, word, STRING, 0, NULL, NULL); + HDATA_VAR(struct t_gui_completion_word, nick_completion, CHAR, 0, NULL, NULL); + HDATA_VAR(struct t_gui_completion_word, count, INTEGER, 0, NULL, NULL); } return hdata; } /* + * Prints list of completion words in WeeChat log file (usually for crash dump). + */ + +void +gui_completion_list_words_print_log (struct t_arraylist *list, + const char *name) +{ + int i; + struct t_gui_completion_word *ptr_completion_word; + + for (i = 0; i < list->size; i++) + { + ptr_completion_word = (struct t_gui_completion_word *)(list->data[i]); + log_printf ("[%s (addr:0x%lx)]", name, ptr_completion_word); + log_printf (" word. . . . . . . . . . : '%s'", ptr_completion_word->word); + log_printf (" nicklist_completion . . : %d", ptr_completion_word->nick_completion); + log_printf (" count . . . . . . . . . : %d", ptr_completion_word->count); + } +} + +/* * Prints completion list in WeeChat log file (usually for crash dump). */ void gui_completion_print_log (struct t_gui_completion *completion) { - struct t_gui_completion_partial *ptr_item; - log_printf ("[completion (addr:0x%lx)]", completion); log_printf (" buffer. . . . . . . . . : 0x%lx", completion->buffer); log_printf (" context . . . . . . . . : %d", completion->context); @@ -1343,29 +1401,22 @@ gui_completion_print_log (struct t_gui_completion *completion) log_printf (" direction . . . . . . . : %d", completion->direction); log_printf (" add_space . . . . . . . : %d", completion->add_space); log_printf (" force_partial_completion: %d", completion->force_partial_completion); - log_printf (" completion_list . . . . : 0x%lx", completion->completion_list); + log_printf (" list. . . . . . . . . . : 0x%lx", completion->list); log_printf (" word_found. . . . . . . : '%s'", completion->word_found); log_printf (" word_found_is_nick. . . : %d", completion->word_found_is_nick); log_printf (" position_replace. . . . : %d", completion->position_replace); log_printf (" diff_size . . . . . . . : %d", completion->diff_size); log_printf (" diff_length . . . . . . : %d", completion->diff_length); - if (completion->completion_list) + if (completion->list) { log_printf (""); - weelist_print_log (completion->completion_list, - "completion list element"); + gui_completion_list_words_print_log (completion->list, + "completion word"); } - if (completion->partial_completion_list) + if (completion->partial_list) { log_printf (""); - for (ptr_item = completion->partial_completion_list; - ptr_item; ptr_item = ptr_item->next_item) - { - log_printf ("[partial completion item (addr:0x%lx)]", ptr_item); - log_printf (" word. . . . . . . . . . : '%s'", ptr_item->word); - log_printf (" count . . . . . . . . . : %d", ptr_item->count); - log_printf (" prev_item . . . . . . . : 0x%lx", ptr_item->prev_item); - log_printf (" next_item . . . . . . . : 0x%lx", ptr_item->next_item); - } + arraylist_print_log (completion->partial_list, + "partial completion word"); } } diff --git a/src/gui/gui-completion.h b/src/gui/gui-completion.h index 7192779de..7c1368937 100644 --- a/src/gui/gui-completion.h +++ b/src/gui/gui-completion.h @@ -25,12 +25,12 @@ #define GUI_COMPLETION_COMMAND_ARG 2 #define GUI_COMPLETION_AUTO 3 -struct t_gui_completion_partial +struct t_gui_completion_word { - char *word; /* (partial) word matching completion */ + char *word; /* word matching completion */ + char nick_completion; /* 1 if it is completion of a nick */ int count; /* number of matching items with this word */ - struct t_gui_completion_partial *prev_item; - struct t_gui_completion_partial *next_item; + /* (for partial completion) */ }; struct t_gui_completion @@ -49,7 +49,7 @@ struct t_gui_completion int force_partial_completion; /* force partial completion? */ /* for command argument completion */ - struct t_weelist *completion_list; /* data list for completion */ + struct t_arraylist *list; /* data list for completion */ /* completion found */ char *word_found; /* word found (to replace base word) */ @@ -59,8 +59,7 @@ struct t_gui_completion int diff_length; /* length difference (<= diff_size) */ /* partial completion */ - struct t_gui_completion_partial *partial_completion_list; - struct t_gui_completion_partial *last_partial_completion; + struct t_arraylist *partial_list; }; /* completion variables */ diff --git a/src/plugins/plugin-api.c b/src/plugins/plugin-api.c index ca3c5ed5d..661939251 100644 --- a/src/plugins/plugin-api.c +++ b/src/plugins/plugin-api.c @@ -1298,8 +1298,6 @@ plugin_api_init () &gui_buffer_hdata_buffer_visited_cb, NULL); hook_hdata (NULL, "completion", N_("structure with completion"), &gui_completion_hdata_completion_cb, NULL); - hook_hdata (NULL, "completion_partial", N_("structure with partial completion"), - &gui_completion_hdata_completion_partial_cb, NULL); hook_hdata (NULL, "config_file", N_("config file"), &config_file_hdata_config_file_cb, NULL); hook_hdata (NULL, "config_section", N_("config section"), diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4a9a9132c..19ff1003e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -30,6 +30,7 @@ include_directories(${CPPUTEST_INCLUDE_DIRS} ${PROJECT_BINARY_DIR} ${PROJECT_SOU # unit tests set(LIB_WEECHAT_UNIT_TESTS_SRC + unit/core/test-arraylist.cpp unit/core/test-eval.cpp unit/core/test-hashtable.cpp unit/core/test-hdata.cpp @@ -53,6 +54,8 @@ set(LIBS ${PROJECT_BINARY_DIR}/src/gui/curses/libweechat_gui_curses.a ${CMAKE_CURRENT_BINARY_DIR}/libweechat_ncurses_fake.a ${CMAKE_CURRENT_BINARY_DIR}/libweechat_unit_tests.a + # due to circular references, we must link two times with libweechat_core.a + ${PROJECT_BINARY_DIR}/src/core/libweechat_core.a ${EXTRA_LIBS} ${CURL_LIBRARIES} ${CPPUTEST_LIBRARIES}) diff --git a/tests/Makefile.am b/tests/Makefile.am index e5b2c39a1..5e04c5122 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -23,7 +23,8 @@ noinst_LIBRARIES = lib_ncurses_fake.a lib_weechat_unit_tests.a lib_ncurses_fake_a_SOURCES = ncurses-fake.c -lib_weechat_unit_tests_a_SOURCES = unit/core/test-eval.cpp \ +lib_weechat_unit_tests_a_SOURCES = unit/core/test-arraylist.cpp \ + unit/core/test-eval.cpp \ unit/core/test-hashtable.cpp \ unit/core/test-hdata.cpp \ unit/core/test-infolist.cpp \ @@ -35,15 +36,15 @@ lib_weechat_unit_tests_a_SOURCES = unit/core/test-eval.cpp \ noinst_PROGRAMS = tests -# Because of a linker bug, we have to link 2 times with lib_weechat_core.a +# Due to circular references, we must link two times with libweechat_core.a # (and it must be 2 different path/names to be kept by linker) tests_LDADD = ./../src/core/lib_weechat_core.a \ ../src/plugins/lib_weechat_plugins.a \ ../src/gui/lib_weechat_gui_common.a \ ../src/gui/curses/lib_weechat_gui_curses.a \ - ../src/core/lib_weechat_core.a \ lib_ncurses_fake.a \ lib_weechat_unit_tests.a \ + ../src/core/lib_weechat_core.a \ $(PLUGINS_LFLAGS) \ $(GCRYPT_LFLAGS) \ $(GNUTLS_LFLAGS) \ diff --git a/tests/tests.cpp b/tests/tests.cpp index 95e3e9b33..f134fed03 100644 --- a/tests/tests.cpp +++ b/tests/tests.cpp @@ -47,6 +47,7 @@ extern "C" #include "CppUTest/CommandLineTestRunner.h" /* import tests from libs */ +IMPORT_TEST_GROUP(Arraylist); IMPORT_TEST_GROUP(Eval); IMPORT_TEST_GROUP(Hashtable); IMPORT_TEST_GROUP(Hdata); diff --git a/tests/unit/core/test-arraylist.cpp b/tests/unit/core/test-arraylist.cpp new file mode 100644 index 000000000..4812dc6f7 --- /dev/null +++ b/tests/unit/core/test-arraylist.cpp @@ -0,0 +1,418 @@ +/* + * test-arraylist.cpp - test arraylist functions + * + * Copyright (C) 2014 Sébastien Helleu <flashcode@flashtux.org> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "CppUTest/TestHarness.h" + +extern "C" +{ +#include <string.h> +#include "src/core/wee-arraylist.h" +#include "src/core/wee-string.h" +} + +#define TEST_ARRAYLIST_ADD(__result, __value) \ + LONGS_EQUAL(__result, \ + arraylist_add (arraylist, (void *)(__value))); + +TEST_GROUP(Arraylist) +{ +}; + +/* + * Test callback comparing two arraylist elements. + * + * Returns: + * -1: element(pointer1) < element(pointer2) + * 0: element(pointer1) == element(pointer2) + * 1: element(pointer1) > element(pointer2) + */ + +int +test_cmp_cb (void *data, struct t_arraylist *arraylist, + void *pointer1, void *pointer2) +{ + if (!pointer1 || !pointer2) + return (pointer1) ? 1 : ((pointer2) ? -1 : 0); + + return string_strcasecmp ((const char *)pointer1, (const char *)pointer2); +} + +void +test_arraylist (int initial_size, int sorted, int allow_duplicates) +{ + struct t_arraylist *arraylist; + int i, index, index_insert, expected_pos; + const char *item_aaa = "aaa"; + const char *item_abc = "abc"; + const char *item_DEF = "DEF"; + const char *item_def = "def"; + const char *item_xxx = "xxx"; + + /* create arraylist */ + arraylist = arraylist_new (initial_size, + sorted, + allow_duplicates, + &test_cmp_cb, NULL, + NULL, NULL); + + /* check values after creation */ + CHECK(arraylist); + LONGS_EQUAL(0, arraylist->size); + LONGS_EQUAL(initial_size, arraylist->size_alloc); + LONGS_EQUAL(initial_size, arraylist->size_alloc_min); + if (initial_size > 0) + { + CHECK(arraylist->data); + for (i = 0; i < initial_size; i++) + { + POINTERS_EQUAL(NULL, arraylist->data[i]); + } + } + else + { + POINTERS_EQUAL(NULL, arraylist->data); + } + LONGS_EQUAL(sorted, arraylist->sorted); + LONGS_EQUAL(allow_duplicates, arraylist->allow_duplicates); + + /* check size */ + LONGS_EQUAL(0, arraylist_size (arraylist)); + + /* get element (this should always fail, the list is empty!) */ + POINTERS_EQUAL(NULL, arraylist_get (NULL, -1)); + POINTERS_EQUAL(NULL, arraylist_get (NULL, 0)); + POINTERS_EQUAL(NULL, arraylist_get (NULL, 1)); + POINTERS_EQUAL(NULL, arraylist_get (arraylist, -1)); + POINTERS_EQUAL(NULL, arraylist_get (arraylist, 0)); + POINTERS_EQUAL(NULL, arraylist_get (arraylist, 1)); + + /* search element (this should always fail, the list is empty!) */ + POINTERS_EQUAL(NULL, arraylist_search (NULL, NULL, NULL, NULL)); + POINTERS_EQUAL(NULL, arraylist_search (arraylist, NULL, NULL, NULL)); + POINTERS_EQUAL(NULL, + arraylist_search (NULL, (void *)item_abc, NULL, NULL)); + POINTERS_EQUAL(NULL, + arraylist_search (arraylist, (void *)item_abc, NULL, NULL)); + + /* invalid add of element */ + LONGS_EQUAL(-1, arraylist_add (NULL, NULL)); + + /* add some elements */ + if (sorted) + { + TEST_ARRAYLIST_ADD(0, item_xxx); + TEST_ARRAYLIST_ADD(0, NULL); + TEST_ARRAYLIST_ADD(1, item_def); + TEST_ARRAYLIST_ADD(1, item_DEF); + TEST_ARRAYLIST_ADD(1, item_abc); + } + else + { + TEST_ARRAYLIST_ADD(0, item_xxx); + TEST_ARRAYLIST_ADD(1, NULL); + TEST_ARRAYLIST_ADD(2, item_def); + TEST_ARRAYLIST_ADD((allow_duplicates) ? 3 : 2, item_DEF); + TEST_ARRAYLIST_ADD((allow_duplicates) ? 4 : 3, item_abc); + } + + /* + * arraylist is now: + * sorted: + * allow dup: [NULL, "abc", "DEF", "def", "xxx", (NULL)] + * no dup : [NULL, "abc", "DEF", "xxx"] + * not sorted: + * allow dup: ["xxx", NULL, "def", "DEF", "abc", (NULL)] + * no dup : ["xxx", NULL, "DEF", "abc"] + */ + + /* check size after adds */ + LONGS_EQUAL((allow_duplicates) ? 5 : 4, arraylist->size); + LONGS_EQUAL((allow_duplicates) ? 5 : 4, arraylist_size (arraylist)); + LONGS_EQUAL((allow_duplicates) ? 6 : 4, arraylist->size_alloc); + + /* check content after adds */ + if (sorted) + { + POINTERS_EQUAL(NULL, arraylist->data[0]); + STRCMP_EQUAL(item_abc, (const char *)arraylist->data[1]); + if (allow_duplicates) + { + STRCMP_EQUAL(item_DEF, (const char *)arraylist->data[2]); + STRCMP_EQUAL(item_def, (const char *)arraylist->data[3]); + STRCMP_EQUAL(item_xxx, (const char *)arraylist->data[4]); + POINTERS_EQUAL(NULL, arraylist->data[5]); + } + else + { + STRCMP_EQUAL(item_DEF, (const char *)arraylist->data[2]); + STRCMP_EQUAL(item_xxx, (const char *)arraylist->data[3]); + } + } + else + { + STRCMP_EQUAL(item_xxx, (const char *)arraylist->data[0]); + POINTERS_EQUAL(NULL, arraylist->data[1]); + if (allow_duplicates) + { + STRCMP_EQUAL(item_def, (const char *)arraylist->data[2]); + STRCMP_EQUAL(item_DEF, (const char *)arraylist->data[3]); + STRCMP_EQUAL(item_abc, (const char *)arraylist->data[4]); + POINTERS_EQUAL(NULL, arraylist->data[5]); + } + else + { + STRCMP_EQUAL(item_DEF, (const char *)arraylist->data[2]); + STRCMP_EQUAL(item_abc, (const char *)arraylist->data[3]); + } + } + + /* search elements */ + if (sorted) + { + /* search first element */ + POINTERS_EQUAL(NULL, arraylist_search (arraylist, NULL, + &index, &index_insert)); + LONGS_EQUAL(0, index); + LONGS_EQUAL(0, index_insert); + + /* search second element */ + POINTERS_EQUAL(item_abc, arraylist_search (arraylist, (void *)item_abc, + &index, &index_insert)); + LONGS_EQUAL(1, index); + LONGS_EQUAL(1, index_insert); + + /* search last element */ + POINTERS_EQUAL(item_xxx, + arraylist_search (arraylist, (void *)item_xxx, + &index, &index_insert)); + LONGS_EQUAL((allow_duplicates) ? 4 : 3, index); + LONGS_EQUAL(-1, index_insert); + + /* search non-existing element */ + POINTERS_EQUAL(NULL, + arraylist_search (arraylist, (void *)item_aaa, + &index, &index_insert)); + LONGS_EQUAL(-1, index); + LONGS_EQUAL(1, index_insert); + } + else + { + /* search first element */ + POINTERS_EQUAL(item_xxx, arraylist_search (arraylist, (void *)item_xxx, + &index, &index_insert)); + LONGS_EQUAL(0, index); + LONGS_EQUAL(-1, index_insert); + + /* search second element */ + POINTERS_EQUAL(NULL, arraylist_search (arraylist, NULL, + &index, &index_insert)); + LONGS_EQUAL(1, index); + LONGS_EQUAL(-1, index_insert); + + /* search last element */ + POINTERS_EQUAL(item_abc, + arraylist_search (arraylist, (void *)item_abc, + &index, &index_insert)); + LONGS_EQUAL((allow_duplicates) ? 4 : 3, index); + LONGS_EQUAL(-1, index_insert); + + /* search non-existing element */ + POINTERS_EQUAL(NULL, + arraylist_search (arraylist, (void *)item_aaa, + &index, &index_insert)); + LONGS_EQUAL(-1, index); + LONGS_EQUAL(-1, index_insert); + } + + /* invalid remove of elements */ + LONGS_EQUAL(-1, arraylist_remove (NULL, -1)); + LONGS_EQUAL(-1, arraylist_remove (arraylist, -1)); + LONGS_EQUAL(-1, arraylist_remove (NULL, 0)); + + /* remove the 3 first elements and check size after each remove */ + LONGS_EQUAL(0, arraylist_remove (arraylist, 0)); + LONGS_EQUAL((allow_duplicates) ? 4 : 3, arraylist->size); + LONGS_EQUAL((allow_duplicates) ? 4 : 3, arraylist_size (arraylist)); + LONGS_EQUAL((allow_duplicates) ? 6 : 4, arraylist->size_alloc); + LONGS_EQUAL(0, arraylist_remove (arraylist, 0)); + LONGS_EQUAL((allow_duplicates) ? 3 : 2, arraylist->size); + LONGS_EQUAL((allow_duplicates) ? 3 : 2, arraylist_size (arraylist)); + LONGS_EQUAL((allow_duplicates) ? 6 : 4, arraylist->size_alloc); + LONGS_EQUAL(0, arraylist_remove (arraylist, 0)); + LONGS_EQUAL((allow_duplicates) ? 2 : 1, arraylist->size); + LONGS_EQUAL((allow_duplicates) ? 2 : 1, arraylist_size (arraylist)); + LONGS_EQUAL((allow_duplicates) ? 3 : 2, arraylist->size_alloc); + + /* + * arraylist is now: + * sorted: + * allow dup: ["def", "xxx", (NULL)] + * no dup : ["xxx"] + * not sorted: + * allow dup: ["DEF", "abc", (NULL)] + * no dup : ["abc"] + */ + + /* check content after the 3 deletions */ + if (sorted) + { + if (allow_duplicates) + { + STRCMP_EQUAL(item_def, (const char *)arraylist->data[0]); + STRCMP_EQUAL(item_xxx, (const char *)arraylist->data[1]); + POINTERS_EQUAL(NULL, arraylist->data[2]); + } + else + { + STRCMP_EQUAL(item_xxx, (const char *)arraylist->data[0]); + } + } + else + { + if (allow_duplicates) + { + STRCMP_EQUAL(item_DEF, (const char *)arraylist->data[0]); + STRCMP_EQUAL(item_abc, (const char *)arraylist->data[1]); + POINTERS_EQUAL(NULL, arraylist->data[2]); + } + else + { + STRCMP_EQUAL(item_abc, (const char *)arraylist->data[0]); + } + } + + /* invalid insert of element */ + LONGS_EQUAL(-1, arraylist_insert (NULL, 0, NULL)); + + /* insert of one element */ + LONGS_EQUAL(0, arraylist_insert (arraylist, 0, (void *)item_aaa)); + + /* + * arraylist is now: + * sorted: + * allow dup: ["aaa", "def", "xxx", (NULL)] + * no dup : ["aaa", "xxx"] + * not sorted: + * allow dup: ["aaa", "DEF", "abc", (NULL)] + * no dup : ["aaa", "abc"] + */ + + /* check size after insert */ + LONGS_EQUAL((allow_duplicates) ? 3 : 2, arraylist->size); + LONGS_EQUAL((allow_duplicates) ? 3 : 2, arraylist_size (arraylist)); + LONGS_EQUAL((allow_duplicates) ? 3 : 2, arraylist->size_alloc); + + /* check content after the insert */ + if (sorted) + { + if (allow_duplicates) + { + STRCMP_EQUAL(item_aaa, (const char *)arraylist->data[0]); + STRCMP_EQUAL(item_def, (const char *)arraylist->data[1]); + STRCMP_EQUAL(item_xxx, (const char *)arraylist->data[2]); + } + else + { + STRCMP_EQUAL(item_aaa, (const char *)arraylist->data[0]); + STRCMP_EQUAL(item_xxx, (const char *)arraylist->data[1]); + } + } + else + { + if (allow_duplicates) + { + STRCMP_EQUAL(item_aaa, (const char *)arraylist->data[0]); + STRCMP_EQUAL(item_DEF, (const char *)arraylist->data[1]); + STRCMP_EQUAL(item_abc, (const char *)arraylist->data[2]); + } + else + { + STRCMP_EQUAL(item_aaa, (const char *)arraylist->data[0]); + STRCMP_EQUAL(item_abc, (const char *)arraylist->data[1]); + } + } + + /* clear arraylist */ + LONGS_EQUAL(0, arraylist_clear (NULL)); + LONGS_EQUAL(1, arraylist_clear (arraylist)); + + /* check size and data after clear */ + LONGS_EQUAL(0, arraylist->size); + LONGS_EQUAL(0, arraylist_size (arraylist)); + LONGS_EQUAL(initial_size, arraylist->size_alloc); + if (initial_size > 0) + { + CHECK(arraylist->data); + for (i = 0; i < initial_size; i++) + { + POINTERS_EQUAL(NULL, arraylist->data[i]); + } + } + else + { + POINTERS_EQUAL(NULL, arraylist->data); + } + + /* free arraylist */ + arraylist_free (arraylist); +} + +/* + * Tests functions: + * arraylist_new + * arraylist_size + * arraylist_get + * arraylist_search + * arraylist_insert + * arraylist_add + * arraylist_remove + * arraylist_clear + * arraylist_free + */ + +TEST(Arraylist, New) +{ + int initial_size, sorted, allow_duplicates; + + /* + * in order to create an arraylist, initial_size must be >= 0 and a + * comparison callback must be given + */ + POINTERS_EQUAL(NULL, + arraylist_new (-1, 0, 0, NULL, NULL, NULL, NULL)); + POINTERS_EQUAL(NULL, + arraylist_new (-1, 0, 0, &test_cmp_cb, NULL, NULL, NULL)); + POINTERS_EQUAL(NULL, + arraylist_new (0, 0, 0, NULL, NULL, NULL, NULL)); + + /* tests on arraylists */ + for (initial_size = 0; initial_size < 2; initial_size++) + { + for (sorted = 0; sorted < 2; sorted++) + { + for (allow_duplicates = 0; allow_duplicates < 2; + allow_duplicates++) + { + test_arraylist (initial_size, sorted, allow_duplicates); + } + } + } +} |