perl 3.0 patch #22 patch #19, continued
[p5sagit/p5-mst-13.2.git] / hash.c
CommitLineData
154e51a4 1/* $Header: hash.c,v 3.0.1.4 90/08/09 03:50:22 lwall Locked $
a687059c 2 *
3 * Copyright (c) 1989, Larry Wall
4 *
5 * You may distribute under the terms of the GNU General Public License
6 * as specified in the README file that comes with the perl 3.0 kit.
8d063cd8 7 *
8 * $Log: hash.c,v $
154e51a4 9 * Revision 3.0.1.4 90/08/09 03:50:22 lwall
10 * patch19: dbmopen(name, 'filename', undef) now refrains from creating
11 *
b1248f16 12 * Revision 3.0.1.3 90/03/27 15:59:09 lwall
13 * patch16: @dbmvalues{'foo','bar'} could use the same cache entry for both values
14 *
663a0e37 15 * Revision 3.0.1.2 89/12/21 20:03:39 lwall
16 * patch7: errno may now be a macro with an lvalue
17 *
bf38876a 18 * Revision 3.0.1.1 89/11/11 04:34:18 lwall
19 * patch2: CX/UX needed to set the key each time in associative iterators
20 *
a687059c 21 * Revision 3.0 89/10/18 15:18:32 lwall
22 * 3.0 baseline
8d063cd8 23 *
24 */
25
8d063cd8 26#include "EXTERN.h"
8d063cd8 27#include "perl.h"
28
154e51a4 29static char coeff[] = {
30 61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,
31 61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,
32 61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,
33 61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,
34 61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,
35 61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,
36 61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1,
37 61,59,53,47,43,41,37,31,29,23,17,13,11,7,3,1};
38
8d063cd8 39STR *
a687059c 40hfetch(tb,key,klen,lval)
8d063cd8 41register HASH *tb;
42char *key;
a687059c 43int klen;
44int lval;
8d063cd8 45{
46 register char *s;
47 register int i;
48 register int hash;
49 register HENT *entry;
a687059c 50 register int maxi;
51 STR *str;
52#ifdef SOME_DBM
53 datum dkey,dcontent;
54#endif
8d063cd8 55
56 if (!tb)
57 return Nullstr;
a687059c 58
59 /* The hash function we use on symbols has to be equal to the first
60 * character when taken modulo 128, so that str_reset() can be implemented
61 * efficiently. We throw in the second character and the last character
62 * (times 128) so that long chains of identifiers starting with the
63 * same letter don't have to be strEQ'ed within hfetch(), since it
64 * compares hash values before trying strEQ().
65 */
66 if (!tb->tbl_coeffsize)
67 hash = *key + 128 * key[1] + 128 * key[klen-1]; /* assuming klen > 0 */
68 else { /* use normal coefficients */
69 if (klen < tb->tbl_coeffsize)
70 maxi = klen;
71 else
72 maxi = tb->tbl_coeffsize;
73 for (s=key, i=0, hash = 0;
74 i < maxi;
75 s++, i++, hash *= 5) {
76 hash += *s * coeff[i];
77 }
8d063cd8 78 }
a687059c 79
8d063cd8 80 entry = tb->tbl_array[hash & tb->tbl_max];
81 for (; entry; entry = entry->hent_next) {
82 if (entry->hent_hash != hash) /* strings can't be equal */
83 continue;
a687059c 84 if (entry->hent_klen != klen)
85 continue;
86 if (bcmp(entry->hent_key,key,klen)) /* is this it? */
8d063cd8 87 continue;
88 return entry->hent_val;
89 }
a687059c 90#ifdef SOME_DBM
91 if (tb->tbl_dbm) {
92 dkey.dptr = key;
93 dkey.dsize = klen;
94 dcontent = dbm_fetch(tb->tbl_dbm,dkey);
95 if (dcontent.dptr) { /* found one */
96 str = Str_new(60,dcontent.dsize);
97 str_nset(str,dcontent.dptr,dcontent.dsize);
98 hstore(tb,key,klen,str,hash); /* cache it */
99 return str;
100 }
101 }
102#endif
103 if (lval) { /* gonna assign to this, so it better be there */
104 str = Str_new(61,0);
105 hstore(tb,key,klen,str,hash);
106 return str;
107 }
8d063cd8 108 return Nullstr;
109}
110
111bool
a687059c 112hstore(tb,key,klen,val,hash)
8d063cd8 113register HASH *tb;
114char *key;
a687059c 115int klen;
8d063cd8 116STR *val;
a687059c 117register int hash;
8d063cd8 118{
119 register char *s;
120 register int i;
8d063cd8 121 register HENT *entry;
122 register HENT **oentry;
a687059c 123 register int maxi;
8d063cd8 124
125 if (!tb)
126 return FALSE;
a687059c 127
128 if (hash)
129 ;
130 else if (!tb->tbl_coeffsize)
131 hash = *key + 128 * key[1] + 128 * key[klen-1];
132 else { /* use normal coefficients */
133 if (klen < tb->tbl_coeffsize)
134 maxi = klen;
135 else
136 maxi = tb->tbl_coeffsize;
137 for (s=key, i=0, hash = 0;
138 i < maxi;
139 s++, i++, hash *= 5) {
140 hash += *s * coeff[i];
141 }
8d063cd8 142 }
143
144 oentry = &(tb->tbl_array[hash & tb->tbl_max]);
145 i = 1;
146
147 for (entry = *oentry; entry; i=0, entry = entry->hent_next) {
148 if (entry->hent_hash != hash) /* strings can't be equal */
149 continue;
a687059c 150 if (entry->hent_klen != klen)
151 continue;
152 if (bcmp(entry->hent_key,key,klen)) /* is this it? */
8d063cd8 153 continue;
a687059c 154 Safefree(entry->hent_val);
8d063cd8 155 entry->hent_val = val;
156 return TRUE;
157 }
a687059c 158 New(501,entry, 1, HENT);
8d063cd8 159
a687059c 160 entry->hent_klen = klen;
161 entry->hent_key = nsavestr(key,klen);
8d063cd8 162 entry->hent_val = val;
163 entry->hent_hash = hash;
164 entry->hent_next = *oentry;
165 *oentry = entry;
166
a687059c 167 /* hdbmstore not necessary here because it's called from stabset() */
168
8d063cd8 169 if (i) { /* initial entry? */
170 tb->tbl_fill++;
a687059c 171#ifdef SOME_DBM
172 if (tb->tbl_dbm && tb->tbl_max >= DBM_CACHE_MAX)
173 return FALSE;
174#endif
175 if (tb->tbl_fill > tb->tbl_dosplit)
8d063cd8 176 hsplit(tb);
177 }
a687059c 178#ifdef SOME_DBM
179 else if (tb->tbl_dbm) { /* is this just a cache for dbm file? */
b1248f16 180 void hentdelayfree();
181
a687059c 182 entry = tb->tbl_array[hash & tb->tbl_max];
183 oentry = &entry->hent_next;
184 entry = *oentry;
185 while (entry) { /* trim chain down to 1 entry */
186 *oentry = entry->hent_next;
b1248f16 187 hentdelayfree(entry); /* no doubt they'll want this next. */
a687059c 188 entry = *oentry;
189 }
190 }
191#endif
8d063cd8 192
193 return FALSE;
194}
195
378cc40b 196STR *
a687059c 197hdelete(tb,key,klen)
8d063cd8 198register HASH *tb;
199char *key;
a687059c 200int klen;
8d063cd8 201{
202 register char *s;
203 register int i;
204 register int hash;
205 register HENT *entry;
206 register HENT **oentry;
378cc40b 207 STR *str;
a687059c 208 int maxi;
209#ifdef SOME_DBM
210 datum dkey;
211#endif
8d063cd8 212
213 if (!tb)
378cc40b 214 return Nullstr;
a687059c 215 if (!tb->tbl_coeffsize)
216 hash = *key + 128 * key[1] + 128 * key[klen-1];
217 else { /* use normal coefficients */
218 if (klen < tb->tbl_coeffsize)
219 maxi = klen;
220 else
221 maxi = tb->tbl_coeffsize;
222 for (s=key, i=0, hash = 0;
223 i < maxi;
224 s++, i++, hash *= 5) {
225 hash += *s * coeff[i];
226 }
8d063cd8 227 }
228
229 oentry = &(tb->tbl_array[hash & tb->tbl_max]);
230 entry = *oentry;
231 i = 1;
378cc40b 232 for (; entry; i=0, oentry = &entry->hent_next, entry = *oentry) {
8d063cd8 233 if (entry->hent_hash != hash) /* strings can't be equal */
234 continue;
a687059c 235 if (entry->hent_klen != klen)
236 continue;
237 if (bcmp(entry->hent_key,key,klen)) /* is this it? */
8d063cd8 238 continue;
8d063cd8 239 *oentry = entry->hent_next;
378cc40b 240 str = str_static(entry->hent_val);
241 hentfree(entry);
8d063cd8 242 if (i)
243 tb->tbl_fill--;
a687059c 244#ifdef SOME_DBM
245 do_dbm_delete:
246 if (tb->tbl_dbm) {
247 dkey.dptr = key;
248 dkey.dsize = klen;
249 dbm_delete(tb->tbl_dbm,dkey);
250 }
251#endif
378cc40b 252 return str;
8d063cd8 253 }
a687059c 254#ifdef SOME_DBM
255 str = Nullstr;
256 goto do_dbm_delete;
257#else
378cc40b 258 return Nullstr;
a687059c 259#endif
8d063cd8 260}
8d063cd8 261
262hsplit(tb)
263HASH *tb;
264{
265 int oldsize = tb->tbl_max + 1;
266 register int newsize = oldsize * 2;
267 register int i;
268 register HENT **a;
269 register HENT **b;
270 register HENT *entry;
271 register HENT **oentry;
272
a687059c 273 a = tb->tbl_array;
274 Renew(a, newsize, HENT*);
275 Zero(&a[oldsize], oldsize, HENT*); /* zero 2nd half*/
8d063cd8 276 tb->tbl_max = --newsize;
a687059c 277 tb->tbl_dosplit = tb->tbl_max * FILLPCT / 100;
8d063cd8 278 tb->tbl_array = a;
279
280 for (i=0; i<oldsize; i++,a++) {
281 if (!*a) /* non-existent */
282 continue;
283 b = a+oldsize;
284 for (oentry = a, entry = *a; entry; entry = *oentry) {
285 if ((entry->hent_hash & newsize) != i) {
286 *oentry = entry->hent_next;
287 entry->hent_next = *b;
288 if (!*b)
289 tb->tbl_fill++;
290 *b = entry;
291 continue;
292 }
293 else
294 oentry = &entry->hent_next;
295 }
296 if (!*a) /* everything moved */
297 tb->tbl_fill--;
298 }
299}
300
301HASH *
a687059c 302hnew(lookat)
303unsigned int lookat;
8d063cd8 304{
a687059c 305 register HASH *tb;
8d063cd8 306
a687059c 307 Newz(502,tb, 1, HASH);
308 if (lookat) {
309 tb->tbl_coeffsize = lookat;
310 tb->tbl_max = 7; /* it's a normal associative array */
311 tb->tbl_dosplit = tb->tbl_max * FILLPCT / 100;
312 }
313 else {
314 tb->tbl_max = 127; /* it's a symbol table */
315 tb->tbl_dosplit = 128; /* so never split */
316 }
317 Newz(503,tb->tbl_array, tb->tbl_max + 1, HENT*);
8d063cd8 318 tb->tbl_fill = 0;
a687059c 319#ifdef SOME_DBM
320 tb->tbl_dbm = 0;
321#endif
322 (void)hiterinit(tb); /* so each() will start off right */
8d063cd8 323 return tb;
324}
325
378cc40b 326void
327hentfree(hent)
328register HENT *hent;
329{
330 if (!hent)
331 return;
332 str_free(hent->hent_val);
a687059c 333 Safefree(hent->hent_key);
334 Safefree(hent);
378cc40b 335}
336
337void
b1248f16 338hentdelayfree(hent)
339register HENT *hent;
340{
341 if (!hent)
342 return;
343 str_2static(hent->hent_val); /* free between statements */
344 Safefree(hent->hent_key);
345 Safefree(hent);
346}
347
348void
378cc40b 349hclear(tb)
350register HASH *tb;
351{
352 register HENT *hent;
353 register HENT *ohent = Null(HENT*);
354
355 if (!tb)
356 return;
a687059c 357 (void)hiterinit(tb);
378cc40b 358 while (hent = hiternext(tb)) { /* concise but not very efficient */
359 hentfree(ohent);
360 ohent = hent;
361 }
362 hentfree(ohent);
363 tb->tbl_fill = 0;
a687059c 364#ifndef lint
365 (void)bzero((char*)tb->tbl_array, (tb->tbl_max + 1) * sizeof(HENT*));
366#endif
378cc40b 367}
368
378cc40b 369void
370hfree(tb)
a687059c 371register HASH *tb;
378cc40b 372{
a687059c 373 register HENT *hent;
374 register HENT *ohent = Null(HENT*);
375
378cc40b 376 if (!tb)
a687059c 377 return;
378 (void)hiterinit(tb);
378cc40b 379 while (hent = hiternext(tb)) {
380 hentfree(ohent);
381 ohent = hent;
382 }
383 hentfree(ohent);
a687059c 384 Safefree(tb->tbl_array);
385 Safefree(tb);
378cc40b 386}
8d063cd8 387
a687059c 388int
8d063cd8 389hiterinit(tb)
390register HASH *tb;
391{
392 tb->tbl_riter = -1;
393 tb->tbl_eiter = Null(HENT*);
394 return tb->tbl_fill;
395}
396
397HENT *
398hiternext(tb)
399register HASH *tb;
400{
401 register HENT *entry;
a687059c 402#ifdef SOME_DBM
403 datum key;
404#endif
8d063cd8 405
406 entry = tb->tbl_eiter;
a687059c 407#ifdef SOME_DBM
408 if (tb->tbl_dbm) {
409 if (entry) {
410#ifdef NDBM
411#ifdef _CX_UX
bf38876a 412 key.dptr = entry->hent_key;
413 key.dsize = entry->hent_klen;
a687059c 414 key = dbm_nextkey(tb->tbl_dbm, key);
415#else
416 key = dbm_nextkey(tb->tbl_dbm);
417#endif /* _CX_UX */
418#else
419 key.dptr = entry->hent_key;
420 key.dsize = entry->hent_klen;
421 key = nextkey(key);
422#endif
423 }
424 else {
425 Newz(504,entry, 1, HENT);
426 tb->tbl_eiter = entry;
427 key = dbm_firstkey(tb->tbl_dbm);
428 }
429 entry->hent_key = key.dptr;
430 entry->hent_klen = key.dsize;
431 if (!key.dptr) {
432 if (entry->hent_val)
433 str_free(entry->hent_val);
434 Safefree(entry);
435 tb->tbl_eiter = Null(HENT*);
436 return Null(HENT*);
437 }
438 return entry;
439 }
440#endif
8d063cd8 441 do {
442 if (entry)
443 entry = entry->hent_next;
444 if (!entry) {
445 tb->tbl_riter++;
446 if (tb->tbl_riter > tb->tbl_max) {
447 tb->tbl_riter = -1;
448 break;
449 }
450 entry = tb->tbl_array[tb->tbl_riter];
451 }
452 } while (!entry);
453
454 tb->tbl_eiter = entry;
455 return entry;
456}
457
458char *
a687059c 459hiterkey(entry,retlen)
8d063cd8 460register HENT *entry;
a687059c 461int *retlen;
8d063cd8 462{
a687059c 463 *retlen = entry->hent_klen;
8d063cd8 464 return entry->hent_key;
465}
466
467STR *
a687059c 468hiterval(tb,entry)
469register HASH *tb;
8d063cd8 470register HENT *entry;
471{
a687059c 472#ifdef SOME_DBM
473 datum key, content;
474
475 if (tb->tbl_dbm) {
476 key.dptr = entry->hent_key;
477 key.dsize = entry->hent_klen;
478 content = dbm_fetch(tb->tbl_dbm,key);
479 if (!entry->hent_val)
480 entry->hent_val = Str_new(62,0);
481 str_nset(entry->hent_val,content.dptr,content.dsize);
482 }
483#endif
8d063cd8 484 return entry->hent_val;
485}
a687059c 486
487#ifdef SOME_DBM
488#if defined(FCNTL) && ! defined(O_CREAT)
489#include <fcntl.h>
490#endif
491
492#ifndef O_RDONLY
493#define O_RDONLY 0
494#endif
495#ifndef O_RDWR
496#define O_RDWR 2
497#endif
498#ifndef O_CREAT
499#define O_CREAT 01000
500#endif
501
502#ifndef NDBM
503static int dbmrefcnt = 0;
504#endif
505
506bool
507hdbmopen(tb,fname,mode)
508register HASH *tb;
509char *fname;
510int mode;
511{
512 if (!tb)
513 return FALSE;
514#ifndef NDBM
515 if (tb->tbl_dbm) /* never really closed it */
516 return TRUE;
517#endif
154e51a4 518 if (tb->tbl_dbm) {
a687059c 519 hdbmclose(tb);
154e51a4 520 tb->tbl_dbm = 0;
521 }
a687059c 522 hclear(tb);
523#ifdef NDBM
154e51a4 524 if (mode >= 0)
525 tb->tbl_dbm = dbm_open(fname, O_RDWR|O_CREAT, mode);
526 if (!tb->tbl_dbm)
527 tb->tbl_dbm = dbm_open(fname, O_RDWR, mode);
a687059c 528#else
529 if (dbmrefcnt++)
530 fatal("Old dbm can only open one database");
531 sprintf(buf,"%s.dir",fname);
532 if (stat(buf, &statbuf) < 0) {
154e51a4 533 if (mode < 0 || close(creat(buf,mode)) < 0)
a687059c 534 return FALSE;
535 sprintf(buf,"%s.pag",fname);
536 if (close(creat(buf,mode)) < 0)
537 return FALSE;
538 }
539 tb->tbl_dbm = dbminit(fname) >= 0;
540#endif
541 return tb->tbl_dbm != 0;
542}
543
544void
545hdbmclose(tb)
546register HASH *tb;
547{
548 if (tb && tb->tbl_dbm) {
549#ifdef NDBM
550 dbm_close(tb->tbl_dbm);
551 tb->tbl_dbm = 0;
552#else
553 /* dbmrefcnt--; */ /* doesn't work, rats */
554#endif
555 }
556 else if (dowarn)
557 warn("Close on unopened dbm file");
558}
559
560bool
561hdbmstore(tb,key,klen,str)
562register HASH *tb;
563char *key;
564int klen;
565register STR *str;
566{
567 datum dkey, dcontent;
568 int error;
569
570 if (!tb || !tb->tbl_dbm)
571 return FALSE;
572 dkey.dptr = key;
573 dkey.dsize = klen;
574 dcontent.dptr = str_get(str);
575 dcontent.dsize = str->str_cur;
576 error = dbm_store(tb->tbl_dbm, dkey, dcontent, DBM_REPLACE);
577 if (error) {
578 if (errno == EPERM)
579 fatal("No write permission to dbm file");
580 warn("dbm store returned %d, errno %d, key \"%s\"",error,errno,key);
581#ifdef NDBM
582 dbm_clearerr(tb->tbl_dbm);
583#endif
584 }
585 return !error;
586}
587#endif /* SOME_DBM */