1356 lines
38 KiB
C
1356 lines
38 KiB
C
/*
|
|
* wee-hashtable.c - implementation of hashtable
|
|
*
|
|
* Copyright (C) 2010-2020 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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
#include "weechat.h"
|
|
#include "wee-hashtable.h"
|
|
#include "wee-infolist.h"
|
|
#include "wee-list.h"
|
|
#include "wee-log.h"
|
|
#include "wee-string.h"
|
|
#include "../plugins/plugin.h"
|
|
|
|
|
|
char *hashtable_type_string[HASHTABLE_NUM_TYPES] =
|
|
{ WEECHAT_HASHTABLE_INTEGER, WEECHAT_HASHTABLE_STRING,
|
|
WEECHAT_HASHTABLE_POINTER, WEECHAT_HASHTABLE_BUFFER,
|
|
WEECHAT_HASHTABLE_TIME };
|
|
|
|
|
|
/*
|
|
* Searches for a hashtable type.
|
|
*
|
|
* Returns index of type in enum t_hashtable_type, -1 if type is not found.
|
|
*/
|
|
|
|
int
|
|
hashtable_get_type (const char *type)
|
|
{
|
|
int i;
|
|
|
|
if (!type)
|
|
return -1;
|
|
|
|
for (i = 0; i < HASHTABLE_NUM_TYPES; i++)
|
|
{
|
|
if (string_strcasecmp (hashtable_type_string[i], type) == 0)
|
|
return i;
|
|
}
|
|
|
|
/* type not found */
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Hashes a string using a variant of djb2 hash.
|
|
*
|
|
* Returns the hash of the string.
|
|
*/
|
|
|
|
unsigned long long
|
|
hashtable_hash_key_djb2 (const char *string)
|
|
{
|
|
uint64_t hash;
|
|
const char *ptr_string;
|
|
|
|
hash = 5381;
|
|
for (ptr_string = string; ptr_string[0]; ptr_string++)
|
|
{
|
|
hash ^= (hash << 5) + (hash >> 2) + (int)(ptr_string[0]);
|
|
}
|
|
|
|
return hash;
|
|
}
|
|
|
|
/*
|
|
* Hashes a key (default callback).
|
|
*
|
|
* Returns the hash of the key, depending on the type.
|
|
*/
|
|
|
|
unsigned long long
|
|
hashtable_hash_key_default_cb (struct t_hashtable *hashtable, const void *key)
|
|
{
|
|
unsigned long long hash;
|
|
|
|
hash = 0;
|
|
|
|
switch (hashtable->type_keys)
|
|
{
|
|
case HASHTABLE_INTEGER:
|
|
hash = (unsigned long long)(*((int *)key));
|
|
break;
|
|
case HASHTABLE_STRING:
|
|
hash = hashtable_hash_key_djb2 ((const char *)key);
|
|
break;
|
|
case HASHTABLE_POINTER:
|
|
hash = (unsigned long long)((unsigned long)((void *)key));
|
|
break;
|
|
case HASHTABLE_BUFFER:
|
|
break;
|
|
case HASHTABLE_TIME:
|
|
hash = (unsigned long long)(*((time_t *)key));
|
|
break;
|
|
case HASHTABLE_NUM_TYPES:
|
|
break;
|
|
}
|
|
|
|
return hash;
|
|
}
|
|
|
|
/*
|
|
* Compares two keys (default callback).
|
|
*
|
|
* Returns:
|
|
* < 0: key1 < key2
|
|
* 0: key1 == key2
|
|
* > 0: key1 > key2
|
|
*/
|
|
|
|
int
|
|
hashtable_keycmp_default_cb (struct t_hashtable *hashtable,
|
|
const void *key1, const void *key2)
|
|
{
|
|
int rc;
|
|
|
|
/* make C compiler happy */
|
|
(void) hashtable;
|
|
|
|
rc = 0;
|
|
|
|
switch (hashtable->type_keys)
|
|
{
|
|
case HASHTABLE_INTEGER:
|
|
if (*((int *)key1) < *((int *)key2))
|
|
rc = -1;
|
|
else if (*((int *)key1) > *((int *)key2))
|
|
rc = 1;
|
|
break;
|
|
case HASHTABLE_STRING:
|
|
rc = strcmp ((const char *)key1, (const char *)key2);
|
|
break;
|
|
case HASHTABLE_POINTER:
|
|
if (key1 < key2)
|
|
rc = -1;
|
|
else if (key1 > key2)
|
|
rc = 1;
|
|
break;
|
|
case HASHTABLE_BUFFER:
|
|
break;
|
|
case HASHTABLE_TIME:
|
|
if (*((time_t *)key1) < *((time_t *)key2))
|
|
rc = -1;
|
|
else if (*((time_t *)key1) > *((time_t *)key2))
|
|
rc = 1;
|
|
break;
|
|
case HASHTABLE_NUM_TYPES:
|
|
break;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* Creates a new hashtable.
|
|
*
|
|
* The size is NOT a limit for number of items in hashtable. It is the size of
|
|
* internal array to store hashed keys: a high value uses more memory, but has
|
|
* better performance because this reduces the collisions of hashed keys and
|
|
* then reduces length of linked lists.
|
|
*
|
|
* Returns pointer to new hashtable, NULL if error.
|
|
*/
|
|
|
|
struct t_hashtable *
|
|
hashtable_new (int size,
|
|
const char *type_keys, const char *type_values,
|
|
t_hashtable_hash_key *callback_hash_key,
|
|
t_hashtable_keycmp *callback_keycmp)
|
|
{
|
|
struct t_hashtable *new_hashtable;
|
|
int i, type_keys_int, type_values_int;
|
|
|
|
if (size <= 0)
|
|
return NULL;
|
|
|
|
type_keys_int = hashtable_get_type (type_keys);
|
|
if (type_keys_int < 0)
|
|
return NULL;
|
|
type_values_int = hashtable_get_type (type_values);
|
|
if (type_values_int < 0)
|
|
return NULL;
|
|
|
|
/* the two callbacks are mandatory if type of keys is "buffer" */
|
|
if ((type_keys_int == HASHTABLE_BUFFER) && (!callback_hash_key || !callback_keycmp))
|
|
return NULL;
|
|
|
|
new_hashtable = malloc (sizeof (*new_hashtable));
|
|
if (new_hashtable)
|
|
{
|
|
new_hashtable->size = size;
|
|
new_hashtable->type_keys = type_keys_int;
|
|
new_hashtable->type_values = type_values_int;
|
|
new_hashtable->htable = malloc (size * sizeof (*(new_hashtable->htable)));
|
|
new_hashtable->keys_values = NULL;
|
|
if (!new_hashtable->htable)
|
|
{
|
|
free (new_hashtable);
|
|
return NULL;
|
|
}
|
|
for (i = 0; i < size; i++)
|
|
{
|
|
new_hashtable->htable[i] = NULL;
|
|
}
|
|
new_hashtable->items_count = 0;
|
|
|
|
new_hashtable->callback_hash_key = (callback_hash_key) ?
|
|
callback_hash_key : &hashtable_hash_key_default_cb;
|
|
new_hashtable->callback_keycmp = (callback_keycmp) ?
|
|
callback_keycmp : &hashtable_keycmp_default_cb;
|
|
|
|
new_hashtable->callback_free_key = NULL;
|
|
new_hashtable->callback_free_value = NULL;
|
|
}
|
|
return new_hashtable;
|
|
}
|
|
|
|
/*
|
|
* Allocates space for a key or value.
|
|
*/
|
|
|
|
void
|
|
hashtable_alloc_type (enum t_hashtable_type type,
|
|
const void *value, int size_value,
|
|
void **pointer, int *size)
|
|
{
|
|
switch (type)
|
|
{
|
|
case HASHTABLE_INTEGER:
|
|
if (value)
|
|
{
|
|
*pointer = malloc (sizeof (int));
|
|
if (*pointer)
|
|
*((int *)(*pointer)) = *((int *)value);
|
|
}
|
|
else
|
|
*pointer = NULL;
|
|
*size = (*pointer) ? sizeof (int) : 0;
|
|
break;
|
|
case HASHTABLE_STRING:
|
|
*pointer = (value) ? strdup ((const char *)value) : NULL;
|
|
*size = (*pointer) ? strlen (*pointer) + 1 : 0;
|
|
break;
|
|
case HASHTABLE_POINTER:
|
|
*pointer = (void *)value;
|
|
*size = sizeof (void *);
|
|
break;
|
|
case HASHTABLE_BUFFER:
|
|
if (value && (size_value > 0))
|
|
{
|
|
*pointer = malloc (size_value);
|
|
if (*pointer)
|
|
memcpy (*pointer, value, size_value);
|
|
}
|
|
else
|
|
*pointer = NULL;
|
|
*size = (*pointer) ? size_value : 0;
|
|
break;
|
|
case HASHTABLE_TIME:
|
|
if (value)
|
|
{
|
|
*pointer = malloc (sizeof (time_t));
|
|
if (*pointer)
|
|
*((time_t *)(*pointer)) = *((time_t *)value);
|
|
}
|
|
else
|
|
*pointer = NULL;
|
|
*size = (*pointer) ? sizeof (time_t) : 0;
|
|
break;
|
|
case HASHTABLE_NUM_TYPES:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Frees space used by a key.
|
|
*/
|
|
|
|
void
|
|
hashtable_free_key (struct t_hashtable *hashtable,
|
|
struct t_hashtable_item *item)
|
|
{
|
|
if (hashtable->callback_free_key)
|
|
{
|
|
(void) (hashtable->callback_free_key) (hashtable,
|
|
item->key);
|
|
}
|
|
else
|
|
{
|
|
switch (hashtable->type_keys)
|
|
{
|
|
case HASHTABLE_INTEGER:
|
|
case HASHTABLE_STRING:
|
|
case HASHTABLE_BUFFER:
|
|
case HASHTABLE_TIME:
|
|
if (item->key)
|
|
free (item->key);
|
|
break;
|
|
case HASHTABLE_POINTER:
|
|
break;
|
|
case HASHTABLE_NUM_TYPES:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Frees space used by a value.
|
|
*/
|
|
|
|
void
|
|
hashtable_free_value (struct t_hashtable *hashtable,
|
|
struct t_hashtable_item *item)
|
|
{
|
|
if (hashtable->callback_free_value)
|
|
{
|
|
(void) (hashtable->callback_free_value) (hashtable,
|
|
item->key,
|
|
item->value);
|
|
}
|
|
else
|
|
{
|
|
switch (hashtable->type_values)
|
|
{
|
|
case HASHTABLE_INTEGER:
|
|
case HASHTABLE_STRING:
|
|
case HASHTABLE_BUFFER:
|
|
case HASHTABLE_TIME:
|
|
if (item->value)
|
|
free (item->value);
|
|
break;
|
|
case HASHTABLE_POINTER:
|
|
break;
|
|
case HASHTABLE_NUM_TYPES:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Sets value for a key in hashtable.
|
|
*
|
|
* The size arguments are used only for type "buffer".
|
|
*
|
|
* Returns pointer to item created/updated, NULL if error.
|
|
*/
|
|
|
|
struct t_hashtable_item *
|
|
hashtable_set_with_size (struct t_hashtable *hashtable,
|
|
const void *key, int key_size,
|
|
const void *value, int value_size)
|
|
{
|
|
unsigned long long hash;
|
|
struct t_hashtable_item *ptr_item, *pos_item, *new_item;
|
|
|
|
if (!hashtable || !key
|
|
|| ((hashtable->type_keys == HASHTABLE_BUFFER) && (key_size <= 0))
|
|
|| ((hashtable->type_values == HASHTABLE_BUFFER) && (value_size <= 0)))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/* search position for item in hashtable */
|
|
hash = hashtable->callback_hash_key (hashtable, key) % hashtable->size;
|
|
pos_item = NULL;
|
|
for (ptr_item = hashtable->htable[hash];
|
|
ptr_item
|
|
&& ((int)(hashtable->callback_keycmp) (hashtable, key, ptr_item->key) > 0);
|
|
ptr_item = ptr_item->next_item)
|
|
{
|
|
pos_item = ptr_item;
|
|
}
|
|
|
|
/* replace value if item is already in hashtable */
|
|
if (ptr_item && (hashtable->callback_keycmp (hashtable, key, ptr_item->key) == 0))
|
|
{
|
|
hashtable_free_value (hashtable, ptr_item);
|
|
hashtable_alloc_type (hashtable->type_values,
|
|
value, value_size,
|
|
&ptr_item->value, &ptr_item->value_size);
|
|
return ptr_item;
|
|
}
|
|
|
|
/* create new item */
|
|
new_item = malloc (sizeof (*new_item));
|
|
if (!new_item)
|
|
return NULL;
|
|
|
|
/* set key and value */
|
|
hashtable_alloc_type (hashtable->type_keys,
|
|
key, key_size,
|
|
&new_item->key, &new_item->key_size);
|
|
hashtable_alloc_type (hashtable->type_values,
|
|
value, value_size,
|
|
&new_item->value, &new_item->value_size);
|
|
|
|
/* add item */
|
|
if (pos_item)
|
|
{
|
|
/* insert item after position found */
|
|
new_item->prev_item = pos_item;
|
|
new_item->next_item = pos_item->next_item;
|
|
if (pos_item->next_item)
|
|
(pos_item->next_item)->prev_item = new_item;
|
|
pos_item->next_item = new_item;
|
|
}
|
|
else
|
|
{
|
|
/* insert item at beginning of list */
|
|
new_item->prev_item = NULL;
|
|
new_item->next_item = hashtable->htable[hash];
|
|
if (hashtable->htable[hash])
|
|
(hashtable->htable[hash])->prev_item = new_item;
|
|
hashtable->htable[hash] = new_item;
|
|
}
|
|
|
|
hashtable->items_count++;
|
|
|
|
return new_item;
|
|
}
|
|
|
|
/*
|
|
* Sets value for a key in hashtable.
|
|
*
|
|
* Note: this function can be called *only* if key AND value are *not* of type
|
|
* "buffer".
|
|
*
|
|
* Returns pointer to item created/updated, NULL if error.
|
|
*/
|
|
|
|
struct t_hashtable_item *
|
|
hashtable_set (struct t_hashtable *hashtable,
|
|
const void *key, const void *value)
|
|
{
|
|
return hashtable_set_with_size (hashtable, key, 0, value, 0);
|
|
}
|
|
|
|
/*
|
|
* Searches for an item in hashtable.
|
|
*
|
|
* If hash is non NULL, then it is set with hash value of key (even if key is
|
|
* not found).
|
|
*/
|
|
|
|
struct t_hashtable_item *
|
|
hashtable_get_item (struct t_hashtable *hashtable, const void *key,
|
|
unsigned long long *hash)
|
|
{
|
|
unsigned long long key_hash;
|
|
struct t_hashtable_item *ptr_item;
|
|
|
|
if (!hashtable || !key)
|
|
return NULL;
|
|
|
|
key_hash = hashtable->callback_hash_key (hashtable, key) % hashtable->size;
|
|
if (hash)
|
|
*hash = key_hash;
|
|
for (ptr_item = hashtable->htable[key_hash];
|
|
ptr_item && hashtable->callback_keycmp (hashtable, key, ptr_item->key) > 0;
|
|
ptr_item = ptr_item->next_item)
|
|
{
|
|
}
|
|
|
|
if (ptr_item
|
|
&& (hashtable->callback_keycmp (hashtable, key, ptr_item->key) == 0))
|
|
{
|
|
return ptr_item;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Gets value for a key in hashtable.
|
|
*
|
|
* Returns pointer to value for key, NULL if key is not found.
|
|
*/
|
|
|
|
void *
|
|
hashtable_get (struct t_hashtable *hashtable, const void *key)
|
|
{
|
|
struct t_hashtable_item *ptr_item;
|
|
|
|
ptr_item = hashtable_get_item (hashtable, key, NULL);
|
|
|
|
return (ptr_item) ? ptr_item->value : NULL;
|
|
}
|
|
|
|
/*
|
|
* Checks if a key exists in the hashtable.
|
|
*
|
|
* Returns:
|
|
* 1: key exists
|
|
* 0: key does not exist
|
|
*/
|
|
|
|
int
|
|
hashtable_has_key (struct t_hashtable *hashtable, const void *key)
|
|
{
|
|
return (hashtable_get_item (hashtable, key, NULL) != NULL) ? 1 : 0;
|
|
}
|
|
|
|
/*
|
|
* Converts a value (from any type) to a string.
|
|
*
|
|
* Returns pointer to a static buffer (for type string, returns pointer to
|
|
* string itself), which must be used immediately, it is overwritten by
|
|
* subsequent calls to this function.
|
|
*/
|
|
|
|
const char *
|
|
hashtable_to_string (enum t_hashtable_type type, const void *value)
|
|
{
|
|
static char str_value[128];
|
|
|
|
switch (type)
|
|
{
|
|
case HASHTABLE_INTEGER:
|
|
snprintf (str_value, sizeof (str_value), "%d", *((int *)value));
|
|
return str_value;
|
|
break;
|
|
case HASHTABLE_STRING:
|
|
return (const char *)value;
|
|
break;
|
|
case HASHTABLE_POINTER:
|
|
case HASHTABLE_BUFFER:
|
|
snprintf (str_value, sizeof (str_value),
|
|
"0x%lx", (unsigned long)value);
|
|
return str_value;
|
|
break;
|
|
case HASHTABLE_TIME:
|
|
snprintf (str_value, sizeof (str_value),
|
|
"%lld", (long long)(*((time_t *)value)));
|
|
return str_value;
|
|
break;
|
|
case HASHTABLE_NUM_TYPES:
|
|
break;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Calls a function on all hashtable entries.
|
|
*/
|
|
|
|
void
|
|
hashtable_map (struct t_hashtable *hashtable,
|
|
t_hashtable_map *callback_map,
|
|
void *callback_map_data)
|
|
{
|
|
int i;
|
|
struct t_hashtable_item *ptr_item, *ptr_next_item;
|
|
|
|
if (!hashtable)
|
|
return;
|
|
|
|
for (i = 0; i < hashtable->size; i++)
|
|
{
|
|
ptr_item = hashtable->htable[i];
|
|
while (ptr_item)
|
|
{
|
|
ptr_next_item = ptr_item->next_item;
|
|
|
|
(void) (callback_map) (callback_map_data,
|
|
hashtable,
|
|
ptr_item->key,
|
|
ptr_item->value);
|
|
|
|
ptr_item = ptr_next_item;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Calls a function on all hashtable entries (sends keys and values as strings).
|
|
*/
|
|
|
|
void
|
|
hashtable_map_string (struct t_hashtable *hashtable,
|
|
t_hashtable_map_string *callback_map,
|
|
void *callback_map_data)
|
|
{
|
|
int i;
|
|
struct t_hashtable_item *ptr_item, *ptr_next_item;
|
|
const char *str_key, *str_value;
|
|
char *key, *value;
|
|
|
|
if (!hashtable)
|
|
return;
|
|
|
|
for (i = 0; i < hashtable->size; i++)
|
|
{
|
|
ptr_item = hashtable->htable[i];
|
|
while (ptr_item)
|
|
{
|
|
ptr_next_item = ptr_item->next_item;
|
|
|
|
str_key = hashtable_to_string (hashtable->type_keys,
|
|
ptr_item->key);
|
|
key = (str_key) ? strdup (str_key) : NULL;
|
|
|
|
str_value = hashtable_to_string (hashtable->type_values,
|
|
ptr_item->value);
|
|
value = (str_value) ? strdup (str_value) : NULL;
|
|
|
|
(void) (callback_map) (callback_map_data,
|
|
hashtable,
|
|
key,
|
|
value);
|
|
|
|
if (key)
|
|
free (key);
|
|
if (value)
|
|
free (value);
|
|
|
|
ptr_item = ptr_next_item;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Duplicates key/value in another hashtable (callback called for each variable
|
|
* in hashtable).
|
|
*/
|
|
|
|
void
|
|
hashtable_duplicate_map_cb (void *data,
|
|
struct t_hashtable *hashtable,
|
|
const void *key, const void *value)
|
|
{
|
|
struct t_hashtable *hashtable2;
|
|
|
|
/* make C compiler happy */
|
|
(void) hashtable;
|
|
|
|
hashtable2 = (struct t_hashtable *)data;
|
|
if (hashtable2)
|
|
hashtable_set (hashtable2, key, value);
|
|
}
|
|
|
|
/*
|
|
* Duplicates a hashtable.
|
|
*
|
|
* Returns pointer to new hashtable, NULL if error.
|
|
*/
|
|
|
|
struct t_hashtable *
|
|
hashtable_dup (struct t_hashtable *hashtable)
|
|
{
|
|
struct t_hashtable *new_hashtable;
|
|
|
|
new_hashtable = hashtable_new (hashtable->size,
|
|
hashtable_type_string[hashtable->type_keys],
|
|
hashtable_type_string[hashtable->type_values],
|
|
hashtable->callback_hash_key,
|
|
hashtable->callback_keycmp);
|
|
if (new_hashtable)
|
|
{
|
|
new_hashtable->callback_free_key = hashtable->callback_free_key;
|
|
new_hashtable->callback_free_value = hashtable->callback_free_value;
|
|
hashtable_map (hashtable,
|
|
&hashtable_duplicate_map_cb,
|
|
new_hashtable);
|
|
}
|
|
|
|
return new_hashtable;
|
|
}
|
|
|
|
/*
|
|
* Builds sorted list of keys (callback called for each variable in hashtable).
|
|
*/
|
|
|
|
void
|
|
hashtable_get_list_keys_map_cb (void *data,
|
|
struct t_hashtable *hashtable,
|
|
const void *key, const void *value)
|
|
{
|
|
struct t_weelist *list;
|
|
|
|
/* make C compiler happy */
|
|
(void) hashtable;
|
|
(void) value;
|
|
|
|
list = (struct t_weelist *)data;
|
|
|
|
weelist_add (list, hashtable_to_string (hashtable->type_keys, key),
|
|
WEECHAT_LIST_POS_SORT, NULL);
|
|
}
|
|
|
|
/*
|
|
* Gets list with sorted keys of hashtable.
|
|
*
|
|
* Note: list must be freed after use.
|
|
*/
|
|
|
|
struct t_weelist *
|
|
hashtable_get_list_keys (struct t_hashtable *hashtable)
|
|
{
|
|
struct t_weelist *weelist;
|
|
|
|
weelist = weelist_new ();
|
|
if (weelist)
|
|
hashtable_map (hashtable, &hashtable_get_list_keys_map_cb, weelist);
|
|
return weelist;
|
|
}
|
|
|
|
/*
|
|
* Gets a hashtable property as integer.
|
|
*/
|
|
|
|
int
|
|
hashtable_get_integer (struct t_hashtable *hashtable, const char *property)
|
|
{
|
|
if (hashtable && property)
|
|
{
|
|
if (string_strcasecmp (property, "size") == 0)
|
|
return hashtable->size;
|
|
else if (string_strcasecmp (property, "items_count") == 0)
|
|
return hashtable->items_count;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Computes length of all keys (callback called for each variable in hashtable).
|
|
*/
|
|
|
|
void
|
|
hashtable_compute_length_keys_cb (void *data,
|
|
struct t_hashtable *hashtable,
|
|
const void *key, const void *value)
|
|
{
|
|
const char *str_key;
|
|
int *length;
|
|
|
|
/* make C compiler happy */
|
|
(void) value;
|
|
|
|
length = (int *)data;
|
|
|
|
str_key = hashtable_to_string (hashtable->type_keys, key);
|
|
if (str_key)
|
|
*length += strlen (str_key) + 1;
|
|
}
|
|
|
|
/*
|
|
* Computes length of all values (callback called for each variable in
|
|
* hashtable).
|
|
*/
|
|
|
|
void
|
|
hashtable_compute_length_values_cb (void *data,
|
|
struct t_hashtable *hashtable,
|
|
const void *key, const void *value)
|
|
{
|
|
const char *str_value;
|
|
int *length;
|
|
|
|
/* make C compiler happy */
|
|
(void) key;
|
|
|
|
length = (int *)data;
|
|
|
|
if (value)
|
|
{
|
|
str_value = hashtable_to_string (hashtable->type_values, value);
|
|
if (str_value)
|
|
*length += strlen (str_value) + 1;
|
|
}
|
|
else
|
|
{
|
|
*length += strlen ("(null)") + 1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Computes length of all keys + values (callback called for each variable in
|
|
* hashtable).
|
|
*/
|
|
|
|
void
|
|
hashtable_compute_length_keys_values_cb (void *data,
|
|
struct t_hashtable *hashtable,
|
|
const void *key, const void *value)
|
|
{
|
|
hashtable_compute_length_keys_cb (data, hashtable, key, value);
|
|
hashtable_compute_length_values_cb (data, hashtable, key, value);
|
|
}
|
|
|
|
/*
|
|
* Builds a string with all keys (callback called for each variable in
|
|
* hashtable).
|
|
*/
|
|
|
|
void
|
|
hashtable_build_string_keys_cb (void *data,
|
|
struct t_hashtable *hashtable,
|
|
const void *key, const void *value)
|
|
{
|
|
const char *str_key;
|
|
char *str;
|
|
|
|
/* make C compiler happy */
|
|
(void) value;
|
|
|
|
str = (char *)data;
|
|
|
|
if (str[0])
|
|
strcat (str, ",");
|
|
|
|
str_key = hashtable_to_string (hashtable->type_keys, key);
|
|
if (str_key)
|
|
strcat (str, str_key);
|
|
}
|
|
|
|
/*
|
|
* Builds a string with all values (callback called for each variable in
|
|
* hashtable).
|
|
*/
|
|
|
|
void
|
|
hashtable_build_string_values_cb (void *data,
|
|
struct t_hashtable *hashtable,
|
|
const void *key, const void *value)
|
|
{
|
|
const char *str_value;
|
|
char *str;
|
|
|
|
/* make C compiler happy */
|
|
(void) key;
|
|
|
|
str = (char *)data;
|
|
|
|
if (str[0])
|
|
strcat (str, ",");
|
|
|
|
if (value)
|
|
{
|
|
str_value = hashtable_to_string (hashtable->type_values, value);
|
|
if (str_value)
|
|
strcat (str, str_value);
|
|
}
|
|
else
|
|
{
|
|
strcat (str, "(null)");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Builds a string with all keys + values (callback called for each variable in
|
|
* hashtable).
|
|
*/
|
|
|
|
void
|
|
hashtable_build_string_keys_values_cb (void *data,
|
|
struct t_hashtable *hashtable,
|
|
const void *key, const void *value)
|
|
{
|
|
const char *str_key, *str_value;
|
|
char *str;
|
|
|
|
str = (char *)data;
|
|
|
|
if (str[0])
|
|
strcat (str, ",");
|
|
|
|
str_key = hashtable_to_string (hashtable->type_keys, key);
|
|
if (str_key)
|
|
strcat (str, str_key);
|
|
|
|
strcat (str, ":");
|
|
|
|
if (value)
|
|
{
|
|
str_value = hashtable_to_string (hashtable->type_values, value);
|
|
if (str_value)
|
|
strcat (str, str_value);
|
|
}
|
|
else
|
|
{
|
|
strcat (str, "(null)");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Gets keys and/or values of hashtable as string.
|
|
*
|
|
* Returns a string with one of these formats:
|
|
* if keys == 1 and values == 0: "key1,key2,key3"
|
|
* if keys == 0 and values == 1: "value1,value2,value3"
|
|
* if keys == 1 and values == 1: "key1:value1,key2:value2,key3:value3"
|
|
*/
|
|
|
|
const char *
|
|
hashtable_get_keys_values (struct t_hashtable *hashtable,
|
|
int keys, int sort_keys, int values)
|
|
{
|
|
int length;
|
|
struct t_weelist *list_keys;
|
|
struct t_weelist_item *ptr_item;
|
|
|
|
if (hashtable->keys_values)
|
|
{
|
|
free (hashtable->keys_values);
|
|
hashtable->keys_values = NULL;
|
|
}
|
|
|
|
/* first compute length of string */
|
|
length = 0;
|
|
hashtable_map (hashtable,
|
|
(keys && values) ? &hashtable_compute_length_keys_values_cb :
|
|
((keys) ? &hashtable_compute_length_keys_cb :
|
|
&hashtable_compute_length_values_cb),
|
|
&length);
|
|
if (length == 0)
|
|
return hashtable->keys_values;
|
|
|
|
/* build string */
|
|
hashtable->keys_values = malloc (length + 1);
|
|
if (!hashtable->keys_values)
|
|
return NULL;
|
|
hashtable->keys_values[0] = '\0';
|
|
if (keys && sort_keys)
|
|
{
|
|
list_keys = hashtable_get_list_keys (hashtable);
|
|
if (list_keys)
|
|
{
|
|
for (ptr_item = list_keys->items; ptr_item;
|
|
ptr_item = ptr_item->next_item)
|
|
{
|
|
if (values)
|
|
{
|
|
hashtable_build_string_keys_values_cb (hashtable->keys_values,
|
|
hashtable,
|
|
ptr_item->data,
|
|
hashtable_get (hashtable,
|
|
ptr_item->data));
|
|
}
|
|
else
|
|
{
|
|
hashtable_build_string_keys_cb (hashtable->keys_values,
|
|
hashtable,
|
|
ptr_item->data,
|
|
NULL);
|
|
}
|
|
}
|
|
weelist_free (list_keys);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hashtable_map (hashtable,
|
|
(keys && values) ? &hashtable_build_string_keys_values_cb :
|
|
((keys) ? &hashtable_build_string_keys_cb :
|
|
&hashtable_build_string_values_cb),
|
|
hashtable->keys_values);
|
|
}
|
|
|
|
return hashtable->keys_values;
|
|
}
|
|
|
|
/*
|
|
* Gets a hashtable property as string.
|
|
*/
|
|
|
|
const char *
|
|
hashtable_get_string (struct t_hashtable *hashtable, const char *property)
|
|
{
|
|
if (hashtable && property)
|
|
{
|
|
if (string_strcasecmp (property, "type_keys") == 0)
|
|
return hashtable_type_string[hashtable->type_keys];
|
|
else if (string_strcasecmp (property, "type_values") == 0)
|
|
return hashtable_type_string[hashtable->type_values];
|
|
else if (string_strcasecmp (property, "keys") == 0)
|
|
return hashtable_get_keys_values (hashtable, 1, 0, 0);
|
|
else if (string_strcasecmp (property, "keys_sorted") == 0)
|
|
return hashtable_get_keys_values (hashtable, 1, 1, 0);
|
|
else if (string_strcasecmp (property, "values") == 0)
|
|
return hashtable_get_keys_values (hashtable, 0, 0, 1);
|
|
else if (string_strcasecmp (property, "keys_values") == 0)
|
|
return hashtable_get_keys_values (hashtable, 1, 0, 1);
|
|
else if (string_strcasecmp (property, "keys_values_sorted") == 0)
|
|
return hashtable_get_keys_values (hashtable, 1, 1, 1);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Sets a hashtable property (pointer).
|
|
*/
|
|
|
|
void
|
|
hashtable_set_pointer (struct t_hashtable *hashtable, const char *property,
|
|
void *pointer)
|
|
{
|
|
if (hashtable && property)
|
|
{
|
|
if (string_strcasecmp (property, "callback_free_key") == 0)
|
|
hashtable->callback_free_key = pointer;
|
|
else if (string_strcasecmp (property, "callback_free_value") == 0)
|
|
hashtable->callback_free_value = pointer;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Adds hashtable keys and values in an infolist.
|
|
*
|
|
* Returns:
|
|
* 1: OK
|
|
* 0: error
|
|
*/
|
|
|
|
int
|
|
hashtable_add_to_infolist (struct t_hashtable *hashtable,
|
|
struct t_infolist_item *infolist_item,
|
|
const char *prefix)
|
|
{
|
|
int i, item_number;
|
|
struct t_hashtable_item *ptr_item;
|
|
char option_name[128];
|
|
|
|
if (!hashtable || !infolist_item || !prefix)
|
|
return 0;
|
|
|
|
item_number = 0;
|
|
for (i = 0; i < hashtable->size; i++)
|
|
{
|
|
for (ptr_item = hashtable->htable[i]; ptr_item;
|
|
ptr_item = ptr_item->next_item)
|
|
{
|
|
snprintf (option_name, sizeof (option_name),
|
|
"%s_name_%05d", prefix, item_number);
|
|
if (!infolist_new_var_string (infolist_item, option_name,
|
|
hashtable_to_string (hashtable->type_keys,
|
|
ptr_item->key)))
|
|
return 0;
|
|
snprintf (option_name, sizeof (option_name),
|
|
"%s_value_%05d", prefix, item_number);
|
|
switch (hashtable->type_values)
|
|
{
|
|
case HASHTABLE_INTEGER:
|
|
if (!infolist_new_var_integer (infolist_item, option_name,
|
|
*((int *)ptr_item->value)))
|
|
return 0;
|
|
break;
|
|
case HASHTABLE_STRING:
|
|
if (!infolist_new_var_string (infolist_item, option_name,
|
|
(const char *)ptr_item->value))
|
|
return 0;
|
|
break;
|
|
case HASHTABLE_POINTER:
|
|
if (!infolist_new_var_pointer (infolist_item, option_name,
|
|
ptr_item->value))
|
|
return 0;
|
|
break;
|
|
case HASHTABLE_BUFFER:
|
|
if (!infolist_new_var_buffer (infolist_item, option_name,
|
|
ptr_item->value,
|
|
ptr_item->value_size))
|
|
return 0;
|
|
break;
|
|
case HASHTABLE_TIME:
|
|
if (!infolist_new_var_time (infolist_item, option_name,
|
|
*((time_t *)ptr_item->value)))
|
|
return 0;
|
|
break;
|
|
case HASHTABLE_NUM_TYPES:
|
|
break;
|
|
}
|
|
item_number++;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Adds hashtable keys and values from an infolist.
|
|
*
|
|
* Returns:
|
|
* 1: OK
|
|
* 0: error
|
|
*/
|
|
|
|
int
|
|
hashtable_add_from_infolist (struct t_hashtable *hashtable,
|
|
struct t_infolist *infolist,
|
|
const char *prefix)
|
|
{
|
|
struct t_infolist_var *ptr_name, *ptr_value;
|
|
char prefix_name[128], option_value[128];
|
|
int prefix_length;
|
|
|
|
if (!hashtable || !infolist || !infolist->ptr_item || !prefix)
|
|
return 0;
|
|
|
|
/* TODO: implement other key types */
|
|
if (hashtable->type_keys != HASHTABLE_STRING)
|
|
return 0;
|
|
|
|
snprintf (prefix_name, sizeof (prefix_name), "%s_name_", prefix);
|
|
prefix_length = strlen (prefix_name);
|
|
|
|
for (ptr_name = infolist->ptr_item->vars; ptr_name;
|
|
ptr_name = ptr_name->next_var)
|
|
{
|
|
if (string_strncasecmp (ptr_name->name, prefix_name,
|
|
prefix_length) == 0)
|
|
{
|
|
snprintf (option_value, sizeof (option_value),
|
|
"%s_value_%s", prefix, ptr_name->name + prefix_length);
|
|
|
|
ptr_value = infolist_search_var (infolist, option_value);
|
|
if (ptr_value)
|
|
{
|
|
switch (hashtable->type_values)
|
|
{
|
|
case HASHTABLE_INTEGER:
|
|
if (ptr_value->type != INFOLIST_INTEGER)
|
|
return 0;
|
|
break;
|
|
case HASHTABLE_STRING:
|
|
if (ptr_value->type != INFOLIST_STRING)
|
|
return 0;
|
|
break;
|
|
case HASHTABLE_POINTER:
|
|
if (ptr_value->type != INFOLIST_POINTER)
|
|
return 0;
|
|
break;
|
|
case HASHTABLE_BUFFER:
|
|
if (ptr_value->type != INFOLIST_BUFFER)
|
|
return 0;
|
|
break;
|
|
case HASHTABLE_TIME:
|
|
if (ptr_value->type != INFOLIST_TIME)
|
|
return 0;
|
|
break;
|
|
case HASHTABLE_NUM_TYPES:
|
|
break;
|
|
}
|
|
if (hashtable->type_values == HASHTABLE_BUFFER)
|
|
{
|
|
hashtable_set_with_size (hashtable,
|
|
ptr_name->value,
|
|
0,
|
|
ptr_value->value,
|
|
ptr_value->size);
|
|
}
|
|
else
|
|
{
|
|
hashtable_set (hashtable,
|
|
ptr_name->value,
|
|
ptr_value->value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Removes an item from hashtable.
|
|
*/
|
|
|
|
void
|
|
hashtable_remove_item (struct t_hashtable *hashtable,
|
|
struct t_hashtable_item *item,
|
|
unsigned long long hash)
|
|
{
|
|
if (!hashtable || !item)
|
|
return;
|
|
|
|
/* free key and value */
|
|
hashtable_free_value (hashtable, item);
|
|
hashtable_free_key (hashtable, item);
|
|
|
|
/* remove 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 (hashtable->htable[hash] == item)
|
|
hashtable->htable[hash] = item->next_item;
|
|
|
|
free (item);
|
|
|
|
hashtable->items_count--;
|
|
}
|
|
|
|
/*
|
|
* Removes an item from hashtable (searches it with key).
|
|
*/
|
|
|
|
void
|
|
hashtable_remove (struct t_hashtable *hashtable, const void *key)
|
|
{
|
|
struct t_hashtable_item *ptr_item;
|
|
unsigned long long hash;
|
|
|
|
if (!hashtable || !key)
|
|
return;
|
|
|
|
ptr_item = hashtable_get_item (hashtable, key, &hash);
|
|
if (ptr_item)
|
|
hashtable_remove_item (hashtable, ptr_item, hash);
|
|
}
|
|
|
|
/*
|
|
* Removes all items from hashtable.
|
|
*/
|
|
|
|
void
|
|
hashtable_remove_all (struct t_hashtable *hashtable)
|
|
{
|
|
int i;
|
|
|
|
if (!hashtable)
|
|
return;
|
|
|
|
for (i = 0; i < hashtable->size; i++)
|
|
{
|
|
while (hashtable->htable[i])
|
|
{
|
|
hashtable_remove_item (hashtable, hashtable->htable[i], i);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Frees a hashtable: removes all items and frees hashtable.
|
|
*/
|
|
|
|
void
|
|
hashtable_free (struct t_hashtable *hashtable)
|
|
{
|
|
if (!hashtable)
|
|
return;
|
|
|
|
hashtable_remove_all (hashtable);
|
|
free (hashtable->htable);
|
|
if (hashtable->keys_values)
|
|
free (hashtable->keys_values);
|
|
free (hashtable);
|
|
}
|
|
|
|
/*
|
|
* Prints hashtable in WeeChat log file (usually for crash dump).
|
|
*/
|
|
|
|
void
|
|
hashtable_print_log (struct t_hashtable *hashtable, const char *name)
|
|
{
|
|
struct t_hashtable_item *ptr_item;
|
|
int i;
|
|
|
|
log_printf ("");
|
|
log_printf ("[hashtable %s (addr:0x%lx)]", name, hashtable);
|
|
log_printf (" size . . . . . . . . . : %d", hashtable->size);
|
|
log_printf (" htable . . . . . . . . : 0x%lx", hashtable->htable);
|
|
log_printf (" items_count. . . . . . : %d", hashtable->items_count);
|
|
log_printf (" type_keys. . . . . . . : %d (%s)",
|
|
hashtable->type_keys,
|
|
hashtable_type_string[hashtable->type_keys]);
|
|
log_printf (" type_values. . . . . . : %d (%s)",
|
|
hashtable->type_values,
|
|
hashtable_type_string[hashtable->type_values]);
|
|
log_printf (" callback_hash_key. . . : 0x%lx", hashtable->callback_hash_key);
|
|
log_printf (" callback_keycmp. . . . : 0x%lx", hashtable->callback_keycmp);
|
|
log_printf (" callback_free_key. . . : 0x%lx", hashtable->callback_free_key);
|
|
log_printf (" callback_free_value. . : 0x%lx", hashtable->callback_free_value);
|
|
log_printf (" keys_values. . . . . . : '%s'", hashtable->keys_values);
|
|
|
|
for (i = 0; i < hashtable->size; i++)
|
|
{
|
|
log_printf (" htable[%06d] . . . . : 0x%lx", i, hashtable->htable[i]);
|
|
for (ptr_item = hashtable->htable[i]; ptr_item;
|
|
ptr_item = ptr_item->next_item)
|
|
{
|
|
log_printf (" [item 0x%lx]", hashtable->htable);
|
|
switch (hashtable->type_keys)
|
|
{
|
|
case HASHTABLE_INTEGER:
|
|
log_printf (" key (integer). . . : %d", *((int *)ptr_item->key));
|
|
break;
|
|
case HASHTABLE_STRING:
|
|
log_printf (" key (string) . . . : '%s'", (char *)ptr_item->key);
|
|
break;
|
|
case HASHTABLE_POINTER:
|
|
log_printf (" key (pointer). . . : 0x%lx", ptr_item->key);
|
|
break;
|
|
case HASHTABLE_BUFFER:
|
|
log_printf (" key (buffer) . . . : 0x%lx", ptr_item->key);
|
|
break;
|
|
case HASHTABLE_TIME:
|
|
log_printf (" key (time) . . . . : %lld", (long long)(*((time_t *)ptr_item->key)));
|
|
break;
|
|
case HASHTABLE_NUM_TYPES:
|
|
break;
|
|
}
|
|
log_printf (" key_size . . . . . : %d", ptr_item->key_size);
|
|
switch (hashtable->type_values)
|
|
{
|
|
case HASHTABLE_INTEGER:
|
|
log_printf (" value (integer). . : %d", *((int *)ptr_item->value));
|
|
break;
|
|
case HASHTABLE_STRING:
|
|
log_printf (" value (string) . . : '%s'", (char *)ptr_item->value);
|
|
break;
|
|
case HASHTABLE_POINTER:
|
|
log_printf (" value (pointer). . : 0x%lx", ptr_item->value);
|
|
break;
|
|
case HASHTABLE_BUFFER:
|
|
log_printf (" value (buffer) . . : 0x%lx", ptr_item->value);
|
|
break;
|
|
case HASHTABLE_TIME:
|
|
log_printf (" value (time) . . . : %lld", (long long)(*((time_t *)ptr_item->value)));
|
|
break;
|
|
case HASHTABLE_NUM_TYPES:
|
|
break;
|
|
}
|
|
log_printf (" value_size . . . . : %d", ptr_item->value_size);
|
|
log_printf (" prev_item. . . . . : 0x%lx", ptr_item->prev_item);
|
|
log_printf (" next_item. . . . . : 0x%lx", ptr_item->next_item);
|
|
}
|
|
}
|
|
}
|