xref: /dragonfly/crypto/libressl/crypto/lhash/lhash.c (revision c6ecc293)
1 /* $OpenBSD: lhash.c,v 1.19 2019/05/12 00:09:59 beck Exp $ */
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 /* 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 
101 #include <openssl/opensslconf.h>
102 
103 #include <openssl/crypto.h>
104 #include <openssl/lhash.h>
105 
106 #undef MIN_NODES
107 #define MIN_NODES	16
108 #define UP_LOAD		(2*LH_LOAD_MULT) /* load times 256  (default 2) */
109 #define DOWN_LOAD	(LH_LOAD_MULT)   /* load times 256  (default 1) */
110 
111 static void expand(_LHASH *lh);
112 static void contract(_LHASH *lh);
113 static LHASH_NODE **getrn(_LHASH *lh, const void *data, unsigned long *rhash);
114 
115 _LHASH *
116 lh_new(LHASH_HASH_FN_TYPE h, LHASH_COMP_FN_TYPE c)
117 {
118 	_LHASH *ret;
119 
120 	if ((ret = calloc(1, sizeof(_LHASH))) == NULL)
121 		return NULL;
122 	if ((ret->b = calloc(MIN_NODES, sizeof(LHASH_NODE *))) == NULL) {
123 		free(ret);
124 		return NULL;
125 	}
126 	ret->comp = ((c == NULL) ? (LHASH_COMP_FN_TYPE)strcmp : c);
127 	ret->hash = ((h == NULL) ? (LHASH_HASH_FN_TYPE)lh_strhash : h);
128 	ret->num_nodes = MIN_NODES / 2;
129 	ret->num_alloc_nodes = MIN_NODES;
130 	ret->pmax = MIN_NODES / 2;
131 	ret->up_load = UP_LOAD;
132 	ret->down_load = DOWN_LOAD;
133 
134 	return (ret);
135 }
136 
137 void
138 lh_free(_LHASH *lh)
139 {
140 	unsigned int i;
141 	LHASH_NODE *n, *nn;
142 
143 	if (lh == NULL)
144 		return;
145 
146 	for (i = 0; i < lh->num_nodes; i++) {
147 		n = lh->b[i];
148 		while (n != NULL) {
149 			nn = n->next;
150 			free(n);
151 			n = nn;
152 		}
153 	}
154 	free(lh->b);
155 	free(lh);
156 }
157 
158 void *
159 lh_insert(_LHASH *lh, void *data)
160 {
161 	unsigned long hash;
162 	LHASH_NODE *nn, **rn;
163 	void *ret;
164 
165 	lh->error = 0;
166 	if (lh->up_load <= (lh->num_items * LH_LOAD_MULT / lh->num_nodes))
167 		expand(lh);
168 
169 	rn = getrn(lh, data, &hash);
170 
171 	if (*rn == NULL) {
172 		if ((nn = malloc(sizeof(LHASH_NODE))) == NULL) {
173 			lh->error++;
174 			return (NULL);
175 		}
176 		nn->data = data;
177 		nn->next = NULL;
178 #ifndef OPENSSL_NO_HASH_COMP
179 		nn->hash = hash;
180 #endif
181 		*rn = nn;
182 		ret = NULL;
183 		lh->num_insert++;
184 		lh->num_items++;
185 	}
186 	else /* replace same key */
187 	{
188 		ret = (*rn)->data;
189 		(*rn)->data = data;
190 		lh->num_replace++;
191 	}
192 	return (ret);
193 }
194 
195 void *
196 lh_delete(_LHASH *lh, const void *data)
197 {
198 	unsigned long hash;
199 	LHASH_NODE *nn, **rn;
200 	void *ret;
201 
202 	lh->error = 0;
203 	rn = getrn(lh, data, &hash);
204 
205 	if (*rn == NULL) {
206 		lh->num_no_delete++;
207 		return (NULL);
208 	} else {
209 		nn= *rn;
210 		*rn = nn->next;
211 		ret = nn->data;
212 		free(nn);
213 		lh->num_delete++;
214 	}
215 
216 	lh->num_items--;
217 	if ((lh->num_nodes > MIN_NODES) &&
218 	    (lh->down_load >= (lh->num_items * LH_LOAD_MULT / lh->num_nodes)))
219 		contract(lh);
220 
221 	return (ret);
222 }
223 
224 void *
225 lh_retrieve(_LHASH *lh, const void *data)
226 {
227 	unsigned long hash;
228 	LHASH_NODE **rn;
229 	void *ret;
230 
231 	lh->error = 0;
232 	rn = getrn(lh, data, &hash);
233 
234 	if (*rn == NULL) {
235 		lh->num_retrieve_miss++;
236 		return (NULL);
237 	} else {
238 		ret = (*rn)->data;
239 		lh->num_retrieve++;
240 	}
241 	return (ret);
242 }
243 
244 static void
245 doall_util_fn(_LHASH *lh, int use_arg, LHASH_DOALL_FN_TYPE func,
246     LHASH_DOALL_ARG_FN_TYPE func_arg, void *arg)
247 {
248 	int i;
249 	LHASH_NODE *a, *n;
250 
251 	if (lh == NULL)
252 		return;
253 
254 	/* reverse the order so we search from 'top to bottom'
255 	 * We were having memory leaks otherwise */
256 	for (i = lh->num_nodes - 1; i >= 0; i--) {
257 		a = lh->b[i];
258 		while (a != NULL) {
259 			/* 28/05/91 - eay - n added so items can be deleted
260 			 * via lh_doall */
261 			/* 22/05/08 - ben - eh? since a is not passed,
262 			 * this should not be needed */
263 			n = a->next;
264 			if (use_arg)
265 				func_arg(a->data, arg);
266 			else
267 				func(a->data);
268 			a = n;
269 		}
270 	}
271 }
272 
273 void
274 lh_doall(_LHASH *lh, LHASH_DOALL_FN_TYPE func)
275 {
276 	doall_util_fn(lh, 0, func, (LHASH_DOALL_ARG_FN_TYPE)0, NULL);
277 }
278 
279 void
280 lh_doall_arg(_LHASH *lh, LHASH_DOALL_ARG_FN_TYPE func, void *arg)
281 {
282 	doall_util_fn(lh, 1, (LHASH_DOALL_FN_TYPE)0, func, arg);
283 }
284 
285 static void
286 expand(_LHASH *lh)
287 {
288 	LHASH_NODE **n, **n1, **n2, *np;
289 	unsigned int p, i, j;
290 	unsigned long hash, nni;
291 
292 	lh->num_nodes++;
293 	lh->num_expands++;
294 	p = (int)lh->p++;
295 	n1 = &(lh->b[p]);
296 	n2 = &(lh->b[p + (int)lh->pmax]);
297 	*n2 = NULL;        /* 27/07/92 - eay - undefined pointer bug */
298 	nni = lh->num_alloc_nodes;
299 
300 	for (np = *n1; np != NULL; ) {
301 #ifndef OPENSSL_NO_HASH_COMP
302 		hash = np->hash;
303 #else
304 		hash = lh->hash(np->data);
305 		lh->num_hash_calls++;
306 #endif
307 		if ((hash % nni) != p) { /* move it */
308 			*n1 = (*n1)->next;
309 			np->next= *n2;
310 			*n2 = np;
311 		} else
312 			n1 = &((*n1)->next);
313 		np= *n1;
314 	}
315 
316 	if ((lh->p) >= lh->pmax) {
317 		j = (int)lh->num_alloc_nodes * 2;
318 		n = reallocarray(lh->b, j, sizeof(LHASH_NODE *));
319 		if (n == NULL) {
320 /*			fputs("realloc error in lhash", stderr); */
321 			lh->error++;
322 			lh->p = 0;
323 			return;
324 		}
325 		/* else */
326 		for (i = (int)lh->num_alloc_nodes; i < j; i++)/* 26/02/92 eay */
327 			n[i] = NULL;			  /* 02/03/92 eay */
328 		lh->pmax = lh->num_alloc_nodes;
329 		lh->num_alloc_nodes = j;
330 		lh->num_expand_reallocs++;
331 		lh->p = 0;
332 		lh->b = n;
333 	}
334 }
335 
336 static void
337 contract(_LHASH *lh)
338 {
339 	LHASH_NODE **n, *n1, *np;
340 
341 	np = lh->b[lh->p + lh->pmax - 1];
342 	lh->b[lh->p+lh->pmax - 1] = NULL; /* 24/07-92 - eay - weird but :-( */
343 	if (lh->p == 0) {
344 		n = reallocarray(lh->b, lh->pmax, sizeof(LHASH_NODE *));
345 		if (n == NULL) {
346 /*			fputs("realloc error in lhash", stderr); */
347 			lh->error++;
348 			return;
349 		}
350 		lh->num_contract_reallocs++;
351 		lh->num_alloc_nodes /= 2;
352 		lh->pmax /= 2;
353 		lh->p = lh->pmax - 1;
354 		lh->b = n;
355 	} else
356 		lh->p--;
357 
358 	lh->num_nodes--;
359 	lh->num_contracts++;
360 
361 	n1 = lh->b[(int)lh->p];
362 	if (n1 == NULL)
363 		lh->b[(int)lh->p] = np;
364 	else {
365 		while (n1->next != NULL)
366 			n1 = n1->next;
367 		n1->next = np;
368 	}
369 }
370 
371 static LHASH_NODE **getrn(_LHASH *lh, const void *data, unsigned long *rhash)
372 {
373 	LHASH_NODE **ret, *n1;
374 	unsigned long hash, nn;
375 	LHASH_COMP_FN_TYPE cf;
376 
377 	hash = (*(lh->hash))(data);
378 	lh->num_hash_calls++;
379 	*rhash = hash;
380 
381 	nn = hash % lh->pmax;
382 	if (nn < lh->p)
383 		nn = hash % lh->num_alloc_nodes;
384 
385 	cf = lh->comp;
386 	ret = &(lh->b[(int)nn]);
387 	for (n1 = *ret; n1 != NULL; n1 = n1->next) {
388 #ifndef OPENSSL_NO_HASH_COMP
389 		lh->num_hash_comps++;
390 		if (n1->hash != hash) {
391 			ret = &(n1->next);
392 			continue;
393 		}
394 #endif
395 		lh->num_comp_calls++;
396 		if (cf(n1->data, data) == 0)
397 			break;
398 		ret = &(n1->next);
399 	}
400 	return (ret);
401 }
402 
403 /* The following hash seems to work very well on normal text strings
404  * no collisions on /usr/dict/words and it distributes on %2^n quite
405  * well, not as good as MD5, but still good.
406  */
407 unsigned long
408 lh_strhash(const char *c)
409 {
410 	unsigned long ret = 0;
411 	unsigned long n, v;
412 	unsigned int r;
413 
414 	if (c == NULL || *c == '\0')
415 		return ret;
416 
417 	n = 0x100;
418 	while (*c) {
419 		v = n | *c;
420 		n += 0x100;
421 		if ((r = ((v >> 2) ^ v) & 0x0f) != 0)
422 			ret = (ret << r) | (ret >> (32 - r));
423 		ret &= 0xFFFFFFFFUL;
424 		ret ^= v * v;
425 		c++;
426 	}
427 	return (ret >> 16) ^ ret;
428 }
429 
430 unsigned long
431 lh_num_items(const _LHASH *lh)
432 {
433 	return lh ? lh->num_items : 0;
434 }
435