Annotation of win32/gnome/gmime-x.x.x/gmime-iconv.c, revision 1.1
1.1 ! paf 1: /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
! 2: /*
! 3: * Authors: Jeffrey Stedfast <fejj@ximian.com>
! 4: *
! 5: * Copyright 2002 Ximian, Inc. (www.ximian.com)
! 6: *
! 7: * This program is free software; you can redistribute it and/or modify
! 8: * it under the terms of the GNU General Public License as published by
! 9: * the Free Software Foundation; either version 2 of the License, or
! 10: * (at your option) any later version.
! 11: *
! 12: * This program is distributed in the hope that it will be useful,
! 13: * but WITHOUT ANY WARRANTY; without even the implied warranty of
! 14: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
! 15: * GNU General Public License for more details.
! 16: *
! 17: * You should have received a copy of the GNU General Public License
! 18: * along with this program; if not, write to the Free Software
! 19: * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
! 20: *
! 21: */
! 22:
! 23:
! 24: #ifdef HAVE_CONFIG_H
! 25: #include <config.h>
! 26: #endif
! 27:
! 28: #include <glib.h>
! 29: #include <errno.h>
! 30: #include <string.h>
! 31: #include <stdio.h>
! 32: #include <iconv.h>
! 33:
! 34: #ifdef HAVE_ALLOCA_H
! 35: #include <alloca.h>
! 36: #endif
! 37:
! 38: #include "gmime-charset.h"
! 39: #include "gmime-iconv.h"
! 40: #include "memchunk.h"
! 41:
! 42:
! 43: #define ICONV_CACHE_SIZE (10)
! 44:
! 45:
! 46: struct _iconv_node {
! 47: struct _iconv_node *next;
! 48: struct _iconv_node *prev;
! 49:
! 50: struct _iconv_cache_bucket *bucket;
! 51:
! 52: gboolean used;
! 53: iconv_t cd;
! 54: };
! 55:
! 56: struct _iconv_cache_bucket {
! 57: struct _iconv_cache_bucket *next;
! 58: struct _iconv_cache_bucket *prev;
! 59:
! 60: struct _iconv_node *unused;
! 61: struct _iconv_node *used;
! 62:
! 63: char *key;
! 64: };
! 65:
! 66:
! 67:
! 68: static struct _iconv_cache_bucket *iconv_cache_buckets;
! 69: static struct _iconv_cache_bucket *iconv_cache_tail;
! 70: static unsigned int iconv_cache_size = 0;
! 71: static GHashTable *iconv_cache;
! 72: static GHashTable *iconv_open_hash;
! 73: static MemChunk *node_chunk;
! 74:
! 75:
! 76: static struct _iconv_node *
! 77: iconv_node_new (struct _iconv_cache_bucket *bucket)
! 78: {
! 79: struct _iconv_node *node;
! 80:
! 81: node = memchunk_alloc (node_chunk);
! 82: node->next = NULL;
! 83: node->prev = NULL;
! 84: node->bucket = bucket;
! 85: node->used = FALSE;
! 86: node->cd = (iconv_t) -1;
! 87:
! 88: return node;
! 89: }
! 90:
! 91: static void
! 92: iconv_node_set_used (struct _iconv_node *node, gboolean used)
! 93: {
! 94: if (node->used == used)
! 95: return;
! 96:
! 97: node->used = used;
! 98:
! 99: if (used) {
! 100: /* this should be a lone unused node, so prepend it to the used list */
! 101: node->prev = NULL;
! 102: node->next = node->bucket->used;
! 103: if (node->bucket->used)
! 104: node->bucket->used->prev = node;
! 105: node->bucket->used = node;
! 106:
! 107: /* add to the open hash */
! 108: g_hash_table_insert (iconv_open_hash, node->cd, node);
! 109: } else {
! 110: /* this could be anywhere in the used node list... */
! 111: if (node->prev) {
! 112: node->prev->next = node->next;
! 113: if (node->next)
! 114: node->next->prev = node->prev;
! 115: } else {
! 116: node->bucket->used = node->next;
! 117: if (node->next)
! 118: node->next->prev = NULL;
! 119: }
! 120:
! 121: /* remove from the iconv open hash */
! 122: g_hash_table_remove (iconv_open_hash, node->cd);
! 123: }
! 124: }
! 125:
! 126: static void
! 127: iconv_node_destroy (struct _iconv_node *node)
! 128: {
! 129: if (node) {
! 130: if (node->cd != (iconv_t) -1)
! 131: iconv_close (node->cd);
! 132:
! 133: memchunk_free (node_chunk, node);
! 134: }
! 135: }
! 136:
! 137:
! 138:
! 139: static struct _iconv_cache_bucket *
! 140: iconv_cache_bucket_new (const char *key)
! 141: {
! 142: struct _iconv_cache_bucket *bucket;
! 143:
! 144: bucket = g_new (struct _iconv_cache_bucket, 1);
! 145: bucket->next = NULL;
! 146: bucket->prev = NULL;
! 147: bucket->unused = NULL;
! 148: bucket->used = NULL;
! 149: bucket->key = g_strdup (key);
! 150:
! 151: return bucket;
! 152: }
! 153:
! 154: static void
! 155: iconv_cache_bucket_add (struct _iconv_cache_bucket *bucket)
! 156: {
! 157: if (iconv_cache_buckets)
! 158: bucket->prev = iconv_cache_tail;
! 159:
! 160: iconv_cache_tail->next = bucket;
! 161: iconv_cache_tail = bucket;
! 162:
! 163: g_hash_table_insert (iconv_cache, bucket->key, bucket);
! 164: }
! 165:
! 166: static void
! 167: iconv_cache_bucket_add_node (struct _iconv_cache_bucket *bucket, struct _iconv_node *node)
! 168: {
! 169: node->prev = NULL;
! 170: node->next = bucket->unused;
! 171: if (bucket->unused)
! 172: bucket->unused->prev = node;
! 173: bucket->unused = node;
! 174: }
! 175:
! 176: static void
! 177: iconv_cache_bucket_remove (struct _iconv_cache_bucket *bucket)
! 178: {
! 179: if (bucket->prev) {
! 180: bucket->prev->next = bucket->next;
! 181: if (bucket->next)
! 182: bucket->next->prev = bucket->prev;
! 183: else
! 184: iconv_cache_tail = bucket->prev;
! 185: } else {
! 186: bucket->next->prev = NULL;
! 187: iconv_cache_buckets = bucket->next;
! 188: if (!iconv_cache_buckets)
! 189: iconv_cache_tail = (struct _iconv_cache_bucket *) &iconv_cache_buckets;
! 190: }
! 191: }
! 192:
! 193: static struct _iconv_node *
! 194: iconv_cache_bucket_get_first_unused (struct _iconv_cache_bucket *bucket)
! 195: {
! 196: struct _iconv_node *node = NULL;
! 197:
! 198: if (bucket->unused) {
! 199: node = bucket->unused;
! 200: bucket->unused = node->next;
! 201: if (bucket->unused)
! 202: bucket->unused->prev = NULL;
! 203: node->next = NULL;
! 204: }
! 205:
! 206: return node;
! 207: }
! 208:
! 209: static void
! 210: iconv_cache_bucket_destroy (struct _iconv_cache_bucket *bucket)
! 211: {
! 212: struct _iconv_node *node, *next;
! 213:
! 214: node = bucket->unused;
! 215: while (node) {
! 216: next = node->next;
! 217: iconv_node_destroy (node);
! 218: node = next;
! 219: }
! 220:
! 221: node = bucket->used;
! 222: while (node) {
! 223: next = node->next;
! 224: iconv_node_destroy (node);
! 225: node = next;
! 226: }
! 227:
! 228: g_free (bucket->key);
! 229: g_free (bucket);
! 230: }
! 231:
! 232: static void
! 233: iconv_cache_bucket_flush_unused (struct _iconv_cache_bucket *bucket)
! 234: {
! 235: struct _iconv_node *node, *next;
! 236:
! 237: node = bucket->unused;
! 238: while (node && iconv_cache_size >= ICONV_CACHE_SIZE) {
! 239: next = node->next;
! 240: iconv_node_destroy (node);
! 241: iconv_cache_size--;
! 242: node = next;
! 243: }
! 244:
! 245: bucket->unused = node;
! 246:
! 247: if (!bucket->unused && !bucket->used) {
! 248: /* expire this cache bucket... */
! 249: iconv_cache_bucket_remove (bucket);
! 250: }
! 251: }
! 252:
! 253:
! 254:
! 255: static void
! 256: g_mime_iconv_shutdown (void)
! 257: {
! 258: struct _iconv_cache_bucket *bucket, *next;
! 259:
! 260: bucket = iconv_cache_buckets;
! 261: while (bucket) {
! 262: next = bucket->next;
! 263: iconv_cache_bucket_destroy (bucket);
! 264: bucket = next;
! 265: }
! 266:
! 267: g_hash_table_destroy (iconv_cache);
! 268: g_hash_table_destroy (iconv_open_hash);
! 269:
! 270: memchunk_destroy (node_chunk);
! 271: }
! 272:
! 273:
! 274: /**
! 275: * g_mime_iconv_init:
! 276: *
! 277: * Initialize GMime's iconv cache. This *MUST* be called before any
! 278: * gmime-iconv interfaces will work correctly.
! 279: **/
! 280: void
! 281: g_mime_iconv_init (void)
! 282: {
! 283: static gboolean initialized = FALSE;
! 284:
! 285: if (initialized)
! 286: return;
! 287:
! 288: g_mime_charset_init ();
! 289:
! 290: node_chunk = memchunk_new (sizeof (struct _iconv_node),
! 291: ICONV_CACHE_SIZE, FALSE);
! 292:
! 293: iconv_cache_buckets = NULL;
! 294: iconv_cache_tail = (struct _iconv_cache_bucket *) &iconv_cache_buckets;
! 295: iconv_cache = g_hash_table_new (g_str_hash, g_str_equal);
! 296: iconv_open_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
! 297:
! 298: g_atexit (g_mime_iconv_shutdown);
! 299:
! 300: initialized = TRUE;
! 301: }
! 302:
! 303:
! 304: /**
! 305: * g_mime_iconv_open:
! 306: * @to: charset to convert to
! 307: * @from: charset to convert from
! 308: *
! 309: * Allocates a coversion descriptor suitable for converting byte
! 310: * sequences from charset @from to charset @to. The resulting
! 311: * descriptor can be used with iconv (or the g_mime_iconv wrapper) any
! 312: * number of times until closed using g_mime_iconv_close.
! 313: *
! 314: * Returns a new conversion descriptor for use with iconv on success
! 315: * or (iconv_t) -1 on fail as well as setting an appropriate errno
! 316: * value.
! 317: **/
! 318: iconv_t
! 319: g_mime_iconv_open (const char *to, const char *from)
! 320: {
! 321: struct _iconv_cache_bucket *bucket, *prev;
! 322: struct _iconv_node *node;
! 323: iconv_t cd;
! 324: char *key;
! 325:
! 326: if (from == NULL || to == NULL) {
! 327: errno = EINVAL;
! 328: return (iconv_t) -1;
! 329: }
! 330:
! 331: from = g_mime_charset_name (from);
! 332: to = g_mime_charset_name (to);
! 333: key = alloca (strlen (from) + strlen (to) + 2);
! 334: sprintf (key, "%s:%s", from, to);
! 335:
! 336: bucket = g_hash_table_lookup (iconv_cache, key);
! 337: if (bucket) {
! 338: node = iconv_cache_bucket_get_first_unused (bucket);
! 339: } else {
! 340: /* make room for another cache bucket */
! 341: bucket = iconv_cache_tail;
! 342: while (bucket && iconv_cache_size >= ICONV_CACHE_SIZE) {
! 343: prev = bucket->prev;
! 344: iconv_cache_bucket_flush_unused (bucket);
! 345: bucket = prev;
! 346: }
! 347:
! 348: bucket = iconv_cache_bucket_new (key);
! 349: iconv_cache_bucket_add (bucket);
! 350:
! 351: node = NULL;
! 352: }
! 353:
! 354: if (node == NULL) {
! 355: node = iconv_node_new (bucket);
! 356:
! 357: /* make room for this node */
! 358: bucket = iconv_cache_tail;
! 359: while (bucket && iconv_cache_size >= ICONV_CACHE_SIZE) {
! 360: prev = bucket->prev;
! 361: iconv_cache_bucket_flush_unused (bucket);
! 362: bucket = prev;
! 363: }
! 364:
! 365: cd = iconv_open (to, from);
! 366: if (cd == (iconv_t) -1) {
! 367: iconv_node_destroy (node);
! 368: return cd;
! 369: }
! 370:
! 371: node->cd = cd;
! 372:
! 373: iconv_cache_bucket_add_node (node->bucket, node);
! 374: } else {
! 375: cd = node->cd;
! 376:
! 377: /* reset the iconv descriptor */
! 378: iconv (cd, NULL, NULL, NULL, NULL);
! 379: }
! 380:
! 381: iconv_node_set_used (node, TRUE);
! 382:
! 383: return cd;
! 384: }
! 385:
! 386:
! 387: /**
! 388: * g_mime_iconv_close:
! 389: * @cd: iconv conversion descriptor
! 390: *
! 391: * Closes the iconv descriptor @cd.
! 392: *
! 393: * Returns 0 on success or -1 on fail as well as setting an
! 394: * appropriate errno value.
! 395: **/
! 396: int
! 397: g_mime_iconv_close (iconv_t cd)
! 398: {
! 399: struct _iconv_node *node;
! 400:
! 401: if (cd == (iconv_t) -1)
! 402: return 0;
! 403:
! 404: node = g_hash_table_lookup (iconv_open_hash, cd);
! 405: if (node) {
! 406: iconv_node_set_used (node, FALSE);
! 407: } else {
! 408: g_warning ("This iconv context wasn't opened using g_mime_iconv_open()!");
! 409: return iconv_close (cd);
! 410: }
! 411:
! 412: return 0;
! 413: }
E-mail: