From 36cac270be19b769530a7b68ca6d4e3b79452e41 Mon Sep 17 00:00:00 2001 From: Not Zed Date: Thu, 26 Apr 2001 01:07:44 +0000 Subject: Modified to match e_strv_get behaviour. Assert on bad cases, and return "" 2001-04-26 Not Zed * e-memory.c (e_poolv_get): Modified to match e_strv_get behaviour. Assert on bad cases, and return "" rather than NULL for empty/unset strings. (e_poolv_new): Simplify the mutex cases, only have a single mutex for mempool and hashtable references. (e_poolv_set): Simplify mutex code. Fixed a #ifdef orde prob in profile stuff. Always copy string to our own memory when we add it to the hash, even if we free it; I think this would have led to dangling references otherwise. (e_poolv_destroy): Renamed from poolv_free, for consistency with the rest of the allocators here. (e_poolv_set): Add optional refcounting code here, not currently enabled/used, but should make proper string collection work easily. (e_poolv_destroy): Unrefcount the strings here. (e_poolv_cpy): Add refcounting code here. * e-memory.[ch]: Applied Jacob's patches for 'e-poolv' type, added his name to the authors list. svn path=/trunk/; revision=9570 --- e-util/ChangeLog | 22 ++++ e-util/e-memory.c | 332 +++++++++++++++++++++++++++++++++++++++++++++++++++++- e-util/e-memory.h | 14 ++- 3 files changed, 362 insertions(+), 6 deletions(-) diff --git a/e-util/ChangeLog b/e-util/ChangeLog index 61743701ad..b907b19289 100644 --- a/e-util/ChangeLog +++ b/e-util/ChangeLog @@ -1,3 +1,25 @@ +2001-04-26 Not Zed + + * e-memory.c (e_poolv_get): Modified to match e_strv_get + behaviour. Assert on bad cases, and return "" rather than NULL + for empty/unset strings. + (e_poolv_new): Simplify the mutex cases, only have a single mutex + for mempool and hashtable references. + (e_poolv_set): Simplify mutex code. Fixed a #ifdef orde prob in + profile stuff. Always copy string to our own memory when we add + it to the hash, even if we free it; I think this would have led to + dangling references otherwise. + (e_poolv_destroy): Renamed from poolv_free, for consistency with + the rest of the allocators here. + (e_poolv_set): Add optional refcounting code here, not currently + enabled/used, but should make proper string collection work + easily. + (e_poolv_destroy): Unrefcount the strings here. + (e_poolv_cpy): Add refcounting code here. + + * e-memory.[ch]: Applied Jacob's patches for + 'e-poolv' type, added his name to the authors list. + 2001-04-24 Dan Winship * e-html-utils.c (check_size): If the buffer is too small, making diff --git a/e-util/e-memory.c b/e-util/e-memory.c index 80bd845777..91a625ccc7 100644 --- a/e-util/e-memory.c +++ b/e-util/e-memory.c @@ -1,7 +1,8 @@ /* - * Copyright (c) 2000 Helix Code Inc. + * Copyright (c) 2000, 2001 Helix Code Inc. * - * Author: Michael Zucchi + * Authors: Michael Zucchi + * Jacob Berkman * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -27,6 +28,17 @@ #include #define s(x) /* strv debug */ +#define p(x) /* poolv debug */ +#define p2(x) /* poolv assertion checking */ + +/* #define PROFILE_POOLV */ + +#ifdef PROFILE_POOLV +#include +#define pp(x) x +#else +#define pp(x) +#endif /*#define TIMEIT*/ @@ -588,7 +600,7 @@ e_strv_set_ref(struct _EStrv *strv, int index, char *str) { struct _e_strvunpacked *s; - s(printf("set ref %d '%s'\n ", index, str)); + s(printf("set ref %d '%s'\nawkmeharder: %s\n ", index, str, str)); if (strv->length != STRV_UNPACKED) s = strv_unpack(strv); @@ -621,7 +633,7 @@ e_strv_set_ref_free(struct _EStrv *strv, int index, char *str) { struct _e_strvunpacked *s; - s(printf("set ref %d '%s'\n ", index, str)); + s(printf("set ref %d '%s'\nawkmeevenharder: %s\n ", index, str, str)); if (strv->length != STRV_UNPACKED) s = strv_unpack(strv); @@ -785,6 +797,318 @@ e_strv_destroy(struct _EStrv *strv) g_free(strv); } + + +/* string pool stuff */ + +/* TODO: + garbage collection, using the following technique: + Create a memchunk for each possible size of poolv, and allocate every poolv from those + To garbage collect, scan all memchunk internally, ignoring any free areas (or mark each + poolv when freeing it - set length 0?), and find out which strings are not anywhere, + then free them. + + OR: + Just keep a refcount in the hashtable, instead of duplicating the key pointer. + + either would also require a free for the mempool, so ignore it for now */ + +/*#define POOLV_REFCNT*/ /* Define to enable refcounting code that does + automatic garbage collection of unused strings */ + +static GHashTable *poolv_pool = NULL; +static EMemPool *poolv_mempool = NULL; + +#ifdef PROFILE_POOLV +static gulong poolv_hits = 0; +static gulong poolv_misses = 0; +#endif + +#ifdef G_THREADS_ENABLED +static GStaticMutex poolv_mutex = G_STATIC_MUTEX_INIT; +#endif + +struct _EPoolv { + unsigned char length; + char *s[1]; +}; + +/** + * e_poolv_new: @size: The number of elements in the poolv, maximum of 254 elements. + * + * create a new poolv (string vector which shares a global string + * pool). poolv's can be used to work with arrays of strings which + * save memory by eliminating duplicated allocations of the same + * string. + * + * this is useful when you have a log of read-only strings that do not + * go away and are duplicated a lot (such as email headers). + * + * we should probably in the future ref count the strings contained in + * the hash table, but for now let's not. + * + * Return value: new pooled string vector + **/ +EPoolv * +e_poolv_new(unsigned int size) +{ + EPoolv *poolv; + + g_assert(size < 255); + +#ifdef G_THREADS_ENABLED + g_static_mutex_lock(&poolv_mutex); +#endif + if (!poolv_pool) + poolv_pool = g_hash_table_new(g_str_hash, g_str_equal); + + if (!poolv_mempool) + poolv_mempool = e_mempool_new(32 * 1024, 512, E_MEMPOOL_ALIGN_BYTE); + +#ifdef G_THREADS_ENABLED + g_static_mutex_unlock(&poolv_mutex); +#endif + + poolv = g_malloc0(sizeof (*poolv) + (size - 1) * sizeof (char *)); + poolv->length = size; + + p(g_print ("new poolv=%p\tsize=%d\n", poolv, sizeof(*poolv) + (size-1)*sizeof(char *))); + + return poolv; +} + +/** + * e_poolv_cpy: + * @dest: destination pooled string vector + * @src: source pooled string vector + * + * Copy the contents of a pooled string vector + * + * Return value: @dest + **/ +EPoolv * +e_poolv_cpy(EPoolv *dest, const EPoolv *src) +{ +#ifdef POOLV_REFCNT + int i; + unsigned int ref; + char *key; +#endif + + p2(g_return_val_if_fail (dest != NULL, NULL)); + p2(g_return_val_if_fail (src != NULL, NULL)); + + if (dest->length != src->length) { + e_poolv_destroy(dest); + dest = e_poolv_new(src->length); + } + +#ifdef POOLV_REFCNT +#ifdef G_THREADS_ENABLED + g_static_mutex_lock(&poolv_mutex); +#endif + /* ref new copies */ + for (i=0;ilength;i++) { + if (src->s[i]) { + if (g_hash_table_lookup_extended(poolv_pool, src->s[i], (void **)&key, (void **)&ref)) { + g_hash_table_insert(poolv_pool, key, (void *)(ref+1)); + } else { + g_assert_not_reached(); + } + } + } + + /* unref the old ones */ + for (i=0;ilength;i++) { + if (dest->s[i]) { + if (g_hash_table_lookup_extended(poolv_pool, dest->s[i], (void **)&key, (void **)&ref)) { + /* if ref == 1 free it */ + g_assert(ref > 0); + g_hash_table_insert(poolv_pool, key, (void *)(ref-1)); + } else { + g_assert_not_reached(); + } + } + } +#ifdef G_THREADS_ENABLED + g_static_mutex_unlock(&poolv_mutex); +#endif +#endif + + memcpy(dest->s, src->s, src->length * sizeof (char *)); + + return dest; +} + +#ifdef PROFILE_POOLV +static void +poolv_profile_update (void) +{ + static time_t last_time = 0; + time_t new_time; + + new_time = time (NULL); + if (new_time - last_time < 5) + return; + + printf("poolv profile: %lu hits, %lu misses: %d%% hit rate\n", + poolv_hits, poolv_misses, + (int)(100.0 * ((double) poolv_hits / (double) (poolv_hits + poolv_misses)))); + + last_time = new_time; +} +#endif + +/** + * e_poolv_set: + * @poolv: pooled string vector + * @index: index in vector of string + * @str: string to set + * @freeit: whether the caller is releasing its reference to the + * string + * + * Set a string vector reference. If the caller will no longer be + * referencing the string, freeit should be TRUE. Otherwise, this + * will duplicate the string if it is not found in the pool. + * + * Return value: @poolv + **/ +EPoolv * +e_poolv_set (EPoolv *poolv, int index, char *str, int freeit) +{ +#ifdef POOLV_REFCNT + unsigned int ref; + char *key; +#endif + + p2(g_return_val_if_fail (poolv != NULL, NULL)); + + g_assert(index >=0 && index < poolv->length); + + p(g_print ("setting %d `%s'\n", index, str)); + + if (!str) { +#ifdef POOLV_REFCNT + if (poolv->s[index]) { + if (g_hash_table_lookup_extended(poolv_pool, poolv->s[index], (void **)&key, (void **)&ref)) { + g_assert(ref > 0); + g_hash_table_insert(poolv_pool, key, (void *)(ref-1)); + } else { + g_assert_not_reached(); + } + } +#endif + poolv->s[index] = NULL; + return poolv; + } + +#ifdef G_THREADS_ENABLED + g_static_mutex_lock(&poolv_mutex); +#endif + +#ifdef POOLV_REFCNT + if (g_hash_table_lookup_extended(poolv_pool, str, (void **)&key, (void **)&ref)) { + g_hash_table_insert(poolv_pool, key, (void *)(ref+1)); + poolv->s[index] = key; +# ifdef PROFILE_POOLV + poolv_hits++; + poolv_profile_update (); +# endif + } else { +# ifdef PROFILE_POOLV + poolv_misses++; + poolv_profile_update (); +# endif + poolv->s[index] = e_mempool_strdup(poolv_mempool, str); + g_hash_table_insert(poolv_pool, poolv->s[index], (void *)1); + } + +#else /* !POOLV_REFCNT */ + if ((poolv->s[index] = g_hash_table_lookup(poolv_pool, str)) != NULL) { +# ifdef PROFILE_POOLV + poolv_hits++; + poolv_profile_update (); +# endif + } else { +# ifdef PROFILE_POOLV + poolv_misses++; + poolv_profile_update (); +# endif + poolv->s[index] = e_mempool_strdup(poolv_mempool, str); + g_hash_table_insert(poolv_pool, poolv->s[index], poolv->s[index]); + } +#endif /* !POOLV_REFCNT */ + +#ifdef G_THREADS_ENABLED + g_static_mutex_unlock(&poolv_mutex); +#endif + + if (freeit) + g_free(str); + + return poolv; +} + +/** + * e_poolv_get: + * @poolv: pooled string vector + * @index: index in vector of string + * + * Retrieve a string by index. This could possibly just be a macro. + * + * Since the pool is never freed, this string does not need to be + * duplicated, but should not be modified. + * + * Return value: string at that index. + **/ +const char * +e_poolv_get(EPoolv *poolv, int index) +{ + g_assert(poolv != NULL); + g_assert(index>= 0 && index < poolv->length); + + p(g_print ("get %d = `%s'\n", index, poolv->s[index])); + + return poolv->s[index]?poolv->s[index]:""; +} + +/** + * e_poolv_destroy: + * @poolv: pooled string vector to free + * + * Free a pooled string vector. This doesn't free the strings from + * the vector, however. + **/ +void +e_poolv_destroy(EPoolv *poolv) +{ +#ifdef POOLV_REFCNT + int i; + unsigned int ref; + char *key; + +#ifdef G_THREADS_ENABLED + g_static_mutex_lock(&poolv_mutex); +#endif + for (i=0;ilength;i++) { + if (poolv->s[i]) { + if (g_hash_table_lookup_extended(poolv_pool, poolv->s[i], (void **)&key, (void **)&ref)) { + /* if ref == 1 free it */ + g_assert(ref > 0); + g_hash_table_insert(poolv_pool, key, (void *)(ref-1)); + } else { + g_assert_not_reached(); + } + } + } +#ifdef G_THREADS_ENABLED + g_static_mutex_unlock(&poolv_mutex); +#endif +#endif + + g_free(poolv); +} + #if 0 #define CHUNK_SIZE (20) diff --git a/e-util/e-memory.h b/e-util/e-memory.h index 911bb36b7a..c271914d5f 100644 --- a/e-util/e-memory.h +++ b/e-util/e-memory.h @@ -1,7 +1,8 @@ /* - * Copyright (C) 2000, Helix Code Inc. + * Copyright (C) 2001, Helix Code Inc. * - * Author: Michael Zucchi + * Authors: Michael Zucchi + * Jacob Berkman * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -62,4 +63,13 @@ EStrv *e_strv_pack(EStrv *strv); char *e_strv_get(EStrv *strv, int index); void e_strv_destroy(EStrv *strv); +/* poolv's are similar to strv's, but they store common strings */ +typedef struct _EPoolv EPoolv; + +EPoolv *e_poolv_new(unsigned int size); +EPoolv *e_poolv_cpy(EPoolv *dest, const EPoolv *src); +EPoolv *e_poolv_set(EPoolv *poolv, int index, char *str, int freeit); +const char *e_poolv_get(EPoolv *poolv, int index); +void e_poolv_destroy(EPoolv *poolv); + #endif /* ! _E_MEMORY_H */ -- cgit