core: add shared strings to reduce memory usage
Shared strings are stored in a hashtable with pointer for keys (values are not used). The key has a reference count + the string. The initial reference count is set to 1 and is incremented each time the same string is asked. When removing a shared string, the reference count is decremented. If it becomes 0, then the shared string is removed from the hashtable (and then the string is really destroyed).
This commit is contained in:
parent
784de68a5f
commit
49aacc853c
@ -31,6 +31,7 @@
|
|||||||
#include <wctype.h>
|
#include <wctype.h>
|
||||||
#include <regex.h>
|
#include <regex.h>
|
||||||
#include <wchar.h>
|
#include <wchar.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#ifdef HAVE_ICONV
|
#ifdef HAVE_ICONV
|
||||||
#include <iconv.h>
|
#include <iconv.h>
|
||||||
@ -47,11 +48,17 @@
|
|||||||
#include "weechat.h"
|
#include "weechat.h"
|
||||||
#include "wee-string.h"
|
#include "wee-string.h"
|
||||||
#include "wee-config.h"
|
#include "wee-config.h"
|
||||||
|
#include "wee-hashtable.h"
|
||||||
#include "wee-utf8.h"
|
#include "wee-utf8.h"
|
||||||
#include "../gui/gui-color.h"
|
#include "../gui/gui-color.h"
|
||||||
#include "../plugins/plugin.h"
|
#include "../plugins/plugin.h"
|
||||||
|
|
||||||
|
|
||||||
|
typedef uint32_t string_shared_count_t;
|
||||||
|
|
||||||
|
struct t_hashtable *string_hashtable_shared = NULL;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Defines a "strndup" function for systems where this function does not exist
|
* Defines a "strndup" function for systems where this function does not exist
|
||||||
* (FreeBSD and maybe others).
|
* (FreeBSD and maybe others).
|
||||||
@ -2189,3 +2196,160 @@ string_replace_with_callback (const char *string,
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hashes a shared string.
|
||||||
|
* The string starts after the reference count, which is skipped.
|
||||||
|
*
|
||||||
|
* Returns the hash of the shared string (variant of djb2).
|
||||||
|
*/
|
||||||
|
|
||||||
|
unsigned long
|
||||||
|
string_shared_hash_key (struct t_hashtable *hashtable,
|
||||||
|
const void *key)
|
||||||
|
{
|
||||||
|
/* make C compiler happy */
|
||||||
|
(void) hashtable;
|
||||||
|
|
||||||
|
return hashtable_hash_key_djb2 (((const char *)key) + sizeof (string_shared_count_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compares two shared strings.
|
||||||
|
* Each string starts after the reference count, which is skipped.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* < 0: key1 < key2
|
||||||
|
* 0: key1 == key2
|
||||||
|
* > 0: key1 > key2
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
string_shared_keycmp (struct t_hashtable *hashtable,
|
||||||
|
const void *key1, const void *key2)
|
||||||
|
{
|
||||||
|
/* make C compiler happy */
|
||||||
|
(void) hashtable;
|
||||||
|
|
||||||
|
return strcmp (((const char *)key1) + sizeof (string_shared_count_t),
|
||||||
|
((const char *)key2) + sizeof (string_shared_count_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Frees a shared string.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
string_shared_free_key (struct t_hashtable *hashtable,
|
||||||
|
void *key, const void *value)
|
||||||
|
{
|
||||||
|
/* make C compiler happy */
|
||||||
|
(void) hashtable;
|
||||||
|
(void) value;
|
||||||
|
|
||||||
|
free (key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Gets a pointer to a shared string.
|
||||||
|
*
|
||||||
|
* A shared string is an entry in the hashtable "string_hashtable_shared", with:
|
||||||
|
* - key: reference count (unsigned integer on 32 bits) + string
|
||||||
|
* - value: NULL pointer (not used)
|
||||||
|
*
|
||||||
|
* The initial reference count is set to 1 and is incremented each time this
|
||||||
|
* function is called for a same string (string content, not the pointer).
|
||||||
|
*
|
||||||
|
* Returns the pointer to the shared string (start of string in key, after the
|
||||||
|
* reference count), NULL if error.
|
||||||
|
* The string returned has exactly same content as string received in argument,
|
||||||
|
* but the pointer to the string is different.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const char *
|
||||||
|
string_shared_get (const char *string)
|
||||||
|
{
|
||||||
|
struct t_hashtable_item *ptr_item;
|
||||||
|
char *key;
|
||||||
|
int length;
|
||||||
|
|
||||||
|
if (!string_hashtable_shared)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* use large htable inside hashtable to prevent too many collisions,
|
||||||
|
* which would slow down search of a string in the hashtable
|
||||||
|
*/
|
||||||
|
string_hashtable_shared = hashtable_new (1024,
|
||||||
|
WEECHAT_HASHTABLE_POINTER,
|
||||||
|
WEECHAT_HASHTABLE_POINTER,
|
||||||
|
&string_shared_hash_key,
|
||||||
|
&string_shared_keycmp);
|
||||||
|
if (!string_hashtable_shared)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
string_hashtable_shared->callback_free_key = &string_shared_free_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
length = sizeof (string_shared_count_t) + strlen (string) + 1;
|
||||||
|
key = malloc (length);
|
||||||
|
if (!key)
|
||||||
|
return NULL;
|
||||||
|
*((string_shared_count_t *)key) = 1;
|
||||||
|
strcpy (key + sizeof (string_shared_count_t), string);
|
||||||
|
|
||||||
|
ptr_item = hashtable_get_item (string_hashtable_shared, key, NULL);
|
||||||
|
if (ptr_item)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* the string already exists in the hashtable, then just increase the
|
||||||
|
* reference count on the string
|
||||||
|
*/
|
||||||
|
(*((string_shared_count_t *)(ptr_item->key)))++;
|
||||||
|
free (key);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* add the shared string in the hashtable */
|
||||||
|
ptr_item = hashtable_set (string_hashtable_shared, key, NULL);
|
||||||
|
if (!ptr_item)
|
||||||
|
free (key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (ptr_item) ?
|
||||||
|
((const char *)ptr_item->key) + sizeof (string_shared_count_t) : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Frees a shared string.
|
||||||
|
*
|
||||||
|
* The reference count of the string is decremented. If it becomes 0, then the
|
||||||
|
* shared string is removed from the hashtable (and then the string is really
|
||||||
|
* destroyed).
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
string_shared_free (const char *string)
|
||||||
|
{
|
||||||
|
string_shared_count_t *ptr_count;
|
||||||
|
|
||||||
|
ptr_count = (string_shared_count_t *)(string - sizeof (string_shared_count_t));
|
||||||
|
|
||||||
|
(*ptr_count)--;
|
||||||
|
|
||||||
|
if (*ptr_count == 0)
|
||||||
|
hashtable_remove (string_hashtable_shared, ptr_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Frees all allocated data.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
string_end ()
|
||||||
|
{
|
||||||
|
if (string_hashtable_shared)
|
||||||
|
{
|
||||||
|
hashtable_free (string_hashtable_shared);
|
||||||
|
string_hashtable_shared = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -84,5 +84,8 @@ extern char *string_replace_with_callback (const char *string,
|
|||||||
char *(*callback)(void *data, const char *text),
|
char *(*callback)(void *data, const char *text),
|
||||||
void *callback_data,
|
void *callback_data,
|
||||||
int *errors);
|
int *errors);
|
||||||
|
extern const char *string_shared_get (const char *string);
|
||||||
|
extern void string_shared_free (const char *string);
|
||||||
|
extern void string_end ();
|
||||||
|
|
||||||
#endif /* __WEECHAT_STRING_H */
|
#endif /* __WEECHAT_STRING_H */
|
||||||
|
@ -488,6 +488,7 @@ main (int argc, char *argv[])
|
|||||||
unhook_all (); /* remove all hooks */
|
unhook_all (); /* remove all hooks */
|
||||||
hdata_end (); /* end hdata */
|
hdata_end (); /* end hdata */
|
||||||
eval_end (); /* end eval */
|
eval_end (); /* end eval */
|
||||||
|
string_end (); /* end string */
|
||||||
weechat_shutdown (EXIT_SUCCESS, 0); /* quit WeeChat (oh no, why?) */
|
weechat_shutdown (EXIT_SUCCESS, 0); /* quit WeeChat (oh no, why?) */
|
||||||
|
|
||||||
return EXIT_SUCCESS; /* make C compiler happy */
|
return EXIT_SUCCESS; /* make C compiler happy */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user