diff options
author | Not Zed <NotZed@Ximian.com> | 2001-04-26 09:07:44 +0800 |
---|---|---|
committer | Michael Zucci <zucchi@src.gnome.org> | 2001-04-26 09:07:44 +0800 |
commit | 36cac270be19b769530a7b68ca6d4e3b79452e41 (patch) | |
tree | 6130550502a82dada101bc0b19a8155c6cd49403 /e-util/e-memory.c | |
parent | 4994d35be03b281011c6ce5e8ff5489eed7486d6 (diff) | |
download | gsoc2013-evolution-36cac270be19b769530a7b68ca6d4e3b79452e41.tar.gz gsoc2013-evolution-36cac270be19b769530a7b68ca6d4e3b79452e41.tar.zst gsoc2013-evolution-36cac270be19b769530a7b68ca6d4e3b79452e41.zip |
Modified to match e_strv_get behaviour. Assert on bad cases, and return ""
2001-04-26 Not Zed <NotZed@Ximian.com>
* 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 <jacob@ximian.com> for
'e-poolv' type, added his name to the authors list.
svn path=/trunk/; revision=9570
Diffstat (limited to 'e-util/e-memory.c')
-rw-r--r-- | e-util/e-memory.c | 332 |
1 files changed, 328 insertions, 4 deletions
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 <notzed@helixcode.com> + * Authors: Michael Zucchi <notzed@ximian.com> + * Jacob Berkman <jacob@ximian.com> * * 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 <glib.h> #define s(x) /* strv debug */ +#define p(x) /* poolv debug */ +#define p2(x) /* poolv assertion checking */ + +/* #define PROFILE_POOLV */ + +#ifdef PROFILE_POOLV +#include <time.h> +#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;i<src->length;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;i<dest->length;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;i<poolv->length;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) |