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