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
241887081fSmrg#define FC_HASH_SIZE 227
251887081fSmrg
261887081fSmrgtypedef struct _FcHashBucket {
271887081fSmrg    struct _FcHashBucket  *next;
281887081fSmrg    void                  *key;
291887081fSmrg    void                  *value;
301887081fSmrg} FcHashBucket;
311887081fSmrg
321887081fSmrgstruct _FcHashTable {
331887081fSmrg    FcHashBucket  *buckets[FC_HASH_SIZE];
341887081fSmrg    FcHashFunc     hash_func;
351887081fSmrg    FcCompareFunc  compare_func;
361887081fSmrg    FcCopyFunc     key_copy_func;
371887081fSmrg    FcCopyFunc     value_copy_func;
381887081fSmrg    FcDestroyFunc  key_destroy_func;
391887081fSmrg    FcDestroyFunc  value_destroy_func;
401887081fSmrg};
411887081fSmrg
421887081fSmrg
431887081fSmrgFcBool
441887081fSmrgFcHashStrCopy (const void  *src,
451887081fSmrg	       void       **dest)
461887081fSmrg{
471887081fSmrg    *dest = FcStrdup (src);
481887081fSmrg
491887081fSmrg    return *dest != NULL;
501887081fSmrg}
511887081fSmrg
521887081fSmrgFcHashTable *
531887081fSmrgFcHashTableCreate (FcHashFunc    hash_func,
541887081fSmrg		   FcCompareFunc compare_func,
551887081fSmrg		   FcCopyFunc    key_copy_func,
561887081fSmrg		   FcCopyFunc    value_copy_func,
571887081fSmrg		   FcDestroyFunc key_destroy_func,
581887081fSmrg		   FcDestroyFunc value_destroy_func)
591887081fSmrg{
601887081fSmrg    FcHashTable *ret = malloc (sizeof (FcHashTable));
611887081fSmrg
621887081fSmrg    if (ret)
631887081fSmrg    {
641887081fSmrg	memset (ret->buckets, 0, sizeof (FcHashBucket *) * FC_HASH_SIZE);
651887081fSmrg	ret->hash_func = hash_func;
661887081fSmrg	ret->compare_func = compare_func;
671887081fSmrg	ret->key_copy_func = key_copy_func;
681887081fSmrg	ret->value_copy_func = value_copy_func;
691887081fSmrg	ret->key_destroy_func = key_destroy_func;
701887081fSmrg	ret->value_destroy_func = value_destroy_func;
711887081fSmrg    }
721887081fSmrg    return ret;
731887081fSmrg}
741887081fSmrg
751887081fSmrgvoid
761887081fSmrgFcHashTableDestroy (FcHashTable *table)
771887081fSmrg{
781887081fSmrg    int i;
791887081fSmrg
801887081fSmrg    for (i = 0; i < FC_HASH_SIZE; i++)
811887081fSmrg    {
821887081fSmrg	FcHashBucket *bucket = table->buckets[i], *prev;
831887081fSmrg
841887081fSmrg	while (bucket)
851887081fSmrg	{
861887081fSmrg	    if (table->key_destroy_func)
871887081fSmrg		table->key_destroy_func (bucket->key);
881887081fSmrg	    if (table->value_destroy_func)
891887081fSmrg		table->value_destroy_func (bucket->value);
901887081fSmrg	    prev = bucket;
911887081fSmrg	    bucket = bucket->next;
921887081fSmrg	    free (prev);
931887081fSmrg	}
941887081fSmrg	table->buckets[i] = NULL;
951887081fSmrg    }
961887081fSmrg    free (table);
971887081fSmrg}
981887081fSmrg
991887081fSmrgFcBool
1001887081fSmrgFcHashTableFind (FcHashTable  *table,
1011887081fSmrg		 const void   *key,
1021887081fSmrg		 void        **value)
1031887081fSmrg{
1041887081fSmrg    FcHashBucket *bucket;
1051887081fSmrg    FcChar32 hash = table->hash_func (key);
1061887081fSmrg
1071887081fSmrg    for (bucket = table->buckets[hash % FC_HASH_SIZE]; bucket; bucket = bucket->next)
1081887081fSmrg    {
1091887081fSmrg	if (!table->compare_func(bucket->key, key))
1101887081fSmrg	{
1111887081fSmrg	    if (table->value_copy_func)
1121887081fSmrg	    {
1131887081fSmrg		if (!table->value_copy_func (bucket->value, value))
1141887081fSmrg		    return FcFalse;
1151887081fSmrg	    }
1161887081fSmrg	    else
1171887081fSmrg		*value = bucket->value;
1181887081fSmrg	    return FcTrue;
1191887081fSmrg	}
1201887081fSmrg    }
1211887081fSmrg    return FcFalse;
1221887081fSmrg}
1231887081fSmrg
1241887081fSmrgstatic FcBool
1251887081fSmrgFcHashTableAddInternal (FcHashTable *table,
1261887081fSmrg			void        *key,
1271887081fSmrg			void        *value,
1281887081fSmrg			FcBool       replace)
1291887081fSmrg{
1301887081fSmrg    FcHashBucket **prev, *bucket, *b;
1311887081fSmrg    FcChar32 hash = table->hash_func (key);
1321887081fSmrg    FcBool ret = FcFalse;
1331887081fSmrg
1341887081fSmrg    bucket = (FcHashBucket *) malloc (sizeof (FcHashBucket));
1351887081fSmrg    if (!bucket)
1361887081fSmrg	return FcFalse;
1371887081fSmrg    memset (bucket, 0, sizeof (FcHashBucket));
1381887081fSmrg    if (table->key_copy_func)
1391887081fSmrg	ret |= !table->key_copy_func (key, &bucket->key);
1401887081fSmrg    else
1411887081fSmrg	bucket->key = key;
1421887081fSmrg    if (table->value_copy_func)
1431887081fSmrg	ret |= !table->value_copy_func (value, &bucket->value);
1441887081fSmrg    else
1451887081fSmrg	bucket->value = value;
1461887081fSmrg    if (ret)
1471887081fSmrg    {
1481887081fSmrg    destroy:
1491887081fSmrg	if (bucket->key && table->key_destroy_func)
1501887081fSmrg	    table->key_destroy_func (bucket->key);
1511887081fSmrg	if (bucket->value && table->value_destroy_func)
1521887081fSmrg	    table->value_destroy_func (bucket->value);
1531887081fSmrg	free (bucket);
1541887081fSmrg
1551887081fSmrg	return !ret;
1561887081fSmrg    }
1571887081fSmrg  retry:
1581887081fSmrg    for (prev = &table->buckets[hash % FC_HASH_SIZE];
1591887081fSmrg	 (b = fc_atomic_ptr_get (prev)); prev = &(b->next))
1601887081fSmrg    {
1611887081fSmrg	if (!table->compare_func (b->key, key))
1621887081fSmrg	{
1631887081fSmrg	    if (replace)
1641887081fSmrg	    {
1651887081fSmrg		bucket->next = b->next;
1661887081fSmrg		if (!fc_atomic_ptr_cmpexch (prev, b, bucket))
1671887081fSmrg		    goto retry;
1681887081fSmrg		bucket = b;
1691887081fSmrg	    }
1701887081fSmrg	    else
1711887081fSmrg		ret = FcTrue;
1721887081fSmrg	    goto destroy;
1731887081fSmrg	}
1741887081fSmrg    }
1751887081fSmrg    bucket->next = NULL;
1761887081fSmrg    if (!fc_atomic_ptr_cmpexch (prev, b, bucket))
1771887081fSmrg	goto retry;
1781887081fSmrg
1791887081fSmrg    return FcTrue;
1801887081fSmrg}
1811887081fSmrg
1821887081fSmrgFcBool
1831887081fSmrgFcHashTableAdd (FcHashTable *table,
1841887081fSmrg		void        *key,
1851887081fSmrg		void        *value)
1861887081fSmrg{
1871887081fSmrg    return FcHashTableAddInternal (table, key, value, FcFalse);
1881887081fSmrg}
1891887081fSmrg
1901887081fSmrgFcBool
1911887081fSmrgFcHashTableReplace (FcHashTable *table,
1921887081fSmrg		    void        *key,
1931887081fSmrg		    void        *value)
1941887081fSmrg{
1951887081fSmrg    return FcHashTableAddInternal (table, key, value, FcTrue);
1961887081fSmrg}
1971887081fSmrg
1981887081fSmrgFcBool
1991887081fSmrgFcHashTableRemove (FcHashTable *table,
2001887081fSmrg		   void        *key)
2011887081fSmrg{
2021887081fSmrg    FcHashBucket **prev, *bucket;
2031887081fSmrg    FcChar32 hash = table->hash_func (key);
2041887081fSmrg    FcBool ret = FcFalse;
2051887081fSmrg
2061887081fSmrgretry:
2071887081fSmrg    for (prev = &table->buckets[hash % FC_HASH_SIZE];
2081887081fSmrg	 (bucket = fc_atomic_ptr_get (prev)); prev = &(bucket->next))
2091887081fSmrg    {
2101887081fSmrg	if (!table->compare_func (bucket->key, key))
2111887081fSmrg	{
2121887081fSmrg	    if (!fc_atomic_ptr_cmpexch (prev, bucket, bucket->next))
2131887081fSmrg		goto retry;
2141887081fSmrg	    if (table->key_destroy_func)
2151887081fSmrg		table->key_destroy_func (bucket->key);
2161887081fSmrg	    if (table->value_destroy_func)
2171887081fSmrg		table->value_destroy_func (bucket->value);
2181887081fSmrg	    free (bucket);
2191887081fSmrg	    ret = FcTrue;
2201887081fSmrg	    break;
2211887081fSmrg	}
2221887081fSmrg    }
2231887081fSmrg
2241887081fSmrg    return ret;
2251887081fSmrg}
226