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