OLD | NEW |
---|---|
1 /* crypto/lhash/lhash.c */ | 1 /* crypto/lhash/lhash.c */ |
2 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) | 2 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) |
3 * All rights reserved. | 3 * All rights reserved. |
4 * | 4 * |
5 * This package is an SSL implementation written | 5 * This package is an SSL implementation written |
6 * by Eric Young (eay@cryptsoft.com). | 6 * by Eric Young (eay@cryptsoft.com). |
7 * The implementation was written so as to conform with Netscapes SSL. | 7 * The implementation was written so as to conform with Netscapes SSL. |
8 * | 8 * |
9 * This library is free for commercial and non-commercial use as long as | 9 * This library is free for commercial and non-commercial use as long as |
10 * the following conditions are aheared to. The following conditions | 10 * the following conditions are aheared to. The following conditions |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
87 * 1.4 eay - Fixed lh_doall so the function can call lh_delete 28/05/91 | 87 * 1.4 eay - Fixed lh_doall so the function can call lh_delete 28/05/91 |
88 * | 88 * |
89 * 1.3 eay - Fixed a few lint problems 19/3/1991 | 89 * 1.3 eay - Fixed a few lint problems 19/3/1991 |
90 * | 90 * |
91 * 1.2 eay - Fixed lh_doall problem 13/3/1991 | 91 * 1.2 eay - Fixed lh_doall problem 13/3/1991 |
92 * | 92 * |
93 * 1.1 eay - Added lh_doall | 93 * 1.1 eay - Added lh_doall |
94 * | 94 * |
95 * 1.0 eay - First version | 95 * 1.0 eay - First version |
96 */ | 96 */ |
97 #include <limits.h> | |
97 #include <stdio.h> | 98 #include <stdio.h> |
98 #include <string.h> | 99 #include <string.h> |
99 #include <stdlib.h> | 100 #include <stdlib.h> |
100 #include <openssl/crypto.h> | 101 #include <openssl/crypto.h> |
101 #include <openssl/lhash.h> | 102 #include <openssl/lhash.h> |
102 | 103 |
103 const char lh_version[]="lhash" OPENSSL_VERSION_PTEXT; | 104 const char lh_version[]="lhash" OPENSSL_VERSION_PTEXT; |
104 | 105 |
105 #undef MIN_NODES | 106 #undef MIN_NODES |
106 #define MIN_NODES 16 | 107 #define MIN_NODES 16 |
107 #define UP_LOAD (2*LH_LOAD_MULT) /* load times 256 (default 2) */ | 108 #define UP_LOAD (2*LH_LOAD_MULT) /* load times 256 (default 2) */ |
108 #define DOWN_LOAD (LH_LOAD_MULT) /* load times 256 (default 1) */ | 109 #define DOWN_LOAD (LH_LOAD_MULT) /* load times 256 (default 1) */ |
109 | 110 |
111 /* Maximum number of nodes to guarantee the load computations don't overflow */ | |
112 #define MAX_LOAD_ITEMS (UINT_MAX / LH_LOAD_MULT) | |
113 | |
114 /* The field 'iteration_state' is used to hold data to ensure that a hash | |
115 * table is not resized during an 'insert' or 'delete' operation performed | |
116 * within a lh_doall/lh_doall_arg call. | |
117 * | |
118 * Conceptually, this records two things: | |
119 * | |
120 * - A 'depth' count, which is incremented at the start of lh_doall*, | |
121 * and decremented just before it returns. | |
122 * | |
123 * - A 'mutated' boolean flag, which is set in lh_insert() or lh_delete() | |
124 * when the operation is performed with a non-0 depth. | |
125 * | |
126 * The following are helper macros to handle this state in a more explicit | |
127 * way. | |
128 */ | |
129 | |
130 /* Reset the iteration state to its defaults. */ | |
131 #define LH_ITERATION_RESET(lh) do { \ | |
132 (lh)->iteration_state = 0; \ | |
133 } while (0) | |
134 | |
135 /* Returns 1 if the hash table is currently being iterated on, 0 otherwise. */ | |
136 #define LH_ITERATION_IS_ACTIVE(lh) ((lh)->iteration_state >= 2) | |
137 | |
138 /* Increment iteration depth. This should always be followed by a paired call | |
139 * to LH_ITERATION_DECREMENT_DEPTH(). */ | |
140 #define LH_ITERATION_INCREMENT_DEPTH(lh) do { \ | |
141 (lh)->iteration_state += 2; \ | |
142 } while (0) | |
143 | |
144 /* Decrement iteration depth. This should always be called after a paired call | |
145 * to LH_ITERATION_INCREMENT_DEPTH(). */ | |
146 #define LH_ITERATION_DECREMENT_DEPTH(lh) do { \ | |
147 (lh)->iteration_state -= 2; \ | |
148 } while (0) | |
149 | |
150 /* Return 1 if the iteration 'mutated' flag is set, 0 otherwise. */ | |
151 #define LH_ITERATION_IS_MUTATED(lh) (((lh)->iteration_state & 1) != 0) | |
152 | |
153 /* Set the iteration 'mutated' flag to 1. LH_ITERATION_RESET() to reset it. */ | |
154 #define LH_ITERATION_SET_MUTATED(lh) do { \ | |
155 (lh)->iteration_state |= 1; \ | |
156 } while (0) | |
157 | |
158 /* This macros returns 1 if the hash table should be expanded due to its current | |
159 * load, or 0 otherwise. The exact comparison to be performed is expressed by | |
160 * the mathematical expression (where '//' denotes the exact division operation) : | |
agl
2013/11/04 17:29:30
(I've reviewed this code in https://android-review
| |
161 * | |
162 * (num_items // num_nodes) >= (up_load // LOAD_MULT) or | |
163 * (num_items * LOAD_MULT // num_nodes) >= up_load. | |
164 * | |
165 * Given that the C language operator '/' implements integer division, i.e: | |
166 * a // b == (a / b) + epsilon (with 0 <= epsilon < 1, for positive a & b) | |
167 * | |
168 * This can be rewritten as: | |
169 * (num_items * LOAD_MULT / num_nodes) + epsilon >= up_load | |
170 * (num_items * LOAD_MULT / num_nodes) - up_load >= - epsilon | |
171 * | |
172 * Let's call 'A' the left-hand side of the equation above, it is an integer | |
173 * and: | |
174 * - If A >= 0, the expression is true for any value of epsilon. | |
175 * - If A <= -1, the expression is also true for any value of epsilon. | |
176 * | |
177 * In other words, this is equivalent to 'A >= 0', or: | |
178 * (num_items * LOAD_MULT / num_nodes) >= up_load | |
179 */ | |
180 #define LH_SHOULD_EXPAND(lh) \ | |
181 ((lh)->num_items < MAX_LOAD_ITEMS && \ | |
182 (((lh)->num_items*LH_LOAD_MULT/(lh)->num_nodes) >= (lh)->up_load)) | |
183 | |
184 /* This macro returns 1 if the hash table should be contracted due to its | |
185 * current load, or 0 otherwise. Abbreviated computations are: | |
186 * | |
187 * (num_items // num_nodes) <= (down_load // LOAD_MULT) | |
188 * (num_items * LOAD_MULT // num_nodes) <= down_load | |
189 * (num_items * LOAD_MULT / num_nodes) + epsilon <= down_load | |
190 * (num_items * LOAD_MULT / num_nodes) - down_load <= -epsilon | |
191 * | |
192 * Let's call 'B' the left-hand side of the equation above: | |
193 * - If B <= -1, the expression is true for any value of epsilon. | |
194 * - If B >= 1, the expression is false for any value of epsilon. | |
195 * - If B == 0, the expression is true for 'epsilon == 0', and false | |
196 * otherwise, which is problematic. | |
197 * | |
198 * To work around this problem, while keeping the code simple, just change | |
199 * the initial expression to use a strict inequality, i.e.: | |
200 * | |
201 * (num_items // num_nodes) < (down_load // LOAD_MULT) | |
202 * | |
203 * Which leads to: | |
204 * (num_items * LOAD_MULT / num_nodes) - down_load < -epsilon | |
205 * | |
206 * Then: | |
207 * - If 'B <= -1', the expression is true for any value of epsilon. | |
208 * - If 'B' >= 0, the expression is false for any value of epsilon, | |
209 * | |
210 * In other words, this is equivalent to 'B < 0', or: | |
211 * (num_items * LOAD_MULT / num_nodes) < down_load | |
212 */ | |
213 #define LH_SHOULD_CONTRACT(lh) \ | |
214 (((lh)->num_nodes > MIN_NODES) && \ | |
215 ((lh)->num_items < MAX_LOAD_ITEMS && \ | |
216 ((lh)->num_items*LH_LOAD_MULT/(lh)->num_nodes) < (lh)->down_load)) | |
217 | |
110 static void expand(_LHASH *lh); | 218 static void expand(_LHASH *lh); |
111 static void contract(_LHASH *lh); | 219 static void contract(_LHASH *lh); |
112 static LHASH_NODE **getrn(_LHASH *lh, const void *data, unsigned long *rhash); | 220 static LHASH_NODE **getrn(_LHASH *lh, const void *data, unsigned long *rhash); |
113 | 221 |
114 _LHASH *lh_new(LHASH_HASH_FN_TYPE h, LHASH_COMP_FN_TYPE c) | 222 _LHASH *lh_new(LHASH_HASH_FN_TYPE h, LHASH_COMP_FN_TYPE c) |
115 { | 223 { |
116 _LHASH *ret; | 224 _LHASH *ret; |
117 int i; | 225 int i; |
118 | 226 |
119 if ((ret=OPENSSL_malloc(sizeof(_LHASH))) == NULL) | 227 if ((ret=OPENSSL_malloc(sizeof(_LHASH))) == NULL) |
(...skipping 20 matching lines...) Expand all Loading... | |
140 ret->num_comp_calls=0; | 248 ret->num_comp_calls=0; |
141 ret->num_insert=0; | 249 ret->num_insert=0; |
142 ret->num_replace=0; | 250 ret->num_replace=0; |
143 ret->num_delete=0; | 251 ret->num_delete=0; |
144 ret->num_no_delete=0; | 252 ret->num_no_delete=0; |
145 ret->num_retrieve=0; | 253 ret->num_retrieve=0; |
146 ret->num_retrieve_miss=0; | 254 ret->num_retrieve_miss=0; |
147 ret->num_hash_comps=0; | 255 ret->num_hash_comps=0; |
148 | 256 |
149 ret->error=0; | 257 ret->error=0; |
258 LH_ITERATION_RESET(ret); | |
150 return(ret); | 259 return(ret); |
151 err1: | 260 err1: |
152 OPENSSL_free(ret); | 261 OPENSSL_free(ret); |
153 err0: | 262 err0: |
154 return(NULL); | 263 return(NULL); |
155 } | 264 } |
156 | 265 |
157 void lh_free(_LHASH *lh) | 266 void lh_free(_LHASH *lh) |
158 { | 267 { |
159 unsigned int i; | 268 unsigned int i; |
(...skipping 16 matching lines...) Expand all Loading... | |
176 OPENSSL_free(lh); | 285 OPENSSL_free(lh); |
177 } | 286 } |
178 | 287 |
179 void *lh_insert(_LHASH *lh, void *data) | 288 void *lh_insert(_LHASH *lh, void *data) |
180 { | 289 { |
181 unsigned long hash; | 290 unsigned long hash; |
182 LHASH_NODE *nn,**rn; | 291 LHASH_NODE *nn,**rn; |
183 void *ret; | 292 void *ret; |
184 | 293 |
185 lh->error=0; | 294 lh->error=0; |
186 » if (lh->up_load <= (lh->num_items*LH_LOAD_MULT/lh->num_nodes)) | 295 » /* Do not expand the array if the table is being iterated on. */ |
296 » if (LH_ITERATION_IS_ACTIVE(lh)) | |
297 » » LH_ITERATION_SET_MUTATED(lh); | |
298 » else if (LH_SHOULD_EXPAND(lh)) | |
187 expand(lh); | 299 expand(lh); |
188 | 300 |
189 rn=getrn(lh,data,&hash); | 301 rn=getrn(lh,data,&hash); |
190 | 302 |
191 if (*rn == NULL) | 303 if (*rn == NULL) |
192 { | 304 { |
193 if ((nn=(LHASH_NODE *)OPENSSL_malloc(sizeof(LHASH_NODE))) == NUL L) | 305 if ((nn=(LHASH_NODE *)OPENSSL_malloc(sizeof(LHASH_NODE))) == NUL L) |
194 { | 306 { |
195 lh->error++; | 307 lh->error++; |
196 return(NULL); | 308 return(NULL); |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
231 else | 343 else |
232 { | 344 { |
233 nn= *rn; | 345 nn= *rn; |
234 *rn=nn->next; | 346 *rn=nn->next; |
235 ret=nn->data; | 347 ret=nn->data; |
236 OPENSSL_free(nn); | 348 OPENSSL_free(nn); |
237 lh->num_delete++; | 349 lh->num_delete++; |
238 } | 350 } |
239 | 351 |
240 lh->num_items--; | 352 lh->num_items--; |
241 » if ((lh->num_nodes > MIN_NODES) && | 353 » /* Do not contract the array if the table is being iterated on. */ |
242 » » (lh->down_load >= (lh->num_items*LH_LOAD_MULT/lh->num_nodes))) | 354 » if (LH_ITERATION_IS_ACTIVE(lh)) |
355 » » LH_ITERATION_SET_MUTATED(lh); | |
356 » else if (LH_SHOULD_CONTRACT(lh)) | |
243 contract(lh); | 357 contract(lh); |
244 | 358 |
245 return(ret); | 359 return(ret); |
246 } | 360 } |
247 | 361 |
248 void *lh_retrieve(_LHASH *lh, const void *data) | 362 void *lh_retrieve(_LHASH *lh, const void *data) |
249 { | 363 { |
250 unsigned long hash; | 364 unsigned long hash; |
251 LHASH_NODE **rn; | 365 LHASH_NODE **rn; |
252 void *ret; | 366 void *ret; |
(...skipping 16 matching lines...) Expand all Loading... | |
269 | 383 |
270 static void doall_util_fn(_LHASH *lh, int use_arg, LHASH_DOALL_FN_TYPE func, | 384 static void doall_util_fn(_LHASH *lh, int use_arg, LHASH_DOALL_FN_TYPE func, |
271 LHASH_DOALL_ARG_FN_TYPE func_arg, void *arg) | 385 LHASH_DOALL_ARG_FN_TYPE func_arg, void *arg) |
272 { | 386 { |
273 int i; | 387 int i; |
274 LHASH_NODE *a,*n; | 388 LHASH_NODE *a,*n; |
275 | 389 |
276 if (lh == NULL) | 390 if (lh == NULL) |
277 return; | 391 return; |
278 | 392 |
393 LH_ITERATION_INCREMENT_DEPTH(lh); | |
279 /* reverse the order so we search from 'top to bottom' | 394 /* reverse the order so we search from 'top to bottom' |
280 * We were having memory leaks otherwise */ | 395 * We were having memory leaks otherwise */ |
281 for (i=lh->num_nodes-1; i>=0; i--) | 396 for (i=lh->num_nodes-1; i>=0; i--) |
282 { | 397 { |
283 a=lh->b[i]; | 398 a=lh->b[i]; |
284 while (a != NULL) | 399 while (a != NULL) |
285 { | 400 { |
286 /* 28/05/91 - eay - n added so items can be deleted | 401 /* 28/05/91 - eay - n added so items can be deleted |
287 * via lh_doall */ | 402 * via lh_doall */ |
288 /* 22/05/08 - ben - eh? since a is not passed, | 403 /* 22/05/08 - ben - eh? since a is not passed, |
289 * this should not be needed */ | 404 * this should not be needed */ |
290 n=a->next; | 405 n=a->next; |
291 if(use_arg) | 406 if(use_arg) |
292 func_arg(a->data,arg); | 407 func_arg(a->data,arg); |
293 else | 408 else |
294 func(a->data); | 409 func(a->data); |
295 a=n; | 410 a=n; |
296 } | 411 } |
297 } | 412 } |
413 | |
414 LH_ITERATION_DECREMENT_DEPTH(lh); | |
415 if (!LH_ITERATION_IS_ACTIVE(lh) && LH_ITERATION_IS_MUTATED(lh)) | |
416 { | |
417 LH_ITERATION_RESET(lh); | |
418 /* Resize the buckets array if necessary. Each expand() or | |
419 * contract() call will double/halve the size of the array, | |
420 * respectively, so call them in a loop. */ | |
421 while (LH_SHOULD_EXPAND(lh)) | |
422 expand(lh); | |
423 while (LH_SHOULD_CONTRACT(lh)) | |
424 contract(lh); | |
425 } | |
298 } | 426 } |
299 | 427 |
300 void lh_doall(_LHASH *lh, LHASH_DOALL_FN_TYPE func) | 428 void lh_doall(_LHASH *lh, LHASH_DOALL_FN_TYPE func) |
301 { | 429 { |
302 doall_util_fn(lh, 0, func, (LHASH_DOALL_ARG_FN_TYPE)0, NULL); | 430 doall_util_fn(lh, 0, func, (LHASH_DOALL_ARG_FN_TYPE)0, NULL); |
303 } | 431 } |
304 | 432 |
305 void lh_doall_arg(_LHASH *lh, LHASH_DOALL_ARG_FN_TYPE func, void *arg) | 433 void lh_doall_arg(_LHASH *lh, LHASH_DOALL_ARG_FN_TYPE func, void *arg) |
306 { | 434 { |
307 doall_util_fn(lh, 1, (LHASH_DOALL_FN_TYPE)0, func, arg); | 435 doall_util_fn(lh, 1, (LHASH_DOALL_FN_TYPE)0, func, arg); |
(...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
466 ret^=v*v; | 594 ret^=v*v; |
467 c++; | 595 c++; |
468 } | 596 } |
469 return((ret>>16)^ret); | 597 return((ret>>16)^ret); |
470 } | 598 } |
471 | 599 |
472 unsigned long lh_num_items(const _LHASH *lh) | 600 unsigned long lh_num_items(const _LHASH *lh) |
473 { | 601 { |
474 return lh ? lh->num_items : 0; | 602 return lh ? lh->num_items : 0; |
475 } | 603 } |
OLD | NEW |