lhash.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. /* crypto/lhash/lhash.c */
  2. /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
  3. * All rights reserved.
  4. *
  5. * This package is an SSL implementation written
  6. * by Eric Young (eay@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@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@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@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. /* Code for dynamic hash table routines
  59. * Author - Eric Young v 2.0
  60. *
  61. * 2.2 eay - added #include "crypto.h" so the memory leak checking code is
  62. * present. eay 18-Jun-98
  63. *
  64. * 2.1 eay - Added an 'error in last operation' flag. eay 6-May-98
  65. *
  66. * 2.0 eay - Fixed a bug that occurred when using lh_delete
  67. * from inside lh_doall(). As entries were deleted,
  68. * the 'table' was 'contract()ed', making some entries
  69. * jump from the end of the table to the start, there by
  70. * skipping the lh_doall() processing. eay - 4/12/95
  71. *
  72. * 1.9 eay - Fixed a memory leak in lh_free, the LHASH_NODEs
  73. * were not being free()ed. 21/11/95
  74. *
  75. * 1.8 eay - Put the stats routines into a separate file, lh_stats.c
  76. * 19/09/95
  77. *
  78. * 1.7 eay - Removed the fputs() for realloc failures - the code
  79. * should silently tolerate them. I have also fixed things
  80. * lint complained about 04/05/95
  81. *
  82. * 1.6 eay - Fixed an invalid pointers in contract/expand 27/07/92
  83. *
  84. * 1.5 eay - Fixed a misuse of realloc in expand 02/03/1992
  85. *
  86. * 1.4 eay - Fixed lh_doall so the function can call lh_delete 28/05/91
  87. *
  88. * 1.3 eay - Fixed a few lint problems 19/3/1991
  89. *
  90. * 1.2 eay - Fixed lh_doall problem 13/3/1991
  91. *
  92. * 1.1 eay - Added lh_doall
  93. *
  94. * 1.0 eay - First version
  95. */
  96. #include <stdio.h>
  97. #include <string.h>
  98. #include <stdlib.h>
  99. #include <openssl/crypto.h>
  100. #include <openssl/lhash.h>
  101. const char *lh_version="lhash" OPENSSL_VERSION_PTEXT;
  102. #undef MIN_NODES
  103. #define MIN_NODES 16
  104. #define UP_LOAD (2*LH_LOAD_MULT) /* load times 256 (default 2) */
  105. #define DOWN_LOAD (LH_LOAD_MULT) /* load times 256 (default 1) */
  106. static void expand(LHASH *lh);
  107. static void contract(LHASH *lh);
  108. static LHASH_NODE **getrn(LHASH *lh, const void *data, unsigned long *rhash);
  109. LHASH *lh_new(LHASH_HASH_FN_TYPE h, LHASH_COMP_FN_TYPE c)
  110. {
  111. LHASH *ret;
  112. int i;
  113. if ((ret=(LHASH *)OPENSSL_malloc(sizeof(LHASH))) == NULL)
  114. goto err0;
  115. if ((ret->b=(LHASH_NODE **)OPENSSL_malloc(sizeof(LHASH_NODE *)*MIN_NODES)) == NULL)
  116. goto err1;
  117. for (i=0; i<MIN_NODES; i++)
  118. ret->b[i]=NULL;
  119. ret->comp=((c == NULL)?(LHASH_COMP_FN_TYPE)strcmp:c);
  120. ret->hash=((h == NULL)?(LHASH_HASH_FN_TYPE)lh_strhash:h);
  121. ret->num_nodes=MIN_NODES/2;
  122. ret->num_alloc_nodes=MIN_NODES;
  123. ret->p=0;
  124. ret->pmax=MIN_NODES/2;
  125. ret->up_load=UP_LOAD;
  126. ret->down_load=DOWN_LOAD;
  127. ret->num_items=0;
  128. ret->num_expands=0;
  129. ret->num_expand_reallocs=0;
  130. ret->num_contracts=0;
  131. ret->num_contract_reallocs=0;
  132. ret->num_hash_calls=0;
  133. ret->num_comp_calls=0;
  134. ret->num_insert=0;
  135. ret->num_replace=0;
  136. ret->num_delete=0;
  137. ret->num_no_delete=0;
  138. ret->num_retrieve=0;
  139. ret->num_retrieve_miss=0;
  140. ret->num_hash_comps=0;
  141. ret->error=0;
  142. return(ret);
  143. err1:
  144. OPENSSL_free(ret);
  145. err0:
  146. return(NULL);
  147. }
  148. void lh_free(LHASH *lh)
  149. {
  150. unsigned int i;
  151. LHASH_NODE *n,*nn;
  152. if (lh == NULL)
  153. return;
  154. for (i=0; i<lh->num_nodes; i++)
  155. {
  156. n=lh->b[i];
  157. while (n != NULL)
  158. {
  159. nn=n->next;
  160. OPENSSL_free(n);
  161. n=nn;
  162. }
  163. }
  164. OPENSSL_free(lh->b);
  165. OPENSSL_free(lh);
  166. }
  167. void *lh_insert(LHASH *lh, void *data)
  168. {
  169. unsigned long hash;
  170. LHASH_NODE *nn,**rn;
  171. const void *ret;
  172. lh->error=0;
  173. if (lh->up_load <= (lh->num_items*LH_LOAD_MULT/lh->num_nodes))
  174. expand(lh);
  175. rn=getrn(lh,data,&hash);
  176. if (*rn == NULL)
  177. {
  178. if ((nn=(LHASH_NODE *)OPENSSL_malloc(sizeof(LHASH_NODE))) == NULL)
  179. {
  180. lh->error++;
  181. return(NULL);
  182. }
  183. nn->data=data;
  184. nn->next=NULL;
  185. #ifndef OPENSSL_NO_HASH_COMP
  186. nn->hash=hash;
  187. #endif
  188. *rn=nn;
  189. ret=NULL;
  190. lh->num_insert++;
  191. lh->num_items++;
  192. }
  193. else /* replace same key */
  194. {
  195. ret= (*rn)->data;
  196. (*rn)->data=data;
  197. lh->num_replace++;
  198. }
  199. return((void *)ret);
  200. }
  201. void *lh_delete(LHASH *lh, const void *data)
  202. {
  203. unsigned long hash;
  204. LHASH_NODE *nn,**rn;
  205. const void *ret;
  206. lh->error=0;
  207. rn=getrn(lh,data,&hash);
  208. if (*rn == NULL)
  209. {
  210. lh->num_no_delete++;
  211. return(NULL);
  212. }
  213. else
  214. {
  215. nn= *rn;
  216. *rn=nn->next;
  217. ret=nn->data;
  218. OPENSSL_free(nn);
  219. lh->num_delete++;
  220. }
  221. lh->num_items--;
  222. if ((lh->num_nodes > MIN_NODES) &&
  223. (lh->down_load >= (lh->num_items*LH_LOAD_MULT/lh->num_nodes)))
  224. contract(lh);
  225. return((void *)ret);
  226. }
  227. void *lh_retrieve(LHASH *lh, const void *data)
  228. {
  229. unsigned long hash;
  230. LHASH_NODE **rn;
  231. const void *ret;
  232. lh->error=0;
  233. rn=getrn(lh,data,&hash);
  234. if (*rn == NULL)
  235. {
  236. lh->num_retrieve_miss++;
  237. return(NULL);
  238. }
  239. else
  240. {
  241. ret= (*rn)->data;
  242. lh->num_retrieve++;
  243. }
  244. return((void *)ret);
  245. }
  246. static void doall_util_fn(LHASH *lh, int use_arg, LHASH_DOALL_FN_TYPE func,
  247. LHASH_DOALL_ARG_FN_TYPE func_arg, void *arg)
  248. {
  249. int i;
  250. LHASH_NODE *a,*n;
  251. /* reverse the order so we search from 'top to bottom'
  252. * We were having memory leaks otherwise */
  253. for (i=lh->num_nodes-1; i>=0; i--)
  254. {
  255. a=lh->b[i];
  256. while (a != NULL)
  257. {
  258. /* 28/05/91 - eay - n added so items can be deleted
  259. * via lh_doall */
  260. n=a->next;
  261. if(use_arg)
  262. func_arg(a->data,arg);
  263. else
  264. func(a->data);
  265. a=n;
  266. }
  267. }
  268. }
  269. void lh_doall(LHASH *lh, LHASH_DOALL_FN_TYPE func)
  270. {
  271. doall_util_fn(lh, 0, func, (LHASH_DOALL_ARG_FN_TYPE)0, NULL);
  272. }
  273. void lh_doall_arg(LHASH *lh, LHASH_DOALL_ARG_FN_TYPE func, void *arg)
  274. {
  275. doall_util_fn(lh, 1, (LHASH_DOALL_FN_TYPE)0, func, arg);
  276. }
  277. static void expand(LHASH *lh)
  278. {
  279. LHASH_NODE **n,**n1,**n2,*np;
  280. unsigned int p,i,j;
  281. unsigned long hash,nni;
  282. lh->num_nodes++;
  283. lh->num_expands++;
  284. p=(int)lh->p++;
  285. n1= &(lh->b[p]);
  286. n2= &(lh->b[p+(int)lh->pmax]);
  287. *n2=NULL; /* 27/07/92 - eay - undefined pointer bug */
  288. nni=lh->num_alloc_nodes;
  289. for (np= *n1; np != NULL; )
  290. {
  291. #ifndef OPENSSL_NO_HASH_COMP
  292. hash=np->hash;
  293. #else
  294. hash=lh->hash(np->data);
  295. lh->num_hash_calls++;
  296. #endif
  297. if ((hash%nni) != p)
  298. { /* move it */
  299. *n1= (*n1)->next;
  300. np->next= *n2;
  301. *n2=np;
  302. }
  303. else
  304. n1= &((*n1)->next);
  305. np= *n1;
  306. }
  307. if ((lh->p) >= lh->pmax)
  308. {
  309. j=(int)lh->num_alloc_nodes*2;
  310. n=(LHASH_NODE **)OPENSSL_realloc(lh->b,
  311. (unsigned int)sizeof(LHASH_NODE *)*j);
  312. if (n == NULL)
  313. {
  314. /* fputs("realloc error in lhash",stderr); */
  315. lh->error++;
  316. lh->p=0;
  317. return;
  318. }
  319. /* else */
  320. for (i=(int)lh->num_alloc_nodes; i<j; i++)/* 26/02/92 eay */
  321. n[i]=NULL; /* 02/03/92 eay */
  322. lh->pmax=lh->num_alloc_nodes;
  323. lh->num_alloc_nodes=j;
  324. lh->num_expand_reallocs++;
  325. lh->p=0;
  326. lh->b=n;
  327. }
  328. }
  329. static void contract(LHASH *lh)
  330. {
  331. LHASH_NODE **n,*n1,*np;
  332. np=lh->b[lh->p+lh->pmax-1];
  333. lh->b[lh->p+lh->pmax-1]=NULL; /* 24/07-92 - eay - weird but :-( */
  334. if (lh->p == 0)
  335. {
  336. n=(LHASH_NODE **)OPENSSL_realloc(lh->b,
  337. (unsigned int)(sizeof(LHASH_NODE *)*lh->pmax));
  338. if (n == NULL)
  339. {
  340. /* fputs("realloc error in lhash",stderr); */
  341. lh->error++;
  342. return;
  343. }
  344. lh->num_contract_reallocs++;
  345. lh->num_alloc_nodes/=2;
  346. lh->pmax/=2;
  347. lh->p=lh->pmax-1;
  348. lh->b=n;
  349. }
  350. else
  351. lh->p--;
  352. lh->num_nodes--;
  353. lh->num_contracts++;
  354. n1=lh->b[(int)lh->p];
  355. if (n1 == NULL)
  356. lh->b[(int)lh->p]=np;
  357. else
  358. {
  359. while (n1->next != NULL)
  360. n1=n1->next;
  361. n1->next=np;
  362. }
  363. }
  364. static LHASH_NODE **getrn(LHASH *lh, const void *data, unsigned long *rhash)
  365. {
  366. LHASH_NODE **ret,*n1;
  367. unsigned long hash,nn;
  368. int (*cf)();
  369. hash=(*(lh->hash))(data);
  370. lh->num_hash_calls++;
  371. *rhash=hash;
  372. nn=hash%lh->pmax;
  373. if (nn < lh->p)
  374. nn=hash%lh->num_alloc_nodes;
  375. cf=lh->comp;
  376. ret= &(lh->b[(int)nn]);
  377. for (n1= *ret; n1 != NULL; n1=n1->next)
  378. {
  379. #ifndef OPENSSL_NO_HASH_COMP
  380. lh->num_hash_comps++;
  381. if (n1->hash != hash)
  382. {
  383. ret= &(n1->next);
  384. continue;
  385. }
  386. #endif
  387. lh->num_comp_calls++;
  388. if(cf(n1->data,data) == 0)
  389. break;
  390. ret= &(n1->next);
  391. }
  392. return(ret);
  393. }
  394. /* The following hash seems to work very well on normal text strings
  395. * no collisions on /usr/dict/words and it distributes on %2^n quite
  396. * well, not as good as MD5, but still good.
  397. */
  398. unsigned long lh_strhash(const char *c)
  399. {
  400. unsigned long ret=0;
  401. long n;
  402. unsigned long v;
  403. int r;
  404. if ((c == NULL) || (*c == '\0'))
  405. return(ret);
  406. /*
  407. unsigned char b[16];
  408. MD5(c,strlen(c),b);
  409. return(b[0]|(b[1]<<8)|(b[2]<<16)|(b[3]<<24));
  410. */
  411. n=0x100;
  412. while (*c)
  413. {
  414. v=n|(*c);
  415. n+=0x100;
  416. r= (int)((v>>2)^v)&0x0f;
  417. ret=(ret<<r)|(ret>>(32-r));
  418. ret&=0xFFFFFFFFL;
  419. ret^=v*v;
  420. c++;
  421. }
  422. return((ret>>16)^ret);
  423. }
  424. unsigned long lh_num_items(const LHASH *lh)
  425. {
  426. return lh ? lh->num_items : 0;
  427. }