1 |
/* $MidnightBSD$ */ |
2 |
/*- |
3 |
* Copyright (c) 1990, 1993, 1994 |
4 |
* The Regents of the University of California. All rights reserved. |
5 |
* |
6 |
* This code is derived from software contributed to Berkeley by |
7 |
* Margo Seltzer. |
8 |
* |
9 |
* Redistribution and use in source and binary forms, with or without |
10 |
* modification, are permitted provided that the following conditions |
11 |
* are met: |
12 |
* 1. Redistributions of source code must retain the above copyright |
13 |
* notice, this list of conditions and the following disclaimer. |
14 |
* 2. Redistributions in binary form must reproduce the above copyright |
15 |
* notice, this list of conditions and the following disclaimer in the |
16 |
* documentation and/or other materials provided with the distribution. |
17 |
* 4. Neither the name of the University nor the names of its contributors |
18 |
* may be used to endorse or promote products derived from this software |
19 |
* without specific prior written permission. |
20 |
* |
21 |
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
22 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
23 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
24 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
25 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
26 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
27 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
28 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
29 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
30 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
31 |
* SUCH DAMAGE. |
32 |
*/ |
33 |
|
34 |
#if defined(LIBC_SCCS) && !defined(lint) |
35 |
static char sccsid[] = "@(#)hash_page.c 8.7 (Berkeley) 8/16/94"; |
36 |
#endif /* LIBC_SCCS and not lint */ |
37 |
#include <sys/cdefs.h> |
38 |
__FBSDID("$FreeBSD: stable/10/lib/libc/db/hash/hash_page.c 313532 2017-02-10 06:34:52Z ngie $"); |
39 |
|
40 |
/* |
41 |
* PACKAGE: hashing |
42 |
* |
43 |
* DESCRIPTION: |
44 |
* Page manipulation for hashing package. |
45 |
* |
46 |
* ROUTINES: |
47 |
* |
48 |
* External |
49 |
* __get_page |
50 |
* __add_ovflpage |
51 |
* Internal |
52 |
* overflow_page |
53 |
* open_temp |
54 |
*/ |
55 |
|
56 |
#include "namespace.h" |
57 |
#include <sys/param.h> |
58 |
|
59 |
#include <errno.h> |
60 |
#include <fcntl.h> |
61 |
#include <signal.h> |
62 |
#include <stdio.h> |
63 |
#include <stdlib.h> |
64 |
#include <string.h> |
65 |
#include <unistd.h> |
66 |
#ifdef DEBUG |
67 |
#include <assert.h> |
68 |
#endif |
69 |
#include "un-namespace.h" |
70 |
#include "libc_private.h" |
71 |
|
72 |
#include <db.h> |
73 |
#include "hash.h" |
74 |
#include "page.h" |
75 |
#include "extern.h" |
76 |
|
77 |
static u_int32_t *fetch_bitmap(HTAB *, int); |
78 |
static u_int32_t first_free(u_int32_t); |
79 |
static int open_temp(HTAB *); |
80 |
static u_int16_t overflow_page(HTAB *); |
81 |
static void putpair(char *, const DBT *, const DBT *); |
82 |
static void squeeze_key(u_int16_t *, const DBT *, const DBT *); |
83 |
static int ugly_split(HTAB *, u_int32_t, BUFHEAD *, BUFHEAD *, int, int); |
84 |
|
85 |
#define PAGE_INIT(P) { \ |
86 |
((u_int16_t *)(P))[0] = 0; \ |
87 |
((u_int16_t *)(P))[1] = hashp->BSIZE - 3 * sizeof(u_int16_t); \ |
88 |
((u_int16_t *)(P))[2] = hashp->BSIZE; \ |
89 |
} |
90 |
|
91 |
/* |
92 |
* This is called AFTER we have verified that there is room on the page for |
93 |
* the pair (PAIRFITS has returned true) so we go right ahead and start moving |
94 |
* stuff on. |
95 |
*/ |
96 |
static void |
97 |
putpair(char *p, const DBT *key, const DBT *val) |
98 |
{ |
99 |
u_int16_t *bp, n, off; |
100 |
|
101 |
bp = (u_int16_t *)p; |
102 |
|
103 |
/* Enter the key first. */ |
104 |
n = bp[0]; |
105 |
|
106 |
off = OFFSET(bp) - key->size; |
107 |
memmove(p + off, key->data, key->size); |
108 |
bp[++n] = off; |
109 |
|
110 |
/* Now the data. */ |
111 |
off -= val->size; |
112 |
memmove(p + off, val->data, val->size); |
113 |
bp[++n] = off; |
114 |
|
115 |
/* Adjust page info. */ |
116 |
bp[0] = n; |
117 |
bp[n + 1] = off - ((n + 3) * sizeof(u_int16_t)); |
118 |
bp[n + 2] = off; |
119 |
} |
120 |
|
121 |
/* |
122 |
* Returns: |
123 |
* 0 OK |
124 |
* -1 error |
125 |
*/ |
126 |
int |
127 |
__delpair(HTAB *hashp, BUFHEAD *bufp, int ndx) |
128 |
{ |
129 |
u_int16_t *bp, newoff, pairlen; |
130 |
int n; |
131 |
|
132 |
bp = (u_int16_t *)bufp->page; |
133 |
n = bp[0]; |
134 |
|
135 |
if (bp[ndx + 1] < REAL_KEY) |
136 |
return (__big_delete(hashp, bufp)); |
137 |
if (ndx != 1) |
138 |
newoff = bp[ndx - 1]; |
139 |
else |
140 |
newoff = hashp->BSIZE; |
141 |
pairlen = newoff - bp[ndx + 1]; |
142 |
|
143 |
if (ndx != (n - 1)) { |
144 |
/* Hard Case -- need to shuffle keys */ |
145 |
int i; |
146 |
char *src = bufp->page + (int)OFFSET(bp); |
147 |
char *dst = src + (int)pairlen; |
148 |
memmove(dst, src, bp[ndx + 1] - OFFSET(bp)); |
149 |
|
150 |
/* Now adjust the pointers */ |
151 |
for (i = ndx + 2; i <= n; i += 2) { |
152 |
if (bp[i + 1] == OVFLPAGE) { |
153 |
bp[i - 2] = bp[i]; |
154 |
bp[i - 1] = bp[i + 1]; |
155 |
} else { |
156 |
bp[i - 2] = bp[i] + pairlen; |
157 |
bp[i - 1] = bp[i + 1] + pairlen; |
158 |
} |
159 |
} |
160 |
if (ndx == hashp->cndx) { |
161 |
/* |
162 |
* We just removed pair we were "pointing" to. |
163 |
* By moving back the cndx we ensure subsequent |
164 |
* hash_seq() calls won't skip over any entries. |
165 |
*/ |
166 |
hashp->cndx -= 2; |
167 |
} |
168 |
} |
169 |
/* Finally adjust the page data */ |
170 |
bp[n] = OFFSET(bp) + pairlen; |
171 |
bp[n - 1] = bp[n + 1] + pairlen + 2 * sizeof(u_int16_t); |
172 |
bp[0] = n - 2; |
173 |
hashp->NKEYS--; |
174 |
|
175 |
bufp->flags |= BUF_MOD; |
176 |
return (0); |
177 |
} |
178 |
/* |
179 |
* Returns: |
180 |
* 0 ==> OK |
181 |
* -1 ==> Error |
182 |
*/ |
183 |
int |
184 |
__split_page(HTAB *hashp, u_int32_t obucket, u_int32_t nbucket) |
185 |
{ |
186 |
BUFHEAD *new_bufp, *old_bufp; |
187 |
u_int16_t *ino; |
188 |
char *np; |
189 |
DBT key, val; |
190 |
int n, ndx, retval; |
191 |
u_int16_t copyto, diff, off, moved; |
192 |
char *op; |
193 |
|
194 |
copyto = (u_int16_t)hashp->BSIZE; |
195 |
off = (u_int16_t)hashp->BSIZE; |
196 |
old_bufp = __get_buf(hashp, obucket, NULL, 0); |
197 |
if (old_bufp == NULL) |
198 |
return (-1); |
199 |
new_bufp = __get_buf(hashp, nbucket, NULL, 0); |
200 |
if (new_bufp == NULL) |
201 |
return (-1); |
202 |
|
203 |
old_bufp->flags |= (BUF_MOD | BUF_PIN); |
204 |
new_bufp->flags |= (BUF_MOD | BUF_PIN); |
205 |
|
206 |
ino = (u_int16_t *)(op = old_bufp->page); |
207 |
np = new_bufp->page; |
208 |
|
209 |
moved = 0; |
210 |
|
211 |
for (n = 1, ndx = 1; n < ino[0]; n += 2) { |
212 |
if (ino[n + 1] < REAL_KEY) { |
213 |
retval = ugly_split(hashp, obucket, old_bufp, new_bufp, |
214 |
(int)copyto, (int)moved); |
215 |
old_bufp->flags &= ~BUF_PIN; |
216 |
new_bufp->flags &= ~BUF_PIN; |
217 |
return (retval); |
218 |
|
219 |
} |
220 |
key.data = (u_char *)op + ino[n]; |
221 |
key.size = off - ino[n]; |
222 |
|
223 |
if (__call_hash(hashp, key.data, key.size) == obucket) { |
224 |
/* Don't switch page */ |
225 |
diff = copyto - off; |
226 |
if (diff) { |
227 |
copyto = ino[n + 1] + diff; |
228 |
memmove(op + copyto, op + ino[n + 1], |
229 |
off - ino[n + 1]); |
230 |
ino[ndx] = copyto + ino[n] - ino[n + 1]; |
231 |
ino[ndx + 1] = copyto; |
232 |
} else |
233 |
copyto = ino[n + 1]; |
234 |
ndx += 2; |
235 |
} else { |
236 |
/* Switch page */ |
237 |
val.data = (u_char *)op + ino[n + 1]; |
238 |
val.size = ino[n] - ino[n + 1]; |
239 |
putpair(np, &key, &val); |
240 |
moved += 2; |
241 |
} |
242 |
|
243 |
off = ino[n + 1]; |
244 |
} |
245 |
|
246 |
/* Now clean up the page */ |
247 |
ino[0] -= moved; |
248 |
FREESPACE(ino) = copyto - sizeof(u_int16_t) * (ino[0] + 3); |
249 |
OFFSET(ino) = copyto; |
250 |
|
251 |
#ifdef DEBUG3 |
252 |
(void)fprintf(stderr, "split %d/%d\n", |
253 |
((u_int16_t *)np)[0] / 2, |
254 |
((u_int16_t *)op)[0] / 2); |
255 |
#endif |
256 |
/* unpin both pages */ |
257 |
old_bufp->flags &= ~BUF_PIN; |
258 |
new_bufp->flags &= ~BUF_PIN; |
259 |
return (0); |
260 |
} |
261 |
|
262 |
/* |
263 |
* Called when we encounter an overflow or big key/data page during split |
264 |
* handling. This is special cased since we have to begin checking whether |
265 |
* the key/data pairs fit on their respective pages and because we may need |
266 |
* overflow pages for both the old and new pages. |
267 |
* |
268 |
* The first page might be a page with regular key/data pairs in which case |
269 |
* we have a regular overflow condition and just need to go on to the next |
270 |
* page or it might be a big key/data pair in which case we need to fix the |
271 |
* big key/data pair. |
272 |
* |
273 |
* Returns: |
274 |
* 0 ==> success |
275 |
* -1 ==> failure |
276 |
*/ |
277 |
static int |
278 |
ugly_split(HTAB *hashp, |
279 |
u_int32_t obucket, /* Same as __split_page. */ |
280 |
BUFHEAD *old_bufp, |
281 |
BUFHEAD *new_bufp, |
282 |
int copyto, /* First byte on page which contains key/data values. */ |
283 |
int moved) /* Number of pairs moved to new page. */ |
284 |
{ |
285 |
BUFHEAD *bufp; /* Buffer header for ino */ |
286 |
u_int16_t *ino; /* Page keys come off of */ |
287 |
u_int16_t *np; /* New page */ |
288 |
u_int16_t *op; /* Page keys go on to if they aren't moving */ |
289 |
|
290 |
BUFHEAD *last_bfp; /* Last buf header OVFL needing to be freed */ |
291 |
DBT key, val; |
292 |
SPLIT_RETURN ret; |
293 |
u_int16_t n, off, ov_addr, scopyto; |
294 |
char *cino; /* Character value of ino */ |
295 |
|
296 |
bufp = old_bufp; |
297 |
ino = (u_int16_t *)old_bufp->page; |
298 |
np = (u_int16_t *)new_bufp->page; |
299 |
op = (u_int16_t *)old_bufp->page; |
300 |
last_bfp = NULL; |
301 |
scopyto = (u_int16_t)copyto; /* ANSI */ |
302 |
|
303 |
n = ino[0] - 1; |
304 |
while (n < ino[0]) { |
305 |
if (ino[2] < REAL_KEY && ino[2] != OVFLPAGE) { |
306 |
if (__big_split(hashp, old_bufp, |
307 |
new_bufp, bufp, bufp->addr, obucket, &ret)) |
308 |
return (-1); |
309 |
old_bufp = ret.oldp; |
310 |
if (!old_bufp) |
311 |
return (-1); |
312 |
op = (u_int16_t *)old_bufp->page; |
313 |
new_bufp = ret.newp; |
314 |
if (!new_bufp) |
315 |
return (-1); |
316 |
np = (u_int16_t *)new_bufp->page; |
317 |
bufp = ret.nextp; |
318 |
if (!bufp) |
319 |
return (0); |
320 |
cino = (char *)bufp->page; |
321 |
ino = (u_int16_t *)cino; |
322 |
last_bfp = ret.nextp; |
323 |
} else if (ino[n + 1] == OVFLPAGE) { |
324 |
ov_addr = ino[n]; |
325 |
/* |
326 |
* Fix up the old page -- the extra 2 are the fields |
327 |
* which contained the overflow information. |
328 |
*/ |
329 |
ino[0] -= (moved + 2); |
330 |
FREESPACE(ino) = |
331 |
scopyto - sizeof(u_int16_t) * (ino[0] + 3); |
332 |
OFFSET(ino) = scopyto; |
333 |
|
334 |
bufp = __get_buf(hashp, ov_addr, bufp, 0); |
335 |
if (!bufp) |
336 |
return (-1); |
337 |
|
338 |
ino = (u_int16_t *)bufp->page; |
339 |
n = 1; |
340 |
scopyto = hashp->BSIZE; |
341 |
moved = 0; |
342 |
|
343 |
if (last_bfp) |
344 |
__free_ovflpage(hashp, last_bfp); |
345 |
last_bfp = bufp; |
346 |
} |
347 |
/* Move regular sized pairs of there are any */ |
348 |
off = hashp->BSIZE; |
349 |
for (n = 1; (n < ino[0]) && (ino[n + 1] >= REAL_KEY); n += 2) { |
350 |
cino = (char *)ino; |
351 |
key.data = (u_char *)cino + ino[n]; |
352 |
key.size = off - ino[n]; |
353 |
val.data = (u_char *)cino + ino[n + 1]; |
354 |
val.size = ino[n] - ino[n + 1]; |
355 |
off = ino[n + 1]; |
356 |
|
357 |
if (__call_hash(hashp, key.data, key.size) == obucket) { |
358 |
/* Keep on old page */ |
359 |
if (PAIRFITS(op, (&key), (&val))) |
360 |
putpair((char *)op, &key, &val); |
361 |
else { |
362 |
old_bufp = |
363 |
__add_ovflpage(hashp, old_bufp); |
364 |
if (!old_bufp) |
365 |
return (-1); |
366 |
op = (u_int16_t *)old_bufp->page; |
367 |
putpair((char *)op, &key, &val); |
368 |
} |
369 |
old_bufp->flags |= BUF_MOD; |
370 |
} else { |
371 |
/* Move to new page */ |
372 |
if (PAIRFITS(np, (&key), (&val))) |
373 |
putpair((char *)np, &key, &val); |
374 |
else { |
375 |
new_bufp = |
376 |
__add_ovflpage(hashp, new_bufp); |
377 |
if (!new_bufp) |
378 |
return (-1); |
379 |
np = (u_int16_t *)new_bufp->page; |
380 |
putpair((char *)np, &key, &val); |
381 |
} |
382 |
new_bufp->flags |= BUF_MOD; |
383 |
} |
384 |
} |
385 |
} |
386 |
if (last_bfp) |
387 |
__free_ovflpage(hashp, last_bfp); |
388 |
return (0); |
389 |
} |
390 |
|
391 |
/* |
392 |
* Add the given pair to the page |
393 |
* |
394 |
* Returns: |
395 |
* 0 ==> OK |
396 |
* 1 ==> failure |
397 |
*/ |
398 |
int |
399 |
__addel(HTAB *hashp, BUFHEAD *bufp, const DBT *key, const DBT *val) |
400 |
{ |
401 |
u_int16_t *bp, *sop; |
402 |
int do_expand; |
403 |
|
404 |
bp = (u_int16_t *)bufp->page; |
405 |
do_expand = 0; |
406 |
while (bp[0] && (bp[2] < REAL_KEY || bp[bp[0]] < REAL_KEY)) |
407 |
/* Exception case */ |
408 |
if (bp[2] == FULL_KEY_DATA && bp[0] == 2) |
409 |
/* This is the last page of a big key/data pair |
410 |
and we need to add another page */ |
411 |
break; |
412 |
else if (bp[2] < REAL_KEY && bp[bp[0]] != OVFLPAGE) { |
413 |
bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0); |
414 |
if (!bufp) |
415 |
return (-1); |
416 |
bp = (u_int16_t *)bufp->page; |
417 |
} else if (bp[bp[0]] != OVFLPAGE) { |
418 |
/* Short key/data pairs, no more pages */ |
419 |
break; |
420 |
} else { |
421 |
/* Try to squeeze key on this page */ |
422 |
if (bp[2] >= REAL_KEY && |
423 |
FREESPACE(bp) >= PAIRSIZE(key, val)) { |
424 |
squeeze_key(bp, key, val); |
425 |
goto stats; |
426 |
} else { |
427 |
bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0); |
428 |
if (!bufp) |
429 |
return (-1); |
430 |
bp = (u_int16_t *)bufp->page; |
431 |
} |
432 |
} |
433 |
|
434 |
if (PAIRFITS(bp, key, val)) |
435 |
putpair(bufp->page, key, val); |
436 |
else { |
437 |
do_expand = 1; |
438 |
bufp = __add_ovflpage(hashp, bufp); |
439 |
if (!bufp) |
440 |
return (-1); |
441 |
sop = (u_int16_t *)bufp->page; |
442 |
|
443 |
if (PAIRFITS(sop, key, val)) |
444 |
putpair((char *)sop, key, val); |
445 |
else |
446 |
if (__big_insert(hashp, bufp, key, val)) |
447 |
return (-1); |
448 |
} |
449 |
stats: |
450 |
bufp->flags |= BUF_MOD; |
451 |
/* |
452 |
* If the average number of keys per bucket exceeds the fill factor, |
453 |
* expand the table. |
454 |
*/ |
455 |
hashp->NKEYS++; |
456 |
if (do_expand || |
457 |
(hashp->NKEYS / (hashp->MAX_BUCKET + 1) > hashp->FFACTOR)) |
458 |
return (__expand_table(hashp)); |
459 |
return (0); |
460 |
} |
461 |
|
462 |
/* |
463 |
* |
464 |
* Returns: |
465 |
* pointer on success |
466 |
* NULL on error |
467 |
*/ |
468 |
BUFHEAD * |
469 |
__add_ovflpage(HTAB *hashp, BUFHEAD *bufp) |
470 |
{ |
471 |
u_int16_t *sp, ndx, ovfl_num; |
472 |
#ifdef DEBUG1 |
473 |
int tmp1, tmp2; |
474 |
#endif |
475 |
sp = (u_int16_t *)bufp->page; |
476 |
|
477 |
/* Check if we are dynamically determining the fill factor */ |
478 |
if (hashp->FFACTOR == DEF_FFACTOR) { |
479 |
hashp->FFACTOR = sp[0] >> 1; |
480 |
if (hashp->FFACTOR < MIN_FFACTOR) |
481 |
hashp->FFACTOR = MIN_FFACTOR; |
482 |
} |
483 |
bufp->flags |= BUF_MOD; |
484 |
ovfl_num = overflow_page(hashp); |
485 |
#ifdef DEBUG1 |
486 |
tmp1 = bufp->addr; |
487 |
tmp2 = bufp->ovfl ? bufp->ovfl->addr : 0; |
488 |
#endif |
489 |
if (!ovfl_num || !(bufp->ovfl = __get_buf(hashp, ovfl_num, bufp, 1))) |
490 |
return (NULL); |
491 |
bufp->ovfl->flags |= BUF_MOD; |
492 |
#ifdef DEBUG1 |
493 |
(void)fprintf(stderr, "ADDOVFLPAGE: %d->ovfl was %d is now %d\n", |
494 |
tmp1, tmp2, bufp->ovfl->addr); |
495 |
#endif |
496 |
ndx = sp[0]; |
497 |
/* |
498 |
* Since a pair is allocated on a page only if there's room to add |
499 |
* an overflow page, we know that the OVFL information will fit on |
500 |
* the page. |
501 |
*/ |
502 |
sp[ndx + 4] = OFFSET(sp); |
503 |
sp[ndx + 3] = FREESPACE(sp) - OVFLSIZE; |
504 |
sp[ndx + 1] = ovfl_num; |
505 |
sp[ndx + 2] = OVFLPAGE; |
506 |
sp[0] = ndx + 2; |
507 |
#ifdef HASH_STATISTICS |
508 |
hash_overflows++; |
509 |
#endif |
510 |
return (bufp->ovfl); |
511 |
} |
512 |
|
513 |
/* |
514 |
* Returns: |
515 |
* 0 indicates SUCCESS |
516 |
* -1 indicates FAILURE |
517 |
*/ |
518 |
int |
519 |
__get_page(HTAB *hashp, char *p, u_int32_t bucket, int is_bucket, int is_disk, |
520 |
int is_bitmap) |
521 |
{ |
522 |
int fd, page, size, rsize; |
523 |
u_int16_t *bp; |
524 |
|
525 |
fd = hashp->fp; |
526 |
size = hashp->BSIZE; |
527 |
|
528 |
if ((fd == -1) || !is_disk) { |
529 |
PAGE_INIT(p); |
530 |
return (0); |
531 |
} |
532 |
if (is_bucket) |
533 |
page = BUCKET_TO_PAGE(bucket); |
534 |
else |
535 |
page = OADDR_TO_PAGE(bucket); |
536 |
if ((rsize = pread(fd, p, size, (off_t)page << hashp->BSHIFT)) == -1) |
537 |
return (-1); |
538 |
bp = (u_int16_t *)p; |
539 |
if (!rsize) |
540 |
bp[0] = 0; /* We hit the EOF, so initialize a new page */ |
541 |
else |
542 |
if (rsize != size) { |
543 |
errno = EFTYPE; |
544 |
return (-1); |
545 |
} |
546 |
if (!is_bitmap && !bp[0]) { |
547 |
PAGE_INIT(p); |
548 |
} else |
549 |
if (hashp->LORDER != BYTE_ORDER) { |
550 |
int i, max; |
551 |
|
552 |
if (is_bitmap) { |
553 |
max = hashp->BSIZE >> 2; /* divide by 4 */ |
554 |
for (i = 0; i < max; i++) |
555 |
M_32_SWAP(((int *)p)[i]); |
556 |
} else { |
557 |
M_16_SWAP(bp[0]); |
558 |
max = bp[0] + 2; |
559 |
for (i = 1; i <= max; i++) |
560 |
M_16_SWAP(bp[i]); |
561 |
} |
562 |
} |
563 |
return (0); |
564 |
} |
565 |
|
566 |
/* |
567 |
* Write page p to disk |
568 |
* |
569 |
* Returns: |
570 |
* 0 ==> OK |
571 |
* -1 ==>failure |
572 |
*/ |
573 |
int |
574 |
__put_page(HTAB *hashp, char *p, u_int32_t bucket, int is_bucket, int is_bitmap) |
575 |
{ |
576 |
int fd, page, size; |
577 |
ssize_t wsize; |
578 |
char pbuf[MAX_BSIZE]; |
579 |
|
580 |
size = hashp->BSIZE; |
581 |
if ((hashp->fp == -1) && open_temp(hashp)) |
582 |
return (-1); |
583 |
fd = hashp->fp; |
584 |
|
585 |
if (hashp->LORDER != BYTE_ORDER) { |
586 |
int i, max; |
587 |
|
588 |
memcpy(pbuf, p, size); |
589 |
if (is_bitmap) { |
590 |
max = hashp->BSIZE >> 2; /* divide by 4 */ |
591 |
for (i = 0; i < max; i++) |
592 |
M_32_SWAP(((int *)pbuf)[i]); |
593 |
} else { |
594 |
uint16_t *bp = (uint16_t *)(void *)pbuf; |
595 |
max = bp[0] + 2; |
596 |
for (i = 0; i <= max; i++) |
597 |
M_16_SWAP(bp[i]); |
598 |
} |
599 |
p = pbuf; |
600 |
} |
601 |
if (is_bucket) |
602 |
page = BUCKET_TO_PAGE(bucket); |
603 |
else |
604 |
page = OADDR_TO_PAGE(bucket); |
605 |
if ((wsize = pwrite(fd, p, size, (off_t)page << hashp->BSHIFT)) == -1) |
606 |
/* Errno is set */ |
607 |
return (-1); |
608 |
if (wsize != size) { |
609 |
errno = EFTYPE; |
610 |
return (-1); |
611 |
} |
612 |
return (0); |
613 |
} |
614 |
|
615 |
#define BYTE_MASK ((1 << INT_BYTE_SHIFT) -1) |
616 |
/* |
617 |
* Initialize a new bitmap page. Bitmap pages are left in memory |
618 |
* once they are read in. |
619 |
*/ |
620 |
int |
621 |
__ibitmap(HTAB *hashp, int pnum, int nbits, int ndx) |
622 |
{ |
623 |
u_int32_t *ip; |
624 |
int clearbytes, clearints; |
625 |
|
626 |
if ((ip = (u_int32_t *)malloc(hashp->BSIZE)) == NULL) |
627 |
return (1); |
628 |
hashp->nmaps++; |
629 |
clearints = ((nbits - 1) >> INT_BYTE_SHIFT) + 1; |
630 |
clearbytes = clearints << INT_TO_BYTE; |
631 |
(void)memset((char *)ip, 0, clearbytes); |
632 |
(void)memset(((char *)ip) + clearbytes, 0xFF, |
633 |
hashp->BSIZE - clearbytes); |
634 |
ip[clearints - 1] = ALL_SET << (nbits & BYTE_MASK); |
635 |
SETBIT(ip, 0); |
636 |
hashp->BITMAPS[ndx] = (u_int16_t)pnum; |
637 |
hashp->mapp[ndx] = ip; |
638 |
return (0); |
639 |
} |
640 |
|
641 |
static u_int32_t |
642 |
first_free(u_int32_t map) |
643 |
{ |
644 |
u_int32_t i, mask; |
645 |
|
646 |
mask = 0x1; |
647 |
for (i = 0; i < BITS_PER_MAP; i++) { |
648 |
if (!(mask & map)) |
649 |
return (i); |
650 |
mask = mask << 1; |
651 |
} |
652 |
return (i); |
653 |
} |
654 |
|
655 |
static u_int16_t |
656 |
overflow_page(HTAB *hashp) |
657 |
{ |
658 |
u_int32_t *freep; |
659 |
int max_free, offset, splitnum; |
660 |
u_int16_t addr; |
661 |
int bit, first_page, free_bit, free_page, i, in_use_bits, j; |
662 |
#ifdef DEBUG2 |
663 |
int tmp1, tmp2; |
664 |
#endif |
665 |
splitnum = hashp->OVFL_POINT; |
666 |
max_free = hashp->SPARES[splitnum]; |
667 |
|
668 |
free_page = (max_free - 1) >> (hashp->BSHIFT + BYTE_SHIFT); |
669 |
free_bit = (max_free - 1) & ((hashp->BSIZE << BYTE_SHIFT) - 1); |
670 |
|
671 |
/* Look through all the free maps to find the first free block */ |
672 |
first_page = hashp->LAST_FREED >>(hashp->BSHIFT + BYTE_SHIFT); |
673 |
for ( i = first_page; i <= free_page; i++ ) { |
674 |
if (!(freep = (u_int32_t *)hashp->mapp[i]) && |
675 |
!(freep = fetch_bitmap(hashp, i))) |
676 |
return (0); |
677 |
if (i == free_page) |
678 |
in_use_bits = free_bit; |
679 |
else |
680 |
in_use_bits = (hashp->BSIZE << BYTE_SHIFT) - 1; |
681 |
|
682 |
if (i == first_page) { |
683 |
bit = hashp->LAST_FREED & |
684 |
((hashp->BSIZE << BYTE_SHIFT) - 1); |
685 |
j = bit / BITS_PER_MAP; |
686 |
bit = bit & ~(BITS_PER_MAP - 1); |
687 |
} else { |
688 |
bit = 0; |
689 |
j = 0; |
690 |
} |
691 |
for (; bit <= in_use_bits; j++, bit += BITS_PER_MAP) |
692 |
if (freep[j] != ALL_SET) |
693 |
goto found; |
694 |
} |
695 |
|
696 |
/* No Free Page Found */ |
697 |
hashp->LAST_FREED = hashp->SPARES[splitnum]; |
698 |
hashp->SPARES[splitnum]++; |
699 |
offset = hashp->SPARES[splitnum] - |
700 |
(splitnum ? hashp->SPARES[splitnum - 1] : 0); |
701 |
|
702 |
#define OVMSG "HASH: Out of overflow pages. Increase page size\n" |
703 |
if (offset > SPLITMASK) { |
704 |
if (++splitnum >= NCACHED) { |
705 |
(void)_write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1); |
706 |
errno = EFBIG; |
707 |
return (0); |
708 |
} |
709 |
hashp->OVFL_POINT = splitnum; |
710 |
hashp->SPARES[splitnum] = hashp->SPARES[splitnum-1]; |
711 |
hashp->SPARES[splitnum-1]--; |
712 |
offset = 1; |
713 |
} |
714 |
|
715 |
/* Check if we need to allocate a new bitmap page */ |
716 |
if (free_bit == (hashp->BSIZE << BYTE_SHIFT) - 1) { |
717 |
free_page++; |
718 |
if (free_page >= NCACHED) { |
719 |
(void)_write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1); |
720 |
errno = EFBIG; |
721 |
return (0); |
722 |
} |
723 |
/* |
724 |
* This is tricky. The 1 indicates that you want the new page |
725 |
* allocated with 1 clear bit. Actually, you are going to |
726 |
* allocate 2 pages from this map. The first is going to be |
727 |
* the map page, the second is the overflow page we were |
728 |
* looking for. The init_bitmap routine automatically, sets |
729 |
* the first bit of itself to indicate that the bitmap itself |
730 |
* is in use. We would explicitly set the second bit, but |
731 |
* don't have to if we tell init_bitmap not to leave it clear |
732 |
* in the first place. |
733 |
*/ |
734 |
if (__ibitmap(hashp, |
735 |
(int)OADDR_OF(splitnum, offset), 1, free_page)) |
736 |
return (0); |
737 |
hashp->SPARES[splitnum]++; |
738 |
#ifdef DEBUG2 |
739 |
free_bit = 2; |
740 |
#endif |
741 |
offset++; |
742 |
if (offset > SPLITMASK) { |
743 |
if (++splitnum >= NCACHED) { |
744 |
(void)_write(STDERR_FILENO, OVMSG, |
745 |
sizeof(OVMSG) - 1); |
746 |
errno = EFBIG; |
747 |
return (0); |
748 |
} |
749 |
hashp->OVFL_POINT = splitnum; |
750 |
hashp->SPARES[splitnum] = hashp->SPARES[splitnum-1]; |
751 |
hashp->SPARES[splitnum-1]--; |
752 |
offset = 0; |
753 |
} |
754 |
} else { |
755 |
/* |
756 |
* Free_bit addresses the last used bit. Bump it to address |
757 |
* the first available bit. |
758 |
*/ |
759 |
free_bit++; |
760 |
SETBIT(freep, free_bit); |
761 |
} |
762 |
|
763 |
/* Calculate address of the new overflow page */ |
764 |
addr = OADDR_OF(splitnum, offset); |
765 |
#ifdef DEBUG2 |
766 |
(void)fprintf(stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n", |
767 |
addr, free_bit, free_page); |
768 |
#endif |
769 |
return (addr); |
770 |
|
771 |
found: |
772 |
bit = bit + first_free(freep[j]); |
773 |
SETBIT(freep, bit); |
774 |
#ifdef DEBUG2 |
775 |
tmp1 = bit; |
776 |
tmp2 = i; |
777 |
#endif |
778 |
/* |
779 |
* Bits are addressed starting with 0, but overflow pages are addressed |
780 |
* beginning at 1. Bit is a bit addressnumber, so we need to increment |
781 |
* it to convert it to a page number. |
782 |
*/ |
783 |
bit = 1 + bit + (i * (hashp->BSIZE << BYTE_SHIFT)); |
784 |
if (bit >= hashp->LAST_FREED) |
785 |
hashp->LAST_FREED = bit - 1; |
786 |
|
787 |
/* Calculate the split number for this page */ |
788 |
for (i = 0; (i < splitnum) && (bit > hashp->SPARES[i]); i++); |
789 |
offset = (i ? bit - hashp->SPARES[i - 1] : bit); |
790 |
if (offset >= SPLITMASK) { |
791 |
(void)_write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1); |
792 |
errno = EFBIG; |
793 |
return (0); /* Out of overflow pages */ |
794 |
} |
795 |
addr = OADDR_OF(i, offset); |
796 |
#ifdef DEBUG2 |
797 |
(void)fprintf(stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n", |
798 |
addr, tmp1, tmp2); |
799 |
#endif |
800 |
|
801 |
/* Allocate and return the overflow page */ |
802 |
return (addr); |
803 |
} |
804 |
|
805 |
/* |
806 |
* Mark this overflow page as free. |
807 |
*/ |
808 |
void |
809 |
__free_ovflpage(HTAB *hashp, BUFHEAD *obufp) |
810 |
{ |
811 |
u_int16_t addr; |
812 |
u_int32_t *freep; |
813 |
int bit_address, free_page, free_bit; |
814 |
u_int16_t ndx; |
815 |
|
816 |
addr = obufp->addr; |
817 |
#ifdef DEBUG1 |
818 |
(void)fprintf(stderr, "Freeing %d\n", addr); |
819 |
#endif |
820 |
ndx = (((u_int16_t)addr) >> SPLITSHIFT); |
821 |
bit_address = |
822 |
(ndx ? hashp->SPARES[ndx - 1] : 0) + (addr & SPLITMASK) - 1; |
823 |
if (bit_address < hashp->LAST_FREED) |
824 |
hashp->LAST_FREED = bit_address; |
825 |
free_page = (bit_address >> (hashp->BSHIFT + BYTE_SHIFT)); |
826 |
free_bit = bit_address & ((hashp->BSIZE << BYTE_SHIFT) - 1); |
827 |
|
828 |
if (!(freep = hashp->mapp[free_page])) |
829 |
freep = fetch_bitmap(hashp, free_page); |
830 |
#ifdef DEBUG |
831 |
/* |
832 |
* This had better never happen. It means we tried to read a bitmap |
833 |
* that has already had overflow pages allocated off it, and we |
834 |
* failed to read it from the file. |
835 |
*/ |
836 |
if (!freep) |
837 |
assert(0); |
838 |
#endif |
839 |
CLRBIT(freep, free_bit); |
840 |
#ifdef DEBUG2 |
841 |
(void)fprintf(stderr, "FREE_OVFLPAGE: ADDR: %d BIT: %d PAGE %d\n", |
842 |
obufp->addr, free_bit, free_page); |
843 |
#endif |
844 |
__reclaim_buf(hashp, obufp); |
845 |
} |
846 |
|
847 |
/* |
848 |
* Returns: |
849 |
* 0 success |
850 |
* -1 failure |
851 |
*/ |
852 |
static int |
853 |
open_temp(HTAB *hashp) |
854 |
{ |
855 |
sigset_t set, oset; |
856 |
int len; |
857 |
char *envtmp = NULL; |
858 |
char path[MAXPATHLEN]; |
859 |
|
860 |
if (issetugid() == 0) |
861 |
envtmp = getenv("TMPDIR"); |
862 |
len = snprintf(path, |
863 |
sizeof(path), "%s/_hash.XXXXXX", envtmp ? envtmp : "/tmp"); |
864 |
if (len < 0 || len >= (int)sizeof(path)) { |
865 |
errno = ENAMETOOLONG; |
866 |
return (-1); |
867 |
} |
868 |
|
869 |
/* Block signals; make sure file goes away at process exit. */ |
870 |
(void)sigfillset(&set); |
871 |
(void)__libc_sigprocmask(SIG_BLOCK, &set, &oset); |
872 |
if ((hashp->fp = mkostemp(path, O_CLOEXEC)) != -1) |
873 |
(void)unlink(path); |
874 |
(void)__libc_sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL); |
875 |
return (hashp->fp != -1 ? 0 : -1); |
876 |
} |
877 |
|
878 |
/* |
879 |
* We have to know that the key will fit, but the last entry on the page is |
880 |
* an overflow pair, so we need to shift things. |
881 |
*/ |
882 |
static void |
883 |
squeeze_key(u_int16_t *sp, const DBT *key, const DBT *val) |
884 |
{ |
885 |
char *p; |
886 |
u_int16_t free_space, n, off, pageno; |
887 |
|
888 |
p = (char *)sp; |
889 |
n = sp[0]; |
890 |
free_space = FREESPACE(sp); |
891 |
off = OFFSET(sp); |
892 |
|
893 |
pageno = sp[n - 1]; |
894 |
off -= key->size; |
895 |
sp[n - 1] = off; |
896 |
memmove(p + off, key->data, key->size); |
897 |
off -= val->size; |
898 |
sp[n] = off; |
899 |
memmove(p + off, val->data, val->size); |
900 |
sp[0] = n + 2; |
901 |
sp[n + 1] = pageno; |
902 |
sp[n + 2] = OVFLPAGE; |
903 |
FREESPACE(sp) = free_space - PAIRSIZE(key, val); |
904 |
OFFSET(sp) = off; |
905 |
} |
906 |
|
907 |
static u_int32_t * |
908 |
fetch_bitmap(HTAB *hashp, int ndx) |
909 |
{ |
910 |
if (ndx >= hashp->nmaps) |
911 |
return (NULL); |
912 |
if ((hashp->mapp[ndx] = (u_int32_t *)malloc(hashp->BSIZE)) == NULL) |
913 |
return (NULL); |
914 |
if (__get_page(hashp, |
915 |
(char *)hashp->mapp[ndx], hashp->BITMAPS[ndx], 0, 1, 1)) { |
916 |
free(hashp->mapp[ndx]); |
917 |
return (NULL); |
918 |
} |
919 |
return (hashp->mapp[ndx]); |
920 |
} |
921 |
|
922 |
#ifdef DEBUG4 |
923 |
int |
924 |
print_chain(int addr) |
925 |
{ |
926 |
BUFHEAD *bufp; |
927 |
short *bp, oaddr; |
928 |
|
929 |
(void)fprintf(stderr, "%d ", addr); |
930 |
bufp = __get_buf(hashp, addr, NULL, 0); |
931 |
bp = (short *)bufp->page; |
932 |
while (bp[0] && ((bp[bp[0]] == OVFLPAGE) || |
933 |
((bp[0] > 2) && bp[2] < REAL_KEY))) { |
934 |
oaddr = bp[bp[0] - 1]; |
935 |
(void)fprintf(stderr, "%d ", (int)oaddr); |
936 |
bufp = __get_buf(hashp, (int)oaddr, bufp, 0); |
937 |
bp = (short *)bufp->page; |
938 |
} |
939 |
(void)fprintf(stderr, "\n"); |
940 |
} |
941 |
#endif |