fchash.c revision 1887081f
1/*
2 * Copyright © 2000 Keith Packard
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation, and that the name of the author(s) not be used in
9 * advertising or publicity pertaining to distribution of the software without
10 * specific, written prior permission.  The authors make no
11 * representations about the suitability of this software for any purpose.  It
12 * is provided "as is" without express or implied warranty.
13 *
14 * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20 * PERFORMANCE OF THIS SOFTWARE.
21 */
22#include "fcint.h"
23#if !defined TOOL_FCCACHE
24#ifndef _WIN32
25#include <uuid/uuid.h>
26#endif
27#endif
28
29#define FC_HASH_SIZE 227
30
31typedef struct _FcHashBucket {
32    struct _FcHashBucket  *next;
33    void                  *key;
34    void                  *value;
35} FcHashBucket;
36
37struct _FcHashTable {
38    FcHashBucket  *buckets[FC_HASH_SIZE];
39    FcHashFunc     hash_func;
40    FcCompareFunc  compare_func;
41    FcCopyFunc     key_copy_func;
42    FcCopyFunc     value_copy_func;
43    FcDestroyFunc  key_destroy_func;
44    FcDestroyFunc  value_destroy_func;
45};
46
47
48FcBool
49FcHashStrCopy (const void  *src,
50	       void       **dest)
51{
52    *dest = FcStrdup (src);
53
54    return *dest != NULL;
55}
56
57FcBool
58FcHashUuidCopy (const void  *src,
59		void       **dest)
60{
61#if !defined TOOL_FCCACHE
62#ifndef _WIN32
63    *dest = malloc (sizeof (uuid_t));
64    uuid_copy (*dest, src);
65#endif
66#endif
67    return FcTrue;
68}
69
70void
71FcHashUuidFree (void *data)
72{
73    free (data);
74}
75
76FcHashTable *
77FcHashTableCreate (FcHashFunc    hash_func,
78		   FcCompareFunc compare_func,
79		   FcCopyFunc    key_copy_func,
80		   FcCopyFunc    value_copy_func,
81		   FcDestroyFunc key_destroy_func,
82		   FcDestroyFunc value_destroy_func)
83{
84    FcHashTable *ret = malloc (sizeof (FcHashTable));
85
86    if (ret)
87    {
88	memset (ret->buckets, 0, sizeof (FcHashBucket *) * FC_HASH_SIZE);
89	ret->hash_func = hash_func;
90	ret->compare_func = compare_func;
91	ret->key_copy_func = key_copy_func;
92	ret->value_copy_func = value_copy_func;
93	ret->key_destroy_func = key_destroy_func;
94	ret->value_destroy_func = value_destroy_func;
95    }
96    return ret;
97}
98
99void
100FcHashTableDestroy (FcHashTable *table)
101{
102    int i;
103
104    for (i = 0; i < FC_HASH_SIZE; i++)
105    {
106	FcHashBucket *bucket = table->buckets[i], *prev;
107
108	while (bucket)
109	{
110	    if (table->key_destroy_func)
111		table->key_destroy_func (bucket->key);
112	    if (table->value_destroy_func)
113		table->value_destroy_func (bucket->value);
114	    prev = bucket;
115	    bucket = bucket->next;
116	    free (prev);
117	}
118	table->buckets[i] = NULL;
119    }
120    free (table);
121}
122
123FcBool
124FcHashTableFind (FcHashTable  *table,
125		 const void   *key,
126		 void        **value)
127{
128    FcHashBucket *bucket;
129    FcChar32 hash = table->hash_func (key);
130
131    for (bucket = table->buckets[hash % FC_HASH_SIZE]; bucket; bucket = bucket->next)
132    {
133	if (!table->compare_func(bucket->key, key))
134	{
135	    if (table->value_copy_func)
136	    {
137		if (!table->value_copy_func (bucket->value, value))
138		    return FcFalse;
139	    }
140	    else
141		*value = bucket->value;
142	    return FcTrue;
143	}
144    }
145    return FcFalse;
146}
147
148static FcBool
149FcHashTableAddInternal (FcHashTable *table,
150			void        *key,
151			void        *value,
152			FcBool       replace)
153{
154    FcHashBucket **prev, *bucket, *b;
155    FcChar32 hash = table->hash_func (key);
156    FcBool ret = FcFalse;
157
158    bucket = (FcHashBucket *) malloc (sizeof (FcHashBucket));
159    if (!bucket)
160	return FcFalse;
161    memset (bucket, 0, sizeof (FcHashBucket));
162    if (table->key_copy_func)
163	ret |= !table->key_copy_func (key, &bucket->key);
164    else
165	bucket->key = key;
166    if (table->value_copy_func)
167	ret |= !table->value_copy_func (value, &bucket->value);
168    else
169	bucket->value = value;
170    if (ret)
171    {
172    destroy:
173	if (bucket->key && table->key_destroy_func)
174	    table->key_destroy_func (bucket->key);
175	if (bucket->value && table->value_destroy_func)
176	    table->value_destroy_func (bucket->value);
177	free (bucket);
178
179	return !ret;
180    }
181  retry:
182    for (prev = &table->buckets[hash % FC_HASH_SIZE];
183	 (b = fc_atomic_ptr_get (prev)); prev = &(b->next))
184    {
185	if (!table->compare_func (b->key, key))
186	{
187	    if (replace)
188	    {
189		bucket->next = b->next;
190		if (!fc_atomic_ptr_cmpexch (prev, b, bucket))
191		    goto retry;
192		bucket = b;
193	    }
194	    else
195		ret = FcTrue;
196	    goto destroy;
197	}
198    }
199    bucket->next = NULL;
200    if (!fc_atomic_ptr_cmpexch (prev, b, bucket))
201	goto retry;
202
203    return FcTrue;
204}
205
206FcBool
207FcHashTableAdd (FcHashTable *table,
208		void        *key,
209		void        *value)
210{
211    return FcHashTableAddInternal (table, key, value, FcFalse);
212}
213
214FcBool
215FcHashTableReplace (FcHashTable *table,
216		    void        *key,
217		    void        *value)
218{
219    return FcHashTableAddInternal (table, key, value, FcTrue);
220}
221
222FcBool
223FcHashTableRemove (FcHashTable *table,
224		   void        *key)
225{
226    FcHashBucket **prev, *bucket;
227    FcChar32 hash = table->hash_func (key);
228    FcBool ret = FcFalse;
229
230retry:
231    for (prev = &table->buckets[hash % FC_HASH_SIZE];
232	 (bucket = fc_atomic_ptr_get (prev)); prev = &(bucket->next))
233    {
234	if (!table->compare_func (bucket->key, key))
235	{
236	    if (!fc_atomic_ptr_cmpexch (prev, bucket, bucket->next))
237		goto retry;
238	    if (table->key_destroy_func)
239		table->key_destroy_func (bucket->key);
240	    if (table->value_destroy_func)
241		table->value_destroy_func (bucket->value);
242	    free (bucket);
243	    ret = FcTrue;
244	    break;
245	}
246    }
247
248    return ret;
249}
250