Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /*- |
michael@0 | 2 | * Copyright (c) 1990, 1993, 1994 |
michael@0 | 3 | * The Regents of the University of California. All rights reserved. |
michael@0 | 4 | * |
michael@0 | 5 | * This code is derived from software contributed to Berkeley by |
michael@0 | 6 | * Margo Seltzer. |
michael@0 | 7 | * |
michael@0 | 8 | * Redistribution and use in source and binary forms, with or without |
michael@0 | 9 | * modification, are permitted provided that the following conditions |
michael@0 | 10 | * are met: |
michael@0 | 11 | * 1. Redistributions of source code must retain the above copyright |
michael@0 | 12 | * notice, this list of conditions and the following disclaimer. |
michael@0 | 13 | * 2. Redistributions in binary form must reproduce the above copyright |
michael@0 | 14 | * notice, this list of conditions and the following disclaimer in the |
michael@0 | 15 | * documentation and/or other materials provided with the distribution. |
michael@0 | 16 | * 3. ***REMOVED*** - see |
michael@0 | 17 | * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change |
michael@0 | 18 | * 4. Neither the name of the University nor the names of its contributors |
michael@0 | 19 | * may be used to endorse or promote products derived from this software |
michael@0 | 20 | * without specific prior written permission. |
michael@0 | 21 | * |
michael@0 | 22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
michael@0 | 23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
michael@0 | 24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
michael@0 | 25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
michael@0 | 26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
michael@0 | 27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
michael@0 | 28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
michael@0 | 29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
michael@0 | 30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
michael@0 | 31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
michael@0 | 32 | * SUCH DAMAGE. |
michael@0 | 33 | */ |
michael@0 | 34 | |
michael@0 | 35 | #if defined(LIBC_SCCS) && !defined(lint) |
michael@0 | 36 | static char sccsid[] = "@(#)hash.c 8.9 (Berkeley) 6/16/94"; |
michael@0 | 37 | #endif /* LIBC_SCCS and not lint */ |
michael@0 | 38 | |
michael@0 | 39 | #if !defined(_WIN32) && !defined(_WINDOWS) && !defined(macintosh) |
michael@0 | 40 | #include <sys/param.h> |
michael@0 | 41 | #endif |
michael@0 | 42 | |
michael@0 | 43 | #if !defined(macintosh) |
michael@0 | 44 | #ifdef XP_OS2 |
michael@0 | 45 | #include <sys/types.h> |
michael@0 | 46 | #endif |
michael@0 | 47 | #include <sys/stat.h> |
michael@0 | 48 | #endif |
michael@0 | 49 | |
michael@0 | 50 | #if defined(macintosh) |
michael@0 | 51 | #include <unix.h> |
michael@0 | 52 | #include <unistd.h> |
michael@0 | 53 | #endif |
michael@0 | 54 | |
michael@0 | 55 | #include <errno.h> |
michael@0 | 56 | #include <fcntl.h> |
michael@0 | 57 | #include <stdio.h> |
michael@0 | 58 | #include <stdlib.h> |
michael@0 | 59 | #include <string.h> |
michael@0 | 60 | |
michael@0 | 61 | #if !defined(_WIN32) && !defined(_WINDOWS) && !defined(macintosh) |
michael@0 | 62 | #include <unistd.h> |
michael@0 | 63 | #endif |
michael@0 | 64 | #if defined(_WIN32) || defined(_WINDOWS) |
michael@0 | 65 | #include <windows.h> |
michael@0 | 66 | #endif |
michael@0 | 67 | |
michael@0 | 68 | #include <assert.h> |
michael@0 | 69 | |
michael@0 | 70 | #include "mcom_db.h" |
michael@0 | 71 | #include "hash.h" |
michael@0 | 72 | #include "page.h" |
michael@0 | 73 | |
michael@0 | 74 | /* |
michael@0 | 75 | #include "extern.h" |
michael@0 | 76 | */ |
michael@0 | 77 | static int alloc_segs __P((HTAB *, int)); |
michael@0 | 78 | static int flush_meta __P((HTAB *)); |
michael@0 | 79 | static int hash_access __P((HTAB *, ACTION, DBT *, DBT *)); |
michael@0 | 80 | static int hash_close __P((DB *)); |
michael@0 | 81 | static int hash_delete __P((const DB *, const DBT *, uint)); |
michael@0 | 82 | static int hash_fd __P((const DB *)); |
michael@0 | 83 | static int hash_get __P((const DB *, const DBT *, DBT *, uint)); |
michael@0 | 84 | static int hash_put __P((const DB *, DBT *, const DBT *, uint)); |
michael@0 | 85 | static void *hash_realloc __P((SEGMENT **, size_t, size_t)); |
michael@0 | 86 | static int hash_seq __P((const DB *, DBT *, DBT *, uint)); |
michael@0 | 87 | static int hash_sync __P((const DB *, uint)); |
michael@0 | 88 | static int hdestroy __P((HTAB *)); |
michael@0 | 89 | static HTAB *init_hash __P((HTAB *, const char *, HASHINFO *)); |
michael@0 | 90 | static int init_htab __P((HTAB *, int)); |
michael@0 | 91 | #if BYTE_ORDER == LITTLE_ENDIAN |
michael@0 | 92 | static void swap_header __P((HTAB *)); |
michael@0 | 93 | static void swap_header_copy __P((HASHHDR *, HASHHDR *)); |
michael@0 | 94 | #endif |
michael@0 | 95 | |
michael@0 | 96 | /* Fast arithmetic, relying on powers of 2, */ |
michael@0 | 97 | #define MOD(x, y) ((x) & ((y) - 1)) |
michael@0 | 98 | |
michael@0 | 99 | #define RETURN_ERROR(ERR, LOC) { save_errno = ERR; goto LOC; } |
michael@0 | 100 | |
michael@0 | 101 | /* Return values */ |
michael@0 | 102 | #define SUCCESS (0) |
michael@0 | 103 | #define DBM_ERROR (-1) |
michael@0 | 104 | #define ABNORMAL (1) |
michael@0 | 105 | |
michael@0 | 106 | #ifdef HASH_STATISTICS |
michael@0 | 107 | int hash_accesses, hash_collisions, hash_expansions, hash_overflows; |
michael@0 | 108 | #endif |
michael@0 | 109 | |
michael@0 | 110 | /* A new Lou (montulli@mozilla.com) routine. |
michael@0 | 111 | * |
michael@0 | 112 | * The database is screwed. |
michael@0 | 113 | * |
michael@0 | 114 | * This closes the file, flushing buffers as appropriate. |
michael@0 | 115 | */ |
michael@0 | 116 | static void |
michael@0 | 117 | __remove_database(DB *dbp) |
michael@0 | 118 | { |
michael@0 | 119 | HTAB *hashp = (HTAB *)dbp->internal; |
michael@0 | 120 | |
michael@0 | 121 | assert(0); |
michael@0 | 122 | |
michael@0 | 123 | if (!hashp) |
michael@0 | 124 | return; |
michael@0 | 125 | hdestroy(hashp); |
michael@0 | 126 | dbp->internal = NULL; |
michael@0 | 127 | } |
michael@0 | 128 | |
michael@0 | 129 | /************************** INTERFACE ROUTINES ***************************/ |
michael@0 | 130 | /* OPEN/CLOSE */ |
michael@0 | 131 | |
michael@0 | 132 | |
michael@0 | 133 | extern DB * |
michael@0 | 134 | __hash_open(const char *file, int flags, int mode, const HASHINFO *info, int dflags) |
michael@0 | 135 | { |
michael@0 | 136 | HTAB *hashp=NULL; |
michael@0 | 137 | struct stat statbuf; |
michael@0 | 138 | DB *dbp; |
michael@0 | 139 | int bpages, hdrsize, new_table, nsegs, save_errno; |
michael@0 | 140 | |
michael@0 | 141 | if ((flags & O_ACCMODE) == O_WRONLY) { |
michael@0 | 142 | errno = EINVAL; |
michael@0 | 143 | return NULL; |
michael@0 | 144 | } |
michael@0 | 145 | |
michael@0 | 146 | /* zero the statbuffer so that |
michael@0 | 147 | * we can check it for a non-zero |
michael@0 | 148 | * date to see if stat succeeded |
michael@0 | 149 | */ |
michael@0 | 150 | memset(&statbuf, 0, sizeof(struct stat)); |
michael@0 | 151 | |
michael@0 | 152 | if (!(hashp = (HTAB *)calloc(1, sizeof(HTAB)))) { |
michael@0 | 153 | errno = ENOMEM; |
michael@0 | 154 | return NULL; |
michael@0 | 155 | } |
michael@0 | 156 | hashp->fp = NO_FILE; |
michael@0 | 157 | if(file) |
michael@0 | 158 | hashp->filename = strdup(file); |
michael@0 | 159 | |
michael@0 | 160 | /* |
michael@0 | 161 | * Even if user wants write only, we need to be able to read |
michael@0 | 162 | * the actual file, so we need to open it read/write. But, the |
michael@0 | 163 | * field in the hashp structure needs to be accurate so that |
michael@0 | 164 | * we can check accesses. |
michael@0 | 165 | */ |
michael@0 | 166 | hashp->flags = flags; |
michael@0 | 167 | |
michael@0 | 168 | new_table = 0; |
michael@0 | 169 | if (!file || (flags & O_TRUNC) || (stat(file, &statbuf) && (errno == ENOENT))) |
michael@0 | 170 | { |
michael@0 | 171 | if (errno == ENOENT) |
michael@0 | 172 | errno = 0; /* Just in case someone looks at errno */ |
michael@0 | 173 | new_table = 1; |
michael@0 | 174 | } |
michael@0 | 175 | else if(statbuf.st_mtime && statbuf.st_size == 0) |
michael@0 | 176 | { |
michael@0 | 177 | /* check for a zero length file and delete it |
michael@0 | 178 | * if it exists |
michael@0 | 179 | */ |
michael@0 | 180 | new_table = 1; |
michael@0 | 181 | } |
michael@0 | 182 | hashp->file_size = statbuf.st_size; |
michael@0 | 183 | |
michael@0 | 184 | if (file) { |
michael@0 | 185 | #if defined(_WIN32) || defined(_WINDOWS) || defined (macintosh) || defined(XP_OS2) |
michael@0 | 186 | if ((hashp->fp = DBFILE_OPEN(file, flags | O_BINARY, mode)) == -1) |
michael@0 | 187 | RETURN_ERROR(errno, error1); |
michael@0 | 188 | #else |
michael@0 | 189 | if ((hashp->fp = open(file, flags, mode)) == -1) |
michael@0 | 190 | RETURN_ERROR(errno, error1); |
michael@0 | 191 | (void)fcntl(hashp->fp, F_SETFD, 1); |
michael@0 | 192 | #endif |
michael@0 | 193 | } |
michael@0 | 194 | if (new_table) { |
michael@0 | 195 | if (!init_hash(hashp, file, (HASHINFO *)info)) |
michael@0 | 196 | RETURN_ERROR(errno, error1); |
michael@0 | 197 | } else { |
michael@0 | 198 | /* Table already exists */ |
michael@0 | 199 | if (info && info->hash) |
michael@0 | 200 | hashp->hash = info->hash; |
michael@0 | 201 | else |
michael@0 | 202 | hashp->hash = __default_hash; |
michael@0 | 203 | |
michael@0 | 204 | hdrsize = read(hashp->fp, (char *)&hashp->hdr, sizeof(HASHHDR)); |
michael@0 | 205 | if (hdrsize == -1) |
michael@0 | 206 | RETURN_ERROR(errno, error1); |
michael@0 | 207 | if (hdrsize != sizeof(HASHHDR)) |
michael@0 | 208 | RETURN_ERROR(EFTYPE, error1); |
michael@0 | 209 | #if BYTE_ORDER == LITTLE_ENDIAN |
michael@0 | 210 | swap_header(hashp); |
michael@0 | 211 | #endif |
michael@0 | 212 | /* Verify file type, versions and hash function */ |
michael@0 | 213 | if (hashp->MAGIC != HASHMAGIC) |
michael@0 | 214 | RETURN_ERROR(EFTYPE, error1); |
michael@0 | 215 | #define OLDHASHVERSION 1 |
michael@0 | 216 | if (hashp->VERSION != HASHVERSION && |
michael@0 | 217 | hashp->VERSION != OLDHASHVERSION) |
michael@0 | 218 | RETURN_ERROR(EFTYPE, error1); |
michael@0 | 219 | if (hashp->hash(CHARKEY, sizeof(CHARKEY)) != hashp->H_CHARKEY) |
michael@0 | 220 | RETURN_ERROR(EFTYPE, error1); |
michael@0 | 221 | if (hashp->NKEYS < 0) /* Old bad database. */ |
michael@0 | 222 | RETURN_ERROR(EFTYPE, error1); |
michael@0 | 223 | |
michael@0 | 224 | /* |
michael@0 | 225 | * Figure out how many segments we need. Max_Bucket is the |
michael@0 | 226 | * maximum bucket number, so the number of buckets is |
michael@0 | 227 | * max_bucket + 1. |
michael@0 | 228 | */ |
michael@0 | 229 | nsegs = (hashp->MAX_BUCKET + 1 + hashp->SGSIZE - 1) / |
michael@0 | 230 | hashp->SGSIZE; |
michael@0 | 231 | hashp->nsegs = 0; |
michael@0 | 232 | if (alloc_segs(hashp, nsegs)) |
michael@0 | 233 | /* If alloc_segs fails, errno will have been set. */ |
michael@0 | 234 | RETURN_ERROR(errno, error1); |
michael@0 | 235 | /* Read in bitmaps */ |
michael@0 | 236 | bpages = (hashp->SPARES[hashp->OVFL_POINT] + |
michael@0 | 237 | (hashp->BSIZE << BYTE_SHIFT) - 1) >> |
michael@0 | 238 | (hashp->BSHIFT + BYTE_SHIFT); |
michael@0 | 239 | |
michael@0 | 240 | hashp->nmaps = bpages; |
michael@0 | 241 | (void)memset(&hashp->mapp[0], 0, bpages * sizeof(uint32 *)); |
michael@0 | 242 | } |
michael@0 | 243 | |
michael@0 | 244 | /* Initialize Buffer Manager */ |
michael@0 | 245 | if (info && info->cachesize) |
michael@0 | 246 | __buf_init(hashp, (int32) info->cachesize); |
michael@0 | 247 | else |
michael@0 | 248 | __buf_init(hashp, DEF_BUFSIZE); |
michael@0 | 249 | |
michael@0 | 250 | hashp->new_file = new_table; |
michael@0 | 251 | #ifdef macintosh |
michael@0 | 252 | hashp->save_file = file && !(hashp->flags & O_RDONLY); |
michael@0 | 253 | #else |
michael@0 | 254 | hashp->save_file = file && (hashp->flags & O_RDWR); |
michael@0 | 255 | #endif |
michael@0 | 256 | hashp->cbucket = -1; |
michael@0 | 257 | if (!(dbp = (DB *)malloc(sizeof(DB)))) { |
michael@0 | 258 | RETURN_ERROR(ENOMEM, error1); |
michael@0 | 259 | } |
michael@0 | 260 | dbp->internal = hashp; |
michael@0 | 261 | dbp->close = hash_close; |
michael@0 | 262 | dbp->del = hash_delete; |
michael@0 | 263 | dbp->fd = hash_fd; |
michael@0 | 264 | dbp->get = hash_get; |
michael@0 | 265 | dbp->put = hash_put; |
michael@0 | 266 | dbp->seq = hash_seq; |
michael@0 | 267 | dbp->sync = hash_sync; |
michael@0 | 268 | dbp->type = DB_HASH; |
michael@0 | 269 | |
michael@0 | 270 | #ifdef HASH_STATISTICS |
michael@0 | 271 | hash_overflows = hash_accesses = hash_collisions = hash_expansions = 0; |
michael@0 | 272 | #endif |
michael@0 | 273 | return (dbp); |
michael@0 | 274 | |
michael@0 | 275 | error1: |
michael@0 | 276 | hdestroy(hashp); |
michael@0 | 277 | errno = save_errno; |
michael@0 | 278 | return (NULL); |
michael@0 | 279 | } |
michael@0 | 280 | |
michael@0 | 281 | static int |
michael@0 | 282 | hash_close(DB *dbp) |
michael@0 | 283 | { |
michael@0 | 284 | HTAB *hashp; |
michael@0 | 285 | int retval; |
michael@0 | 286 | |
michael@0 | 287 | if (!dbp) |
michael@0 | 288 | return (DBM_ERROR); |
michael@0 | 289 | |
michael@0 | 290 | hashp = (HTAB *)dbp->internal; |
michael@0 | 291 | if(!hashp) |
michael@0 | 292 | return (DBM_ERROR); |
michael@0 | 293 | |
michael@0 | 294 | retval = hdestroy(hashp); |
michael@0 | 295 | free(dbp); |
michael@0 | 296 | return (retval); |
michael@0 | 297 | } |
michael@0 | 298 | |
michael@0 | 299 | static int hash_fd(const DB *dbp) |
michael@0 | 300 | { |
michael@0 | 301 | HTAB *hashp; |
michael@0 | 302 | |
michael@0 | 303 | if (!dbp) |
michael@0 | 304 | return (DBM_ERROR); |
michael@0 | 305 | |
michael@0 | 306 | hashp = (HTAB *)dbp->internal; |
michael@0 | 307 | if(!hashp) |
michael@0 | 308 | return (DBM_ERROR); |
michael@0 | 309 | |
michael@0 | 310 | if (hashp->fp == -1) { |
michael@0 | 311 | errno = ENOENT; |
michael@0 | 312 | return (-1); |
michael@0 | 313 | } |
michael@0 | 314 | return (hashp->fp); |
michael@0 | 315 | } |
michael@0 | 316 | |
michael@0 | 317 | /************************** LOCAL CREATION ROUTINES **********************/ |
michael@0 | 318 | static HTAB * |
michael@0 | 319 | init_hash(HTAB *hashp, const char *file, HASHINFO *info) |
michael@0 | 320 | { |
michael@0 | 321 | struct stat statbuf; |
michael@0 | 322 | int nelem; |
michael@0 | 323 | |
michael@0 | 324 | nelem = 1; |
michael@0 | 325 | hashp->NKEYS = 0; |
michael@0 | 326 | hashp->LORDER = BYTE_ORDER; |
michael@0 | 327 | hashp->BSIZE = DEF_BUCKET_SIZE; |
michael@0 | 328 | hashp->BSHIFT = DEF_BUCKET_SHIFT; |
michael@0 | 329 | hashp->SGSIZE = DEF_SEGSIZE; |
michael@0 | 330 | hashp->SSHIFT = DEF_SEGSIZE_SHIFT; |
michael@0 | 331 | hashp->DSIZE = DEF_DIRSIZE; |
michael@0 | 332 | hashp->FFACTOR = DEF_FFACTOR; |
michael@0 | 333 | hashp->hash = __default_hash; |
michael@0 | 334 | memset(hashp->SPARES, 0, sizeof(hashp->SPARES)); |
michael@0 | 335 | memset(hashp->BITMAPS, 0, sizeof (hashp->BITMAPS)); |
michael@0 | 336 | |
michael@0 | 337 | /* Fix bucket size to be optimal for file system */ |
michael@0 | 338 | if (file != NULL) { |
michael@0 | 339 | if (stat(file, &statbuf)) |
michael@0 | 340 | return (NULL); |
michael@0 | 341 | |
michael@0 | 342 | #if !defined(_WIN32) && !defined(_WINDOWS) && !defined(macintosh) && !defined(XP_OS2) |
michael@0 | 343 | #if defined(__QNX__) && !defined(__QNXNTO__) |
michael@0 | 344 | hashp->BSIZE = 512; /* preferred blk size on qnx4 */ |
michael@0 | 345 | #else |
michael@0 | 346 | hashp->BSIZE = statbuf.st_blksize; |
michael@0 | 347 | #endif |
michael@0 | 348 | |
michael@0 | 349 | /* new code added by Lou to reduce block |
michael@0 | 350 | * size down below MAX_BSIZE |
michael@0 | 351 | */ |
michael@0 | 352 | if (hashp->BSIZE > MAX_BSIZE) |
michael@0 | 353 | hashp->BSIZE = MAX_BSIZE; |
michael@0 | 354 | #endif |
michael@0 | 355 | hashp->BSHIFT = __log2((uint32)hashp->BSIZE); |
michael@0 | 356 | } |
michael@0 | 357 | |
michael@0 | 358 | if (info) { |
michael@0 | 359 | if (info->bsize) { |
michael@0 | 360 | /* Round pagesize up to power of 2 */ |
michael@0 | 361 | hashp->BSHIFT = __log2(info->bsize); |
michael@0 | 362 | hashp->BSIZE = 1 << hashp->BSHIFT; |
michael@0 | 363 | if (hashp->BSIZE > MAX_BSIZE) { |
michael@0 | 364 | errno = EINVAL; |
michael@0 | 365 | return (NULL); |
michael@0 | 366 | } |
michael@0 | 367 | } |
michael@0 | 368 | if (info->ffactor) |
michael@0 | 369 | hashp->FFACTOR = info->ffactor; |
michael@0 | 370 | if (info->hash) |
michael@0 | 371 | hashp->hash = info->hash; |
michael@0 | 372 | if (info->nelem) |
michael@0 | 373 | nelem = info->nelem; |
michael@0 | 374 | if (info->lorder) { |
michael@0 | 375 | if (info->lorder != BIG_ENDIAN && |
michael@0 | 376 | info->lorder != LITTLE_ENDIAN) { |
michael@0 | 377 | errno = EINVAL; |
michael@0 | 378 | return (NULL); |
michael@0 | 379 | } |
michael@0 | 380 | hashp->LORDER = info->lorder; |
michael@0 | 381 | } |
michael@0 | 382 | } |
michael@0 | 383 | /* init_htab sets errno if it fails */ |
michael@0 | 384 | if (init_htab(hashp, nelem)) |
michael@0 | 385 | return (NULL); |
michael@0 | 386 | else |
michael@0 | 387 | return (hashp); |
michael@0 | 388 | } |
michael@0 | 389 | /* |
michael@0 | 390 | * This calls alloc_segs which may run out of memory. Alloc_segs will |
michael@0 | 391 | * set errno, so we just pass the error information along. |
michael@0 | 392 | * |
michael@0 | 393 | * Returns 0 on No Error |
michael@0 | 394 | */ |
michael@0 | 395 | static int |
michael@0 | 396 | init_htab(HTAB *hashp, int nelem) |
michael@0 | 397 | { |
michael@0 | 398 | register int nbuckets, nsegs; |
michael@0 | 399 | int l2; |
michael@0 | 400 | |
michael@0 | 401 | /* |
michael@0 | 402 | * Divide number of elements by the fill factor and determine a |
michael@0 | 403 | * desired number of buckets. Allocate space for the next greater |
michael@0 | 404 | * power of two number of buckets. |
michael@0 | 405 | */ |
michael@0 | 406 | nelem = (nelem - 1) / hashp->FFACTOR + 1; |
michael@0 | 407 | |
michael@0 | 408 | l2 = __log2((uint32)PR_MAX(nelem, 2)); |
michael@0 | 409 | nbuckets = 1 << l2; |
michael@0 | 410 | |
michael@0 | 411 | hashp->SPARES[l2] = l2 + 1; |
michael@0 | 412 | hashp->SPARES[l2 + 1] = l2 + 1; |
michael@0 | 413 | hashp->OVFL_POINT = l2; |
michael@0 | 414 | hashp->LAST_FREED = 2; |
michael@0 | 415 | |
michael@0 | 416 | /* First bitmap page is at: splitpoint l2 page offset 1 */ |
michael@0 | 417 | if (__ibitmap(hashp, (int)OADDR_OF(l2, 1), l2 + 1, 0)) |
michael@0 | 418 | return (-1); |
michael@0 | 419 | |
michael@0 | 420 | hashp->MAX_BUCKET = hashp->LOW_MASK = nbuckets - 1; |
michael@0 | 421 | hashp->HIGH_MASK = (nbuckets << 1) - 1; |
michael@0 | 422 | hashp->HDRPAGES = ((PR_MAX(sizeof(HASHHDR), MINHDRSIZE) - 1) >> |
michael@0 | 423 | hashp->BSHIFT) + 1; |
michael@0 | 424 | |
michael@0 | 425 | nsegs = (nbuckets - 1) / hashp->SGSIZE + 1; |
michael@0 | 426 | nsegs = 1 << __log2((uint32)nsegs); |
michael@0 | 427 | |
michael@0 | 428 | if (nsegs > hashp->DSIZE) |
michael@0 | 429 | hashp->DSIZE = nsegs; |
michael@0 | 430 | return (alloc_segs(hashp, nsegs)); |
michael@0 | 431 | } |
michael@0 | 432 | |
michael@0 | 433 | /********************** DESTROY/CLOSE ROUTINES ************************/ |
michael@0 | 434 | |
michael@0 | 435 | /* |
michael@0 | 436 | * Flushes any changes to the file if necessary and destroys the hashp |
michael@0 | 437 | * structure, freeing all allocated space. |
michael@0 | 438 | */ |
michael@0 | 439 | static int |
michael@0 | 440 | hdestroy(HTAB *hashp) |
michael@0 | 441 | { |
michael@0 | 442 | int i, save_errno; |
michael@0 | 443 | |
michael@0 | 444 | save_errno = 0; |
michael@0 | 445 | |
michael@0 | 446 | #ifdef HASH_STATISTICS |
michael@0 | 447 | (void)fprintf(stderr, "hdestroy: accesses %ld collisions %ld\n", |
michael@0 | 448 | hash_accesses, hash_collisions); |
michael@0 | 449 | (void)fprintf(stderr, "hdestroy: expansions %ld\n", |
michael@0 | 450 | hash_expansions); |
michael@0 | 451 | (void)fprintf(stderr, "hdestroy: overflows %ld\n", |
michael@0 | 452 | hash_overflows); |
michael@0 | 453 | (void)fprintf(stderr, "keys %ld maxp %d segmentcount %d\n", |
michael@0 | 454 | hashp->NKEYS, hashp->MAX_BUCKET, hashp->nsegs); |
michael@0 | 455 | |
michael@0 | 456 | for (i = 0; i < NCACHED; i++) |
michael@0 | 457 | (void)fprintf(stderr, |
michael@0 | 458 | "spares[%d] = %d\n", i, hashp->SPARES[i]); |
michael@0 | 459 | #endif |
michael@0 | 460 | /* |
michael@0 | 461 | * Call on buffer manager to free buffers, and if required, |
michael@0 | 462 | * write them to disk. |
michael@0 | 463 | */ |
michael@0 | 464 | if (__buf_free(hashp, 1, hashp->save_file)) |
michael@0 | 465 | save_errno = errno; |
michael@0 | 466 | if (hashp->dir) { |
michael@0 | 467 | free(*hashp->dir); /* Free initial segments */ |
michael@0 | 468 | /* Free extra segments */ |
michael@0 | 469 | while (hashp->exsegs--) |
michael@0 | 470 | free(hashp->dir[--hashp->nsegs]); |
michael@0 | 471 | free(hashp->dir); |
michael@0 | 472 | } |
michael@0 | 473 | if (flush_meta(hashp) && !save_errno) |
michael@0 | 474 | save_errno = errno; |
michael@0 | 475 | /* Free Bigmaps */ |
michael@0 | 476 | for (i = 0; i < hashp->nmaps; i++) |
michael@0 | 477 | if (hashp->mapp[i]) |
michael@0 | 478 | free(hashp->mapp[i]); |
michael@0 | 479 | |
michael@0 | 480 | if (hashp->fp != -1) |
michael@0 | 481 | (void)close(hashp->fp); |
michael@0 | 482 | |
michael@0 | 483 | if(hashp->filename) { |
michael@0 | 484 | #if defined(_WIN32) || defined(_WINDOWS) || defined(XP_OS2) |
michael@0 | 485 | if (hashp->is_temp) |
michael@0 | 486 | (void)unlink(hashp->filename); |
michael@0 | 487 | #endif |
michael@0 | 488 | free(hashp->filename); |
michael@0 | 489 | } |
michael@0 | 490 | if (hashp->tmp_buf) |
michael@0 | 491 | free(hashp->tmp_buf); |
michael@0 | 492 | if (hashp->tmp_key) |
michael@0 | 493 | free(hashp->tmp_key); |
michael@0 | 494 | free(hashp); |
michael@0 | 495 | if (save_errno) { |
michael@0 | 496 | errno = save_errno; |
michael@0 | 497 | return (DBM_ERROR); |
michael@0 | 498 | } |
michael@0 | 499 | return (SUCCESS); |
michael@0 | 500 | } |
michael@0 | 501 | |
michael@0 | 502 | #if defined(_WIN32) || defined(_WINDOWS) |
michael@0 | 503 | /* |
michael@0 | 504 | * Close and reopen file to force file length update on windows. |
michael@0 | 505 | * |
michael@0 | 506 | * Returns: |
michael@0 | 507 | * 0 == OK |
michael@0 | 508 | * -1 DBM_ERROR |
michael@0 | 509 | */ |
michael@0 | 510 | static int |
michael@0 | 511 | update_EOF(HTAB *hashp) |
michael@0 | 512 | { |
michael@0 | 513 | #if defined(DBM_REOPEN_ON_FLUSH) |
michael@0 | 514 | char * file = hashp->filename; |
michael@0 | 515 | off_t file_size; |
michael@0 | 516 | int flags; |
michael@0 | 517 | int mode = -1; |
michael@0 | 518 | struct stat statbuf; |
michael@0 | 519 | |
michael@0 | 520 | memset(&statbuf, 0, sizeof statbuf); |
michael@0 | 521 | |
michael@0 | 522 | /* make sure we won't lose the file by closing it. */ |
michael@0 | 523 | if (!file || (stat(file, &statbuf) && (errno == ENOENT))) { |
michael@0 | 524 | /* pretend we did it. */ |
michael@0 | 525 | return 0; |
michael@0 | 526 | } |
michael@0 | 527 | |
michael@0 | 528 | (void)close(hashp->fp); |
michael@0 | 529 | |
michael@0 | 530 | flags = hashp->flags & ~(O_TRUNC | O_CREAT | O_EXCL); |
michael@0 | 531 | |
michael@0 | 532 | if ((hashp->fp = DBFILE_OPEN(file, flags | O_BINARY, mode)) == -1) |
michael@0 | 533 | return -1; |
michael@0 | 534 | file_size = lseek(hashp->fp, (off_t)0, SEEK_END); |
michael@0 | 535 | if (file_size == -1) |
michael@0 | 536 | return -1; |
michael@0 | 537 | hashp->file_size = file_size; |
michael@0 | 538 | return 0; |
michael@0 | 539 | #else |
michael@0 | 540 | int fd = hashp->fp; |
michael@0 | 541 | off_t file_size = lseek(fd, (off_t)0, SEEK_END); |
michael@0 | 542 | HANDLE handle = (HANDLE)_get_osfhandle(fd); |
michael@0 | 543 | BOOL cool = FlushFileBuffers(handle); |
michael@0 | 544 | #ifdef DEBUG3 |
michael@0 | 545 | if (!cool) { |
michael@0 | 546 | DWORD err = GetLastError(); |
michael@0 | 547 | (void)fprintf(stderr, |
michael@0 | 548 | "FlushFileBuffers failed, last error = %d, 0x%08x\n", |
michael@0 | 549 | err, err); |
michael@0 | 550 | } |
michael@0 | 551 | #endif |
michael@0 | 552 | if (file_size == -1) |
michael@0 | 553 | return -1; |
michael@0 | 554 | hashp->file_size = file_size; |
michael@0 | 555 | return cool ? 0 : -1; |
michael@0 | 556 | #endif |
michael@0 | 557 | } |
michael@0 | 558 | #endif |
michael@0 | 559 | |
michael@0 | 560 | /* |
michael@0 | 561 | * Write modified pages to disk |
michael@0 | 562 | * |
michael@0 | 563 | * Returns: |
michael@0 | 564 | * 0 == OK |
michael@0 | 565 | * -1 DBM_ERROR |
michael@0 | 566 | */ |
michael@0 | 567 | static int |
michael@0 | 568 | hash_sync(const DB *dbp, uint flags) |
michael@0 | 569 | { |
michael@0 | 570 | HTAB *hashp; |
michael@0 | 571 | |
michael@0 | 572 | if (flags != 0) { |
michael@0 | 573 | errno = EINVAL; |
michael@0 | 574 | return (DBM_ERROR); |
michael@0 | 575 | } |
michael@0 | 576 | |
michael@0 | 577 | if (!dbp) |
michael@0 | 578 | return (DBM_ERROR); |
michael@0 | 579 | |
michael@0 | 580 | hashp = (HTAB *)dbp->internal; |
michael@0 | 581 | if(!hashp) |
michael@0 | 582 | return (DBM_ERROR); |
michael@0 | 583 | |
michael@0 | 584 | if (!hashp->save_file) |
michael@0 | 585 | return (0); |
michael@0 | 586 | if (__buf_free(hashp, 0, 1) || flush_meta(hashp)) |
michael@0 | 587 | return (DBM_ERROR); |
michael@0 | 588 | #if defined(_WIN32) || defined(_WINDOWS) |
michael@0 | 589 | if (hashp->updateEOF && hashp->filename && !hashp->is_temp) { |
michael@0 | 590 | int status = update_EOF(hashp); |
michael@0 | 591 | hashp->updateEOF = 0; |
michael@0 | 592 | if (status) |
michael@0 | 593 | return status; |
michael@0 | 594 | } |
michael@0 | 595 | #endif |
michael@0 | 596 | hashp->new_file = 0; |
michael@0 | 597 | return (0); |
michael@0 | 598 | } |
michael@0 | 599 | |
michael@0 | 600 | /* |
michael@0 | 601 | * Returns: |
michael@0 | 602 | * 0 == OK |
michael@0 | 603 | * -1 indicates that errno should be set |
michael@0 | 604 | */ |
michael@0 | 605 | static int |
michael@0 | 606 | flush_meta(HTAB *hashp) |
michael@0 | 607 | { |
michael@0 | 608 | HASHHDR *whdrp; |
michael@0 | 609 | #if BYTE_ORDER == LITTLE_ENDIAN |
michael@0 | 610 | HASHHDR whdr; |
michael@0 | 611 | #endif |
michael@0 | 612 | int fp, i, wsize; |
michael@0 | 613 | |
michael@0 | 614 | if (!hashp->save_file) |
michael@0 | 615 | return (0); |
michael@0 | 616 | hashp->MAGIC = HASHMAGIC; |
michael@0 | 617 | hashp->VERSION = HASHVERSION; |
michael@0 | 618 | hashp->H_CHARKEY = hashp->hash(CHARKEY, sizeof(CHARKEY)); |
michael@0 | 619 | |
michael@0 | 620 | fp = hashp->fp; |
michael@0 | 621 | whdrp = &hashp->hdr; |
michael@0 | 622 | #if BYTE_ORDER == LITTLE_ENDIAN |
michael@0 | 623 | whdrp = &whdr; |
michael@0 | 624 | swap_header_copy(&hashp->hdr, whdrp); |
michael@0 | 625 | #endif |
michael@0 | 626 | if ((lseek(fp, (off_t)0, SEEK_SET) == -1) || |
michael@0 | 627 | ((wsize = write(fp, (char*)whdrp, sizeof(HASHHDR))) == -1)) |
michael@0 | 628 | return (-1); |
michael@0 | 629 | else |
michael@0 | 630 | if (wsize != sizeof(HASHHDR)) { |
michael@0 | 631 | errno = EFTYPE; |
michael@0 | 632 | hashp->dbmerrno = errno; |
michael@0 | 633 | return (-1); |
michael@0 | 634 | } |
michael@0 | 635 | for (i = 0; i < NCACHED; i++) |
michael@0 | 636 | if (hashp->mapp[i]) |
michael@0 | 637 | if (__put_page(hashp, (char *)hashp->mapp[i], |
michael@0 | 638 | hashp->BITMAPS[i], 0, 1)) |
michael@0 | 639 | return (-1); |
michael@0 | 640 | return (0); |
michael@0 | 641 | } |
michael@0 | 642 | |
michael@0 | 643 | /*******************************SEARCH ROUTINES *****************************/ |
michael@0 | 644 | /* |
michael@0 | 645 | * All the access routines return |
michael@0 | 646 | * |
michael@0 | 647 | * Returns: |
michael@0 | 648 | * 0 on SUCCESS |
michael@0 | 649 | * 1 to indicate an external DBM_ERROR (i.e. key not found, etc) |
michael@0 | 650 | * -1 to indicate an internal DBM_ERROR (i.e. out of memory, etc) |
michael@0 | 651 | */ |
michael@0 | 652 | static int |
michael@0 | 653 | hash_get( |
michael@0 | 654 | const DB *dbp, |
michael@0 | 655 | const DBT *key, |
michael@0 | 656 | DBT *data, |
michael@0 | 657 | uint flag) |
michael@0 | 658 | { |
michael@0 | 659 | HTAB *hashp; |
michael@0 | 660 | int rv; |
michael@0 | 661 | |
michael@0 | 662 | hashp = (HTAB *)dbp->internal; |
michael@0 | 663 | if (!hashp) |
michael@0 | 664 | return (DBM_ERROR); |
michael@0 | 665 | |
michael@0 | 666 | if (flag) { |
michael@0 | 667 | hashp->dbmerrno = errno = EINVAL; |
michael@0 | 668 | return (DBM_ERROR); |
michael@0 | 669 | } |
michael@0 | 670 | |
michael@0 | 671 | rv = hash_access(hashp, HASH_GET, (DBT *)key, data); |
michael@0 | 672 | |
michael@0 | 673 | if(rv == DATABASE_CORRUPTED_ERROR) |
michael@0 | 674 | { |
michael@0 | 675 | #if defined(unix) && defined(DEBUG) |
michael@0 | 676 | printf("\n\nDBM Database has been corrupted, tell Lou...\n\n"); |
michael@0 | 677 | #endif |
michael@0 | 678 | __remove_database((DB *)dbp); |
michael@0 | 679 | } |
michael@0 | 680 | |
michael@0 | 681 | return(rv); |
michael@0 | 682 | } |
michael@0 | 683 | |
michael@0 | 684 | static int |
michael@0 | 685 | hash_put( |
michael@0 | 686 | const DB *dbp, |
michael@0 | 687 | DBT *key, |
michael@0 | 688 | const DBT *data, |
michael@0 | 689 | uint flag) |
michael@0 | 690 | { |
michael@0 | 691 | HTAB *hashp; |
michael@0 | 692 | int rv; |
michael@0 | 693 | |
michael@0 | 694 | hashp = (HTAB *)dbp->internal; |
michael@0 | 695 | if (!hashp) |
michael@0 | 696 | return (DBM_ERROR); |
michael@0 | 697 | |
michael@0 | 698 | if (flag && flag != R_NOOVERWRITE) { |
michael@0 | 699 | hashp->dbmerrno = errno = EINVAL; |
michael@0 | 700 | return (DBM_ERROR); |
michael@0 | 701 | } |
michael@0 | 702 | if ((hashp->flags & O_ACCMODE) == O_RDONLY) { |
michael@0 | 703 | hashp->dbmerrno = errno = EPERM; |
michael@0 | 704 | return (DBM_ERROR); |
michael@0 | 705 | } |
michael@0 | 706 | |
michael@0 | 707 | rv = hash_access(hashp, flag == R_NOOVERWRITE ? |
michael@0 | 708 | HASH_PUTNEW : HASH_PUT, (DBT *)key, (DBT *)data); |
michael@0 | 709 | |
michael@0 | 710 | if(rv == DATABASE_CORRUPTED_ERROR) |
michael@0 | 711 | { |
michael@0 | 712 | #if defined(unix) && defined(DEBUG) |
michael@0 | 713 | printf("\n\nDBM Database has been corrupted, tell Lou...\n\n"); |
michael@0 | 714 | #endif |
michael@0 | 715 | __remove_database((DB *)dbp); |
michael@0 | 716 | } |
michael@0 | 717 | |
michael@0 | 718 | return(rv); |
michael@0 | 719 | } |
michael@0 | 720 | |
michael@0 | 721 | static int |
michael@0 | 722 | hash_delete( |
michael@0 | 723 | const DB *dbp, |
michael@0 | 724 | const DBT *key, |
michael@0 | 725 | uint flag) /* Ignored */ |
michael@0 | 726 | { |
michael@0 | 727 | HTAB *hashp; |
michael@0 | 728 | int rv; |
michael@0 | 729 | |
michael@0 | 730 | hashp = (HTAB *)dbp->internal; |
michael@0 | 731 | if (!hashp) |
michael@0 | 732 | return (DBM_ERROR); |
michael@0 | 733 | |
michael@0 | 734 | if (flag && flag != R_CURSOR) { |
michael@0 | 735 | hashp->dbmerrno = errno = EINVAL; |
michael@0 | 736 | return (DBM_ERROR); |
michael@0 | 737 | } |
michael@0 | 738 | if ((hashp->flags & O_ACCMODE) == O_RDONLY) { |
michael@0 | 739 | hashp->dbmerrno = errno = EPERM; |
michael@0 | 740 | return (DBM_ERROR); |
michael@0 | 741 | } |
michael@0 | 742 | rv = hash_access(hashp, HASH_DELETE, (DBT *)key, NULL); |
michael@0 | 743 | |
michael@0 | 744 | if(rv == DATABASE_CORRUPTED_ERROR) |
michael@0 | 745 | { |
michael@0 | 746 | #if defined(unix) && defined(DEBUG) |
michael@0 | 747 | printf("\n\nDBM Database has been corrupted, tell Lou...\n\n"); |
michael@0 | 748 | #endif |
michael@0 | 749 | __remove_database((DB *)dbp); |
michael@0 | 750 | } |
michael@0 | 751 | |
michael@0 | 752 | return(rv); |
michael@0 | 753 | } |
michael@0 | 754 | |
michael@0 | 755 | #define MAX_OVERFLOW_HASH_ACCESS_LOOPS 2000 |
michael@0 | 756 | /* |
michael@0 | 757 | * Assume that hashp has been set in wrapper routine. |
michael@0 | 758 | */ |
michael@0 | 759 | static int |
michael@0 | 760 | hash_access( |
michael@0 | 761 | HTAB *hashp, |
michael@0 | 762 | ACTION action, |
michael@0 | 763 | DBT *key, DBT *val) |
michael@0 | 764 | { |
michael@0 | 765 | register BUFHEAD *rbufp; |
michael@0 | 766 | BUFHEAD *bufp, *save_bufp; |
michael@0 | 767 | register uint16 *bp; |
michael@0 | 768 | register long n, ndx, off; |
michael@0 | 769 | register size_t size; |
michael@0 | 770 | register char *kp; |
michael@0 | 771 | uint16 pageno; |
michael@0 | 772 | uint32 ovfl_loop_count=0; |
michael@0 | 773 | int32 last_overflow_page_no = -1; |
michael@0 | 774 | |
michael@0 | 775 | #ifdef HASH_STATISTICS |
michael@0 | 776 | hash_accesses++; |
michael@0 | 777 | #endif |
michael@0 | 778 | |
michael@0 | 779 | off = hashp->BSIZE; |
michael@0 | 780 | size = key->size; |
michael@0 | 781 | kp = (char *)key->data; |
michael@0 | 782 | rbufp = __get_buf(hashp, __call_hash(hashp, kp, size), NULL, 0); |
michael@0 | 783 | if (!rbufp) |
michael@0 | 784 | return (DATABASE_CORRUPTED_ERROR); |
michael@0 | 785 | save_bufp = rbufp; |
michael@0 | 786 | |
michael@0 | 787 | /* Pin the bucket chain */ |
michael@0 | 788 | rbufp->flags |= BUF_PIN; |
michael@0 | 789 | for (bp = (uint16 *)rbufp->page, n = *bp++, ndx = 1; ndx < n;) |
michael@0 | 790 | { |
michael@0 | 791 | |
michael@0 | 792 | if (bp[1] >= REAL_KEY) { |
michael@0 | 793 | /* Real key/data pair */ |
michael@0 | 794 | if (size == (unsigned long)(off - *bp) && |
michael@0 | 795 | memcmp(kp, rbufp->page + *bp, size) == 0) |
michael@0 | 796 | goto found; |
michael@0 | 797 | off = bp[1]; |
michael@0 | 798 | #ifdef HASH_STATISTICS |
michael@0 | 799 | hash_collisions++; |
michael@0 | 800 | #endif |
michael@0 | 801 | bp += 2; |
michael@0 | 802 | ndx += 2; |
michael@0 | 803 | } else if (bp[1] == OVFLPAGE) { |
michael@0 | 804 | |
michael@0 | 805 | /* database corruption: overflow loop detection */ |
michael@0 | 806 | if(last_overflow_page_no == (int32)*bp) |
michael@0 | 807 | return (DATABASE_CORRUPTED_ERROR); |
michael@0 | 808 | |
michael@0 | 809 | last_overflow_page_no = *bp; |
michael@0 | 810 | |
michael@0 | 811 | rbufp = __get_buf(hashp, *bp, rbufp, 0); |
michael@0 | 812 | if (!rbufp) { |
michael@0 | 813 | save_bufp->flags &= ~BUF_PIN; |
michael@0 | 814 | return (DBM_ERROR); |
michael@0 | 815 | } |
michael@0 | 816 | |
michael@0 | 817 | ovfl_loop_count++; |
michael@0 | 818 | if(ovfl_loop_count > MAX_OVERFLOW_HASH_ACCESS_LOOPS) |
michael@0 | 819 | return (DATABASE_CORRUPTED_ERROR); |
michael@0 | 820 | |
michael@0 | 821 | /* FOR LOOP INIT */ |
michael@0 | 822 | bp = (uint16 *)rbufp->page; |
michael@0 | 823 | n = *bp++; |
michael@0 | 824 | ndx = 1; |
michael@0 | 825 | off = hashp->BSIZE; |
michael@0 | 826 | } else if (bp[1] < REAL_KEY) { |
michael@0 | 827 | if ((ndx = |
michael@0 | 828 | __find_bigpair(hashp, rbufp, ndx, kp, (int)size)) > 0) |
michael@0 | 829 | goto found; |
michael@0 | 830 | if (ndx == -2) { |
michael@0 | 831 | bufp = rbufp; |
michael@0 | 832 | if (!(pageno = |
michael@0 | 833 | __find_last_page(hashp, &bufp))) { |
michael@0 | 834 | ndx = 0; |
michael@0 | 835 | rbufp = bufp; |
michael@0 | 836 | break; /* FOR */ |
michael@0 | 837 | } |
michael@0 | 838 | rbufp = __get_buf(hashp, pageno, bufp, 0); |
michael@0 | 839 | if (!rbufp) { |
michael@0 | 840 | save_bufp->flags &= ~BUF_PIN; |
michael@0 | 841 | return (DBM_ERROR); |
michael@0 | 842 | } |
michael@0 | 843 | /* FOR LOOP INIT */ |
michael@0 | 844 | bp = (uint16 *)rbufp->page; |
michael@0 | 845 | n = *bp++; |
michael@0 | 846 | ndx = 1; |
michael@0 | 847 | off = hashp->BSIZE; |
michael@0 | 848 | } else { |
michael@0 | 849 | save_bufp->flags &= ~BUF_PIN; |
michael@0 | 850 | return (DBM_ERROR); |
michael@0 | 851 | |
michael@0 | 852 | } |
michael@0 | 853 | } |
michael@0 | 854 | } |
michael@0 | 855 | |
michael@0 | 856 | /* Not found */ |
michael@0 | 857 | switch (action) { |
michael@0 | 858 | case HASH_PUT: |
michael@0 | 859 | case HASH_PUTNEW: |
michael@0 | 860 | if (__addel(hashp, rbufp, key, val)) { |
michael@0 | 861 | save_bufp->flags &= ~BUF_PIN; |
michael@0 | 862 | return (DBM_ERROR); |
michael@0 | 863 | } else { |
michael@0 | 864 | save_bufp->flags &= ~BUF_PIN; |
michael@0 | 865 | return (SUCCESS); |
michael@0 | 866 | } |
michael@0 | 867 | case HASH_GET: |
michael@0 | 868 | case HASH_DELETE: |
michael@0 | 869 | default: |
michael@0 | 870 | save_bufp->flags &= ~BUF_PIN; |
michael@0 | 871 | return (ABNORMAL); |
michael@0 | 872 | } |
michael@0 | 873 | |
michael@0 | 874 | found: |
michael@0 | 875 | switch (action) { |
michael@0 | 876 | case HASH_PUTNEW: |
michael@0 | 877 | save_bufp->flags &= ~BUF_PIN; |
michael@0 | 878 | return (ABNORMAL); |
michael@0 | 879 | case HASH_GET: |
michael@0 | 880 | bp = (uint16 *)rbufp->page; |
michael@0 | 881 | if (bp[ndx + 1] < REAL_KEY) { |
michael@0 | 882 | if (__big_return(hashp, rbufp, ndx, val, 0)) |
michael@0 | 883 | return (DBM_ERROR); |
michael@0 | 884 | } else { |
michael@0 | 885 | val->data = (uint8 *)rbufp->page + (int)bp[ndx + 1]; |
michael@0 | 886 | val->size = bp[ndx] - bp[ndx + 1]; |
michael@0 | 887 | } |
michael@0 | 888 | break; |
michael@0 | 889 | case HASH_PUT: |
michael@0 | 890 | if ((__delpair(hashp, rbufp, ndx)) || |
michael@0 | 891 | (__addel(hashp, rbufp, key, val))) { |
michael@0 | 892 | save_bufp->flags &= ~BUF_PIN; |
michael@0 | 893 | return (DBM_ERROR); |
michael@0 | 894 | } |
michael@0 | 895 | break; |
michael@0 | 896 | case HASH_DELETE: |
michael@0 | 897 | if (__delpair(hashp, rbufp, ndx)) |
michael@0 | 898 | return (DBM_ERROR); |
michael@0 | 899 | break; |
michael@0 | 900 | default: |
michael@0 | 901 | abort(); |
michael@0 | 902 | } |
michael@0 | 903 | save_bufp->flags &= ~BUF_PIN; |
michael@0 | 904 | return (SUCCESS); |
michael@0 | 905 | } |
michael@0 | 906 | |
michael@0 | 907 | static int |
michael@0 | 908 | hash_seq( |
michael@0 | 909 | const DB *dbp, |
michael@0 | 910 | DBT *key, DBT *data, |
michael@0 | 911 | uint flag) |
michael@0 | 912 | { |
michael@0 | 913 | register uint32 bucket; |
michael@0 | 914 | register BUFHEAD *bufp; |
michael@0 | 915 | HTAB *hashp; |
michael@0 | 916 | uint16 *bp, ndx; |
michael@0 | 917 | |
michael@0 | 918 | hashp = (HTAB *)dbp->internal; |
michael@0 | 919 | if (!hashp) |
michael@0 | 920 | return (DBM_ERROR); |
michael@0 | 921 | |
michael@0 | 922 | if (flag && flag != R_FIRST && flag != R_NEXT) { |
michael@0 | 923 | hashp->dbmerrno = errno = EINVAL; |
michael@0 | 924 | return (DBM_ERROR); |
michael@0 | 925 | } |
michael@0 | 926 | #ifdef HASH_STATISTICS |
michael@0 | 927 | hash_accesses++; |
michael@0 | 928 | #endif |
michael@0 | 929 | if ((hashp->cbucket < 0) || (flag == R_FIRST)) { |
michael@0 | 930 | hashp->cbucket = 0; |
michael@0 | 931 | hashp->cndx = 1; |
michael@0 | 932 | hashp->cpage = NULL; |
michael@0 | 933 | } |
michael@0 | 934 | |
michael@0 | 935 | for (bp = NULL; !bp || !bp[0]; ) { |
michael@0 | 936 | if (!(bufp = hashp->cpage)) { |
michael@0 | 937 | for (bucket = hashp->cbucket; |
michael@0 | 938 | bucket <= (uint32)hashp->MAX_BUCKET; |
michael@0 | 939 | bucket++, hashp->cndx = 1) { |
michael@0 | 940 | bufp = __get_buf(hashp, bucket, NULL, 0); |
michael@0 | 941 | if (!bufp) |
michael@0 | 942 | return (DBM_ERROR); |
michael@0 | 943 | hashp->cpage = bufp; |
michael@0 | 944 | bp = (uint16 *)bufp->page; |
michael@0 | 945 | if (bp[0]) |
michael@0 | 946 | break; |
michael@0 | 947 | } |
michael@0 | 948 | hashp->cbucket = bucket; |
michael@0 | 949 | if (hashp->cbucket > hashp->MAX_BUCKET) { |
michael@0 | 950 | hashp->cbucket = -1; |
michael@0 | 951 | return (ABNORMAL); |
michael@0 | 952 | } |
michael@0 | 953 | } else |
michael@0 | 954 | bp = (uint16 *)hashp->cpage->page; |
michael@0 | 955 | |
michael@0 | 956 | #ifdef DEBUG |
michael@0 | 957 | assert(bp); |
michael@0 | 958 | assert(bufp); |
michael@0 | 959 | #endif |
michael@0 | 960 | while (bp[hashp->cndx + 1] == OVFLPAGE) { |
michael@0 | 961 | bufp = hashp->cpage = |
michael@0 | 962 | __get_buf(hashp, bp[hashp->cndx], bufp, 0); |
michael@0 | 963 | if (!bufp) |
michael@0 | 964 | return (DBM_ERROR); |
michael@0 | 965 | bp = (uint16 *)(bufp->page); |
michael@0 | 966 | hashp->cndx = 1; |
michael@0 | 967 | } |
michael@0 | 968 | if (!bp[0]) { |
michael@0 | 969 | hashp->cpage = NULL; |
michael@0 | 970 | ++hashp->cbucket; |
michael@0 | 971 | } |
michael@0 | 972 | } |
michael@0 | 973 | ndx = hashp->cndx; |
michael@0 | 974 | if (bp[ndx + 1] < REAL_KEY) { |
michael@0 | 975 | if (__big_keydata(hashp, bufp, key, data, 1)) |
michael@0 | 976 | return (DBM_ERROR); |
michael@0 | 977 | } else { |
michael@0 | 978 | key->data = (uint8 *)hashp->cpage->page + bp[ndx]; |
michael@0 | 979 | key->size = (ndx > 1 ? bp[ndx - 1] : hashp->BSIZE) - bp[ndx]; |
michael@0 | 980 | data->data = (uint8 *)hashp->cpage->page + bp[ndx + 1]; |
michael@0 | 981 | data->size = bp[ndx] - bp[ndx + 1]; |
michael@0 | 982 | ndx += 2; |
michael@0 | 983 | if (ndx > bp[0]) { |
michael@0 | 984 | hashp->cpage = NULL; |
michael@0 | 985 | hashp->cbucket++; |
michael@0 | 986 | hashp->cndx = 1; |
michael@0 | 987 | } else |
michael@0 | 988 | hashp->cndx = ndx; |
michael@0 | 989 | } |
michael@0 | 990 | return (SUCCESS); |
michael@0 | 991 | } |
michael@0 | 992 | |
michael@0 | 993 | /********************************* UTILITIES ************************/ |
michael@0 | 994 | |
michael@0 | 995 | /* |
michael@0 | 996 | * Returns: |
michael@0 | 997 | * 0 ==> OK |
michael@0 | 998 | * -1 ==> Error |
michael@0 | 999 | */ |
michael@0 | 1000 | extern int |
michael@0 | 1001 | __expand_table(HTAB *hashp) |
michael@0 | 1002 | { |
michael@0 | 1003 | uint32 old_bucket, new_bucket; |
michael@0 | 1004 | int new_segnum, spare_ndx; |
michael@0 | 1005 | size_t dirsize; |
michael@0 | 1006 | |
michael@0 | 1007 | #ifdef HASH_STATISTICS |
michael@0 | 1008 | hash_expansions++; |
michael@0 | 1009 | #endif |
michael@0 | 1010 | new_bucket = ++hashp->MAX_BUCKET; |
michael@0 | 1011 | old_bucket = (hashp->MAX_BUCKET & hashp->LOW_MASK); |
michael@0 | 1012 | |
michael@0 | 1013 | new_segnum = new_bucket >> hashp->SSHIFT; |
michael@0 | 1014 | |
michael@0 | 1015 | /* Check if we need a new segment */ |
michael@0 | 1016 | if (new_segnum >= hashp->nsegs) { |
michael@0 | 1017 | /* Check if we need to expand directory */ |
michael@0 | 1018 | if (new_segnum >= hashp->DSIZE) { |
michael@0 | 1019 | /* Reallocate directory */ |
michael@0 | 1020 | dirsize = hashp->DSIZE * sizeof(SEGMENT *); |
michael@0 | 1021 | if (!hash_realloc(&hashp->dir, dirsize, dirsize << 1)) |
michael@0 | 1022 | return (-1); |
michael@0 | 1023 | hashp->DSIZE = dirsize << 1; |
michael@0 | 1024 | } |
michael@0 | 1025 | if ((hashp->dir[new_segnum] = |
michael@0 | 1026 | (SEGMENT)calloc((size_t)hashp->SGSIZE, sizeof(SEGMENT))) == NULL) |
michael@0 | 1027 | return (-1); |
michael@0 | 1028 | hashp->exsegs++; |
michael@0 | 1029 | hashp->nsegs++; |
michael@0 | 1030 | } |
michael@0 | 1031 | /* |
michael@0 | 1032 | * If the split point is increasing (MAX_BUCKET's log base 2 |
michael@0 | 1033 | * * increases), we need to copy the current contents of the spare |
michael@0 | 1034 | * split bucket to the next bucket. |
michael@0 | 1035 | */ |
michael@0 | 1036 | spare_ndx = __log2((uint32)(hashp->MAX_BUCKET + 1)); |
michael@0 | 1037 | if (spare_ndx > hashp->OVFL_POINT) { |
michael@0 | 1038 | hashp->SPARES[spare_ndx] = hashp->SPARES[hashp->OVFL_POINT]; |
michael@0 | 1039 | hashp->OVFL_POINT = spare_ndx; |
michael@0 | 1040 | } |
michael@0 | 1041 | |
michael@0 | 1042 | if (new_bucket > (uint32)hashp->HIGH_MASK) { |
michael@0 | 1043 | /* Starting a new doubling */ |
michael@0 | 1044 | hashp->LOW_MASK = hashp->HIGH_MASK; |
michael@0 | 1045 | hashp->HIGH_MASK = new_bucket | hashp->LOW_MASK; |
michael@0 | 1046 | } |
michael@0 | 1047 | /* Relocate records to the new bucket */ |
michael@0 | 1048 | return (__split_page(hashp, old_bucket, new_bucket)); |
michael@0 | 1049 | } |
michael@0 | 1050 | |
michael@0 | 1051 | /* |
michael@0 | 1052 | * If realloc guarantees that the pointer is not destroyed if the realloc |
michael@0 | 1053 | * fails, then this routine can go away. |
michael@0 | 1054 | */ |
michael@0 | 1055 | static void * |
michael@0 | 1056 | hash_realloc( |
michael@0 | 1057 | SEGMENT **p_ptr, |
michael@0 | 1058 | size_t oldsize, size_t newsize) |
michael@0 | 1059 | { |
michael@0 | 1060 | register void *p; |
michael@0 | 1061 | |
michael@0 | 1062 | if ((p = malloc(newsize))) { |
michael@0 | 1063 | memmove(p, *p_ptr, oldsize); |
michael@0 | 1064 | memset((char *)p + oldsize, 0, newsize - oldsize); |
michael@0 | 1065 | free(*p_ptr); |
michael@0 | 1066 | *p_ptr = (SEGMENT *)p; |
michael@0 | 1067 | } |
michael@0 | 1068 | return (p); |
michael@0 | 1069 | } |
michael@0 | 1070 | |
michael@0 | 1071 | extern uint32 |
michael@0 | 1072 | __call_hash(HTAB *hashp, char *k, size_t len) |
michael@0 | 1073 | { |
michael@0 | 1074 | uint32 n, bucket; |
michael@0 | 1075 | |
michael@0 | 1076 | n = hashp->hash(k, len); |
michael@0 | 1077 | bucket = n & hashp->HIGH_MASK; |
michael@0 | 1078 | if (bucket > (uint32)hashp->MAX_BUCKET) |
michael@0 | 1079 | bucket = bucket & hashp->LOW_MASK; |
michael@0 | 1080 | return (bucket); |
michael@0 | 1081 | } |
michael@0 | 1082 | |
michael@0 | 1083 | /* |
michael@0 | 1084 | * Allocate segment table. On error, set errno. |
michael@0 | 1085 | * |
michael@0 | 1086 | * Returns 0 on success |
michael@0 | 1087 | */ |
michael@0 | 1088 | static int |
michael@0 | 1089 | alloc_segs( |
michael@0 | 1090 | HTAB *hashp, |
michael@0 | 1091 | int nsegs) |
michael@0 | 1092 | { |
michael@0 | 1093 | register int i; |
michael@0 | 1094 | register SEGMENT store; |
michael@0 | 1095 | |
michael@0 | 1096 | if ((hashp->dir = |
michael@0 | 1097 | (SEGMENT *)calloc((size_t)hashp->DSIZE, sizeof(SEGMENT *))) == NULL) { |
michael@0 | 1098 | errno = ENOMEM; |
michael@0 | 1099 | return (-1); |
michael@0 | 1100 | } |
michael@0 | 1101 | /* Allocate segments */ |
michael@0 | 1102 | if ((store = |
michael@0 | 1103 | (SEGMENT)calloc((size_t)nsegs << hashp->SSHIFT, sizeof(SEGMENT))) == NULL) { |
michael@0 | 1104 | errno = ENOMEM; |
michael@0 | 1105 | return (-1); |
michael@0 | 1106 | } |
michael@0 | 1107 | for (i = 0; i < nsegs; i++, hashp->nsegs++) |
michael@0 | 1108 | hashp->dir[i] = &store[i << hashp->SSHIFT]; |
michael@0 | 1109 | return (0); |
michael@0 | 1110 | } |
michael@0 | 1111 | |
michael@0 | 1112 | #if BYTE_ORDER == LITTLE_ENDIAN |
michael@0 | 1113 | /* |
michael@0 | 1114 | * Hashp->hdr needs to be byteswapped. |
michael@0 | 1115 | */ |
michael@0 | 1116 | static void |
michael@0 | 1117 | swap_header_copy( |
michael@0 | 1118 | HASHHDR *srcp, HASHHDR *destp) |
michael@0 | 1119 | { |
michael@0 | 1120 | int i; |
michael@0 | 1121 | |
michael@0 | 1122 | P_32_COPY(srcp->magic, destp->magic); |
michael@0 | 1123 | P_32_COPY(srcp->version, destp->version); |
michael@0 | 1124 | P_32_COPY(srcp->lorder, destp->lorder); |
michael@0 | 1125 | P_32_COPY(srcp->bsize, destp->bsize); |
michael@0 | 1126 | P_32_COPY(srcp->bshift, destp->bshift); |
michael@0 | 1127 | P_32_COPY(srcp->dsize, destp->dsize); |
michael@0 | 1128 | P_32_COPY(srcp->ssize, destp->ssize); |
michael@0 | 1129 | P_32_COPY(srcp->sshift, destp->sshift); |
michael@0 | 1130 | P_32_COPY(srcp->ovfl_point, destp->ovfl_point); |
michael@0 | 1131 | P_32_COPY(srcp->last_freed, destp->last_freed); |
michael@0 | 1132 | P_32_COPY(srcp->max_bucket, destp->max_bucket); |
michael@0 | 1133 | P_32_COPY(srcp->high_mask, destp->high_mask); |
michael@0 | 1134 | P_32_COPY(srcp->low_mask, destp->low_mask); |
michael@0 | 1135 | P_32_COPY(srcp->ffactor, destp->ffactor); |
michael@0 | 1136 | P_32_COPY(srcp->nkeys, destp->nkeys); |
michael@0 | 1137 | P_32_COPY(srcp->hdrpages, destp->hdrpages); |
michael@0 | 1138 | P_32_COPY(srcp->h_charkey, destp->h_charkey); |
michael@0 | 1139 | for (i = 0; i < NCACHED; i++) { |
michael@0 | 1140 | P_32_COPY(srcp->spares[i], destp->spares[i]); |
michael@0 | 1141 | P_16_COPY(srcp->bitmaps[i], destp->bitmaps[i]); |
michael@0 | 1142 | } |
michael@0 | 1143 | } |
michael@0 | 1144 | |
michael@0 | 1145 | static void |
michael@0 | 1146 | swap_header(HTAB *hashp) |
michael@0 | 1147 | { |
michael@0 | 1148 | HASHHDR *hdrp; |
michael@0 | 1149 | int i; |
michael@0 | 1150 | |
michael@0 | 1151 | hdrp = &hashp->hdr; |
michael@0 | 1152 | |
michael@0 | 1153 | M_32_SWAP(hdrp->magic); |
michael@0 | 1154 | M_32_SWAP(hdrp->version); |
michael@0 | 1155 | M_32_SWAP(hdrp->lorder); |
michael@0 | 1156 | M_32_SWAP(hdrp->bsize); |
michael@0 | 1157 | M_32_SWAP(hdrp->bshift); |
michael@0 | 1158 | M_32_SWAP(hdrp->dsize); |
michael@0 | 1159 | M_32_SWAP(hdrp->ssize); |
michael@0 | 1160 | M_32_SWAP(hdrp->sshift); |
michael@0 | 1161 | M_32_SWAP(hdrp->ovfl_point); |
michael@0 | 1162 | M_32_SWAP(hdrp->last_freed); |
michael@0 | 1163 | M_32_SWAP(hdrp->max_bucket); |
michael@0 | 1164 | M_32_SWAP(hdrp->high_mask); |
michael@0 | 1165 | M_32_SWAP(hdrp->low_mask); |
michael@0 | 1166 | M_32_SWAP(hdrp->ffactor); |
michael@0 | 1167 | M_32_SWAP(hdrp->nkeys); |
michael@0 | 1168 | M_32_SWAP(hdrp->hdrpages); |
michael@0 | 1169 | M_32_SWAP(hdrp->h_charkey); |
michael@0 | 1170 | for (i = 0; i < NCACHED; i++) { |
michael@0 | 1171 | M_32_SWAP(hdrp->spares[i]); |
michael@0 | 1172 | M_16_SWAP(hdrp->bitmaps[i]); |
michael@0 | 1173 | } |
michael@0 | 1174 | } |
michael@0 | 1175 | #endif |