fchash.c revision 1887081f
11887081fSmrg/*
21887081fSmrg * Copyright © 2000 Keith Packard
31887081fSmrg *
41887081fSmrg * Permission to use, copy, modify, distribute, and sell this software and its
51887081fSmrg * documentation for any purpose is hereby granted without fee, provided that
61887081fSmrg * the above copyright notice appear in all copies and that both that
71887081fSmrg * copyright notice and this permission notice appear in supporting
81887081fSmrg * documentation, and that the name of the author(s) not be used in
91887081fSmrg * advertising or publicity pertaining to distribution of the software without
101887081fSmrg * specific, written prior permission.  The authors make no
111887081fSmrg * representations about the suitability of this software for any purpose.  It
121887081fSmrg * is provided "as is" without express or implied warranty.
131887081fSmrg *
141887081fSmrg * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
151887081fSmrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
161887081fSmrg * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
171887081fSmrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
181887081fSmrg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
191887081fSmrg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
201887081fSmrg * PERFORMANCE OF THIS SOFTWARE.
211887081fSmrg */
221887081fSmrg#include "fcint.h"
231887081fSmrg#if !defined TOOL_FCCACHE
241887081fSmrg#ifndef _WIN32
251887081fSmrg#include <uuid/uuid.h>
261887081fSmrg#endif
271887081fSmrg#endif
281887081fSmrg
291887081fSmrg#define FC_HASH_SIZE 227
301887081fSmrg
311887081fSmrgtypedef struct _FcHashBucket {
321887081fSmrg    struct _FcHashBucket  *next;
331887081fSmrg    void                  *key;
341887081fSmrg    void                  *value;
351887081fSmrg} FcHashBucket;
361887081fSmrg
371887081fSmrgstruct _FcHashTable {
381887081fSmrg    FcHashBucket  *buckets[FC_HASH_SIZE];
391887081fSmrg    FcHashFunc     hash_func;
401887081fSmrg    FcCompareFunc  compare_func;
411887081fSmrg    FcCopyFunc     key_copy_func;
421887081fSmrg    FcCopyFunc     value_copy_func;
431887081fSmrg    FcDestroyFunc  key_destroy_func;
441887081fSmrg    FcDestroyFunc  value_destroy_func;
451887081fSmrg};
461887081fSmrg
471887081fSmrg
481887081fSmrgFcBool
491887081fSmrgFcHashStrCopy (const void  *src,
501887081fSmrg	       void       **dest)
511887081fSmrg{
521887081fSmrg    *dest = FcStrdup (src);
531887081fSmrg
541887081fSmrg    return *dest != NULL;
551887081fSmrg}
561887081fSmrg
571887081fSmrgFcBool
581887081fSmrgFcHashUuidCopy (const void  *src,
591887081fSmrg		void       **dest)
601887081fSmrg{
611887081fSmrg#if !defined TOOL_FCCACHE
621887081fSmrg#ifndef _WIN32
631887081fSmrg    *dest = malloc (sizeof (uuid_t));
641887081fSmrg    uuid_copy (*dest, src);
651887081fSmrg#endif
661887081fSmrg#endif
671887081fSmrg    return FcTrue;
681887081fSmrg}
691887081fSmrg
701887081fSmrgvoid
711887081fSmrgFcHashUuidFree (void *data)
721887081fSmrg{
731887081fSmrg    free (data);
741887081fSmrg}
751887081fSmrg
761887081fSmrgFcHashTable *
771887081fSmrgFcHashTableCreate (FcHashFunc    hash_func,
781887081fSmrg		   FcCompareFunc compare_func,
791887081fSmrg		   FcCopyFunc    key_copy_func,
801887081fSmrg		   FcCopyFunc    value_copy_func,
811887081fSmrg		   FcDestroyFunc key_destroy_func,
821887081fSmrg		   FcDestroyFunc value_destroy_func)
831887081fSmrg{
841887081fSmrg    FcHashTable *ret = malloc (sizeof (FcHashTable));
851887081fSmrg
861887081fSmrg    if (ret)
871887081fSmrg    {
881887081fSmrg	memset (ret->buckets, 0, sizeof (FcHashBucket *) * FC_HASH_SIZE);
891887081fSmrg	ret->hash_func = hash_func;
901887081fSmrg	ret->compare_func = compare_func;
911887081fSmrg	ret->key_copy_func = key_copy_func;
921887081fSmrg	ret->value_copy_func = value_copy_func;
931887081fSmrg	ret->key_destroy_func = key_destroy_func;
941887081fSmrg	ret->value_destroy_func = value_destroy_func;
951887081fSmrg    }
961887081fSmrg    return ret;
971887081fSmrg}
981887081fSmrg
991887081fSmrgvoid
1001887081fSmrgFcHashTableDestroy (FcHashTable *table)
1011887081fSmrg{
1021887081fSmrg    int i;
1031887081fSmrg
1041887081fSmrg    for (i = 0; i < FC_HASH_SIZE; i++)
1051887081fSmrg    {
1061887081fSmrg	FcHashBucket *bucket = table->buckets[i], *prev;
1071887081fSmrg
1081887081fSmrg	while (bucket)
1091887081fSmrg	{
1101887081fSmrg	    if (table->key_destroy_func)
1111887081fSmrg		table->key_destroy_func (bucket->key);
1121887081fSmrg	    if (table->value_destroy_func)
1131887081fSmrg		table->value_destroy_func (bucket->value);
1141887081fSmrg	    prev = bucket;
1151887081fSmrg	    bucket = bucket->next;
1161887081fSmrg	    free (prev);
1171887081fSmrg	}
1181887081fSmrg	table->buckets[i] = NULL;
1191887081fSmrg    }
1201887081fSmrg    free (table);
1211887081fSmrg}
1221887081fSmrg
1231887081fSmrgFcBool
1241887081fSmrgFcHashTableFind (FcHashTable  *table,
1251887081fSmrg		 const void   *key,
1261887081fSmrg		 void        **value)
1271887081fSmrg{
1281887081fSmrg    FcHashBucket *bucket;
1291887081fSmrg    FcChar32 hash = table->hash_func (key);
1301887081fSmrg
1311887081fSmrg    for (bucket = table->buckets[hash % FC_HASH_SIZE]; bucket; bucket = bucket->next)
1321887081fSmrg    {
1331887081fSmrg	if (!table->compare_func(bucket->key, key))
1341887081fSmrg	{
1351887081fSmrg	    if (table->value_copy_func)
1361887081fSmrg	    {
1371887081fSmrg		if (!table->value_copy_func (bucket->value, value))
1381887081fSmrg		    return FcFalse;
1391887081fSmrg	    }
1401887081fSmrg	    else
1411887081fSmrg		*value = bucket->value;
1421887081fSmrg	    return FcTrue;
1431887081fSmrg	}
1441887081fSmrg    }
1451887081fSmrg    return FcFalse;
1461887081fSmrg}
1471887081fSmrg
1481887081fSmrgstatic FcBool
1491887081fSmrgFcHashTableAddInternal (FcHashTable *table,
1501887081fSmrg			void        *key,
1511887081fSmrg			void        *value,
1521887081fSmrg			FcBool       replace)
1531887081fSmrg{
1541887081fSmrg    FcHashBucket **prev, *bucket, *b;
1551887081fSmrg    FcChar32 hash = table->hash_func (key);
1561887081fSmrg    FcBool ret = FcFalse;
1571887081fSmrg
1581887081fSmrg    bucket = (FcHashBucket *) malloc (sizeof (FcHashBucket));
1591887081fSmrg    if (!bucket)
1601887081fSmrg	return FcFalse;
1611887081fSmrg    memset (bucket, 0, sizeof (FcHashBucket));
1621887081fSmrg    if (table->key_copy_func)
1631887081fSmrg	ret |= !table->key_copy_func (key, &bucket->key);
1641887081fSmrg    else
1651887081fSmrg	bucket->key = key;
1661887081fSmrg    if (table->value_copy_func)
1671887081fSmrg	ret |= !table->value_copy_func (value, &bucket->value);
1681887081fSmrg    else
1691887081fSmrg	bucket->value = value;
1701887081fSmrg    if (ret)
1711887081fSmrg    {
1721887081fSmrg    destroy:
1731887081fSmrg	if (bucket->key && table->key_destroy_func)
1741887081fSmrg	    table->key_destroy_func (bucket->key);
1751887081fSmrg	if (bucket->value && table->value_destroy_func)
1761887081fSmrg	    table->value_destroy_func (bucket->value);
1771887081fSmrg	free (bucket);
1781887081fSmrg
1791887081fSmrg	return !ret;
1801887081fSmrg    }
1811887081fSmrg  retry:
1821887081fSmrg    for (prev = &table->buckets[hash % FC_HASH_SIZE];
1831887081fSmrg	 (b = fc_atomic_ptr_get (prev)); prev = &(b->next))
1841887081fSmrg    {
1851887081fSmrg	if (!table->compare_func (b->key, key))
1861887081fSmrg	{
1871887081fSmrg	    if (replace)
1881887081fSmrg	    {
1891887081fSmrg		bucket->next = b->next;
1901887081fSmrg		if (!fc_atomic_ptr_cmpexch (prev, b, bucket))
1911887081fSmrg		    goto retry;
1921887081fSmrg		bucket = b;
1931887081fSmrg	    }
1941887081fSmrg	    else
1951887081fSmrg		ret = FcTrue;
1961887081fSmrg	    goto destroy;
1971887081fSmrg	}
1981887081fSmrg    }
1991887081fSmrg    bucket->next = NULL;
2001887081fSmrg    if (!fc_atomic_ptr_cmpexch (prev, b, bucket))
2011887081fSmrg	goto retry;
2021887081fSmrg
2031887081fSmrg    return FcTrue;
2041887081fSmrg}
2051887081fSmrg
2061887081fSmrgFcBool
2071887081fSmrgFcHashTableAdd (FcHashTable *table,
2081887081fSmrg		void        *key,
2091887081fSmrg		void        *value)
2101887081fSmrg{
2111887081fSmrg    return FcHashTableAddInternal (table, key, value, FcFalse);
2121887081fSmrg}
2131887081fSmrg
2141887081fSmrgFcBool
2151887081fSmrgFcHashTableReplace (FcHashTable *table,
2161887081fSmrg		    void        *key,
2171887081fSmrg		    void        *value)
2181887081fSmrg{
2191887081fSmrg    return FcHashTableAddInternal (table, key, value, FcTrue);
2201887081fSmrg}
2211887081fSmrg
2221887081fSmrgFcBool
2231887081fSmrgFcHashTableRemove (FcHashTable *table,
2241887081fSmrg		   void        *key)
2251887081fSmrg{
2261887081fSmrg    FcHashBucket **prev, *bucket;
2271887081fSmrg    FcChar32 hash = table->hash_func (key);
2281887081fSmrg    FcBool ret = FcFalse;
2291887081fSmrg
2301887081fSmrgretry:
2311887081fSmrg    for (prev = &table->buckets[hash % FC_HASH_SIZE];
2321887081fSmrg	 (bucket = fc_atomic_ptr_get (prev)); prev = &(bucket->next))
2331887081fSmrg    {
2341887081fSmrg	if (!table->compare_func (bucket->key, key))
2351887081fSmrg	{
2361887081fSmrg	    if (!fc_atomic_ptr_cmpexch (prev, bucket, bucket->next))
2371887081fSmrg		goto retry;
2381887081fSmrg	    if (table->key_destroy_func)
2391887081fSmrg		table->key_destroy_func (bucket->key);
2401887081fSmrg	    if (table->value_destroy_func)
2411887081fSmrg		table->value_destroy_func (bucket->value);
2421887081fSmrg	    free (bucket);
2431887081fSmrg	    ret = FcTrue;
2441887081fSmrg	    break;
2451887081fSmrg	}
2461887081fSmrg    }
2471887081fSmrg
2481887081fSmrg    return ret;
2491887081fSmrg}
250