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