#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid[] = "@(#)hash.c 8.12 (Berkeley) 11/7/95";
#endif
#include <sys/param.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#ifdef DEBUG
#include <assert.h>
#endif
#include "db-int.h"
#include "hash.h"
#include "page.h"
#include "extern.h"
static int32_t flush_meta __P((HTAB *));
static int32_t hash_access __P((HTAB *, ACTION, const DBT *, DBT *));
static int32_t hash_close __P((DB *));
static int32_t hash_delete __P((const DB *, const DBT *, u_int32_t));
static int32_t hash_fd __P((const DB *));
static int32_t hash_get __P((const DB *, const DBT *, DBT *, u_int32_t));
static int32_t hash_put __P((const DB *, DBT *, const DBT *, u_int32_t));
static int32_t hash_seq __P((const DB *, DBT *, DBT *, u_int32_t));
static int32_t hash_sync __P((const DB *, u_int32_t));
static int32_t hdestroy __P((HTAB *));
static int32_t cursor_get __P((const DB *, CURSOR *, DBT *, DBT *, \
u_int32_t));
static int32_t cursor_delete __P((const DB *, CURSOR *, u_int32_t));
static HTAB *init_hash __P((HTAB *, const char *, const HASHINFO *));
static int32_t init_htab __P((HTAB *, int32_t));
#if DB_BYTE_ORDER == DB_LITTLE_ENDIAN
static void swap_header __P((HTAB *));
static void swap_header_copy __P((HASHHDR *, HASHHDR *));
#endif
static u_int32_t hget_header __P((HTAB *, u_int32_t));
static void hput_header __P((HTAB *));
#define RETURN_ERROR(ERR, LOC) { save_errno = ERR; goto LOC; }
#define SUCCESS (0)
#define ERROR (-1)
#define ABNORMAL (1)
#ifdef HASH_STATISTICS
u_int32_t hash_accesses, hash_collisions, hash_expansions, hash_overflows,
hash_bigpages;
#endif
extern DB *
__kdb2_hash_open(file, flags, mode, info, dflags)
const char *file;
int flags, mode, dflags;
const HASHINFO *info;
{
struct stat statbuf;
DB *dbp;
DBT mpool_key;
HTAB *hashp;
int32_t bpages, csize, new_table, save_errno, specified_file;
if ((flags & O_ACCMODE) == O_WRONLY) {
errno = EINVAL;
return (NULL);
}
if (!(hashp = (HTAB *)calloc(1, sizeof(HTAB))))
return (NULL);
hashp->fp = -1;
specified_file = (file != NULL);
if (!file) {
file = tmpnam(NULL);
hashp->fname = file;
#ifdef DEBUG
fprintf(stderr, "Using file name %s.\n", file);
#endif
}
hashp->flags = flags;
hashp->save_file = specified_file && (hashp->flags & O_RDWR);
new_table = 0;
if (!file || (flags & O_TRUNC) ||
(stat(file, &statbuf) && (errno == ENOENT))) {
if (errno == ENOENT)
errno = 0;
new_table = 1;
}
if (file) {
if ((hashp->fp = open(file, flags|O_BINARY, mode)) == -1)
RETURN_ERROR(errno, error0);
(void)fcntl(hashp->fp, F_SETFD, 1);
}
if (new_table) {
if (!(hashp = init_hash(hashp, file, info)))
RETURN_ERROR(errno, error1);
} else {
if (info && info->hash)
hashp->hash = info->hash;
else
hashp->hash = __default_hash;
if (hget_header(hashp,
(info && info->bsize ? info->bsize : DEF_BUCKET_SIZE)) !=
sizeof(HASHHDR))
RETURN_ERROR(EFTYPE, error1);
if (hashp->hdr.magic != HASHMAGIC)
RETURN_ERROR(EFTYPE, error1);
#define OLDHASHVERSION 1
if (hashp->hdr.version != HASHVERSION &&
hashp->hdr.version != OLDHASHVERSION)
RETURN_ERROR(EFTYPE, error1);
if (hashp->hash(CHARKEY, sizeof(CHARKEY))
!= hashp->hdr.h_charkey)
RETURN_ERROR(EFTYPE, error1);
bpages = (hashp->hdr.spares[hashp->hdr.ovfl_point] +
(hashp->hdr.bsize << BYTE_SHIFT) - 1) >>
(hashp->hdr.bshift + BYTE_SHIFT);
hashp->nmaps = bpages;
(void)memset(&hashp->mapp[0], 0, bpages * sizeof(u_int32_t *));
}
mpool_key.data = (u_int8_t *)file;
mpool_key.size = strlen(file);
if (info && info->cachesize)
csize = info->cachesize / hashp->hdr.bsize;
else
csize = DEF_CACHESIZE / hashp->hdr.bsize;
hashp->mp = mpool_open(&mpool_key, hashp->fp, hashp->hdr.bsize, csize);
if (!hashp->mp)
RETURN_ERROR(errno, error1);
mpool_filter(hashp->mp, __pgin_routine, __pgout_routine, hashp);
if (new_table &&
init_htab(hashp, info && info->nelem ? info->nelem : 1))
goto error2;
TAILQ_INIT(&hashp->curs_queue);
hashp->seq_cursor = NULL;
hashp->split_buf = (PAGE16 *)malloc(hashp->hdr.bsize);
if (!hashp->split_buf)
goto error2;
hashp->new_file = new_table;
if (!(dbp = (DB *)malloc(sizeof(DB))))
goto error2;
dbp->internal = hashp;
dbp->close = hash_close;
dbp->del = hash_delete;
dbp->fd = hash_fd;
dbp->get = hash_get;
dbp->put = hash_put;
dbp->seq = hash_seq;
dbp->sync = hash_sync;
dbp->type = DB_HASH;
#ifdef DEBUG
(void)fprintf(stderr,
"%s\n%s%lx\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%x\n%s%x\n%s%d\n%s%d\n",
"init_htab:",
"TABLE POINTER ", (void *)hashp,
"BUCKET SIZE ", hashp->hdr.bsize,
"BUCKET SHIFT ", hashp->hdr.bshift,
"FILL FACTOR ", hashp->hdr.ffactor,
"MAX BUCKET ", hashp->hdr.max_bucket,
"OVFL POINT ", hashp->hdr.ovfl_point,
"LAST FREED ", hashp->hdr.last_freed,
"HIGH MASK ", hashp->hdr.high_mask,
"LOW MASK ", hashp->hdr.low_mask,
"NKEYS ", hashp->hdr.nkeys);
#endif
#ifdef HASH_STATISTICS
hash_overflows = hash_accesses = hash_collisions = hash_expansions = 0;
hash_bigpages = 0;
#endif
return (dbp);
error2:
save_errno = errno;
hdestroy(hashp);
errno = save_errno;
return (NULL);
error1:
if (hashp != NULL)
(void)close(hashp->fp);
error0:
free(hashp);
errno = save_errno;
return (NULL);
}
static int32_t
hash_close(dbp)
DB *dbp;
{
HTAB *hashp;
int32_t retval;
if (!dbp)
return (ERROR);
hashp = (HTAB *)dbp->internal;
retval = hdestroy(hashp);
free(dbp);
return (retval);
}
static int32_t
hash_fd(dbp)
const DB *dbp;
{
HTAB *hashp;
if (!dbp)
return (ERROR);
hashp = (HTAB *)dbp->internal;
if (hashp->fp == -1) {
errno = ENOENT;
return (-1);
}
return (hashp->fp);
}
static HTAB *
init_hash(hashp, file, info)
HTAB *hashp;
const char *file;
const HASHINFO *info;
{
struct stat statbuf;
int32_t nelem;
nelem = 1;
hashp->hdr.nkeys = 0;
hashp->hdr.lorder = DB_BYTE_ORDER;
hashp->hdr.bsize = DEF_BUCKET_SIZE;
hashp->hdr.bshift = DEF_BUCKET_SHIFT;
hashp->hdr.ffactor = DEF_FFACTOR;
hashp->hash = __default_hash;
memset(hashp->hdr.spares, 0, sizeof(hashp->hdr.spares));
memset(hashp->hdr.bitmaps, 0, sizeof(hashp->hdr.bitmaps));
if (file != NULL) {
if (stat(file, &statbuf))
return (NULL);
hashp->hdr.bsize = statbuf.st_blksize;
if (hashp->hdr.bsize > MAX_BSIZE)
hashp->hdr.bsize = MAX_BSIZE;
hashp->hdr.bshift = __log2(hashp->hdr.bsize);
}
if (info) {
if (info->bsize) {
hashp->hdr.bshift = __log2(info->bsize);
hashp->hdr.bsize = 1 << hashp->hdr.bshift;
if (hashp->hdr.bsize > MAX_BSIZE) {
errno = EINVAL;
return (NULL);
}
}
if (info->ffactor)
hashp->hdr.ffactor = info->ffactor;
if (info->hash)
hashp->hash = info->hash;
if (info->lorder) {
if ((info->lorder != DB_BIG_ENDIAN) &&
(info->lorder != DB_LITTLE_ENDIAN)) {
errno = EINVAL;
return (NULL);
}
hashp->hdr.lorder = info->lorder;
}
}
return (hashp);
}
static int32_t
init_htab(hashp, nelem)
HTAB *hashp;
int32_t nelem;
{
int32_t l2, nbuckets;
nelem = (nelem - 1) / hashp->hdr.ffactor + 1;
l2 = __log2(MAX(nelem, 2));
nbuckets = 1 << l2;
hashp->hdr.spares[l2] = l2 + 1;
hashp->hdr.spares[l2 + 1] = l2 + 1;
hashp->hdr.ovfl_point = l2;
hashp->hdr.last_freed = 2;
hashp->hdr.max_bucket = hashp->hdr.low_mask = nbuckets - 1;
hashp->hdr.high_mask = (nbuckets << 1) - 1;
hashp->hdr.hdrpages =
(sizeof(HASHHDR) / (hashp->hdr.bsize - HEADER_OVERHEAD)) +
(((sizeof(HASHHDR) % (hashp->hdr.bsize - HEADER_OVERHEAD)) == 0)
? 0 : 1);
if (__ibitmap(hashp, OADDR_OF(l2, 1), l2 + 1, 0))
return (-1);
return (0);
}
static u_int32_t
hget_header(hashp, page_size)
HTAB *hashp;
u_int32_t page_size;
{
u_int32_t num_copied, i;
u_int8_t *hdr_dest;
num_copied = 0;
i = 0;
hdr_dest = (u_int8_t *)&hashp->hdr;
lseek(hashp->fp, 0, SEEK_SET);
num_copied = read(hashp->fp, hdr_dest, sizeof(HASHHDR));
if (num_copied != sizeof(HASHHDR)) {
fprintf(stderr, "hash: could not retrieve header");
return (0);
}
#if DB_BYTE_ORDER == DB_LITTLE_ENDIAN
swap_header(hashp);
#endif
return (num_copied);
}
static void
hput_header(hashp)
HTAB *hashp;
{
HASHHDR *whdrp;
#if DB_BYTE_ORDER == DB_LITTLE_ENDIAN
HASHHDR whdr;
#endif
u_int32_t num_copied, i;
num_copied = i = 0;
whdrp = &hashp->hdr;
#if DB_BYTE_ORDER == DB_LITTLE_ENDIAN
whdrp = &whdr;
swap_header_copy(&hashp->hdr, whdrp);
#endif
lseek(hashp->fp, 0, SEEK_SET);
num_copied = write(hashp->fp, whdrp, sizeof(HASHHDR));
if (num_copied != sizeof(HASHHDR))
(void)fprintf(stderr, "hash: could not write hash header");
return;
}
static int32_t
hdestroy(hashp)
HTAB *hashp;
{
int32_t save_errno;
save_errno = 0;
#ifdef HASH_STATISTICS
{ int i;
(void)fprintf(stderr, "hdestroy: accesses %ld collisions %ld\n",
hash_accesses, hash_collisions);
(void)fprintf(stderr,
"hdestroy: expansions %ld\n", hash_expansions);
(void)fprintf(stderr,
"hdestroy: overflows %ld\n", hash_overflows);
(void)fprintf(stderr,
"hdestroy: big key/data pages %ld\n", hash_bigpages);
(void)fprintf(stderr,
"keys %ld maxp %d\n", hashp->hdr.nkeys, hashp->hdr.max_bucket);
for (i = 0; i < NCACHED; i++)
(void)fprintf(stderr,
"spares[%d] = %d\n", i, hashp->hdr.spares[i]);
}
#endif
if (flush_meta(hashp) && !save_errno)
save_errno = errno;
if (hashp->split_buf)
free(hashp->split_buf);
if (hashp->bigkey_buf)
free(hashp->bigkey_buf);
if (hashp->bigdata_buf)
free(hashp->bigdata_buf);
if (hashp->seq_cursor)
hashp->seq_cursor->delete(NULL, hashp->seq_cursor, 0);
mpool_sync(hashp->mp);
mpool_close(hashp->mp);
if (hashp->fp != -1)
(void)close(hashp->fp);
if (hashp->fname && !hashp->save_file) {
#ifdef DEBUG
fprintf(stderr, "Unlinking file %s.\n", hashp->fname);
#endif
chmod(hashp->fname, 0700);
unlink(hashp->fname);
tmpnam(NULL);
}
free(hashp);
if (save_errno) {
errno = save_errno;
return (ERROR);
}
return (SUCCESS);
}
static int32_t
hash_sync(dbp, flags)
const DB *dbp;
u_int32_t flags;
{
HTAB *hashp;
hashp = (HTAB *)dbp->internal;
return (flush_meta(hashp) || mpool_sync(hashp->mp));
}
static int32_t
flush_meta(hashp)
HTAB *hashp;
{
int32_t i;
if (!hashp->save_file)
return (0);
hashp->hdr.magic = HASHMAGIC;
hashp->hdr.version = HASHVERSION;
hashp->hdr.h_charkey = hashp->hash(CHARKEY, sizeof(CHARKEY));
hput_header(hashp);
for (i = 0; i < NCACHED; i++)
if (hashp->mapp[i]) {
if (__put_page(hashp,
(PAGE16 *)hashp->mapp[i], A_BITMAP, 1))
return (-1);
hashp->mapp[i] = NULL;
}
return (0);
}
static int32_t
hash_get(dbp, key, data, flag)
const DB *dbp;
const DBT *key;
DBT *data;
u_int32_t flag;
{
HTAB *hashp;
hashp = (HTAB *)dbp->internal;
if (flag) {
hashp->local_errno = errno = EINVAL;
return (ERROR);
}
return (hash_access(hashp, HASH_GET, key, data));
}
static int32_t
hash_put(dbp, key, data, flag)
const DB *dbp;
DBT *key;
const DBT *data;
u_int32_t flag;
{
HTAB *hashp;
hashp = (HTAB *)dbp->internal;
if (flag && flag != R_NOOVERWRITE) {
hashp->local_errno = errno = EINVAL;
return (ERROR);
}
if ((hashp->flags & O_ACCMODE) == O_RDONLY) {
hashp->local_errno = errno = EPERM;
return (ERROR);
}
return (hash_access(hashp, flag == R_NOOVERWRITE ?
HASH_PUTNEW : HASH_PUT, key, (DBT *)data));
}
static int32_t
hash_delete(dbp, key, flag)
const DB *dbp;
const DBT *key;
u_int32_t flag;
{
HTAB *hashp;
hashp = (HTAB *)dbp->internal;
if (flag) {
hashp->local_errno = errno = EINVAL;
return (ERROR);
}
if ((hashp->flags & O_ACCMODE) == O_RDONLY) {
hashp->local_errno = errno = EPERM;
return (ERROR);
}
return (hash_access(hashp, HASH_DELETE, key, NULL));
}
static int32_t
hash_access(hashp, action, key, val)
HTAB *hashp;
ACTION action;
const DBT *key;
DBT *val;
{
DBT page_key, page_val;
CURSOR cursor;
ITEM_INFO item_info;
u_int32_t bucket;
u_int32_t num_items;
#ifdef HASH_STATISTICS
hash_accesses++;
#endif
num_items = 0;
if (action == HASH_PUT || action == HASH_PUTNEW) {
if (ISBIG(key->size + val->size, hashp))
item_info.seek_size = PAIR_OVERHEAD;
else
item_info.seek_size = key->size + val->size;
} else
item_info.seek_size = 0;
item_info.seek_found_page = 0;
bucket = __call_hash(hashp, (int8_t *)key->data, key->size);
cursor.pagep = NULL;
__get_item_reset(hashp, &cursor);
cursor.bucket = bucket;
while (1) {
__get_item_next(hashp, &cursor, &page_key, &page_val, &item_info);
if (item_info.status == ITEM_ERROR)
return (ABNORMAL);
if (item_info.status == ITEM_NO_MORE)
break;
num_items++;
if (item_info.key_off == BIGPAIR) {
if (__find_bigpair(hashp, &cursor, (int8_t *)key->data,
key->size) > 0)
goto found;
} else if (key->size == page_key.size &&
!memcmp(key->data, page_key.data, key->size))
goto found;
}
#ifdef HASH_STATISTICS
hash_collisions++;
#endif
__get_item_done(hashp, &cursor);
switch (action) {
case HASH_PUT:
case HASH_PUTNEW:
if (__addel(hashp, &item_info, key, val, num_items, 0))
return (ERROR);
break;
case HASH_GET:
case HASH_DELETE:
default:
return (ABNORMAL);
}
if (item_info.caused_expand)
__expand_table(hashp);
return (SUCCESS);
found: __get_item_done(hashp, &cursor);
switch (action) {
case HASH_PUTNEW:
return (ABNORMAL);
case HASH_GET:
if (item_info.key_off == BIGPAIR) {
if (__big_return(hashp, &item_info, val, 0))
return (ERROR);
} else {
val->data = page_val.data;
val->size = page_val.size;
}
break;
case HASH_PUT:
if (__delpair(hashp, &cursor, &item_info) ||
__addel(hashp, &item_info, key, val, UNKNOWN, 0))
return (ERROR);
__get_item_done(hashp, &cursor);
if (item_info.caused_expand)
__expand_table(hashp);
break;
case HASH_DELETE:
if (__delpair(hashp, &cursor, &item_info))
return (ERROR);
break;
default:
abort();
}
return (SUCCESS);
}
CURSOR *
__cursor_creat(dbp)
const DB *dbp;
{
CURSOR *new_curs;
HTAB *hashp;
new_curs = (CURSOR *)malloc(sizeof(struct cursor_t));
if (!new_curs)
return NULL;
new_curs->internal =
(struct item_info *)malloc(sizeof(struct item_info));
if (!new_curs->internal) {
free(new_curs);
return NULL;
}
new_curs->get = cursor_get;
new_curs->delete = cursor_delete;
new_curs->bucket = 0;
new_curs->pgno = INVALID_PGNO;
new_curs->ndx = 0;
new_curs->pgndx = 0;
new_curs->pagep = NULL;
hashp = (HTAB *)dbp->internal;
TAILQ_INSERT_TAIL(&hashp->curs_queue, new_curs, queue);
return new_curs;
}
static int32_t
cursor_get(dbp, cursorp, key, val, flags)
const DB *dbp;
CURSOR *cursorp;
DBT *key, *val;
u_int32_t flags;
{
HTAB *hashp;
ITEM_INFO item_info;
hashp = (HTAB *)dbp->internal;
if (flags && flags != R_FIRST && flags != R_NEXT) {
hashp->local_errno = errno = EINVAL;
return (ERROR);
}
#ifdef HASH_STATISTICS
hash_accesses++;
#endif
item_info.seek_size = 0;
if (flags == R_FIRST)
__get_item_first(hashp, cursorp, key, val, &item_info);
else
__get_item_next(hashp, cursorp, key, val, &item_info);
while (1) {
if (item_info.status == ITEM_OK) {
if (item_info.key_off == BIGPAIR &&
__big_keydata(hashp, cursorp->pagep, key, val,
item_info.pgndx))
return (ABNORMAL);
break;
} else if (item_info.status != ITEM_NO_MORE)
return (ABNORMAL);
__put_page(hashp, cursorp->pagep, A_RAW, 0);
cursorp->ndx = cursorp->pgndx = 0;
cursorp->bucket++;
cursorp->pgno = INVALID_PGNO;
cursorp->pagep = NULL;
if (cursorp->bucket > hashp->hdr.max_bucket)
return (ABNORMAL);
__get_item_next(hashp, cursorp, key, val, &item_info);
}
__get_item_done(hashp, cursorp);
return (0);
}
static int32_t
cursor_delete(dbp, cursor, flags)
const DB *dbp;
CURSOR *cursor;
u_int32_t flags;
{
free(cursor->internal);
free(cursor);
return (0);
}
static int32_t
hash_seq(dbp, key, val, flag)
const DB *dbp;
DBT *key, *val;
u_int32_t flag;
{
HTAB *hashp;
hashp = (HTAB *)dbp->internal;
if (!hashp->seq_cursor)
hashp->seq_cursor = __cursor_creat(dbp);
return (hashp->seq_cursor->get(dbp, hashp->seq_cursor, key, val, flag));
}
int32_t
__expand_table(hashp)
HTAB *hashp;
{
u_int32_t old_bucket, new_bucket;
int32_t spare_ndx;
#ifdef HASH_STATISTICS
hash_expansions++;
#endif
new_bucket = ++hashp->hdr.max_bucket;
old_bucket = (hashp->hdr.max_bucket & hashp->hdr.low_mask);
if (__new_page(hashp, new_bucket, A_BUCKET) != 0)
return (-1);
spare_ndx = __log2(hashp->hdr.max_bucket + 1);
if (spare_ndx > hashp->hdr.ovfl_point) {
hashp->hdr.spares[spare_ndx] = hashp->hdr.spares[hashp->hdr.ovfl_point];
hashp->hdr.ovfl_point = spare_ndx;
}
if (new_bucket > hashp->hdr.high_mask) {
hashp->hdr.low_mask = hashp->hdr.high_mask;
hashp->hdr.high_mask = new_bucket | hashp->hdr.low_mask;
}
if (BUCKET_TO_PAGE(new_bucket) > MAX_PAGES(hashp)) {
fprintf(stderr, "hash: Cannot allocate new bucket. Pages exhausted.\n");
return (-1);
}
return (__split_page(hashp, old_bucket, new_bucket));
}
u_int32_t
__call_hash(hashp, k, len)
HTAB *hashp;
int8_t *k;
int32_t len;
{
int32_t n, bucket;
n = hashp->hash(k, len);
bucket = n & hashp->hdr.high_mask;
if (bucket > hashp->hdr.max_bucket)
bucket = bucket & hashp->hdr.low_mask;
return (bucket);
}
#if DB_BYTE_ORDER == DB_LITTLE_ENDIAN
static void
swap_header_copy(srcp, destp)
HASHHDR *srcp, *destp;
{
int32_t i;
P_32_COPY(srcp->magic, destp->magic);
P_32_COPY(srcp->version, destp->version);
P_32_COPY(srcp->lorder, destp->lorder);
P_32_COPY(srcp->bsize, destp->bsize);
P_32_COPY(srcp->bshift, destp->bshift);
P_32_COPY(srcp->ovfl_point, destp->ovfl_point);
P_32_COPY(srcp->last_freed, destp->last_freed);
P_32_COPY(srcp->max_bucket, destp->max_bucket);
P_32_COPY(srcp->high_mask, destp->high_mask);
P_32_COPY(srcp->low_mask, destp->low_mask);
P_32_COPY(srcp->ffactor, destp->ffactor);
P_32_COPY(srcp->nkeys, destp->nkeys);
P_32_COPY(srcp->hdrpages, destp->hdrpages);
P_32_COPY(srcp->h_charkey, destp->h_charkey);
for (i = 0; i < NCACHED; i++) {
P_32_COPY(srcp->spares[i], destp->spares[i]);
P_16_COPY(srcp->bitmaps[i], destp->bitmaps[i]);
}
}
static void
swap_header(hashp)
HTAB *hashp;
{
HASHHDR *hdrp;
int32_t i;
hdrp = &hashp->hdr;
M_32_SWAP(hdrp->magic);
M_32_SWAP(hdrp->version);
M_32_SWAP(hdrp->lorder);
M_32_SWAP(hdrp->bsize);
M_32_SWAP(hdrp->bshift);
M_32_SWAP(hdrp->ovfl_point);
M_32_SWAP(hdrp->last_freed);
M_32_SWAP(hdrp->max_bucket);
M_32_SWAP(hdrp->high_mask);
M_32_SWAP(hdrp->low_mask);
M_32_SWAP(hdrp->ffactor);
M_32_SWAP(hdrp->nkeys);
M_32_SWAP(hdrp->hdrpages);
M_32_SWAP(hdrp->h_charkey);
for (i = 0; i < NCACHED; i++) {
M_32_SWAP(hdrp->spares[i]);
M_16_SWAP(hdrp->bitmaps[i]);
}
}
#endif