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: