mirror of
https://github.com/weechat/weechat.git
synced 2026-06-25 20:36:38 +02:00
2867996d1f
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).
672 lines
17 KiB
C
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);
|
|
}
|