mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-04-22 23:29:16 +02:00
Rewritten to cache iconv conversion descriptors. On at least some Unix
2002-01-21 Jeffrey Stedfast <fejj@ximian.com> * glib/gconvert.c (open_converter): Rewritten to cache iconv conversion descriptors. On at least some Unix systems like Solaris, iconv_open() must dlopen the necessary charset modules in order to setup the descriptor. This can take a major toll on performace if you are constantly opening and closing conversion descriptors for the same charset conversions over and over. (g_convert_with_fallback): Use close_converter() rather than g_iconv_close() since open_converter() now caches iconv descriptors.
This commit is contained in:
parent
5a7cf7fa60
commit
0751448ad7
12
ChangeLog
12
ChangeLog
@ -1,3 +1,15 @@
|
|||||||
|
2002-01-21 Jeffrey Stedfast <fejj@ximian.com>
|
||||||
|
|
||||||
|
* glib/gconvert.c (open_converter): Rewritten to cache iconv
|
||||||
|
conversion descriptors. On at least some Unix systems like
|
||||||
|
Solaris, iconv_open() must dlopen the necessary charset modules in
|
||||||
|
order to setup the descriptor. This can take a major toll on
|
||||||
|
performace if you are constantly opening and closing conversion
|
||||||
|
descriptors for the same charset conversions over and over.
|
||||||
|
(g_convert_with_fallback): Use close_converter() rather than
|
||||||
|
g_iconv_close() since open_converter() now caches iconv
|
||||||
|
descriptors.
|
||||||
|
|
||||||
Tue Jan 29 11:18:44 2002 Owen Taylor <otaylor@redhat.com>
|
Tue Jan 29 11:18:44 2002 Owen Taylor <otaylor@redhat.com>
|
||||||
|
|
||||||
* 1.3.13
|
* 1.3.13
|
||||||
|
@ -1,3 +1,15 @@
|
|||||||
|
2002-01-21 Jeffrey Stedfast <fejj@ximian.com>
|
||||||
|
|
||||||
|
* glib/gconvert.c (open_converter): Rewritten to cache iconv
|
||||||
|
conversion descriptors. On at least some Unix systems like
|
||||||
|
Solaris, iconv_open() must dlopen the necessary charset modules in
|
||||||
|
order to setup the descriptor. This can take a major toll on
|
||||||
|
performace if you are constantly opening and closing conversion
|
||||||
|
descriptors for the same charset conversions over and over.
|
||||||
|
(g_convert_with_fallback): Use close_converter() rather than
|
||||||
|
g_iconv_close() since open_converter() now caches iconv
|
||||||
|
descriptors.
|
||||||
|
|
||||||
Tue Jan 29 11:18:44 2002 Owen Taylor <otaylor@redhat.com>
|
Tue Jan 29 11:18:44 2002 Owen Taylor <otaylor@redhat.com>
|
||||||
|
|
||||||
* 1.3.13
|
* 1.3.13
|
||||||
|
@ -1,3 +1,15 @@
|
|||||||
|
2002-01-21 Jeffrey Stedfast <fejj@ximian.com>
|
||||||
|
|
||||||
|
* glib/gconvert.c (open_converter): Rewritten to cache iconv
|
||||||
|
conversion descriptors. On at least some Unix systems like
|
||||||
|
Solaris, iconv_open() must dlopen the necessary charset modules in
|
||||||
|
order to setup the descriptor. This can take a major toll on
|
||||||
|
performace if you are constantly opening and closing conversion
|
||||||
|
descriptors for the same charset conversions over and over.
|
||||||
|
(g_convert_with_fallback): Use close_converter() rather than
|
||||||
|
g_iconv_close() since open_converter() now caches iconv
|
||||||
|
descriptors.
|
||||||
|
|
||||||
Tue Jan 29 11:18:44 2002 Owen Taylor <otaylor@redhat.com>
|
Tue Jan 29 11:18:44 2002 Owen Taylor <otaylor@redhat.com>
|
||||||
|
|
||||||
* 1.3.13
|
* 1.3.13
|
||||||
|
@ -1,3 +1,15 @@
|
|||||||
|
2002-01-21 Jeffrey Stedfast <fejj@ximian.com>
|
||||||
|
|
||||||
|
* glib/gconvert.c (open_converter): Rewritten to cache iconv
|
||||||
|
conversion descriptors. On at least some Unix systems like
|
||||||
|
Solaris, iconv_open() must dlopen the necessary charset modules in
|
||||||
|
order to setup the descriptor. This can take a major toll on
|
||||||
|
performace if you are constantly opening and closing conversion
|
||||||
|
descriptors for the same charset conversions over and over.
|
||||||
|
(g_convert_with_fallback): Use close_converter() rather than
|
||||||
|
g_iconv_close() since open_converter() now caches iconv
|
||||||
|
descriptors.
|
||||||
|
|
||||||
Tue Jan 29 11:18:44 2002 Owen Taylor <otaylor@redhat.com>
|
Tue Jan 29 11:18:44 2002 Owen Taylor <otaylor@redhat.com>
|
||||||
|
|
||||||
* 1.3.13
|
* 1.3.13
|
||||||
|
@ -1,3 +1,15 @@
|
|||||||
|
2002-01-21 Jeffrey Stedfast <fejj@ximian.com>
|
||||||
|
|
||||||
|
* glib/gconvert.c (open_converter): Rewritten to cache iconv
|
||||||
|
conversion descriptors. On at least some Unix systems like
|
||||||
|
Solaris, iconv_open() must dlopen the necessary charset modules in
|
||||||
|
order to setup the descriptor. This can take a major toll on
|
||||||
|
performace if you are constantly opening and closing conversion
|
||||||
|
descriptors for the same charset conversions over and over.
|
||||||
|
(g_convert_with_fallback): Use close_converter() rather than
|
||||||
|
g_iconv_close() since open_converter() now caches iconv
|
||||||
|
descriptors.
|
||||||
|
|
||||||
Tue Jan 29 11:18:44 2002 Owen Taylor <otaylor@redhat.com>
|
Tue Jan 29 11:18:44 2002 Owen Taylor <otaylor@redhat.com>
|
||||||
|
|
||||||
* 1.3.13
|
* 1.3.13
|
||||||
|
@ -1,3 +1,15 @@
|
|||||||
|
2002-01-21 Jeffrey Stedfast <fejj@ximian.com>
|
||||||
|
|
||||||
|
* glib/gconvert.c (open_converter): Rewritten to cache iconv
|
||||||
|
conversion descriptors. On at least some Unix systems like
|
||||||
|
Solaris, iconv_open() must dlopen the necessary charset modules in
|
||||||
|
order to setup the descriptor. This can take a major toll on
|
||||||
|
performace if you are constantly opening and closing conversion
|
||||||
|
descriptors for the same charset conversions over and over.
|
||||||
|
(g_convert_with_fallback): Use close_converter() rather than
|
||||||
|
g_iconv_close() since open_converter() now caches iconv
|
||||||
|
descriptors.
|
||||||
|
|
||||||
Tue Jan 29 11:18:44 2002 Owen Taylor <otaylor@redhat.com>
|
Tue Jan 29 11:18:44 2002 Owen Taylor <otaylor@redhat.com>
|
||||||
|
|
||||||
* 1.3.13
|
* 1.3.13
|
||||||
|
@ -1,3 +1,15 @@
|
|||||||
|
2002-01-21 Jeffrey Stedfast <fejj@ximian.com>
|
||||||
|
|
||||||
|
* glib/gconvert.c (open_converter): Rewritten to cache iconv
|
||||||
|
conversion descriptors. On at least some Unix systems like
|
||||||
|
Solaris, iconv_open() must dlopen the necessary charset modules in
|
||||||
|
order to setup the descriptor. This can take a major toll on
|
||||||
|
performace if you are constantly opening and closing conversion
|
||||||
|
descriptors for the same charset conversions over and over.
|
||||||
|
(g_convert_with_fallback): Use close_converter() rather than
|
||||||
|
g_iconv_close() since open_converter() now caches iconv
|
||||||
|
descriptors.
|
||||||
|
|
||||||
Tue Jan 29 11:18:44 2002 Owen Taylor <otaylor@redhat.com>
|
Tue Jan 29 11:18:44 2002 Owen Taylor <otaylor@redhat.com>
|
||||||
|
|
||||||
* 1.3.13
|
* 1.3.13
|
||||||
|
@ -1,3 +1,15 @@
|
|||||||
|
2002-01-21 Jeffrey Stedfast <fejj@ximian.com>
|
||||||
|
|
||||||
|
* glib/gconvert.c (open_converter): Rewritten to cache iconv
|
||||||
|
conversion descriptors. On at least some Unix systems like
|
||||||
|
Solaris, iconv_open() must dlopen the necessary charset modules in
|
||||||
|
order to setup the descriptor. This can take a major toll on
|
||||||
|
performace if you are constantly opening and closing conversion
|
||||||
|
descriptors for the same charset conversions over and over.
|
||||||
|
(g_convert_with_fallback): Use close_converter() rather than
|
||||||
|
g_iconv_close() since open_converter() now caches iconv
|
||||||
|
descriptors.
|
||||||
|
|
||||||
Tue Jan 29 11:18:44 2002 Owen Taylor <otaylor@redhat.com>
|
Tue Jan 29 11:18:44 2002 Owen Taylor <otaylor@redhat.com>
|
||||||
|
|
||||||
* 1.3.13
|
* 1.3.13
|
||||||
|
249
glib/gconvert.c
249
glib/gconvert.c
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include <iconv.h>
|
#include <iconv.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
@ -187,15 +188,201 @@ g_iconv_close (GIConv converter)
|
|||||||
return iconv_close (cd);
|
return iconv_close (cd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define ICONV_CACHE_SIZE (16)
|
||||||
|
|
||||||
|
struct _iconv_cache_bucket {
|
||||||
|
gchar *key;
|
||||||
|
guint32 refcount;
|
||||||
|
gboolean used;
|
||||||
|
iconv_t cd;
|
||||||
|
};
|
||||||
|
|
||||||
|
static GList *iconv_cache_list;
|
||||||
|
static GHashTable *iconv_cache;
|
||||||
|
static GHashTable *iconv_open_hash;
|
||||||
|
static guint iconv_cache_size = 0;
|
||||||
|
G_LOCK_DEFINE_STATIC (iconv_cache_lock);
|
||||||
|
|
||||||
|
/* caller *must* hold the iconv_cache_lock */
|
||||||
|
static void
|
||||||
|
iconv_cache_init (void)
|
||||||
|
{
|
||||||
|
static gboolean initialized = FALSE;
|
||||||
|
|
||||||
|
if (initialized)
|
||||||
|
return;
|
||||||
|
|
||||||
|
iconv_cache_list = NULL;
|
||||||
|
iconv_cache = g_hash_table_new (g_str_hash, g_str_equal);
|
||||||
|
iconv_open_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
|
||||||
|
|
||||||
|
initialized = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iconv_cache_bucket_new:
|
||||||
|
* @key: cache key
|
||||||
|
* @cd: iconv descriptor
|
||||||
|
*
|
||||||
|
* Creates a new cache bucket, inserts it into the cache and
|
||||||
|
* increments the cache size.
|
||||||
|
*
|
||||||
|
* Returns a pointer to the newly allocated cache bucket.
|
||||||
|
**/
|
||||||
|
struct _iconv_cache_bucket *
|
||||||
|
iconv_cache_bucket_new (const gchar *key, iconv_t cd)
|
||||||
|
{
|
||||||
|
struct _iconv_cache_bucket *bucket;
|
||||||
|
|
||||||
|
bucket = g_new (struct _iconv_cache_bucket, 1);
|
||||||
|
bucket->key = g_strdup (key);
|
||||||
|
bucket->refcount = 1;
|
||||||
|
bucket->used = TRUE;
|
||||||
|
bucket->cd = cd;
|
||||||
|
|
||||||
|
g_hash_table_insert (iconv_cache, bucket->key, bucket);
|
||||||
|
|
||||||
|
/* FIXME: if we sorted the list so items with few refcounts were
|
||||||
|
first, then we could expire them faster in iconv_cache_expire_unused () */
|
||||||
|
iconv_cache_list = g_list_prepend (iconv_cache_list, bucket);
|
||||||
|
|
||||||
|
iconv_cache_size++;
|
||||||
|
|
||||||
|
return bucket;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iconv_cache_bucket_expire:
|
||||||
|
* @node: cache bucket's node
|
||||||
|
* @bucket: cache bucket
|
||||||
|
*
|
||||||
|
* Expires a single cache bucket @bucket. This should only ever be
|
||||||
|
* called on a bucket that currently has no used iconv descriptors
|
||||||
|
* open.
|
||||||
|
*
|
||||||
|
* @node is not a required argument. If @node is not supplied, we
|
||||||
|
* search for it ourselves.
|
||||||
|
**/
|
||||||
|
static void
|
||||||
|
iconv_cache_bucket_expire (GList *node, struct _iconv_cache_bucket *bucket)
|
||||||
|
{
|
||||||
|
g_hash_table_remove (iconv_cache, bucket->key);
|
||||||
|
|
||||||
|
if (node == NULL)
|
||||||
|
node = g_list_find (iconv_cache_list, bucket);
|
||||||
|
|
||||||
|
g_assert (node != NULL);
|
||||||
|
|
||||||
|
if (node->prev)
|
||||||
|
{
|
||||||
|
node->prev->next = node->next;
|
||||||
|
if (node->next)
|
||||||
|
node->next->prev = node->prev;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
iconv_cache_list = node->next;
|
||||||
|
if (node->next)
|
||||||
|
node->next->prev = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_list_free_1 (node);
|
||||||
|
|
||||||
|
g_free (bucket->key);
|
||||||
|
g_iconv_close (bucket->cd);
|
||||||
|
g_free (bucket);
|
||||||
|
|
||||||
|
iconv_cache_size--;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iconv_cache_expire_unused:
|
||||||
|
*
|
||||||
|
* Expires as many unused cache buckets as it needs to in order to get
|
||||||
|
* the total number of buckets < ICONV_CACHE_SIZE.
|
||||||
|
**/
|
||||||
|
static void
|
||||||
|
iconv_cache_expire_unused (void)
|
||||||
|
{
|
||||||
|
struct _iconv_cache_bucket *bucket;
|
||||||
|
GList *node, *next;
|
||||||
|
|
||||||
|
node = iconv_cache_list;
|
||||||
|
while (node && iconv_cache_size >= ICONV_CACHE_SIZE)
|
||||||
|
{
|
||||||
|
next = node->next;
|
||||||
|
|
||||||
|
bucket = node->data;
|
||||||
|
if (bucket->refcount == 0)
|
||||||
|
iconv_cache_bucket_expire (node, bucket);
|
||||||
|
|
||||||
|
node = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static GIConv
|
static GIConv
|
||||||
open_converter (const gchar *to_codeset,
|
open_converter (const gchar *to_codeset,
|
||||||
const gchar *from_codeset,
|
const gchar *from_codeset,
|
||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
GIConv cd = g_iconv_open (to_codeset, from_codeset);
|
struct _iconv_cache_bucket *bucket;
|
||||||
|
gchar *key;
|
||||||
|
GIConv cd;
|
||||||
|
|
||||||
if (cd == (iconv_t) -1)
|
/* create our key */
|
||||||
|
key = g_alloca (strlen (from_codeset) + strlen (to_codeset) + 2);
|
||||||
|
sprintf (key, "%s:%s", from_codeset, to_codeset);
|
||||||
|
|
||||||
|
G_LOCK (iconv_cache_lock);
|
||||||
|
|
||||||
|
/* make sure the cache has been initialized */
|
||||||
|
iconv_cache_init ();
|
||||||
|
|
||||||
|
bucket = g_hash_table_lookup (iconv_cache, key);
|
||||||
|
if (bucket)
|
||||||
{
|
{
|
||||||
|
if (bucket->used)
|
||||||
|
{
|
||||||
|
cd = g_iconv_open (to_codeset, from_codeset);
|
||||||
|
if (cd == (iconv_t) -1)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cd = bucket->cd;
|
||||||
|
bucket->used = TRUE;
|
||||||
|
|
||||||
|
/* reset the descriptor */
|
||||||
|
g_iconv (cd, NULL, NULL, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
bucket->refcount++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cd = g_iconv_open (to_codeset, from_codeset);
|
||||||
|
if (cd == (iconv_t) -1)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
iconv_cache_expire_unused ();
|
||||||
|
|
||||||
|
bucket = iconv_cache_bucket_new (key, cd);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_hash_table_insert (iconv_open_hash, cd, bucket->key);
|
||||||
|
|
||||||
|
G_UNLOCK (iconv_cache_lock);
|
||||||
|
|
||||||
|
return cd;
|
||||||
|
|
||||||
|
error:
|
||||||
|
|
||||||
|
G_UNLOCK (iconv_cache_lock);
|
||||||
|
|
||||||
/* Something went wrong. */
|
/* Something went wrong. */
|
||||||
if (errno == EINVAL)
|
if (errno == EINVAL)
|
||||||
g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_NO_CONVERSION,
|
g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_NO_CONVERSION,
|
||||||
@ -205,12 +392,60 @@ open_converter (const gchar *to_codeset,
|
|||||||
g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED,
|
g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED,
|
||||||
_("Could not open converter from '%s' to '%s': %s"),
|
_("Could not open converter from '%s' to '%s': %s"),
|
||||||
from_codeset, to_codeset, strerror (errno));
|
from_codeset, to_codeset, strerror (errno));
|
||||||
}
|
|
||||||
|
|
||||||
return cd;
|
return cd;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
close_converter (GIConv converter)
|
||||||
|
{
|
||||||
|
struct _iconv_cache_bucket *bucket;
|
||||||
|
const gchar *key;
|
||||||
|
iconv_t cd;
|
||||||
|
|
||||||
|
cd = (iconv_t) converter;
|
||||||
|
|
||||||
|
if (cd == (iconv_t) -1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
G_LOCK (iconv_cache_lock);
|
||||||
|
|
||||||
|
key = g_hash_table_lookup (iconv_open_hash, cd);
|
||||||
|
if (key)
|
||||||
|
{
|
||||||
|
g_hash_table_remove (iconv_open_hash, cd);
|
||||||
|
|
||||||
|
bucket = g_hash_table_lookup (iconv_cache, key);
|
||||||
|
g_assert (bucket);
|
||||||
|
|
||||||
|
bucket->refcount--;
|
||||||
|
|
||||||
|
if (cd == bucket->cd)
|
||||||
|
bucket->used = FALSE;
|
||||||
|
else
|
||||||
|
g_iconv_close (cd);
|
||||||
|
|
||||||
|
if (!bucket->refcount && iconv_cache_size > ICONV_CACHE_SIZE)
|
||||||
|
{
|
||||||
|
/* expire this cache bucket */
|
||||||
|
iconv_cache_bucket_expire (NULL, bucket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
G_UNLOCK (iconv_cache_lock);
|
||||||
|
|
||||||
|
g_warning ("This iconv context wasn't opened using open_converter");
|
||||||
|
|
||||||
|
return g_iconv_close (converter);
|
||||||
|
}
|
||||||
|
|
||||||
|
G_UNLOCK (iconv_cache_lock);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* g_convert:
|
* g_convert:
|
||||||
* @str: the string to convert
|
* @str: the string to convert
|
||||||
@ -269,7 +504,7 @@ g_convert (const gchar *str,
|
|||||||
bytes_read, bytes_written,
|
bytes_read, bytes_written,
|
||||||
error);
|
error);
|
||||||
|
|
||||||
g_iconv_close (cd);
|
close_converter (cd);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@ -498,7 +733,7 @@ g_convert_with_fallback (const gchar *str,
|
|||||||
bytes_read, &inbytes_remaining, error);
|
bytes_read, &inbytes_remaining, error);
|
||||||
if (!utf8)
|
if (!utf8)
|
||||||
{
|
{
|
||||||
g_iconv_close (cd);
|
close_converter (cd);
|
||||||
if (bytes_written)
|
if (bytes_written)
|
||||||
*bytes_written = 0;
|
*bytes_written = 0;
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -599,7 +834,7 @@ g_convert_with_fallback (const gchar *str,
|
|||||||
*/
|
*/
|
||||||
*outp = '\0';
|
*outp = '\0';
|
||||||
|
|
||||||
g_iconv_close (cd);
|
close_converter (cd);
|
||||||
|
|
||||||
if (bytes_written)
|
if (bytes_written)
|
||||||
*bytes_written = outp - dest; /* Doesn't include '\0' */
|
*bytes_written = outp - dest; /* Doesn't include '\0' */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user