1
0
mirror of https://github.com/weechat/weechat.git synced 2026-06-25 20:36:38 +02:00
Files
weechat/src/core/wee-arraylist.c
T
Sébastien Helleu 2867996d1f core: fix search/insert of elements in sorted arraylist with duplicates
The pointer and index returned is now the first element found with the value
(with the lower index if there are many elements with same value).
And the index for insert is the last element with same value + 1
(the higher index + 1).
2014-10-11 15:47:09 +02:00

672 lines
17 KiB
C

/*
* 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 = end + 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 + 1;
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 + 1;
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 ((ret_index >= 0) && arraylist->allow_duplicates)
{
/*
* in case of duplicates in table, the index of element found
* is the first element with the value, and the index for
* insert is the last element with the value + 1
*/
start = ret_index - 1;
while (start >= 0)
{
rc = (arraylist->callback_cmp) (
arraylist->callback_cmp_data,
arraylist,
pointer,
arraylist->data[start]);
if (rc != 0)
break;
start--;
}
start++;
end = ret_index + 1;
while (end < arraylist->size)
{
rc = (arraylist->callback_cmp) (
arraylist->callback_cmp_data,
arraylist,
pointer,
arraylist->data[end]);
if (rc != 0)
break;
end++;
}
end--;
ret_index = start;
ret_index_insert = end + 1;
ret_pointer = arraylist->data[start];
}
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);
}