Line | Count | Source (jump to first uncovered line) |
1 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
2 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
3 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
4 | | |
5 | | /* |
6 | | * hash.c |
7 | | * |
8 | | * This is merely a couple wrappers around NSPR's PLHashTable, using |
9 | | * the identity hash and arena-aware allocators. |
10 | | * This is a copy of ckfw/hash.c, with modifications to use NSS types |
11 | | * (not Cryptoki types). Would like for this to be a single implementation, |
12 | | * but doesn't seem like it will work. |
13 | | */ |
14 | | |
15 | | #ifndef BASE_H |
16 | | #include "base.h" |
17 | | #endif /* BASE_H */ |
18 | | |
19 | | #include "prbit.h" |
20 | | |
21 | | /* |
22 | | * nssHash |
23 | | * |
24 | | * nssHash_Create |
25 | | * nssHash_Destroy |
26 | | * nssHash_Add |
27 | | * nssHash_Remove |
28 | | * nssHash_Count |
29 | | * nssHash_Exists |
30 | | * nssHash_Lookup |
31 | | * nssHash_Iterate |
32 | | */ |
33 | | |
34 | | struct nssHashStr { |
35 | | NSSArena *arena; |
36 | | PRBool i_alloced_arena; |
37 | | PRLock *mutex; |
38 | | |
39 | | /* |
40 | | * The invariant that mutex protects is: |
41 | | * The count accurately reflects the hashtable state. |
42 | | */ |
43 | | |
44 | | PLHashTable *plHashTable; |
45 | | PRUint32 count; |
46 | | }; |
47 | | |
48 | | static PLHashNumber |
49 | | nss_identity_hash(const void *key) |
50 | 0 | { |
51 | 0 | return (PLHashNumber)((char *)key - (char *)NULL); |
52 | 0 | } |
53 | | |
54 | | static PLHashNumber |
55 | | nss_item_hash(const void *key) |
56 | 0 | { |
57 | 0 | unsigned int i; |
58 | 0 | PLHashNumber h; |
59 | 0 | NSSItem *it = (NSSItem *)key; |
60 | 0 | h = 0; |
61 | 0 | for (i = 0; i < it->size; i++) |
62 | 0 | h = PR_ROTATE_LEFT32(h, 4) ^ ((unsigned char *)it->data)[i]; |
63 | 0 | return h; |
64 | 0 | } |
65 | | |
66 | | static int |
67 | | nss_compare_items(const void *v1, const void *v2) |
68 | 0 | { |
69 | 0 | PRStatus ignore; |
70 | 0 | return (int)nssItem_Equal((NSSItem *)v1, (NSSItem *)v2, &ignore); |
71 | 0 | } |
72 | | |
73 | | /* |
74 | | * nssHash_create |
75 | | * |
76 | | */ |
77 | | NSS_IMPLEMENT nssHash * |
78 | | nssHash_Create(NSSArena *arenaOpt, PRUint32 numBuckets, PLHashFunction keyHash, |
79 | | PLHashComparator keyCompare, PLHashComparator valueCompare) |
80 | 0 | { |
81 | 0 | nssHash *rv; |
82 | 0 | NSSArena *arena; |
83 | 0 | PRBool i_alloced; |
84 | |
|
85 | | #ifdef NSSDEBUG |
86 | | if (arenaOpt && PR_SUCCESS != nssArena_verifyPointer(arenaOpt)) { |
87 | | nss_SetError(NSS_ERROR_INVALID_POINTER); |
88 | | return (nssHash *)NULL; |
89 | | } |
90 | | #endif /* NSSDEBUG */ |
91 | |
|
92 | 0 | if (arenaOpt) { |
93 | 0 | arena = arenaOpt; |
94 | 0 | i_alloced = PR_FALSE; |
95 | 0 | } else { |
96 | 0 | arena = nssArena_Create(); |
97 | 0 | i_alloced = PR_TRUE; |
98 | 0 | } |
99 | |
|
100 | 0 | rv = nss_ZNEW(arena, nssHash); |
101 | 0 | if ((nssHash *)NULL == rv) { |
102 | 0 | goto loser; |
103 | 0 | } |
104 | | |
105 | 0 | rv->mutex = PZ_NewLock(nssILockOther); |
106 | 0 | if ((PZLock *)NULL == rv->mutex) { |
107 | 0 | goto loser; |
108 | 0 | } |
109 | | |
110 | 0 | rv->plHashTable = |
111 | 0 | PL_NewHashTable(numBuckets, keyHash, keyCompare, valueCompare, |
112 | 0 | &nssArenaHashAllocOps, arena); |
113 | 0 | if ((PLHashTable *)NULL == rv->plHashTable) { |
114 | 0 | (void)PZ_DestroyLock(rv->mutex); |
115 | 0 | goto loser; |
116 | 0 | } |
117 | | |
118 | 0 | rv->count = 0; |
119 | 0 | rv->arena = arena; |
120 | 0 | rv->i_alloced_arena = i_alloced; |
121 | |
|
122 | 0 | return rv; |
123 | 0 | loser: |
124 | 0 | (void)nss_ZFreeIf(rv); |
125 | 0 | return (nssHash *)NULL; |
126 | 0 | } |
127 | | |
128 | | /* |
129 | | * nssHash_CreatePointer |
130 | | * |
131 | | */ |
132 | | NSS_IMPLEMENT nssHash * |
133 | | nssHash_CreatePointer(NSSArena *arenaOpt, PRUint32 numBuckets) |
134 | 0 | { |
135 | 0 | return nssHash_Create(arenaOpt, numBuckets, nss_identity_hash, |
136 | 0 | PL_CompareValues, PL_CompareValues); |
137 | 0 | } |
138 | | |
139 | | /* |
140 | | * nssHash_CreateString |
141 | | * |
142 | | */ |
143 | | NSS_IMPLEMENT nssHash * |
144 | | nssHash_CreateString(NSSArena *arenaOpt, PRUint32 numBuckets) |
145 | 0 | { |
146 | 0 | return nssHash_Create(arenaOpt, numBuckets, PL_HashString, |
147 | 0 | PL_CompareStrings, PL_CompareStrings); |
148 | 0 | } |
149 | | |
150 | | /* |
151 | | * nssHash_CreateItem |
152 | | * |
153 | | */ |
154 | | NSS_IMPLEMENT nssHash * |
155 | | nssHash_CreateItem(NSSArena *arenaOpt, PRUint32 numBuckets) |
156 | 0 | { |
157 | 0 | return nssHash_Create(arenaOpt, numBuckets, nss_item_hash, |
158 | 0 | nss_compare_items, PL_CompareValues); |
159 | 0 | } |
160 | | |
161 | | /* |
162 | | * nssHash_Destroy |
163 | | * |
164 | | */ |
165 | | NSS_IMPLEMENT void |
166 | | nssHash_Destroy(nssHash *hash) |
167 | 0 | { |
168 | 0 | (void)PZ_DestroyLock(hash->mutex); |
169 | 0 | PL_HashTableDestroy(hash->plHashTable); |
170 | 0 | if (hash->i_alloced_arena) { |
171 | 0 | nssArena_Destroy(hash->arena); |
172 | 0 | } else { |
173 | 0 | nss_ZFreeIf(hash); |
174 | 0 | } |
175 | 0 | } |
176 | | |
177 | | /* |
178 | | * nssHash_Add |
179 | | * |
180 | | */ |
181 | | NSS_IMPLEMENT PRStatus |
182 | | nssHash_Add(nssHash *hash, const void *key, const void *value) |
183 | 0 | { |
184 | 0 | PRStatus error = PR_FAILURE; |
185 | 0 | PLHashEntry *he; |
186 | |
|
187 | 0 | PZ_Lock(hash->mutex); |
188 | |
|
189 | 0 | he = PL_HashTableAdd(hash->plHashTable, key, (void *)value); |
190 | 0 | if ((PLHashEntry *)NULL == he) { |
191 | 0 | nss_SetError(NSS_ERROR_NO_MEMORY); |
192 | 0 | } else if (he->value != value) { |
193 | 0 | nss_SetError(NSS_ERROR_HASH_COLLISION); |
194 | 0 | } else { |
195 | 0 | hash->count++; |
196 | 0 | error = PR_SUCCESS; |
197 | 0 | } |
198 | |
|
199 | 0 | (void)PZ_Unlock(hash->mutex); |
200 | |
|
201 | 0 | return error; |
202 | 0 | } |
203 | | |
204 | | /* |
205 | | * nssHash_Remove |
206 | | * |
207 | | */ |
208 | | NSS_IMPLEMENT void |
209 | | nssHash_Remove(nssHash *hash, const void *it) |
210 | 0 | { |
211 | 0 | PRBool found; |
212 | |
|
213 | 0 | PZ_Lock(hash->mutex); |
214 | |
|
215 | 0 | found = PL_HashTableRemove(hash->plHashTable, it); |
216 | 0 | if (found) { |
217 | 0 | hash->count--; |
218 | 0 | } |
219 | |
|
220 | 0 | (void)PZ_Unlock(hash->mutex); |
221 | 0 | return; |
222 | 0 | } |
223 | | |
224 | | /* |
225 | | * nssHash_Count |
226 | | * |
227 | | */ |
228 | | NSS_IMPLEMENT PRUint32 |
229 | | nssHash_Count(nssHash *hash) |
230 | 0 | { |
231 | 0 | PRUint32 count; |
232 | |
|
233 | 0 | PZ_Lock(hash->mutex); |
234 | |
|
235 | 0 | count = hash->count; |
236 | |
|
237 | 0 | (void)PZ_Unlock(hash->mutex); |
238 | |
|
239 | 0 | return count; |
240 | 0 | } |
241 | | |
242 | | /* |
243 | | * nssHash_Exists |
244 | | * |
245 | | */ |
246 | | NSS_IMPLEMENT PRBool |
247 | | nssHash_Exists(nssHash *hash, const void *it) |
248 | 0 | { |
249 | 0 | void *value; |
250 | |
|
251 | 0 | PZ_Lock(hash->mutex); |
252 | |
|
253 | 0 | value = PL_HashTableLookup(hash->plHashTable, it); |
254 | |
|
255 | 0 | (void)PZ_Unlock(hash->mutex); |
256 | |
|
257 | 0 | if ((void *)NULL == value) { |
258 | 0 | return PR_FALSE; |
259 | 0 | } else { |
260 | 0 | return PR_TRUE; |
261 | 0 | } |
262 | 0 | } |
263 | | |
264 | | /* |
265 | | * nssHash_Lookup |
266 | | * |
267 | | */ |
268 | | NSS_IMPLEMENT void * |
269 | | nssHash_Lookup(nssHash *hash, const void *it) |
270 | 0 | { |
271 | 0 | void *rv; |
272 | |
|
273 | 0 | PZ_Lock(hash->mutex); |
274 | |
|
275 | 0 | rv = PL_HashTableLookup(hash->plHashTable, it); |
276 | |
|
277 | 0 | (void)PZ_Unlock(hash->mutex); |
278 | |
|
279 | 0 | return rv; |
280 | 0 | } |
281 | | |
282 | | struct arg_str { |
283 | | nssHashIterator fcn; |
284 | | void *closure; |
285 | | }; |
286 | | |
287 | | static PRIntn |
288 | | nss_hash_enumerator(PLHashEntry *he, PRIntn index, void *arg) |
289 | 0 | { |
290 | 0 | struct arg_str *as = (struct arg_str *)arg; |
291 | 0 | as->fcn(he->key, he->value, as->closure); |
292 | 0 | return HT_ENUMERATE_NEXT; |
293 | 0 | } |
294 | | |
295 | | /* |
296 | | * nssHash_Iterate |
297 | | * |
298 | | * NOTE that the iteration function will be called with the hashtable locked. |
299 | | */ |
300 | | NSS_IMPLEMENT void |
301 | | nssHash_Iterate(nssHash *hash, nssHashIterator fcn, void *closure) |
302 | 0 | { |
303 | 0 | struct arg_str as; |
304 | 0 | as.fcn = fcn; |
305 | 0 | as.closure = closure; |
306 | |
|
307 | 0 | PZ_Lock(hash->mutex); |
308 | |
|
309 | 0 | PL_HashTableEnumerateEntries(hash->plHashTable, nss_hash_enumerator, &as); |
310 | |
|
311 | 0 | (void)PZ_Unlock(hash->mutex); |
312 | |
|
313 | 0 | return; |
314 | 0 | } |