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 macro 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 division over real numbers): |
| 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 » » » /* note that 'a' can be deleted by the callback */ |
287 » » » * via lh_doall */ | |
288 » » » /* 22/05/08 - ben - eh? since a is not passed, | |
289 » » » * this should not be needed */ | |
290 n=a->next; | 402 n=a->next; |
291 if(use_arg) | 403 if(use_arg) |
292 func_arg(a->data,arg); | 404 func_arg(a->data,arg); |
293 else | 405 else |
294 func(a->data); | 406 func(a->data); |
295 a=n; | 407 a=n; |
296 } | 408 } |
297 } | 409 } |
| 410 |
| 411 LH_ITERATION_DECREMENT_DEPTH(lh); |
| 412 if (!LH_ITERATION_IS_ACTIVE(lh) && LH_ITERATION_IS_MUTATED(lh)) |
| 413 { |
| 414 LH_ITERATION_RESET(lh); |
| 415 /* Resize the buckets array if necessary. Each expand() or |
| 416 * contract() call will double/halve the size of the array, |
| 417 * respectively, so call them in a loop. */ |
| 418 while (LH_SHOULD_EXPAND(lh)) |
| 419 expand(lh); |
| 420 while (LH_SHOULD_CONTRACT(lh)) |
| 421 contract(lh); |
| 422 } |
298 } | 423 } |
299 | 424 |
300 void lh_doall(_LHASH *lh, LHASH_DOALL_FN_TYPE func) | 425 void lh_doall(_LHASH *lh, LHASH_DOALL_FN_TYPE func) |
301 { | 426 { |
302 doall_util_fn(lh, 0, func, (LHASH_DOALL_ARG_FN_TYPE)0, NULL); | 427 doall_util_fn(lh, 0, func, (LHASH_DOALL_ARG_FN_TYPE)0, NULL); |
303 } | 428 } |
304 | 429 |
305 void lh_doall_arg(_LHASH *lh, LHASH_DOALL_ARG_FN_TYPE func, void *arg) | 430 void lh_doall_arg(_LHASH *lh, LHASH_DOALL_ARG_FN_TYPE func, void *arg) |
306 { | 431 { |
307 doall_util_fn(lh, 1, (LHASH_DOALL_FN_TYPE)0, func, arg); | 432 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; | 591 ret^=v*v; |
467 c++; | 592 c++; |
468 } | 593 } |
469 return((ret>>16)^ret); | 594 return((ret>>16)^ret); |
470 } | 595 } |
471 | 596 |
472 unsigned long lh_num_items(const _LHASH *lh) | 597 unsigned long lh_num_items(const _LHASH *lh) |
473 { | 598 { |
474 return lh ? lh->num_items : 0; | 599 return lh ? lh->num_items : 0; |
475 } | 600 } |
OLD | NEW |