1
0
mirror of https://github.com/weechat/weechat.git synced 2026-07-06 17:53:13 +02:00

Add hashtable in core and plugin API

This commit is contained in:
Sebastien Helleu
2010-06-28 10:40:30 +02:00
parent ff97274bca
commit cb18bdb10b
9 changed files with 1646 additions and 0 deletions
+1
View File
@@ -26,6 +26,7 @@ wee-command.c wee-command.h
wee-config.c wee-config.h
wee-config-file.c wee-config-file.h
wee-debug.c wee-debug.h
wee-hashtable.c wee-hashtable.h
wee-hook.c wee-hook.h
wee-infolist.c wee-infolist.h
wee-input.c wee-input.h
+2
View File
@@ -33,6 +33,8 @@ lib_weechat_core_a_SOURCES = weechat.c \
wee-config-file.h \
wee-debug.c \
wee-debug.h \
wee-hashtable.c \
wee-hashtable.h \
wee-hook.c \
wee-hook.h \
wee-infolist.c \
+549
View File
@@ -0,0 +1,549 @@
/*
* Copyright (C) 2010 Sebastien 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/>.
*/
/*
* wee-hashtable.c: implementation of hashtable
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "weechat.h"
#include "wee-hashtable.h"
#include "wee-log.h"
#include "wee-string.h"
#include "../plugins/weechat-plugin.h"
char *hashtable_type_names[HASHTABLE_NUM_TYPES] =
{ "integer", "string", "pointer", "buffer", "time" };
/*
* hashtable_get_type: get integer for type (string)
*/
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_names[i], type) == 0)
return i;
}
/* type not found */
return -1;
}
/*
* hashtable_hash_key_string_cb: default callback to hash a string key
*/
unsigned int
hashtable_hash_key_string_cb (struct t_hashtable *hashtable, const void *key)
{
const char *ptr_key;
unsigned long hash;
/* variant of djb2 hash */
hash = 5381;
for (ptr_key = (const char *)key; ptr_key[0]; ptr_key++)
{
hash ^= (hash << 5) + (hash >> 2) + (int)(ptr_key[0]);
}
return hash % hashtable->size;
}
/*
* hashtable_keycmp_string_cb: default callback for string comparison on keys
*/
int
hashtable_keycmp_string_cb (struct t_hashtable *hashtable,
const void *key1, const void *key2)
{
/* make C compiler happy */
(void) hashtable;
return strcmp ((const char *)key1, (const char *)key2);
}
/*
* hashtable_new: create a new hash table
*/
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;
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;
if ((type_keys_int != HASHTABLE_STRING) && (!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)));
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;
if ((type_keys_int == HASHTABLE_STRING) && !callback_hash_key)
new_hashtable->callback_hash_key = &hashtable_hash_key_string_cb;
else
new_hashtable->callback_hash_key = callback_hash_key;
if ((type_keys_int == HASHTABLE_STRING) && !callback_keycmp)
new_hashtable->callback_keycmp = &hashtable_keycmp_string_cb;
else
new_hashtable->callback_keycmp = callback_keycmp;
}
return new_hashtable;
}
/*
* hashtable_alloc_type: alloc space for a key or value
*/
void
hashtable_alloc_type (enum t_hashtable_type type, void *value, int size_value,
void **pointer, int *size)
{
switch (type)
{
case HASHTABLE_INTEGER:
*pointer = malloc (sizeof (int));
if (*pointer)
*((int *)(*pointer)) = *((int *)value);
*size = (*pointer) ? sizeof (int) : 0;
break;
case HASHTABLE_STRING:
*pointer = strdup ((const char *)value);
*size = (*pointer) ? strlen (*pointer) + 1 : 0;
break;
case HASHTABLE_POINTER:
*pointer = value;
*size = sizeof (void *);
break;
case HASHTABLE_BUFFER:
*pointer = malloc (size_value);
if (*pointer)
memcpy (*pointer, value, size_value);
*size = (*pointer) ? size_value : 0;
break;
case HASHTABLE_TIME:
*pointer = malloc (sizeof (time_t));
if (*pointer)
*((time_t *)(*pointer)) = *((time_t *)value);
*size = (*pointer) ? sizeof (time_t) : 0;
break;
case HASHTABLE_NUM_TYPES:
break;
}
}
/*
* hashtable_free_type: free space used by a key or value
*/
void
hashtable_free_type (enum t_hashtable_type type, void *value)
{
switch (type)
{
case HASHTABLE_INTEGER:
case HASHTABLE_STRING:
case HASHTABLE_BUFFER:
case HASHTABLE_TIME:
free (value);
break;
case HASHTABLE_POINTER:
break;
case HASHTABLE_NUM_TYPES:
break;
}
}
/*
* hashtable_set_with_size: set value for item in hash table
* argument size is used only for type "buffer"
* return 1 if ok, 0 if error
*/
int
hashtable_set_with_size (struct t_hashtable *hashtable,
void *key, int key_size,
void *value, int value_size)
{
unsigned int hash;
struct t_hashtable_item *ptr_item, *pos_item, *new_item;
if (!hashtable
|| ((hashtable->type_keys == HASHTABLE_BUFFER) && (key_size <= 0))
|| ((hashtable->type_values == HASHTABLE_BUFFER) && (value_size <= 0)))
{
return 0;
}
/* search position for item in hash table */
hash = hashtable->callback_hash_key (hashtable, key);
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 hash table */
if (ptr_item && (hashtable->callback_keycmp (hashtable, key, ptr_item->key) == 0))
{
ptr_item->value = value;
return 1;
}
/* create new item */
new_item = malloc (sizeof (*new_item));
if (!new_item)
return 0;
/* 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 1;
}
/*
* hashtable_set: set value for item in hash table
* return 1 if ok, 0 if error
* Note: this function can be called *only* if key AND value are
* *not* of type "buffer"
*/
int
hashtable_set (struct t_hashtable *hashtable, void *key, void *value)
{
return hashtable_set_with_size (hashtable, key, 0, value, 0);
}
/*
* hashtable_get_item: search an item in hashtable
* if hash is non NULL pointer, 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 int *hash)
{
unsigned int key_hash;
struct t_hashtable_item *ptr_item;
if (!hashtable)
return NULL;
key_hash = hashtable->callback_hash_key (hashtable, key);
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;
}
/*
* hashtable_get: get value for a key in hash table
* return pointer to "value" for key,
* or NULL if key is not found in hash table
*/
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;
}
/*
* hashtable_map: call 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;
if (!hashtable)
return;
for (i = 0; i < hashtable->size; i++)
{
for (ptr_item = hashtable->htable[i]; ptr_item;
ptr_item = ptr_item->next_item)
{
(void) (callback_map) (callback_map_data,
hashtable,
ptr_item->key,
ptr_item->value);
}
}
}
/*
* hashtable_get_integer: get 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;
}
/*
* hashtable_remove_item: remove an item from hashmap
*/
void
hashtable_remove_item (struct t_hashtable *hashtable,
struct t_hashtable_item *item,
unsigned int hash)
{
if (!hashtable || !item)
return;
/* free key and value */
hashtable_free_type (hashtable->type_keys, item->key);
hashtable_free_type (hashtable->type_values, item->value);
/* 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;
hashtable->items_count--;
}
/*
* hashtable_remove: remove an item from hashmap (search it with key)
*/
void
hashtable_remove (struct t_hashtable *hashtable, const void *key)
{
struct t_hashtable_item *ptr_item;
unsigned int hash;
ptr_item = hashtable_get_item (hashtable, key, &hash);
if (ptr_item)
hashtable_remove_item (hashtable, ptr_item, hash);
}
/*
* hashtable_remove_all: remove all items from hashmap
*/
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);
}
}
}
/*
* hashtable_free: free hashtable (remove all items and free hashtable)
*/
void
hashtable_free (struct t_hashtable *hashtable)
{
hashtable_remove_all (hashtable);
free (hashtable->htable);
free (hashtable);
}
/*
* hashtable_print_log: print hashtable in log (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", hashtable->type_keys);
log_printf (" type_values. . . . . . : %d", hashtable->type_values);
log_printf (" callback_hash_key. . . : 0x%lx", hashtable->callback_hash_key);
log_printf (" callback_keycmp. . . . : 0x%lx", hashtable->callback_keycmp);
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) . . . . : %ld", *((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) . . . : %d", *((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);
}
}
}
+123
View File
@@ -0,0 +1,123 @@
/*
* Copyright (C) 2010 Sebastien 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_HASHTABLE_H
#define __WEECHAT_HASHTABLE_H 1
struct t_hashtable;
typedef unsigned int (t_hashtable_hash_key)(struct t_hashtable *hashtable,
const void *key);
typedef int (t_hashtable_keycmp)(struct t_hashtable *hashtable,
const void *key1, const void *key2);
typedef void (t_hashtable_map)(void *data,
struct t_hashtable *hashtable,
const void *key, const void *value);
/*
* Hashtable is a structure with an array "htable", each entry is a pointer
* to a linked list, and it is read with hashed key (as unsigned int).
* Keys with same hashed key are grouped in a linked list pointed by htable.
* htable is not sorted, linked list is sorted.
*
* Example of a hashtable with size 8 and 6 items added inside, items are:
* "weechat", "fast", "light", "extensible", "chat", "client"
* Keys "fast" and "light" have same hashed value, so they are together in
* linked list.
*
* Result is:
* +-----+
* | 0 |
* +-----+
* | 1 |
* +-----+
* | 2 | --> "extensible"
* +-----+
* | 3 | --> "fast" --> "light"
* +-----+
* | 4 | --> "weechat"
* +-----+
* | 5 | --> "chat"
* +-----+
* | 6 | --> "client"
* +-----+
* | 7 |
* +-----+
*/
enum t_hashtable_type
{
HASHTABLE_INTEGER = 0,
HASHTABLE_STRING,
HASHTABLE_POINTER,
HASHTABLE_BUFFER,
HASHTABLE_TIME,
/* number of hashtable types */
HASHTABLE_NUM_TYPES,
};
struct t_hashtable_item
{
void *key; /* item key */
int key_size; /* size of key (in bytes) */
void *value; /* pointer to value */
int value_size; /* size of value (in bytes) */
struct t_hashtable_item *prev_item; /* link to previous item */
struct t_hashtable_item *next_item; /* link to next item */
};
struct t_hashtable
{
int size; /* hashtable size */
struct t_hashtable_item **htable; /* table to map hashes with linked */
/* lists */
int items_count; /* number of items in hashtable */
/* type for keys and values */
enum t_hashtable_type type_keys; /* type for keys: int/str/pointer */
enum t_hashtable_type type_values; /* type for values: int/str/pointer */
/* callbacks */
t_hashtable_hash_key *callback_hash_key; /* hash key to integer value */
t_hashtable_keycmp *callback_keycmp; /* compare two keys */
};
extern struct t_hashtable *hashtable_new (int size,
const char *type_keys,
const char *type_values,
t_hashtable_hash_key *hash_key_cb,
t_hashtable_keycmp *keycmp_cb);
extern int hashtable_set_with_size (struct t_hashtable *hashtable,
void *key, int key_size,
void *value, int value_size);
extern int hashtable_set (struct t_hashtable *hashtable, void *key,
void *value);
extern void *hashtable_get (struct t_hashtable *hashtable, const void *key);
extern void hashtable_map (struct t_hashtable *hashtable,
t_hashtable_map *callback_map,
void *callback_map_data);
extern int hashtable_get_integer (struct t_hashtable *hashtable,
const char *property);
extern void hashtable_remove (struct t_hashtable *hashtable, const void *key);
extern void hashtable_remove_all (struct t_hashtable *hashtable);
extern void hashtable_free (struct t_hashtable *hashtable);
extern void hashtable_print_log (struct t_hashtable *hashtable,
const char *name);
#endif /* __WEECHAT_HASHTABLE_H */
+11
View File
@@ -38,6 +38,7 @@
#include "../core/weechat.h"
#include "../core/wee-config.h"
#include "../core/wee-hashtable.h"
#include "../core/wee-hook.h"
#include "../core/wee-infolist.h"
#include "../core/wee-list.h"
@@ -520,6 +521,16 @@ plugin_load (const char *filename)
new_plugin->list_remove_all = &weelist_remove_all;
new_plugin->list_free = &weelist_free;
new_plugin->hashtable_new = &hashtable_new;
new_plugin->hashtable_set_with_size = &hashtable_set_with_size;
new_plugin->hashtable_set = &hashtable_set;
new_plugin->hashtable_get = &hashtable_get;
new_plugin->hashtable_map = &hashtable_map;
new_plugin->hashtable_get_integer = &hashtable_get_integer;
new_plugin->hashtable_remove = &hashtable_remove;
new_plugin->hashtable_remove_all = &hashtable_remove_all;
new_plugin->hashtable_free = &hashtable_free;
new_plugin->config_new = &config_file_new;
new_plugin->config_new_section = &config_file_new_section;
new_plugin->config_search_section = &config_file_search_section;
+60
View File
@@ -35,6 +35,7 @@ struct t_gui_bar_item;
struct t_gui_completion;
struct t_infolist;
struct t_weelist;
struct t_hashtable;
struct timeval;
/*
@@ -93,6 +94,13 @@ struct timeval;
#define WEECHAT_LIST_POS_BEGINNING "beginning"
#define WEECHAT_LIST_POS_END "end"
/* type for keys and values in hashtable */
#define WEECHAT_HASHTABLE_INTEGER "integer"
#define WEECHAT_HASHTABLE_STRING "string"
#define WEECHAT_HASHTABLE_POINTER "pointer"
#define WEECHAT_HASHTABLE_BUFFER "buffer"
#define WEECHAT_HASHTABLE_TIME "time"
/* buffer hotlist */
#define WEECHAT_HOTLIST_LOW "0"
#define WEECHAT_HOTLIST_MESSAGE "1"
@@ -241,6 +249,33 @@ struct t_weechat_plugin
void (*list_remove_all) (struct t_weelist *weelist);
void (*list_free) (struct t_weelist *weelist);
/* hash tables */
struct t_hashtable *(*hashtable_new) (int size,
const char *type_keys,
const char *type_values,
unsigned int (*callback_hash_key)(struct t_hashtable *hashtable,
const void *key),
int (*callback_keycmp)(struct t_hashtable *hashtable,
const void *key1,
const void *key2));
int (*hashtable_set_with_size) (struct t_hashtable *hashtable,
void *key, int key_size,
void *value, int value_size);
int (*hashtable_set) (struct t_hashtable *hashtable, void *key,
void *value);
void *(*hashtable_get) (struct t_hashtable *hashtable, const void *key);
void (*hashtable_map) (struct t_hashtable *hashtable,
void (*callback_map) (void *data,
struct t_hashtable *hashtable,
const void *key,
const void *value),
void *callback_map_data);
int (*hashtable_get_integer) (struct t_hashtable *hashtable,
const char *property);
void (*hashtable_remove) (struct t_hashtable *hashtable, const void *key);
void (*hashtable_remove_all) (struct t_hashtable *hashtable);
void (*hashtable_free) (struct t_hashtable *hashtable);
/* config files */
struct t_config_file *(*config_new) (struct t_weechat_plugin *plugin,
const char *name,
@@ -829,6 +864,31 @@ extern int weechat_plugin_end (struct t_weechat_plugin *plugin);
#define weechat_list_free(__list) \
weechat_plugin->list_free(__list)
/* hash tables */
#define weechat_hashtable_new(__size, __type_keys, __type_values, \
__hash_key_cb, __keycmp_cb) \
weechat_plugin->hashtable_new(__size, __type_keys, __type_values, \
__hash_key_cb, __keycmp_cb)
#define weechat_hashtable_set_with_size(__hashtable, __key, __key_size, \
__value, __value_size) \
weechat_plugin->hashtable_set_with_size(__hashtable, __key, \
__key_size, __value, \
__value_size)
#define weechat_hashtable_set(__hashtable, __key, __value) \
weechat_plugin->hashtable_set(__hashtable, __key, __value)
#define weechat_hashtable_get(__hashtable, __key) \
weechat_plugin->hashtable_get(__hashtable, __key)
#define weechat_hashtable_map(__hashtable, __cb_map, __cb_map_data) \
weechat_plugin->hashtable_map(__hashtable, __cb_map, __cb_map_data)
#define weechat_hashtable_get_integer(__hashtable, __property) \
weechat_plugin->hashtable_get_integer(__hashtable, __property)
#define weechat_hashtable_remove(__hashtable, __key) \
weechat_plugin->hashtable_remove(__hashtable, __key)
#define weechat_hashtable_remove_all(__hashtable) \
weechat_plugin->hashtable_remove_all(__hashtable)
#define weechat_hashtable_free(__hashtable) \
weechat_plugin->hashtable_free(__hashtable)
/* config files */
#define weechat_config_new(__name, __callback_reload, \
__callback_reload_data) \