don't increment/change a void *, cast to char * (thanks GCC!)
[unix-history] / usr / src / lib / libc / db / hash / hash.c
CommitLineData
cb07bd68 1/*-
f4f2bd03
KB
2 * Copyright (c) 1990, 1993
3 * The Regents of the University of California. All rights reserved.
cb07bd68
KB
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Margo Seltzer.
7 *
8 * %sccs.include.redist.c%
9 */
10
11#if defined(LIBC_SCCS) && !defined(lint)
d67f376f 12static char sccsid[] = "@(#)hash.c 8.2 (Berkeley) %G%";
cb07bd68
KB
13#endif /* LIBC_SCCS and not lint */
14
15#include <sys/param.h>
cb07bd68 16#include <sys/stat.h>
e5278b07 17
80216352
KB
18#include <errno.h>
19#include <fcntl.h>
4c56dde1 20#include <stdio.h>
fb91cf55 21#include <stdlib.h>
cb07bd68 22#include <string.h>
80216352
KB
23#include <unistd.h>
24#ifdef DEBUG
25#include <assert.h>
26#endif
27
94ac72c5 28#include <db.h>
fb91cf55 29#include "hash.h"
6f0d4752
KB
30#include "page.h"
31#include "extern.h"
32
80216352
KB
33static int alloc_segs __P((HTAB *, int));
34static int flush_meta __P((HTAB *));
35static int hash_access __P((HTAB *, ACTION, DBT *, DBT *));
6f0d4752
KB
36static int hash_close __P((DB *));
37static int hash_delete __P((const DB *, const DBT *, u_int));
bf239654 38static int hash_fd __P((const DB *));
eb9054a0 39static int hash_get __P((const DB *, const DBT *, DBT *, u_int));
e4fcc5c3 40static int hash_put __P((const DB *, DBT *, const DBT *, u_int));
6f0d4752
KB
41static void *hash_realloc __P((SEGMENT **, int, int));
42static int hash_seq __P((const DB *, DBT *, DBT *, u_int));
aefe4f31 43static int hash_sync __P((const DB *, u_int));
80216352 44static int hdestroy __P((HTAB *));
bf239654 45static HTAB *init_hash __P((HTAB *, const char *, HASHINFO *));
80216352 46static int init_htab __P((HTAB *, int));
6f0d4752 47#if BYTE_ORDER == LITTLE_ENDIAN
c7ab9734 48static void swap_header __P((HTAB *));
6f0d4752
KB
49static void swap_header_copy __P((HASHHDR *, HASHHDR *));
50#endif
cb07bd68 51
cb07bd68 52/* Fast arithmetic, relying on powers of 2, */
6f0d4752 53#define MOD(x, y) ((x) & ((y) - 1))
cb07bd68 54
6f0d4752 55#define RETURN_ERROR(ERR, LOC) { save_errno = ERR; goto LOC; }
cb07bd68
KB
56
57/* Return values */
6f0d4752
KB
58#define SUCCESS (0)
59#define ERROR (-1)
60#define ABNORMAL (1)
cb07bd68 61
cb07bd68
KB
62#ifdef HASH_STATISTICS
63long hash_accesses, hash_collisions, hash_expansions, hash_overflows;
64#endif
65
6f0d4752 66/************************** INTERFACE ROUTINES ***************************/
cb07bd68
KB
67/* OPEN/CLOSE */
68
6f0d4752 69extern DB *
e5610f74 70__hash_open(file, flags, mode, info)
6f0d4752
KB
71 const char *file;
72 int flags, mode;
73 const HASHINFO *info; /* Special directives for create */
cb07bd68 74{
80216352 75 HTAB *hashp;
6f0d4752
KB
76 struct stat statbuf;
77 DB *dbp;
78 int bpages, hdrsize, new_table, nsegs, save_errno;
cb07bd68 79
f9dbf178 80 if ((flags & O_ACCMODE) == O_WRONLY) {
56969865
MS
81 errno = EINVAL;
82 return (NULL);
83 }
84
6f0d4752
KB
85 if (!(hashp = calloc(1, sizeof(HTAB))))
86 return (NULL);
87 hashp->fp = -1;
88 /*
89 * Select flags relevant to us. Even if user wants write only, we need
90 * to be able to read the actual file, so we need to open it read/write.
91 * But, the field in the hashp structure needs to be accurate so that
92 * we can check accesses.
93 */
e4fcc5c3 94 hashp->flags = flags = flags & __USE_OPEN_FLAGS;
6f0d4752
KB
95
96 new_table = 0;
97 if (!file || (flags & O_TRUNC) ||
98 (stat(file, &statbuf) && (errno == ENOENT))) {
99 if (errno == ENOENT)
100 errno = 0; /* Just in case someone looks at errno */
101 new_table = 1;
cb07bd68 102 }
6f0d4752
KB
103 if (file) {
104 if ((hashp->fp = open(file, flags, mode)) == -1)
105 RETURN_ERROR(errno, error0);
106 (void)fcntl(hashp->fp, F_SETFD, 1);
107 }
108 if (new_table) {
bf239654 109 if (!(hashp = init_hash(hashp, file, (HASHINFO *)info)))
6f0d4752
KB
110 RETURN_ERROR(errno, error1);
111 } else {
112 /* Table already exists */
113 if (info && info->hash)
114 hashp->hash = info->hash;
115 else
80216352 116 hashp->hash = __default_hash;
cb07bd68 117
6f0d4752 118 hdrsize = read(hashp->fp, &hashp->hdr, sizeof(HASHHDR));
cb07bd68 119#if BYTE_ORDER == LITTLE_ENDIAN
80216352 120 swap_header(hashp);
cb07bd68 121#endif
6f0d4752
KB
122 if (hdrsize == -1)
123 RETURN_ERROR(errno, error1);
124 if (hdrsize != sizeof(HASHHDR))
125 RETURN_ERROR(EFTYPE, error1);
126 /* Verify file type, versions and hash function */
127 if (hashp->MAGIC != HASHMAGIC)
128 RETURN_ERROR(EFTYPE, error1);
c8526f36 129 if (hashp->VERSION != HASHVERSION)
6f0d4752
KB
130 RETURN_ERROR(EFTYPE, error1);
131 if (hashp->hash(CHARKEY, sizeof(CHARKEY)) != hashp->H_CHARKEY)
132 RETURN_ERROR(EFTYPE, error1);
eb9054a0
KB
133 /*
134 * Figure out how many segments we need. Max_Bucket is the
135 * maximum bucket number, so the number of buckets is
136 * max_bucket + 1.
137 */
138 nsegs = (hashp->MAX_BUCKET + 1 + hashp->SGSIZE - 1) /
6f0d4752
KB
139 hashp->SGSIZE;
140 hashp->nsegs = 0;
80216352 141 if (alloc_segs(hashp, nsegs))
6f0d4752
KB
142 /*
143 * If alloc_segs fails, table will have been destroyed
144 * and errno will have been set.
145 */
146 return (NULL);
147 /* Read in bitmaps */
c8526f36 148 bpages = (hashp->SPARES[hashp->OVFL_POINT] +
6f0d4752
KB
149 (hashp->BSIZE << BYTE_SHIFT) - 1) >>
150 (hashp->BSHIFT + BYTE_SHIFT);
151
152 hashp->nmaps = bpages;
eb9054a0 153 (void)memset(&hashp->mapp[0], 0, bpages * sizeof(u_long *));
cb07bd68
KB
154 }
155
6f0d4752
KB
156 /* Initialize Buffer Manager */
157 if (info && info->cachesize)
80216352 158 __buf_init(hashp, info->cachesize);
6f0d4752 159 else
80216352 160 __buf_init(hashp, DEF_BUFSIZE);
6f0d4752
KB
161
162 hashp->new_file = new_table;
56969865 163 hashp->save_file = file && (hashp->flags & O_RDWR);
6f0d4752
KB
164 hashp->cbucket = -1;
165 if (!(dbp = malloc(sizeof(DB)))) {
166 save_errno = errno;
80216352 167 hdestroy(hashp);
6f0d4752
KB
168 errno = save_errno;
169 return (NULL);
cb07bd68 170 }
80216352 171 dbp->internal = hashp;
6f0d4752
KB
172 dbp->close = hash_close;
173 dbp->del = hash_delete;
bf239654 174 dbp->fd = hash_fd;
6f0d4752
KB
175 dbp->get = hash_get;
176 dbp->put = hash_put;
177 dbp->seq = hash_seq;
178 dbp->sync = hash_sync;
179 dbp->type = DB_HASH;
cb07bd68
KB
180
181#ifdef DEBUG
6f0d4752 182 (void)fprintf(stderr,
c8526f36 183"%s\n%s%x\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%x\n%s%x\n%s%d\n%s%d\n",
6f0d4752
KB
184 "init_htab:",
185 "TABLE POINTER ", hashp,
186 "BUCKET SIZE ", hashp->BSIZE,
187 "BUCKET SHIFT ", hashp->BSHIFT,
188 "DIRECTORY SIZE ", hashp->DSIZE,
189 "SEGMENT SIZE ", hashp->SGSIZE,
190 "SEGMENT SHIFT ", hashp->SSHIFT,
191 "FILL FACTOR ", hashp->FFACTOR,
192 "MAX BUCKET ", hashp->MAX_BUCKET,
c8526f36
KB
193 "OVFL POINT ", hashp->OVFL_POINT,
194 "LAST FREED ", hashp->LAST_FREED,
6f0d4752
KB
195 "HIGH MASK ", hashp->HIGH_MASK,
196 "LOW MASK ", hashp->LOW_MASK,
197 "NSEGS ", hashp->nsegs,
198 "NKEYS ", hashp->NKEYS);
cb07bd68
KB
199#endif
200#ifdef HASH_STATISTICS
201 hash_overflows = hash_accesses = hash_collisions = hash_expansions = 0;
202#endif
6f0d4752 203 return (dbp);
cb07bd68
KB
204
205error1:
992dde47
KB
206 if (hashp != NULL)
207 (void)close(hashp->fp);
cb07bd68
KB
208
209error0:
6f0d4752
KB
210 free(hashp);
211 errno = save_errno;
212 return (NULL);
cb07bd68
KB
213}
214
215static int
6f0d4752
KB
216hash_close(dbp)
217 DB *dbp;
cb07bd68 218{
80216352 219 HTAB *hashp;
6f0d4752 220 int retval;
f1f9da3d 221
6f0d4752
KB
222 if (!dbp)
223 return (ERROR);
80216352 224
cb07bd68 225 hashp = (HTAB *)dbp->internal;
80216352 226 retval = hdestroy(hashp);
6f0d4752
KB
227 free(dbp);
228 return (retval);
cb07bd68
KB
229}
230
bf239654
KB
231static int
232hash_fd(dbp)
233 const DB *dbp;
234{
235 HTAB *hashp;
236
237 if (!dbp)
238 return (ERROR);
239
240 hashp = (HTAB *)dbp->internal;
241 if (hashp->fp == -1) {
242 errno = ENOENT;
243 return (-1);
244 }
245 return (hashp->fp);
246}
247
cb07bd68 248/************************** LOCAL CREATION ROUTINES **********************/
6f0d4752 249static HTAB *
bf239654 250init_hash(hashp, file, info)
80216352 251 HTAB *hashp;
bf239654 252 const char *file;
6f0d4752 253 HASHINFO *info;
cb07bd68 254{
bf239654 255 struct stat statbuf;
6f0d4752 256 int nelem;
cb07bd68
KB
257
258 nelem = 1;
c8526f36 259 hashp->NKEYS = 0;
6f0d4752
KB
260 hashp->LORDER = BYTE_ORDER;
261 hashp->BSIZE = DEF_BUCKET_SIZE;
262 hashp->BSHIFT = DEF_BUCKET_SHIFT;
263 hashp->SGSIZE = DEF_SEGSIZE;
264 hashp->SSHIFT = DEF_SEGSIZE_SHIFT;
265 hashp->DSIZE = DEF_DIRSIZE;
266 hashp->FFACTOR = DEF_FFACTOR;
80216352 267 hashp->hash = __default_hash;
1911b5bf
KB
268 memset(hashp->SPARES, 0, sizeof(hashp->SPARES));
269 memset(hashp->BITMAPS, 0, sizeof (hashp->BITMAPS));
6f0d4752 270
bf239654
KB
271 /* Fix bucket size to be optimal for file system */
272 if (file != NULL) {
273 if (stat(file, &statbuf))
274 return (NULL);
275 hashp->BSIZE = statbuf.st_blksize;
276 hashp->BSHIFT = __log2(hashp->BSIZE);
277 }
278
6f0d4752
KB
279 if (info) {
280 if (info->bsize) {
281 /* Round pagesize up to power of 2 */
282 hashp->BSHIFT = __log2(info->bsize);
283 hashp->BSIZE = 1 << hashp->BSHIFT;
284 if (hashp->BSIZE > MAX_BSIZE) {
285 errno = EINVAL;
286 return (NULL);
287 }
cb07bd68 288 }
6f0d4752
KB
289 if (info->ffactor)
290 hashp->FFACTOR = info->ffactor;
291 if (info->hash)
292 hashp->hash = info->hash;
293 if (info->nelem)
294 nelem = info->nelem;
295 if (info->lorder) {
296 if (info->lorder != BIG_ENDIAN &&
297 info->lorder != LITTLE_ENDIAN) {
298 errno = EINVAL;
299 return (NULL);
300 }
301 hashp->LORDER = info->lorder;
cb07bd68 302 }
cb07bd68 303 }
cb07bd68 304 /* init_htab should destroy the table and set errno if it fails */
80216352 305 if (init_htab(hashp, nelem))
6f0d4752
KB
306 return (NULL);
307 else
308 return (hashp);
cb07bd68 309}
cb07bd68 310/*
6f0d4752
KB
311 * This calls alloc_segs which may run out of memory. Alloc_segs will destroy
312 * the table and set errno, so we just pass the error information along.
313 *
314 * Returns 0 on No Error
315 */
cb07bd68 316static int
80216352
KB
317init_htab(hashp, nelem)
318 HTAB *hashp;
6f0d4752 319 int nelem;
cb07bd68 320{
6f0d4752
KB
321 register int nbuckets, nsegs;
322 int l2;
323
324 /*
325 * Divide number of elements by the fill factor and determine a
326 * desired number of buckets. Allocate space for the next greater
327 * power of two number of buckets.
328 */
cb07bd68
KB
329 nelem = (nelem - 1) / hashp->FFACTOR + 1;
330
6f9c5818 331 l2 = __log2(MAX(nelem, 2));
cb07bd68 332 nbuckets = 1 << l2;
cb07bd68
KB
333
334 hashp->SPARES[l2] = l2 + 1;
6f0d4752 335 hashp->SPARES[l2 + 1] = l2 + 1;
c8526f36
KB
336 hashp->OVFL_POINT = l2;
337 hashp->LAST_FREED = 2;
338
6f0d4752 339 /* First bitmap page is at: splitpoint l2 page offset 1 */
80216352 340 if (__init_bitmap(hashp, OADDR_OF(l2, 1), l2 + 1, 0))
eb9054a0 341 return (-1);
cb07bd68
KB
342
343 hashp->MAX_BUCKET = hashp->LOW_MASK = nbuckets - 1;
344 hashp->HIGH_MASK = (nbuckets << 1) - 1;
6f0d4752
KB
345 hashp->HDRPAGES = ((MAX(sizeof(HASHHDR), MINHDRSIZE) - 1) >>
346 hashp->BSHIFT) + 1;
cb07bd68
KB
347
348 nsegs = (nbuckets - 1) / hashp->SGSIZE + 1;
349 nsegs = 1 << __log2(nsegs);
350
6f0d4752
KB
351 if (nsegs > hashp->DSIZE)
352 hashp->DSIZE = nsegs;
80216352 353 return (alloc_segs(hashp, nsegs));
cb07bd68
KB
354}
355
356/********************** DESTROY/CLOSE ROUTINES ************************/
357
358/*
6f0d4752
KB
359 * Flushes any changes to the file if necessary and destroys the hashp
360 * structure, freeing all allocated space.
361 */
cb07bd68 362static int
80216352
KB
363hdestroy(hashp)
364 HTAB *hashp;
cb07bd68 365{
6f0d4752 366 int i, save_errno;
cb07bd68
KB
367
368 save_errno = 0;
369
cb07bd68 370#ifdef HASH_STATISTICS
80216352
KB
371 (void)fprintf(stderr, "hdestroy: accesses %ld collisions %ld\n",
372 hash_accesses, hash_collisions);
373 (void)fprintf(stderr, "hdestroy: expansions %ld\n",
374 hash_expansions);
375 (void)fprintf(stderr, "hdestroy: overflows %ld\n",
376 hash_overflows);
377 (void)fprintf(stderr, "keys %ld maxp %d segmentcount %d\n",
378 hashp->NKEYS, hashp->MAX_BUCKET, hashp->nsegs);
379
380 for (i = 0; i < NCACHED; i++)
381 (void)fprintf(stderr,
382 "spares[%d] = %d\n", i, hashp->SPARES[i]);
cb07bd68 383#endif
80216352
KB
384 /*
385 * Call on buffer manager to free buffers, and if required,
386 * write them to disk.
387 */
388 if (__buf_free(hashp, 1, hashp->save_file))
389 save_errno = errno;
390 if (hashp->dir) {
391 free(*hashp->dir); /* Free initial segments */
392 /* Free extra segments */
393 while (hashp->exsegs--)
394 free(hashp->dir[--hashp->nsegs]);
395 free(hashp->dir);
cb07bd68 396 }
80216352
KB
397 if (flush_meta(hashp) && !save_errno)
398 save_errno = errno;
399 /* Free Bigmaps */
400 for (i = 0; i < hashp->nmaps; i++)
401 if (hashp->mapp[i])
402 free(hashp->mapp[i]);
403
404 if (hashp->fp != -1)
405 (void)close(hashp->fp);
406
6f0d4752
KB
407 if (save_errno) {
408 errno = save_errno;
409 return (ERROR);
cb07bd68 410 }
6f0d4752 411 return (SUCCESS);
cb07bd68 412}
6f0d4752
KB
413/*
414 * Write modified pages to disk
415 *
416 * Returns:
417 * 0 == OK
418 * -1 ERROR
419 */
cb07bd68 420static int
aefe4f31 421hash_sync(dbp, flags)
6f0d4752 422 const DB *dbp;
aefe4f31 423 u_int flags;
cb07bd68 424{
80216352
KB
425 HTAB *hashp;
426
aefe4f31
KB
427 if (flags != 0) {
428 errno = EINVAL;
429 return (ERROR);
430 }
431
6f0d4752
KB
432 if (!dbp)
433 return (ERROR);
cb07bd68 434
80216352 435 hashp = (HTAB *)dbp->internal;
6f0d4752
KB
436 if (!hashp->save_file)
437 return (0);
80216352 438 if (__buf_free(hashp, 0, 1) || flush_meta(hashp))
6f0d4752 439 return (ERROR);
cb07bd68 440 hashp->new_file = 0;
6f0d4752 441 return (0);
cb07bd68
KB
442}
443
444/*
6f0d4752
KB
445 * Returns:
446 * 0 == OK
447 * -1 indicates that errno should be set
448 */
cb07bd68 449static int
80216352
KB
450flush_meta(hashp)
451 HTAB *hashp;
cb07bd68 452{
129017de
MT
453 HASHHDR *whdrp;
454#if BYTE_ORDER == LITTLE_ENDIAN
455 HASHHDR whdr;
456#endif
6f0d4752
KB
457 int fp, i, wsize;
458
459 if (!hashp->save_file)
460 return (0);
cb07bd68 461 hashp->MAGIC = HASHMAGIC;
c8526f36 462 hashp->VERSION = HASHVERSION;
6f0d4752 463 hashp->H_CHARKEY = hashp->hash(CHARKEY, sizeof(CHARKEY));
cb07bd68
KB
464
465 fp = hashp->fp;
466 whdrp = &hashp->hdr;
467#if BYTE_ORDER == LITTLE_ENDIAN
468 whdrp = &whdr;
6f0d4752 469 swap_header_copy(&hashp->hdr, whdrp);
cb07bd68 470#endif
3144de01 471 if ((lseek(fp, (off_t)0, SEEK_SET) == -1) ||
6f0d4752
KB
472 ((wsize = write(fp, whdrp, sizeof(HASHHDR))) == -1))
473 return (-1);
474 else
475 if (wsize != sizeof(HASHHDR)) {
476 errno = EFTYPE;
477 hashp->errno = errno;
478 return (-1);
cb07bd68 479 }
6f0d4752
KB
480 for (i = 0; i < NCACHED; i++)
481 if (hashp->mapp[i])
80216352 482 if (__put_page(hashp, (char *)hashp->mapp[i],
6f0d4752
KB
483 hashp->BITMAPS[i], 0, 1))
484 return (-1);
485 return (0);
cb07bd68 486}
6f0d4752 487
cb07bd68
KB
488/*******************************SEARCH ROUTINES *****************************/
489/*
6f0d4752
KB
490 * All the access routines return
491 *
492 * Returns:
493 * 0 on SUCCESS
494 * 1 to indicate an external ERROR (i.e. key not found, etc)
495 * -1 to indicate an internal ERROR (i.e. out of memory, etc)
496 */
cb07bd68 497static int
6f0d4752
KB
498hash_get(dbp, key, data, flag)
499 const DB *dbp;
eb9054a0
KB
500 const DBT *key;
501 DBT *data;
6f0d4752 502 u_int flag;
cb07bd68 503{
80216352
KB
504 HTAB *hashp;
505
506 hashp = (HTAB *)dbp->internal;
6f0d4752
KB
507 if (flag) {
508 hashp->errno = errno = EINVAL;
509 return (ERROR);
510 }
80216352 511 return (hash_access(hashp, HASH_GET, (DBT *)key, data));
cb07bd68
KB
512}
513
514static int
6f0d4752
KB
515hash_put(dbp, key, data, flag)
516 const DB *dbp;
e4fcc5c3
KB
517 DBT *key;
518 const DBT *data;
6f0d4752 519 u_int flag;
cb07bd68 520{
80216352
KB
521 HTAB *hashp;
522
523 hashp = (HTAB *)dbp->internal;
6f0d4752
KB
524 if (flag && flag != R_NOOVERWRITE) {
525 hashp->errno = errno = EINVAL;
526 return (ERROR);
527 }
6f0d4752
KB
528 if ((hashp->flags & O_ACCMODE) == O_RDONLY) {
529 hashp->errno = errno = EPERM;
530 return (ERROR);
531 }
80216352 532 return (hash_access(hashp, flag == R_NOOVERWRITE ?
6f0d4752 533 HASH_PUTNEW : HASH_PUT, (DBT *)key, (DBT *)data));
cb07bd68
KB
534}
535
536static int
6f0d4752
KB
537hash_delete(dbp, key, flag)
538 const DB *dbp;
539 const DBT *key;
540 u_int flag; /* Ignored */
cb07bd68 541{
80216352
KB
542 HTAB *hashp;
543
544 hashp = (HTAB *)dbp->internal;
6f0d4752
KB
545 if (flag && flag != R_CURSOR) {
546 hashp->errno = errno = EINVAL;
547 return (ERROR);
548 }
6f0d4752
KB
549 if ((hashp->flags & O_ACCMODE) == O_RDONLY) {
550 hashp->errno = errno = EPERM;
551 return (ERROR);
552 }
80216352 553 return (hash_access(hashp, HASH_DELETE, (DBT *)key, NULL));
cb07bd68
KB
554}
555
556/*
6f0d4752
KB
557 * Assume that hashp has been set in wrapper routine.
558 */
cb07bd68 559static int
80216352
KB
560hash_access(hashp, action, key, val)
561 HTAB *hashp;
6f0d4752
KB
562 ACTION action;
563 DBT *key, *val;
cb07bd68 564{
6f0d4752
KB
565 register BUFHEAD *rbufp;
566 BUFHEAD *bufp, *save_bufp;
567 register u_short *bp;
568 register int n, ndx, off, size;
569 register char *kp;
570 u_short pageno;
cb07bd68
KB
571
572#ifdef HASH_STATISTICS
573 hash_accesses++;
574#endif
575
6f0d4752 576 off = hashp->BSIZE;
cb07bd68 577 size = key->size;
c6ad6315 578 kp = (char *)key->data;
80216352 579 rbufp = __get_buf(hashp, __call_hash(hashp, kp, size), NULL, 0);
6f0d4752
KB
580 if (!rbufp)
581 return (ERROR);
f1f9da3d 582 save_bufp = rbufp;
cb07bd68 583
f1f9da3d
KB
584 /* Pin the bucket chain */
585 rbufp->flags |= BUF_PIN;
6f0d4752
KB
586 for (bp = (u_short *)rbufp->page, n = *bp++, ndx = 1; ndx < n;)
587 if (bp[1] >= REAL_KEY) {
588 /* Real key/data pair */
589 if (size == off - *bp &&
1911b5bf 590 memcmp(kp, rbufp->page + *bp, size) == 0)
6f0d4752
KB
591 goto found;
592 off = bp[1];
cb07bd68 593#ifdef HASH_STATISTICS
6f0d4752 594 hash_collisions++;
cb07bd68 595#endif
6f0d4752
KB
596 bp += 2;
597 ndx += 2;
598 } else if (bp[1] == OVFLPAGE) {
80216352 599 rbufp = __get_buf(hashp, *bp, rbufp, 0);
6f0d4752
KB
600 if (!rbufp) {
601 save_bufp->flags &= ~BUF_PIN;
602 return (ERROR);
603 }
604 /* FOR LOOP INIT */
605 bp = (u_short *)rbufp->page;
606 n = *bp++;
607 ndx = 1;
608 off = hashp->BSIZE;
609 } else if (bp[1] < REAL_KEY) {
80216352
KB
610 if ((ndx =
611 __find_bigpair(hashp, rbufp, ndx, kp, size)) > 0)
6f0d4752
KB
612 goto found;
613 if (ndx == -2) {
614 bufp = rbufp;
80216352
KB
615 if (!(pageno =
616 __find_last_page(hashp, &bufp))) {
6f0d4752
KB
617 ndx = 0;
618 rbufp = bufp;
619 break; /* FOR */
620 }
80216352 621 rbufp = __get_buf(hashp, pageno, bufp, 0);
6f0d4752
KB
622 if (!rbufp) {
623 save_bufp->flags &= ~BUF_PIN;
624 return (ERROR);
625 }
626 /* FOR LOOP INIT */
627 bp = (u_short *)rbufp->page;
628 n = *bp++;
629 ndx = 1;
630 off = hashp->BSIZE;
631 } else {
632 save_bufp->flags &= ~BUF_PIN;
633 return (ERROR);
634 }
cb07bd68 635 }
cb07bd68
KB
636
637 /* Not found */
6f0d4752
KB
638 switch (action) {
639 case HASH_PUT:
640 case HASH_PUTNEW:
80216352 641 if (__addel(hashp, rbufp, key, val)) {
6f0d4752
KB
642 save_bufp->flags &= ~BUF_PIN;
643 return (ERROR);
cb07bd68 644 } else {
6f0d4752
KB
645 save_bufp->flags &= ~BUF_PIN;
646 return (SUCCESS);
cb07bd68 647 }
6f0d4752
KB
648 case HASH_GET:
649 case HASH_DELETE:
650 default:
f1f9da3d 651 save_bufp->flags &= ~BUF_PIN;
6f0d4752 652 return (ABNORMAL);
cb07bd68
KB
653 }
654
655found:
656 switch (action) {
6f0d4752 657 case HASH_PUTNEW:
f1f9da3d 658 save_bufp->flags &= ~BUF_PIN;
6f0d4752
KB
659 return (ABNORMAL);
660 case HASH_GET:
cb07bd68 661 bp = (u_short *)rbufp->page;
e5610f74 662 if (bp[ndx + 1] < REAL_KEY) {
80216352 663 if (__big_return(hashp, rbufp, ndx, val, 0))
eb9054a0 664 return (ERROR);
e5610f74 665 } else {
6f0d4752
KB
666 val->data = (u_char *)rbufp->page + (int)bp[ndx + 1];
667 val->size = bp[ndx] - bp[ndx + 1];
cb07bd68
KB
668 }
669 break;
6f0d4752 670 case HASH_PUT:
80216352
KB
671 if ((__delpair(hashp, rbufp, ndx)) ||
672 (__addel(hashp, rbufp, key, val))) {
6f0d4752
KB
673 save_bufp->flags &= ~BUF_PIN;
674 return (ERROR);
f1f9da3d 675 }
cb07bd68 676 break;
6f0d4752 677 case HASH_DELETE:
80216352 678 if (__delpair(hashp, rbufp, ndx))
6f0d4752 679 return (ERROR);
cb07bd68 680 break;
80216352
KB
681 default:
682 abort();
cb07bd68 683 }
f1f9da3d 684 save_bufp->flags &= ~BUF_PIN;
cb07bd68
KB
685 return (SUCCESS);
686}
687
688static int
689hash_seq(dbp, key, data, flag)
6f0d4752
KB
690 const DB *dbp;
691 DBT *key, *data;
692 u_int flag;
cb07bd68 693{
6f0d4752
KB
694 register u_int bucket;
695 register BUFHEAD *bufp;
80216352 696 HTAB *hashp;
6f0d4752 697 u_short *bp, ndx;
cb07bd68 698
80216352 699 hashp = (HTAB *)dbp->internal;
6f0d4752
KB
700 if (flag && flag != R_FIRST && flag != R_NEXT) {
701 hashp->errno = errno = EINVAL;
702 return (ERROR);
703 }
cb07bd68
KB
704#ifdef HASH_STATISTICS
705 hash_accesses++;
706#endif
6f0d4752
KB
707 if ((hashp->cbucket < 0) || (flag == R_FIRST)) {
708 hashp->cbucket = 0;
709 hashp->cndx = 1;
710 hashp->cpage = NULL;
cb07bd68 711 }
6f0d4752 712
81ab80ea 713 for (bp = NULL; !bp || !bp[0]; ) {
e5278b07
KB
714 if (!(bufp = hashp->cpage)) {
715 for (bucket = hashp->cbucket;
716 bucket <= hashp->MAX_BUCKET;
717 bucket++, hashp->cndx = 1) {
80216352 718 bufp = __get_buf(hashp, bucket, NULL, 0);
e5278b07
KB
719 if (!bufp)
720 return (ERROR);
721 hashp->cpage = bufp;
722 bp = (u_short *)bufp->page;
723 if (bp[0])
724 break;
725 }
726 hashp->cbucket = bucket;
727 if (hashp->cbucket > hashp->MAX_BUCKET) {
728 hashp->cbucket = -1;
729 return (ABNORMAL);
730 }
731 } else
732 bp = (u_short *)hashp->cpage->page;
cb07bd68 733
cb07bd68 734#ifdef DEBUG
e5278b07
KB
735 assert(bp);
736 assert(bufp);
cb07bd68 737#endif
e5278b07
KB
738 while (bp[hashp->cndx + 1] == OVFLPAGE) {
739 bufp = hashp->cpage =
80216352 740 __get_buf(hashp, bp[hashp->cndx], bufp, 0);
e5278b07
KB
741 if (!bufp)
742 return (ERROR);
743 bp = (u_short *)(bufp->page);
744 hashp->cndx = 1;
745 }
81ab80ea 746 if (!bp[0]) {
e5278b07 747 hashp->cpage = NULL;
81ab80ea
KB
748 ++hashp->cbucket;
749 }
cb07bd68
KB
750 }
751 ndx = hashp->cndx;
6f0d4752 752 if (bp[ndx + 1] < REAL_KEY) {
80216352 753 if (__big_keydata(hashp, bufp, key, data, 1))
6f0d4752 754 return (ERROR);
cb07bd68 755 } else {
6f0d4752
KB
756 key->data = (u_char *)hashp->cpage->page + bp[ndx];
757 key->size = (ndx > 1 ? bp[ndx - 1] : hashp->BSIZE) - bp[ndx];
758 data->data = (u_char *)hashp->cpage->page + bp[ndx + 1];
759 data->size = bp[ndx] - bp[ndx + 1];
760 ndx += 2;
761 if (ndx > bp[0]) {
762 hashp->cpage = NULL;
763 hashp->cbucket++;
764 hashp->cndx = 1;
765 } else
766 hashp->cndx = ndx;
cb07bd68
KB
767 }
768 return (SUCCESS);
769}
770
771/********************************* UTILITIES ************************/
6f0d4752 772
cb07bd68 773/*
6f0d4752
KB
774 * Returns:
775 * 0 ==> OK
776 * -1 ==> Error
777 */
cb07bd68 778extern int
80216352
KB
779__expand_table(hashp)
780 HTAB *hashp;
cb07bd68 781{
6f0d4752
KB
782 u_int old_bucket, new_bucket;
783 int dirsize, new_segnum, spare_ndx;
784
cb07bd68
KB
785#ifdef HASH_STATISTICS
786 hash_expansions++;
787#endif
cb07bd68
KB
788 new_bucket = ++hashp->MAX_BUCKET;
789 old_bucket = (hashp->MAX_BUCKET & hashp->LOW_MASK);
790
791 new_segnum = new_bucket >> hashp->SSHIFT;
792
793 /* Check if we need a new segment */
6f0d4752
KB
794 if (new_segnum >= hashp->nsegs) {
795 /* Check if we need to expand directory */
796 if (new_segnum >= hashp->DSIZE) {
797 /* Reallocate directory */
798 dirsize = hashp->DSIZE * sizeof(SEGMENT *);
799 if (!hash_realloc(&hashp->dir, dirsize, dirsize << 1))
800 return (-1);
801 hashp->DSIZE = dirsize << 1;
cb07bd68 802 }
6f0d4752
KB
803 if (!(hashp->dir[new_segnum] =
804 calloc(hashp->SGSIZE, sizeof(SEGMENT))))
805 return (-1);
806 hashp->exsegs++;
807 hashp->nsegs++;
cb07bd68 808 }
cb07bd68 809 /*
6f0d4752
KB
810 * If the split point is increasing (MAX_BUCKET's log base 2
811 * * increases), we need to copy the current contents of the spare
812 * split bucket to the next bucket.
813 */
6f9c5818
KB
814 spare_ndx = __log2(hashp->MAX_BUCKET + 1);
815 if (spare_ndx > hashp->OVFL_POINT) {
c8526f36
KB
816 hashp->SPARES[spare_ndx] = hashp->SPARES[hashp->OVFL_POINT];
817 hashp->OVFL_POINT = spare_ndx;
818 }
819
6f0d4752
KB
820 if (new_bucket > hashp->HIGH_MASK) {
821 /* Starting a new doubling */
822 hashp->LOW_MASK = hashp->HIGH_MASK;
823 hashp->HIGH_MASK = new_bucket | hashp->LOW_MASK;
cb07bd68 824 }
6f0d4752 825 /* Relocate records to the new bucket */
80216352 826 return (__split_page(hashp, old_bucket, new_bucket));
cb07bd68 827}
6f0d4752 828
cb07bd68 829/*
6f0d4752
KB
830 * If realloc guarantees that the pointer is not destroyed if the realloc
831 * fails, then this routine can go away.
832 */
833static void *
834hash_realloc(p_ptr, oldsize, newsize)
835 SEGMENT **p_ptr;
836 int oldsize, newsize;
cb07bd68 837{
6f0d4752 838 register void *p;
cb07bd68 839
6f0d4752 840 if (p = malloc(newsize)) {
1911b5bf 841 memmove(p, *p_ptr, oldsize);
d67f376f 842 memset((char *)p + oldsize, 0, newsize - oldsize);
cb07bd68
KB
843 free(*p_ptr);
844 *p_ptr = p;
845 }
846 return (p);
847}
848
de31e977 849extern u_int
80216352
KB
850__call_hash(hashp, k, len)
851 HTAB *hashp;
6f0d4752
KB
852 char *k;
853 int len;
cb07bd68 854{
6f0d4752 855 int n, bucket;
cb07bd68 856
6f0d4752 857 n = hashp->hash(k, len);
cb07bd68 858 bucket = n & hashp->HIGH_MASK;
6f0d4752
KB
859 if (bucket > hashp->MAX_BUCKET)
860 bucket = bucket & hashp->LOW_MASK;
861 return (bucket);
cb07bd68
KB
862}
863
864/*
6f0d4752
KB
865 * Allocate segment table. On error, destroy the table and set errno.
866 *
867 * Returns 0 on success
868 */
cb07bd68 869static int
80216352
KB
870alloc_segs(hashp, nsegs)
871 HTAB *hashp;
6f0d4752 872 int nsegs;
cb07bd68 873{
6f0d4752
KB
874 register int i;
875 register SEGMENT store;
cb07bd68 876
6f0d4752 877 int save_errno;
cb07bd68 878
6f0d4752
KB
879 if (!(hashp->dir = calloc(hashp->DSIZE, sizeof(SEGMENT *)))) {
880 save_errno = errno;
80216352 881 (void)hdestroy(hashp);
6f0d4752
KB
882 errno = save_errno;
883 return (-1);
884 }
885 /* Allocate segments */
886 store = calloc(nsegs << hashp->SSHIFT, sizeof(SEGMENT));
887 if (!store) {
888 save_errno = errno;
80216352 889 (void)hdestroy(hashp);
6f0d4752
KB
890 errno = save_errno;
891 return (-1);
892 }
893 for (i = 0; i < nsegs; i++, hashp->nsegs++)
894 hashp->dir[i] = &store[i << hashp->SSHIFT];
895 return (0);
cb07bd68
KB
896}
897
6f0d4752 898#if BYTE_ORDER == LITTLE_ENDIAN
cb07bd68 899/*
6f0d4752
KB
900 * Hashp->hdr needs to be byteswapped.
901 */
cb07bd68 902static void
6f0d4752
KB
903swap_header_copy(srcp, destp)
904 HASHHDR *srcp, *destp;
cb07bd68 905{
6f0d4752
KB
906 int i;
907
908 BLSWAP_COPY(srcp->magic, destp->magic);
909 BLSWAP_COPY(srcp->version, destp->version);
910 BLSWAP_COPY(srcp->lorder, destp->lorder);
911 BLSWAP_COPY(srcp->bsize, destp->bsize);
912 BLSWAP_COPY(srcp->bshift, destp->bshift);
913 BLSWAP_COPY(srcp->dsize, destp->dsize);
914 BLSWAP_COPY(srcp->ssize, destp->ssize);
915 BLSWAP_COPY(srcp->sshift, destp->sshift);
c8526f36
KB
916 BLSWAP_COPY(srcp->ovfl_point, destp->ovfl_point);
917 BLSWAP_COPY(srcp->last_freed, destp->last_freed);
6f0d4752
KB
918 BLSWAP_COPY(srcp->max_bucket, destp->max_bucket);
919 BLSWAP_COPY(srcp->high_mask, destp->high_mask);
920 BLSWAP_COPY(srcp->low_mask, destp->low_mask);
921 BLSWAP_COPY(srcp->ffactor, destp->ffactor);
922 BLSWAP_COPY(srcp->nkeys, destp->nkeys);
923 BLSWAP_COPY(srcp->hdrpages, destp->hdrpages);
924 BLSWAP_COPY(srcp->h_charkey, destp->h_charkey);
925 for (i = 0; i < NCACHED; i++) {
926 BLSWAP_COPY(srcp->spares[i], destp->spares[i]);
927 BSSWAP_COPY(srcp->bitmaps[i], destp->bitmaps[i]);
928 }
cb07bd68
KB
929}
930
931static void
80216352
KB
932swap_header(hashp)
933 HTAB *hashp;
cb07bd68 934{
6f0d4752
KB
935 HASHHDR *hdrp;
936 int i;
937
938 hdrp = &hashp->hdr;
939
940 BLSWAP(hdrp->magic);
941 BLSWAP(hdrp->version);
942 BLSWAP(hdrp->lorder);
943 BLSWAP(hdrp->bsize);
944 BLSWAP(hdrp->bshift);
945 BLSWAP(hdrp->dsize);
946 BLSWAP(hdrp->ssize);
947 BLSWAP(hdrp->sshift);
c8526f36
KB
948 BLSWAP(hdrp->ovfl_point);
949 BLSWAP(hdrp->last_freed);
6f0d4752
KB
950 BLSWAP(hdrp->max_bucket);
951 BLSWAP(hdrp->high_mask);
952 BLSWAP(hdrp->low_mask);
953 BLSWAP(hdrp->ffactor);
954 BLSWAP(hdrp->nkeys);
955 BLSWAP(hdrp->hdrpages);
956 BLSWAP(hdrp->h_charkey);
957 for (i = 0; i < NCACHED; i++) {
958 BLSWAP(hdrp->spares[i]);
959 BSSWAP(hdrp->bitmaps[i]);
960 }
cb07bd68 961}
6f0d4752 962#endif