Merge branch 'shared-strings'
This commit is contained in:
commit
0409faee7f
@ -31,6 +31,7 @@
|
||||
#include <wctype.h>
|
||||
#include <regex.h>
|
||||
#include <wchar.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef HAVE_ICONV
|
||||
#include <iconv.h>
|
||||
@ -47,11 +48,17 @@
|
||||
#include "weechat.h"
|
||||
#include "wee-string.h"
|
||||
#include "wee-config.h"
|
||||
#include "wee-hashtable.h"
|
||||
#include "wee-utf8.h"
|
||||
#include "../gui/gui-color.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
|
||||
* (FreeBSD and maybe others).
|
||||
@ -1052,6 +1059,9 @@ string_has_highlight_regex (const char *string, const char *regex)
|
||||
/*
|
||||
* Splits a string according to separators.
|
||||
*
|
||||
* This function must not be called directly (call string_split or
|
||||
* string_split_shared instead).
|
||||
*
|
||||
* Examples:
|
||||
* string_split ("abc de fghi", " ", 0, 0, NULL)
|
||||
* ==> array[0] = "abc"
|
||||
@ -1066,12 +1076,13 @@ string_has_highlight_regex (const char *string, const char *regex)
|
||||
*/
|
||||
|
||||
char **
|
||||
string_split (const char *string, const char *separators, int keep_eol,
|
||||
int num_items_max, int *num_items)
|
||||
string_split_internal (const char *string, const char *separators, int keep_eol,
|
||||
int num_items_max, int *num_items, int shared)
|
||||
{
|
||||
int i, j, n_items;
|
||||
char *string2, **array;
|
||||
char *ptr, *ptr1, *ptr2;
|
||||
const char *str_shared;
|
||||
|
||||
if (num_items != NULL)
|
||||
*num_items = 0;
|
||||
@ -1141,13 +1152,18 @@ string_split (const char *string, const char *separators, int keep_eol,
|
||||
{
|
||||
if (keep_eol)
|
||||
{
|
||||
array[i] = strdup (ptr1);
|
||||
array[i] = (shared) ? (char *)string_shared_get (ptr1) : strdup (ptr1);
|
||||
if (!array[i])
|
||||
{
|
||||
for (j = 0; j < n_items; j++)
|
||||
{
|
||||
if (array[j])
|
||||
free (array[j]);
|
||||
{
|
||||
if (shared)
|
||||
string_shared_free (array[j]);
|
||||
else
|
||||
free (array[j]);
|
||||
}
|
||||
}
|
||||
free (array);
|
||||
free (string2);
|
||||
@ -1162,7 +1178,12 @@ string_split (const char *string, const char *separators, int keep_eol,
|
||||
for (j = 0; j < n_items; j++)
|
||||
{
|
||||
if (array[j])
|
||||
free (array[j]);
|
||||
{
|
||||
if (shared)
|
||||
string_shared_free (array[j]);
|
||||
else
|
||||
free (array[j]);
|
||||
}
|
||||
}
|
||||
free (array);
|
||||
free (string2);
|
||||
@ -1170,6 +1191,23 @@ string_split (const char *string, const char *separators, int keep_eol,
|
||||
}
|
||||
strncpy (array[i], ptr1, ptr2 - ptr1);
|
||||
array[i][ptr2 - ptr1] = '\0';
|
||||
if (shared)
|
||||
{
|
||||
str_shared = string_shared_get (array[i]);
|
||||
if (!str_shared)
|
||||
{
|
||||
for (j = 0; j < n_items; j++)
|
||||
{
|
||||
if (array[j])
|
||||
string_shared_free (array[j]);
|
||||
}
|
||||
free (array);
|
||||
free (string2);
|
||||
return NULL;
|
||||
}
|
||||
free (array[i]);
|
||||
array[i] = (char *)str_shared;
|
||||
}
|
||||
}
|
||||
ptr1 = ++ptr2;
|
||||
}
|
||||
@ -1189,6 +1227,35 @@ string_split (const char *string, const char *separators, int keep_eol,
|
||||
return array;
|
||||
}
|
||||
|
||||
/*
|
||||
* Splits a string according to separators.
|
||||
*
|
||||
* For full description, see function string_split_internal.
|
||||
*/
|
||||
|
||||
char **
|
||||
string_split (const char *string, const char *separators, int keep_eol,
|
||||
int num_items_max, int *num_items)
|
||||
{
|
||||
return string_split_internal (string, separators, keep_eol,
|
||||
num_items_max, num_items, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Splits a string according to separators, and use shared strings for the
|
||||
* strings in the array returned.
|
||||
*
|
||||
* For full description, see function string_split_internal.
|
||||
*/
|
||||
|
||||
char **
|
||||
string_split_shared (const char *string, const char *separators, int keep_eol,
|
||||
int num_items_max, int *num_items)
|
||||
{
|
||||
return string_split_internal (string, separators, keep_eol,
|
||||
num_items_max, num_items, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Splits a string like the shell does for a command with arguments.
|
||||
*
|
||||
@ -1398,6 +1465,23 @@ string_free_split (char **split_string)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Frees a split string (using shared strings).
|
||||
*/
|
||||
|
||||
void
|
||||
string_free_split_shared (char **split_string)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (split_string)
|
||||
{
|
||||
for (i = 0; split_string[i]; i++)
|
||||
string_shared_free (split_string[i]);
|
||||
free (split_string);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Builds a string with a split string.
|
||||
*
|
||||
@ -2193,3 +2277,163 @@ string_replace_with_callback (const char *string,
|
||||
|
||||
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.
|
||||
*
|
||||
* IMPORTANT: the returned string must NEVER be changed in any way, because it
|
||||
* is used itself as the key of the hashtable.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -59,8 +59,12 @@ extern int string_has_highlight_regex_compiled (const char *string,
|
||||
extern int string_has_highlight_regex (const char *string, const char *regex);
|
||||
extern char **string_split (const char *string, const char *separators,
|
||||
int keep_eol, int num_items_max, int *num_items);
|
||||
extern char **string_split_shared (const char *string, const char *separators,
|
||||
int keep_eol, int num_items_max,
|
||||
int *num_items);
|
||||
extern char **string_split_shell (const char *string);
|
||||
extern void string_free_split (char **split_string);
|
||||
extern void string_free_split_shared (char **split_string);
|
||||
extern char *string_build_with_split_string (const char **split_string,
|
||||
const char *separator);
|
||||
extern char **string_split_command (const char *command, char separator);
|
||||
@ -84,5 +88,8 @@ extern char *string_replace_with_callback (const char *string,
|
||||
char *(*callback)(void *data, const char *text),
|
||||
void *callback_data,
|
||||
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 */
|
||||
|
@ -488,6 +488,7 @@ main (int argc, char *argv[])
|
||||
unhook_all (); /* remove all hooks */
|
||||
hdata_end (); /* end hdata */
|
||||
eval_end (); /* end eval */
|
||||
string_end (); /* end string */
|
||||
weechat_shutdown (EXIT_SUCCESS, 0); /* quit WeeChat (oh no, why?) */
|
||||
|
||||
return EXIT_SUCCESS; /* make C compiler happy */
|
||||
|
@ -86,6 +86,40 @@ gui_lines_free (struct t_gui_lines *lines)
|
||||
free (lines);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocates array with tags in a line_data.
|
||||
*/
|
||||
|
||||
void
|
||||
gui_line_tags_alloc (struct t_gui_line_data *line_data, const char *tags)
|
||||
{
|
||||
if (tags)
|
||||
{
|
||||
line_data->tags_array = string_split_shared (tags, ",", 0, 0,
|
||||
&line_data->tags_count);
|
||||
}
|
||||
else
|
||||
{
|
||||
line_data->tags_count = 0;
|
||||
line_data->tags_array = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Frees array with tags in a line_data.
|
||||
*/
|
||||
|
||||
void
|
||||
gui_line_tags_free (struct t_gui_line_data *line_data)
|
||||
{
|
||||
if (line_data->tags_array)
|
||||
{
|
||||
string_free_split_shared (line_data->tags_array);
|
||||
line_data->tags_count = 0;
|
||||
line_data->tags_array = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks if prefix on line is a nick and is the same as nick on previous line.
|
||||
*
|
||||
@ -871,10 +905,9 @@ gui_line_remove_from_list (struct t_gui_buffer *buffer,
|
||||
{
|
||||
if (line->data->str_time)
|
||||
free (line->data->str_time);
|
||||
if (line->data->tags_array)
|
||||
string_free_split (line->data->tags_array);
|
||||
gui_line_tags_free (line->data);
|
||||
if (line->data->prefix)
|
||||
free (line->data->prefix);
|
||||
string_shared_free (line->data->prefix);
|
||||
if (line->data->message)
|
||||
free (line->data->message);
|
||||
free (line->data);
|
||||
@ -1090,19 +1123,10 @@ gui_line_add (struct t_gui_buffer *buffer, time_t date,
|
||||
new_line->data->date = date;
|
||||
new_line->data->date_printed = date_printed;
|
||||
new_line->data->str_time = gui_chat_get_time_string (date);
|
||||
if (tags)
|
||||
{
|
||||
new_line->data->tags_array = string_split (tags, ",", 0, 0,
|
||||
&new_line->data->tags_count);
|
||||
}
|
||||
else
|
||||
{
|
||||
new_line->data->tags_count = 0;
|
||||
new_line->data->tags_array = NULL;
|
||||
}
|
||||
gui_line_tags_alloc (new_line->data, tags);
|
||||
new_line->data->refresh_needed = 0;
|
||||
new_line->data->prefix = (prefix) ?
|
||||
strdup (prefix) : ((date != 0) ? strdup ("") : NULL);
|
||||
(char *)string_shared_get (prefix) : ((date != 0) ? (char *)string_shared_get ("") : NULL);
|
||||
new_line->data->prefix_length = (prefix) ?
|
||||
gui_chat_strlen_screen (prefix) : 0;
|
||||
new_line->data->message = (message) ? strdup (message) : strdup ("");
|
||||
@ -1318,8 +1342,8 @@ void
|
||||
gui_line_clear (struct t_gui_line *line)
|
||||
{
|
||||
if (line->data->prefix)
|
||||
free (line->data->prefix);
|
||||
line->data->prefix = strdup ("");
|
||||
string_shared_free (line->data->prefix);
|
||||
line->data->prefix = (char *)string_shared_get ("");
|
||||
|
||||
if (line->data->message)
|
||||
free (line->data->message);
|
||||
@ -1521,18 +1545,8 @@ gui_line_hdata_line_data_update_cb (void *data,
|
||||
if (hashtable_has_key (hashtable, "tags_array"))
|
||||
{
|
||||
value = hashtable_get (hashtable, "tags_array");
|
||||
if (line_data->tags_array)
|
||||
string_free_split (line_data->tags_array);
|
||||
if (value)
|
||||
{
|
||||
line_data->tags_array = string_split (value, ",", 0, 0,
|
||||
&line_data->tags_count);
|
||||
}
|
||||
else
|
||||
{
|
||||
line_data->tags_count = 0;
|
||||
line_data->tags_array = NULL;
|
||||
}
|
||||
gui_line_tags_free (line_data);
|
||||
gui_line_tags_alloc (line_data, value);
|
||||
rc++;
|
||||
}
|
||||
|
||||
|
@ -268,8 +268,8 @@ gui_nicklist_add_group (struct t_gui_buffer *buffer,
|
||||
if (!new_group)
|
||||
return NULL;
|
||||
|
||||
new_group->name = strdup (name);
|
||||
new_group->color = (color) ? strdup (color) : NULL;
|
||||
new_group->name = (char *)string_shared_get (name);
|
||||
new_group->color = (color) ? (char *)string_shared_get (color) : NULL;
|
||||
new_group->visible = visible;
|
||||
new_group->parent = (parent_group) ? parent_group : buffer->nicklist_root;
|
||||
new_group->level = (new_group->parent) ? new_group->parent->level + 1 : 0;
|
||||
@ -442,10 +442,10 @@ gui_nicklist_add_nick (struct t_gui_buffer *buffer,
|
||||
return NULL;
|
||||
|
||||
new_nick->group = (group) ? group : buffer->nicklist_root;
|
||||
new_nick->name = strdup (name);
|
||||
new_nick->color = (color) ? strdup (color) : NULL;
|
||||
new_nick->prefix = (prefix) ? strdup (prefix) : NULL;
|
||||
new_nick->prefix_color = (prefix_color) ? strdup (prefix_color) : NULL;
|
||||
new_nick->name = (char *)string_shared_get (name);
|
||||
new_nick->color = (color) ? (char *)string_shared_get (color) : NULL;
|
||||
new_nick->prefix = (prefix) ? (char *)string_shared_get (prefix) : NULL;
|
||||
new_nick->prefix_color = (prefix_color) ? (char *)string_shared_get (prefix_color) : NULL;
|
||||
new_nick->visible = visible;
|
||||
|
||||
gui_nicklist_insert_nick_sorted (new_nick->group, new_nick);
|
||||
@ -495,13 +495,13 @@ gui_nicklist_remove_nick (struct t_gui_buffer *buffer,
|
||||
|
||||
/* free data */
|
||||
if (nick->name)
|
||||
free (nick->name);
|
||||
string_shared_free (nick->name);
|
||||
if (nick->color)
|
||||
free (nick->color);
|
||||
string_shared_free (nick->color);
|
||||
if (nick->prefix)
|
||||
free (nick->prefix);
|
||||
string_shared_free (nick->prefix);
|
||||
if (nick->prefix_color)
|
||||
free (nick->prefix_color);
|
||||
string_shared_free (nick->prefix_color);
|
||||
|
||||
buffer->nicklist_count--;
|
||||
buffer->nicklist_nicks_count--;
|
||||
@ -575,9 +575,9 @@ gui_nicklist_remove_group (struct t_gui_buffer *buffer,
|
||||
|
||||
/* free data */
|
||||
if (group->name)
|
||||
free (group->name);
|
||||
string_shared_free (group->name);
|
||||
if (group->color)
|
||||
free (group->color);
|
||||
string_shared_free (group->color);
|
||||
|
||||
if (group->visible)
|
||||
{
|
||||
@ -884,8 +884,8 @@ gui_nicklist_group_set (struct t_gui_buffer *buffer,
|
||||
if (string_strcasecmp (property, "color") == 0)
|
||||
{
|
||||
if (group->color)
|
||||
free (group->color);
|
||||
group->color = (value[0]) ? strdup (value) : NULL;
|
||||
string_shared_free (group->color);
|
||||
group->color = (value[0]) ? (char *)string_shared_get (value) : NULL;
|
||||
group_changed = 1;
|
||||
}
|
||||
else if (string_strcasecmp (property, "visible") == 0)
|
||||
@ -995,22 +995,22 @@ gui_nicklist_nick_set (struct t_gui_buffer *buffer,
|
||||
if (string_strcasecmp (property, "color") == 0)
|
||||
{
|
||||
if (nick->color)
|
||||
free (nick->color);
|
||||
nick->color = (value[0]) ? strdup (value) : NULL;
|
||||
string_shared_free (nick->color);
|
||||
nick->color = (value[0]) ? (char *)string_shared_get (value) : NULL;
|
||||
nick_changed = 1;
|
||||
}
|
||||
else if (string_strcasecmp (property, "prefix") == 0)
|
||||
{
|
||||
if (nick->prefix)
|
||||
free (nick->prefix);
|
||||
nick->prefix = (value[0]) ? strdup (value) : NULL;
|
||||
string_shared_free (nick->prefix);
|
||||
nick->prefix = (value[0]) ? (char *)string_shared_get (value) : NULL;
|
||||
nick_changed = 1;
|
||||
}
|
||||
else if (string_strcasecmp (property, "prefix_color") == 0)
|
||||
{
|
||||
if (nick->prefix_color)
|
||||
free (nick->prefix_color);
|
||||
nick->prefix_color = (value[0]) ? strdup (value) : NULL;
|
||||
string_shared_free (nick->prefix_color);
|
||||
nick->prefix_color = (value[0]) ? (char *)string_shared_get (value) : NULL;
|
||||
nick_changed = 1;
|
||||
}
|
||||
else if (string_strcasecmp (property, "visible") == 0)
|
||||
|
Loading…
x
Reference in New Issue
Block a user