1 /* crypto/lhash/lhash.c */ 2 /* Copyright (C) 1995-1998 Eric Young (eay (at) cryptsoft.com) 3 * All rights reserved. 4 * 5 * This package is an SSL implementation written 6 * by Eric Young (eay (at) cryptsoft.com). 7 * The implementation was written so as to conform with Netscapes SSL. 8 * 9 * This library is free for commercial and non-commercial use as long as 10 * the following conditions are aheared to. The following conditions 11 * apply to all code found in this distribution, be it the RC4, RSA, 12 * lhash, DES, etc., code; not just the SSL code. The SSL documentation 13 * included with this distribution is covered by the same copyright terms 14 * except that the holder is Tim Hudson (tjh (at) cryptsoft.com). 15 * 16 * Copyright remains Eric Young's, and as such any Copyright notices in 17 * the code are not to be removed. 18 * If this package is used in a product, Eric Young should be given attribution 19 * as the author of the parts of the library used. 20 * This can be in the form of a textual message at program startup or 21 * in documentation (online or textual) provided with the package. 22 * 23 * Redistribution and use in source and binary forms, with or without 24 * modification, are permitted provided that the following conditions 25 * are met: 26 * 1. Redistributions of source code must retain the copyright 27 * notice, this list of conditions and the following disclaimer. 28 * 2. Redistributions in binary form must reproduce the above copyright 29 * notice, this list of conditions and the following disclaimer in the 30 * documentation and/or other materials provided with the distribution. 31 * 3. All advertising materials mentioning features or use of this software 32 * must display the following acknowledgement: 33 * "This product includes cryptographic software written by 34 * Eric Young (eay (at) cryptsoft.com)" 35 * The word 'cryptographic' can be left out if the rouines from the library 36 * being used are not cryptographic related :-). 37 * 4. If you include any Windows specific code (or a derivative thereof) from 38 * the apps directory (application code) you must include an acknowledgement: 39 * "This product includes software written by Tim Hudson (tjh (at) cryptsoft.com)" 40 * 41 * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND 42 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 43 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 44 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 45 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 46 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 47 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 48 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 49 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 50 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 51 * SUCH DAMAGE. 52 * 53 * The licence and distribution terms for any publically available version or 54 * derivative of this code cannot be changed. i.e. this code cannot simply be 55 * copied and put under another distribution licence 56 * [including the GNU Public Licence.] 57 */ 58 59 /* Code for dynamic hash table routines 60 * Author - Eric Young v 2.0 61 * 62 * 2.2 eay - added #include "crypto.h" so the memory leak checking code is 63 * present. eay 18-Jun-98 64 * 65 * 2.1 eay - Added an 'error in last operation' flag. eay 6-May-98 66 * 67 * 2.0 eay - Fixed a bug that occurred when using lh_delete 68 * from inside lh_doall(). As entries were deleted, 69 * the 'table' was 'contract()ed', making some entries 70 * jump from the end of the table to the start, there by 71 * skipping the lh_doall() processing. eay - 4/12/95 72 * 73 * 1.9 eay - Fixed a memory leak in lh_free, the LHASH_NODEs 74 * were not being free()ed. 21/11/95 75 * 76 * 1.8 eay - Put the stats routines into a separate file, lh_stats.c 77 * 19/09/95 78 * 79 * 1.7 eay - Removed the fputs() for realloc failures - the code 80 * should silently tolerate them. I have also fixed things 81 * lint complained about 04/05/95 82 * 83 * 1.6 eay - Fixed an invalid pointers in contract/expand 27/07/92 84 * 85 * 1.5 eay - Fixed a misuse of realloc in expand 02/03/1992 86 * 87 * 1.4 eay - Fixed lh_doall so the function can call lh_delete 28/05/91 88 * 89 * 1.3 eay - Fixed a few lint problems 19/3/1991 90 * 91 * 1.2 eay - Fixed lh_doall problem 13/3/1991 92 * 93 * 1.1 eay - Added lh_doall 94 * 95 * 1.0 eay - First version 96 */ 97 #include <stdio.h> 98 #include <string.h> 99 #include <stdlib.h> 100 #include <openssl/crypto.h> 101 #include <openssl/lhash.h> 102 103 const char lh_version[]="lhash" OPENSSL_VERSION_PTEXT; 104 105 #undef MIN_NODES 106 #define MIN_NODES 16 107 #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 110 static void expand(_LHASH *lh); 111 static void contract(_LHASH *lh); 112 static LHASH_NODE **getrn(_LHASH *lh, const void *data, unsigned long *rhash); 113 114 _LHASH *lh_new(LHASH_HASH_FN_TYPE h, LHASH_COMP_FN_TYPE c) 115 { 116 _LHASH *ret; 117 int i; 118 119 if ((ret=OPENSSL_malloc(sizeof(_LHASH))) == NULL) 120 goto err0; 121 if ((ret->b=OPENSSL_malloc(sizeof(LHASH_NODE *)*MIN_NODES)) == NULL) 122 goto err1; 123 for (i=0; i<MIN_NODES; i++) 124 ret->b[i]=NULL; 125 ret->comp=((c == NULL)?(LHASH_COMP_FN_TYPE)strcmp:c); 126 ret->hash=((h == NULL)?(LHASH_HASH_FN_TYPE)lh_strhash:h); 127 ret->num_nodes=MIN_NODES/2; 128 ret->num_alloc_nodes=MIN_NODES; 129 ret->p=0; 130 ret->pmax=MIN_NODES/2; 131 ret->up_load=UP_LOAD; 132 ret->down_load=DOWN_LOAD; 133 ret->num_items=0; 134 135 ret->num_expands=0; 136 ret->num_expand_reallocs=0; 137 ret->num_contracts=0; 138 ret->num_contract_reallocs=0; 139 ret->num_hash_calls=0; 140 ret->num_comp_calls=0; 141 ret->num_insert=0; 142 ret->num_replace=0; 143 ret->num_delete=0; 144 ret->num_no_delete=0; 145 ret->num_retrieve=0; 146 ret->num_retrieve_miss=0; 147 ret->num_hash_comps=0; 148 149 ret->error=0; 150 return(ret); 151 err1: 152 OPENSSL_free(ret); 153 err0: 154 return(NULL); 155 } 156 157 void lh_free(_LHASH *lh) 158 { 159 unsigned int i; 160 LHASH_NODE *n,*nn; 161 162 if (lh == NULL) 163 return; 164 165 for (i=0; i<lh->num_nodes; i++) 166 { 167 n=lh->b[i]; 168 while (n != NULL) 169 { 170 nn=n->next; 171 OPENSSL_free(n); 172 n=nn; 173 } 174 } 175 OPENSSL_free(lh->b); 176 OPENSSL_free(lh); 177 } 178 179 void *lh_insert(_LHASH *lh, void *data) 180 { 181 unsigned long hash; 182 LHASH_NODE *nn,**rn; 183 void *ret; 184 185 lh->error=0; 186 if (lh->up_load <= (lh->num_items*LH_LOAD_MULT/lh->num_nodes)) 187 expand(lh); 188 189 rn=getrn(lh,data,&hash); 190 191 if (*rn == NULL) 192 { 193 if ((nn=(LHASH_NODE *)OPENSSL_malloc(sizeof(LHASH_NODE))) == NULL) 194 { 195 lh->error++; 196 return(NULL); 197 } 198 nn->data=data; 199 nn->next=NULL; 200 #ifndef OPENSSL_NO_HASH_COMP 201 nn->hash=hash; 202 #endif 203 *rn=nn; 204 ret=NULL; 205 lh->num_insert++; 206 lh->num_items++; 207 } 208 else /* replace same key */ 209 { 210 ret= (*rn)->data; 211 (*rn)->data=data; 212 lh->num_replace++; 213 } 214 return(ret); 215 } 216 217 void *lh_delete(_LHASH *lh, const void *data) 218 { 219 unsigned long hash; 220 LHASH_NODE *nn,**rn; 221 void *ret; 222 223 lh->error=0; 224 rn=getrn(lh,data,&hash); 225 226 if (*rn == NULL) 227 { 228 lh->num_no_delete++; 229 return(NULL); 230 } 231 else 232 { 233 nn= *rn; 234 *rn=nn->next; 235 ret=nn->data; 236 OPENSSL_free(nn); 237 lh->num_delete++; 238 } 239 240 lh->num_items--; 241 if ((lh->num_nodes > MIN_NODES) && 242 (lh->down_load >= (lh->num_items*LH_LOAD_MULT/lh->num_nodes))) 243 contract(lh); 244 245 return(ret); 246 } 247 248 void *lh_retrieve(_LHASH *lh, const void *data) 249 { 250 unsigned long hash; 251 LHASH_NODE **rn; 252 void *ret; 253 254 lh->error=0; 255 rn=getrn(lh,data,&hash); 256 257 if (*rn == NULL) 258 { 259 lh->num_retrieve_miss++; 260 return(NULL); 261 } 262 else 263 { 264 ret= (*rn)->data; 265 lh->num_retrieve++; 266 } 267 return(ret); 268 } 269 270 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) 272 { 273 int i; 274 LHASH_NODE *a,*n; 275 276 if (lh == NULL) 277 return; 278 279 /* reverse the order so we search from 'top to bottom' 280 * We were having memory leaks otherwise */ 281 for (i=lh->num_nodes-1; i>=0; i--) 282 { 283 a=lh->b[i]; 284 while (a != NULL) 285 { 286 /* 28/05/91 - eay - n added so items can be deleted 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; 291 if(use_arg) 292 func_arg(a->data,arg); 293 else 294 func(a->data); 295 a=n; 296 } 297 } 298 } 299 300 void lh_doall(_LHASH *lh, LHASH_DOALL_FN_TYPE func) 301 { 302 doall_util_fn(lh, 0, func, (LHASH_DOALL_ARG_FN_TYPE)0, NULL); 303 } 304 305 void lh_doall_arg(_LHASH *lh, LHASH_DOALL_ARG_FN_TYPE func, void *arg) 306 { 307 doall_util_fn(lh, 1, (LHASH_DOALL_FN_TYPE)0, func, arg); 308 } 309 310 static void expand(_LHASH *lh) 311 { 312 LHASH_NODE **n,**n1,**n2,*np; 313 unsigned int p,i,j; 314 unsigned long hash,nni; 315 316 lh->num_nodes++; 317 lh->num_expands++; 318 p=(int)lh->p++; 319 n1= &(lh->b[p]); 320 n2= &(lh->b[p+(int)lh->pmax]); 321 *n2=NULL; /* 27/07/92 - eay - undefined pointer bug */ 322 nni=lh->num_alloc_nodes; 323 324 for (np= *n1; np != NULL; ) 325 { 326 #ifndef OPENSSL_NO_HASH_COMP 327 hash=np->hash; 328 #else 329 hash=lh->hash(np->data); 330 lh->num_hash_calls++; 331 #endif 332 if ((hash%nni) != p) 333 { /* move it */ 334 *n1= (*n1)->next; 335 np->next= *n2; 336 *n2=np; 337 } 338 else 339 n1= &((*n1)->next); 340 np= *n1; 341 } 342 343 if ((lh->p) >= lh->pmax) 344 { 345 j=(int)lh->num_alloc_nodes*2; 346 n=(LHASH_NODE **)OPENSSL_realloc(lh->b, 347 (int)(sizeof(LHASH_NODE *)*j)); 348 if (n == NULL) 349 { 350 /* fputs("realloc error in lhash",stderr); */ 351 lh->error++; 352 lh->p=0; 353 return; 354 } 355 /* else */ 356 for (i=(int)lh->num_alloc_nodes; i<j; i++)/* 26/02/92 eay */ 357 n[i]=NULL; /* 02/03/92 eay */ 358 lh->pmax=lh->num_alloc_nodes; 359 lh->num_alloc_nodes=j; 360 lh->num_expand_reallocs++; 361 lh->p=0; 362 lh->b=n; 363 } 364 } 365 366 static void contract(_LHASH *lh) 367 { 368 LHASH_NODE **n,*n1,*np; 369 370 np=lh->b[lh->p+lh->pmax-1]; 371 lh->b[lh->p+lh->pmax-1]=NULL; /* 24/07-92 - eay - weird but :-( */ 372 if (lh->p == 0) 373 { 374 n=(LHASH_NODE **)OPENSSL_realloc(lh->b, 375 (unsigned int)(sizeof(LHASH_NODE *)*lh->pmax)); 376 if (n == NULL) 377 { 378 /* fputs("realloc error in lhash",stderr); */ 379 lh->error++; 380 return; 381 } 382 lh->num_contract_reallocs++; 383 lh->num_alloc_nodes/=2; 384 lh->pmax/=2; 385 lh->p=lh->pmax-1; 386 lh->b=n; 387 } 388 else 389 lh->p--; 390 391 lh->num_nodes--; 392 lh->num_contracts++; 393 394 n1=lh->b[(int)lh->p]; 395 if (n1 == NULL) 396 lh->b[(int)lh->p]=np; 397 else 398 { 399 while (n1->next != NULL) 400 n1=n1->next; 401 n1->next=np; 402 } 403 } 404 405 static LHASH_NODE **getrn(_LHASH *lh, const void *data, unsigned long *rhash) 406 { 407 LHASH_NODE **ret,*n1; 408 unsigned long hash,nn; 409 LHASH_COMP_FN_TYPE cf; 410 411 hash=(*(lh->hash))(data); 412 lh->num_hash_calls++; 413 *rhash=hash; 414 415 nn=hash%lh->pmax; 416 if (nn < lh->p) 417 nn=hash%lh->num_alloc_nodes; 418 419 cf=lh->comp; 420 ret= &(lh->b[(int)nn]); 421 for (n1= *ret; n1 != NULL; n1=n1->next) 422 { 423 #ifndef OPENSSL_NO_HASH_COMP 424 lh->num_hash_comps++; 425 if (n1->hash != hash) 426 { 427 ret= &(n1->next); 428 continue; 429 } 430 #endif 431 lh->num_comp_calls++; 432 if(cf(n1->data,data) == 0) 433 break; 434 ret= &(n1->next); 435 } 436 return(ret); 437 } 438 439 /* The following hash seems to work very well on normal text strings 440 * no collisions on /usr/dict/words and it distributes on %2^n quite 441 * well, not as good as MD5, but still good. 442 */ 443 unsigned long lh_strhash(const char *c) 444 { 445 unsigned long ret=0; 446 long n; 447 unsigned long v; 448 int r; 449 450 if ((c == NULL) || (*c == '\0')) 451 return(ret); 452 /* 453 unsigned char b[16]; 454 MD5(c,strlen(c),b); 455 return(b[0]|(b[1]<<8)|(b[2]<<16)|(b[3]<<24)); 456 */ 457 458 n=0x100; 459 while (*c) 460 { 461 v=n|(*c); 462 n+=0x100; 463 r= (int)((v>>2)^v)&0x0f; 464 ret=(ret<<r)|(ret>>(32-r)); 465 ret&=0xFFFFFFFFL; 466 ret^=v*v; 467 c++; 468 } 469 return((ret>>16)^ret); 470 } 471 472 unsigned long lh_num_items(const _LHASH *lh) 473 { 474 return lh ? lh->num_items : 0; 475 } 476