Merge branch 'arraylist'

This commit is contained in:
Sébastien Helleu 2014-10-05 08:35:17 +02:00
commit f53baf628e
15 changed files with 1388 additions and 210 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 \

632
src/core/wee-arraylist.c Normal file
View File

@ -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);
}

64
src/core/wee-arraylist.h Normal file
View File

@ -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 */

View File

@ -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

View File

@ -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, " ");
}
}

View File

@ -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"
@ -50,6 +51,48 @@ int gui_completion_freeze = 0; /* 1 to freeze completions (do not */
/* stop partial completion on key) */
/*
* 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))
completion_word = malloc (sizeof (*completion_word));
if (completion_word)
{
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
{
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)
{
item_is_nick = ((long)(ptr_item->user_data) == 1);
if ((item_is_nick
&& (gui_completion_nickncmp (completion->base_word, ptr_item->data,
if (completion->direction < 0)
index = completion->list->size - 1;
else
index = 0;
}
while ((index >= 0) && (index < completion->list->size))
{
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,14 +1354,34 @@ 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).
*/
@ -1329,8 +1389,6 @@ gui_completion_hdata_completion_partial_cb (void *data, const char *hdata_name)
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");
}
}

View File

@ -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 */

View File

@ -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"),

View File

@ -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})

View File

@ -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) \

View File

@ -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);

View File

@ -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);
}
}
}
}