[alt.sources] lq-text Full Text Retrieval Database Part 13/13

lee@sq.sq.com (Liam R. E. Quin) (03/04/91)

: cut here --- cut here --
: To unbundle, sh this file
#! /bin/sh
: part 13
echo x - lq-text/src/ozmahash/hash.h 1>&2
sed 's/^X//' >lq-text/src/ozmahash/hash.h <<'@@@End of lq-text/src/ozmahash/hash.h'
X/*-
X * Copyright (c) 1990 The Regents of the University of California.
X * All rights reserved.
X *
X * This code is derived from software contributed to Berkeley by
X * Margo Seltzer.
X *
X * %sccs.include.redist.c%
X *
X *	%W% (Berkeley) %G%
X */
X
X/* Operations */
Xtypedef enum { HASH_GET, HASH_PUT, HASH_PUTNEW, HASH_DELETE, 
X		HASH_FIRST, HASH_NEXT } ACTION;
X
X/* Buffer Management structures */
Xtypedef struct _bufhead BUFHEAD;
X
Xstruct _bufhead {
X    BUFHEAD	*prev;		/* LRU links */
X    BUFHEAD	*next;		/* LRU links */
X    BUFHEAD	*ovfl;		/* Overflow page buffer header */
X    int		addr;		/* Address of this page */
X    char	*page;		/* Actual page data */
X    char	flags;	/* Combination of BUF_MOD, BUF_DISK, BUF_BUCKET */
X};
X
X/* Flag Values */
X#define	BUF_MOD		0x0001
X#define BUF_DISK	0x0002
X#define	BUF_BUCKET	0x0004
X
X#define IS_BUCKET(X)	(X & BUF_BUCKET)
X
Xtypedef BUFHEAD	**SEGMENT;
X
X/* Hash Table Information */
Xtypedef struct hashhdr {	/* Disk resident portion */
X	int magic;	/* Magic NO for hash tables */
X	int version;	/* Version ID */
X	long lorder;	/* Byte Order */
X	int bsize;	/* Bucket/Page Size */
X	int bshift;	/* Bucket shift */
X	int dsize;	/* Directory Size */
X	int ssize;	/* Segment Size */
X	int sshift;	/* Segment shift */
X	int max_bucket;	/* ID of Maximum bucket in use */
X	int high_mask;	/* Mask to modulo into entire table */
X	int low_mask;	/* Mask to modulo into lower half of table */
X	int ffactor;	/* Fill factor */
X	int nkeys;	/* Number of keys in hash table */
X	int hdrpages;	/* Size of table header */
X	int h_charkey;	/* value of hash(CHARKEY) */
X# define NCACHED		32	/* number of bit maps and spare points*/
X	int spares[NCACHED];	/* spare pages for overflow */
X	u_short bitmaps[NCACHED];	/* address of overflow page bitmaps */
X} HASHHDR;
X
Xtypedef struct htab {	/* Memory resident data structure */
X	HASHHDR hdr;	/* Header */
X	int nsegs;	/* Number of allocated segments */
X	int exsegs;	/* Number of extra allocated segments */
X	int (*hash)();	/* Hash Function */
X	int flags;	/* Flag values */
X	int fp;		/* File pointer */
X	char *tmp_buf;	/* Temporary Buffer for BIG data */
X	char *tmp_key;	/* Temporary Buffer for BIG keys */
X	BUFHEAD *cpage;	/* Current page */
X	int cbucket;	/* Current bucket */
X	int cndx;  	/* Index of next item on cpage */
X	int errno;	/* Error Number -- for DBM compatability */
X	int new_file;	/* Indicates whether fd is backing store or no */
X	int save_file;	/* Indicates whether we need to flush file at exit */
X	u_long *mapp[NCACHED];	/* Pointers to page maps */
X	int nbufs;	/* Number of buffers left to allocate */
X	BUFHEAD	bufhead; /* Header of buffer lru list */
X	SEGMENT	 *dir;	/* Hash Bucket directory */
X} HTAB;
X
X
X/*
X * Constants
X */
X#define	MAX_BSIZE		65536	/* 2^16 */
X#define MIN_BUFFERS		6
X#define MINHDRSIZE		512
X#define DEF_BUFSIZE		65536	/* 64 K */
X#define DEF_BUCKET_SIZE	256
X#define DEF_BUCKET_SHIFT	8	/* log2(BUCKET) */
X#define DEF_SEGSIZE		256
X#define DEF_SEGSIZE_SHIFT		8      /* log2(SEGSIZE)	 */
X#define DEF_DIRSIZE		256
X#define DEF_FFACTOR		5
X#define SPLTMAX		8
X#define CHARKEY		"%$sniglet^&"
X#define NUMKEY			1038583
X#define VERSION_NO		3
X#define BYTE_SHIFT		3
X#define INT_TO_BYTE		2
X#define INT_BYTE_SHIFT		5
X#define ALL_SET		((unsigned)0xFFFFFFFF)
X#define ALL_CLEAR		0
X
X
X#define MAX(A,B)	( (A>B)?A:B )
X#define PTROF(X)	((BUFHEAD *)((unsigned)(X)&~0x3))
X#define ISMOD(X)	((unsigned)(X)&0x1)
X#define DOMOD(X)	(X = (char *)( (unsigned)X | 0x1))
X#define ISDISK(X)	((unsigned)(X)&0x2)
X#define DODISK(X)	(X = (char *)( (unsigned)X | 0x2))
X
X#define BITS_PER_MAP    32
X
X/* Given the address of the beginning of a big map, clear/set the nth bit */
X
X#define CLRBIT(A,N) ((A)[N/BITS_PER_MAP] &= ~(1<<(N%BITS_PER_MAP)))
X#define SETBIT(A,N) ((A)[N/BITS_PER_MAP] |= (1<<(N%BITS_PER_MAP)))
X#define ISSET(A,N) ((A)[N/BITS_PER_MAP] & (1<<(N%BITS_PER_MAP)))
X
X/* Overflow management */
X/*
X	Overflow page numbers are allocated per split point.
X	At each doubling of the table, we can allocate extra
X	pages.  So, an overflow page number has the top 5 bits
X	indicate which split point and the lower 11 bits indicate
X	which page at that split point is indicated (pages within
X	split points are numberered starting with 1).
X
X
X*/
X
X#define SPLITSHIFT	11
X#define SPLITMASK	0x7FF
X#define SPLITNUM(N)	(((unsigned)N) >> SPLITSHIFT)
X#define OPAGENUM(N)	(N & SPLITMASK)
X#define	OADDR_OF(S,O)	((S << SPLITSHIFT) + O)
X
X#define BUCKET_TO_PAGE(B) \
X	B + hashp->HDRPAGES + (B ? hashp->SPARES[log2(B+1)-1] : 0)
X#define OADDR_TO_PAGE(B) 	\
X	BUCKET_TO_PAGE ( (1 << SPLITNUM(B)) -1 ) + OPAGENUM(B);
X
X/*
X    page.h contains a detailed description of the page format.
X
X    Normally, keys and data are accessed from offset tables in the
X    top of each page which point to the beginning of the key and
X    data.  There are four flag values which may be stored in these
X    offset tables which indicate the following:
X
X	OVFLPAGE	Rather than a key data pair, this pair contains
X			the address of an overflow page.  The format of
X			the pair is:
X			    OVERFLOW_PAGE_NUMBER OVFLPAGE
X
X	PARTIAL_KEY	This must be the first key/data pair on a page 
X			and implies that page contains only a partial key.  
X			That is, the key is too big to fit on a single page 
X			so it starts on this page and continues on the next.
X			The format of the page is:
X			    KEY_OFF PARTIAL_KEY OVFL_PAGENO OVFLPAGE
X			    
X			    KEY_OFF -- offset of the beginning of the key
X			    PARTIAL_KEY -- 1
X			    OVFL_PAGENO - page number of the next overflow page
X			    OVFLPAGE -- 0
X	FULL_KEY	This must be the first key/data pair on the page.  It
X			is used in two cases.  
X
X			Case 1:
X			    There is a complete key on the page but no data
X			    (because it wouldn't fit).  The next page contains
X			    the data.
X
X			    Page format it:
X			    KEY_OFF FULL_KEY OVFL_PAGENO OVFL_PAGE
X
X			    KEY_OFF -- offset of the beginning of the key
X			    FULL_KEY -- 2
X			    OVFL_PAGENO - page number of the next overflow page
X			    OVFLPAGE -- 0
X
X			Case 2:
X			    This page contains no key, but part of a large 
X			    data field, which is continued on the next page.
X
X			    Page format it:
X			    DATA_OFF FULL_KEY OVFL_PAGENO OVFL_PAGE
X
X			    KEY_OFF -- offset of the beginning of the data on
X					this page
X			    FULL_KEY -- 2
X			    OVFL_PAGENO - page number of the next overflow page
X			    OVFLPAGE -- 0
X
X	FULL_KEY_DATA	This must be the first key/data pair on the page.
X			There are two cases:
X
X			Case 1:
X			    This page contains a key and the beginning of the
X			    data field, but the data field is continued on the
X			    next page.
X
X			    Page format is:
X			    KEY_OFF FULL_KEY_DATA OVFL_PAGENO DATA_OFF
X
X			    KEY_OFF -- offset of the beginning of the key
X			    FULL_KEY_DATA -- 3
X			    OVFL_PAGENO - page number of the next overflow page
X			    DATA_OFF -- offset of the beginning of the data 
X
X			Case 2:
X			    This page contains the last page of a big data pair.
X			    There is no key, only the  tail end of the data 
X			    on this page.
X
X			    Page format is:
X			    DATA_OFF FULL_KEY_DATA <OVFL_PAGENO> <OVFLPAGE>
X
X			    DATA_OFF -- offset of the beginning of the data on
X					this page
X			    FULL_KEY_DATA -- 3
X			    OVFL_PAGENO - page number of the next overflow page
X			    OVFLPAGE -- 0
X
X			    OVFL_PAGENO and OVFLPAGE are optional (they are
X			    not present if there is no next page).
X*/
X#define OVFLPAGE	0
X#define PARTIAL_KEY	1
X#define FULL_KEY	2
X#define FULL_KEY_DATA	3
X#define	REAL_KEY	4
X
X
X/* Short hands for accessing structure */
X#define BSIZE	hdr.bsize
X#define BSHIFT	hdr.bshift
X#define DSIZE	hdr.dsize
X#define SGSIZE	hdr.ssize
X#define SSHIFT	hdr.sshift
X#define LORDER	hdr.lorder
X#define MAX_BUCKET	hdr.max_bucket
X#define FFACTOR		hdr.ffactor
X#define HIGH_MASK	hdr.high_mask
X#define LOW_MASK	hdr.low_mask
X#define NKEYS		hdr.nkeys
X#define HDRPAGES	hdr.hdrpages
X#define SPARES		hdr.spares
X#define BITMAPS		hdr.bitmaps
X#define VERSION		hdr.version
X#define MAGIC		hdr.magic
X#define NEXT_FREE	hdr.next_free
X#define H_CHARKEY	hdr.h_charkey
@@@End of lq-text/src/ozmahash/hash.h
echo x - lq-text/src/ozmahash/hfunc.c 1>&2
sed 's/^X//' >lq-text/src/ozmahash/hfunc.c <<'@@@End of lq-text/src/ozmahash/hfunc.c'
X/*-
X * Copyright (c) 1990 The Regents of the University of California.
X * All rights reserved.
X *
X * This code is derived from software contributed to Berkeley by
X * Margo Seltzer.
X *
X * %sccs.include.redist.c%
X */
X
X#if defined(LIBC_SCCS) && !defined(lint)
Xstatic char sccsid[] = "%W% (Berkeley) %G%";
X#endif /* LIBC_SCCS and not lint */
X
X/* Global default hash function */
Xstatic	int	hash1();
Xstatic	int	hash2();
Xstatic	int	hash3();
Xstatic	int	hash4();
X
Xint	(*default_hash)() = hash4;
X
X/******************************* HASH FUNCTIONS **************************/
X/*
X	Assume that we've already split the bucket to which this
X	key hashes, calculate that bucket, and check that in fact
X	we did already split it.
X
X	This came from ejb's hsearch.
X*/
X
X# define PRIME1			37
X# define PRIME2			1048583
X
Xstatic int
Xhash1(key,len)
Xchar *key;
Xint len;
X{
X	register int h;
X	register int l = len;
X	register unsigned char *k = (unsigned char *) key;
X
X	h = 0;
X	/*
X	 * Convert string to integer
X	 */
X	while (l--) h = h * PRIME1 ^ (*k++ - ' ');
X	h %= PRIME2;
X
X	return (h);
X}
X
X/*
X    Phong's linear congruential hash
X*/
X#define dcharhash(h, c)	((h) = 0x63c63cd9*(h) + 0x9c39c33d + (c))
X
Xstatic int
Xhash2(str, n)
X	register unsigned char *str;
X	int n;
X{
X	register unsigned char *e, c;
X	register int h;
X
X	e = str + n;
X	for (h = 0; str != e;) {
X		c = *str++;
X		if (!c && str > e)
X			break;
X		dcharhash(h,c);
X	}
X	return(h);
X}
X
X/*
X * This is INCREDIBLY ugly, but fast.
X * We break the string up into 8 byte units.  On the first time
X * through the loop we get the "leftover bytes" (strlen % 8).
X * On every other iteration, we perform 8 HASHC's so we handle
X * all 8 bytes.  Essentially, this saves us 7 cmp & branch
X * instructions.  If this routine is heavily used enough, it's
X * worth the ugly coding
X * 
X * OZ's original sdbm hash
X */
Xstatic int
Xhash3(key,nbytes)
Xchar *key;
Xint nbytes;
X{
X        register int n = 0;
X	register char *str = key;
X	register int loop;
X	register int len = nbytes;
X
X#define HASHC   n = *str++ + 65599 * n
X
X        if (len > 0) {
X                loop = (len + 8 - 1) >> 3;
X
X                switch(len & (8 - 1)) {
X			case 0: do {		/* All fall throughs */
X					HASHC;  
X				case 7: HASHC;
X				case 6: HASHC;  
X				case 5: HASHC;
X				case 4: HASHC;  
X				case 3: HASHC;
X				case 2: HASHC;  
X				case 1: HASHC;
X                        } while (--loop);
X                }
X
X        }
X	return(n);
X}
X
X/* Hash function from Chris Torek */
Xstatic int
Xhash4(key,nbytes)
Xchar	*key;
Xint	nbytes;
X{
X        register int h = 0;
X	register char *p = key;
X	register int loop;
X	register int len = nbytes;
X
X#define HASH4a   h = (h << 5) - h + *p++;
X#define HASH4b   h = (h << 5) + h + *p++;
X#define HASH4 HASH4b
X
X        if (len > 0) {
X                loop = (len + 8 - 1) >> 3;
X
X                switch(len & (8 - 1)) {
X			case 0: do {		/* All fall throughs */
X					HASH4;  
X				case 7: HASH4;
X				case 6: HASH4;  
X				case 5: HASH4;
X				case 4: HASH4;  
X				case 3: HASH4;
X				case 2: HASH4;  
X				case 1: HASH4;
X                        } while (--loop);
X                }
X
X        }
X	return(h);
X}
@@@End of lq-text/src/ozmahash/hfunc.c
echo x - lq-text/src/ozmahash/hsearch.c 1>&2
sed 's/^X//' >lq-text/src/ozmahash/hsearch.c <<'@@@End of lq-text/src/ozmahash/hsearch.c'
X/*-
X * Copyright (c) 1990 The Regents of the University of California.
X * All rights reserved.
X *
X * This code is derived from software contributed to Berkeley by
X * Margo Seltzer.
X *
X * %sccs.include.redist.c%
X */
X
X#if defined(LIBC_SCCS) && !defined(lint)
Xstatic char sccsid[] = "%W% (Berkeley) %G%";
X#endif /* LIBC_SCCS and not lint */
X#include	<sys/file.h>
X#include	<sys/types.h>
X#include	<stdio.h>
X#include	<db.h>
X#include	<search.h>
X
Xstatic	DB	*dbp = NULL;
Xstatic	ENTRY	retval;
X
Xextern	int
Xhcreate ( nel )
Xunsigned	nel;
X{
X    int	status;
X    HASHINFO	info;
X
X    info.nelem = nel;
X    info.bsize = 256;
X    info.ffactor = 8;
X    info.ncached = NULL;
X    info.hash = NULL;
X    info.lorder = 0;
X    dbp = hash_open ( NULL, O_CREAT|O_RDWR, 0600, &info );
X    return ( (int) dbp );
X}
X
X
Xextern ENTRY	*
Xhsearch ( item, action )
XENTRY	item;
XACTION	action;
X{
X    int	status;
X    DBT	key, val;
X
X    if ( !dbp ) {
X	return(NULL);
X    }
X
X    key.data = item.key;
X    key.size = strlen(item.key) + 1;
X
X    if ( action == ENTER ) {
X	val.data = item.data;
X	val.size = strlen(item.data) + 1;
X	status = (dbp->put) ( dbp, &key, &val, R_NOOVERWRITE );
X	if ( status ) {
X	    return(NULL);
X	} 
X    } else {
X	/* FIND */
X	status = (dbp->get) ( dbp, &key, &val );
X	if ( status ) {
X	    return ( NULL );
X	} else {
X	    item.data = val.data;
X	}
X    }
X    return ( &item );
X}
X
X
Xextern void
Xhdestroy ()
X{
X    if (dbp) {
X	(void)(dbp->close) (dbp);
X	dbp = NULL;
X    }
X    return;
X}
X
X
@@@End of lq-text/src/ozmahash/hsearch.c
echo x - lq-text/src/ozmahash/log2.c 1>&2
sed 's/^X//' >lq-text/src/ozmahash/log2.c <<'@@@End of lq-text/src/ozmahash/log2.c'
X/*-
X * Copyright (c) 1990 The Regents of the University of California.
X * All rights reserved.
X *
X * This code is derived from software contributed to Berkeley by
X * Margo Seltzer.
X *
X * %sccs.include.redist.c%
X */
X
X#if defined(LIBC_SCCS) && !defined(lint)
Xstatic char sccsid[] = "%W% (Berkeley) %G%";
X#endif /* LIBC_SCCS and not lint */
X
Xlog2( num )
Xint	num;
X{
X    register int	i;
X    register int	limit = 1;
X
X    for ( i = 0; limit < num; limit = limit << 1, i++ );
X    return (i);
X}
@@@End of lq-text/src/ozmahash/log2.c
echo x - lq-text/src/ozmahash/mkstemp.c 1>&2
sed 's/^X//' >lq-text/src/ozmahash/mkstemp.c <<'@@@End of lq-text/src/ozmahash/mkstemp.c'
X/*-
X * Copyright (c) 1990 The Regents of the University of California.
X * All rights reserved.
X *
X * This code is derived from software contributed to Berkeley by
X * Margo Seltzer.
X *
X * %sccs.include.redist.c%
X */
X
X#if defined(LIBC_SCCS) && !defined(lint)
Xstatic char sccsid[] = "%W% (Berkeley) %G%";
X#endif /* LIBC_SCCS and not lint */
X
X#include <sys/file.h>
X
Xmkstemp(as)
X	char *as;
X{
X	register char *s;
X	register unsigned int pid;
X	register int fd, i;
X
X	pid = getpid();
X	s = as;
X	while (*s++)
X		/* void */;
X	s--;
X	while (*--s == 'X') {
X		*s = (pid % 10) + '0';
X		pid /= 10;
X	}
X	s++;
X	i = 'a';
X	while ((fd = open(as, O_CREAT|O_EXCL|O_RDWR, 0600)) == -1) {
X		if (i == 'z')
X			return(-1);
X		*s = i++;
X	}
X	return(fd);
X}
@@@End of lq-text/src/ozmahash/mkstemp.c
echo x - lq-text/src/ozmahash/ndbm.c 1>&2
sed 's/^X//' >lq-text/src/ozmahash/ndbm.c <<'@@@End of lq-text/src/ozmahash/ndbm.c'
X/*-
X * Copyright (c) 1990 The Regents of the University of California.
X * All rights reserved.
X *
X * This code is derived from software contributed to Berkeley by
X * Margo Seltzer.
X *
X * %sccs.include.redist.c%
X */
X
X#if defined(LIBC_SCCS) && !defined(lint)
Xstatic char sccsid[] = "%W% (Berkeley) %G%";
X#endif /* LIBC_SCCS and not lint */
X/*
X    This package provides a dbm compatible interface to the new hashing
X    package described in db(3)
X*/
X
X#include <sys/types.h>
X#include <stdio.h>
X#include <ndbm.h>
X#include <hash.h>
X
X/*
X    return 	*DBM on success
X		NULL on failure
X*/
Xextern DBM *
Xdbm_open( file, flags, mode )
Xchar 	*file;
Xint	flags;
Xint	mode;
X{
X    HASHINFO	info;
X
X    info.bsize = 1024;
X    info.ffactor = 256; /* Liam -- smaller databases? */
X    /** info.ffactor = 5; **/
X    info.nelem = 1;
X    info.ncached = NULL;
X    info.hash = NULL;
X    info.lorder = 0;
X    return( hash_open ( file, flags, mode, &info ) );
X}
X
Xextern void 
Xdbm_close(db)
XDBM	*db;
X{
X    (void)(db->close) (db);
X}
X
X/*
X    Returns 	DATUM on success
X		NULL on failure
X*/
Xextern datum 
Xdbm_fetch( db, key )
XDBM 	*db;
Xdatum	key;
X{
X    int	status;
X    datum	retval;
X
X    status = (db->get) ( db, (DBT *)&key, (DBT *)&retval );
X    if ( status ) {
X	retval.dptr = NULL;
X	retval.dsize = 0;
X    }
X    return(retval);
X}
X
X/*
X    Returns 	DATUM on success
X		NULL on failure
X*/
Xextern datum 
Xdbm_firstkey(db)
XDBM 	*db;
X{
X    int	status;
X    datum	retkey;
X    datum	retdata;
X
X    status = (db->seq) ( db, (DBT *)&retkey, (DBT *)&retdata, R_FIRST );
X    if ( status ) {
X	retkey.dptr = NULL;
X	retkey.dsize = 0;
X    }
X    return(retkey);
X}
X/*
X    Returns 	DATUM on success
X		NULL on failure
X*/
Xextern datum 
Xdbm_nextkey(db)
XDBM 	*db;
X{
X    int	status;
X    datum	retkey;
X    datum	retdata;
X
X    status = (db->seq) ( db, (DBT *)&retkey, (DBT *)&retdata, R_NEXT );
X    if ( status ) {
X	retkey.dptr = NULL;
X	retkey.dsize = 0;
X    }
X    return(retkey);
X}
X
X/*
X    0 on success
X    <0 failure
X*/
Xextern int 
Xdbm_delete(db, key)
XDBM 	*db;
Xdatum	key;
X{
X    int	status;
X
X    status = (db->delete)( db, (DBT *)&key );
X    if ( status ) {
X	return(-1);
X    } else {
X	return(0);
X    }
X}
X
X/*
X    0 on success
X    <0 failure
X    1 if DBM_INSERT and entry exists
X*/
Xextern int 
Xdbm_store(db, key, content, flags)
XDBM 	*db;
Xdatum	key;
Xdatum	content;
Xint	flags;
X{
X    return ((db->put)( db, (DBT *)&key, (DBT *)&content, 
X			(flags == DBM_INSERT) ? R_NOOVERWRITE : 0 ));
X}
X
Xextern int
Xdbm_error(db)
XDBM	*db;
X{
X    HTAB	*hp;
X
X    hp = (HTAB *)db->internal;
X    return ( hp->errno );
X}
X
Xextern int
Xdbm_clearerr(db)
XDBM	*db;
X{
X    HTAB	*hp;
X
X    hp = (HTAB *)db->internal;
X    hp->errno = 0;
X    return ( 0 );
X}
@@@End of lq-text/src/ozmahash/ndbm.c
echo x - lq-text/src/ozmahash/ndbm.h 1>&2
sed 's/^X//' >lq-text/src/ozmahash/ndbm.h <<'@@@End of lq-text/src/ozmahash/ndbm.h'
X/*
X * Copyright (c) 1989 The Regents of the University of California.
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms are permitted
X * provided that the above copyright notice and this paragraph are
X * duplicated in all such forms and that any documentation,
X * advertising materials, and other materials related to such
X * distribution and use acknowledge that the software was developed
X * by the University of California, Berkeley.  The name of the
X * University may not be used to endorse or promote products derived
X * from this software without specific prior written permission.
X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
X * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
X *
X *	%W% (Berkeley) %G%
X */
X#include <db.h>
X
X/* Map dbm interface onto hash(3) */
X#define DBM_RDONLY	O_RDONLY
X
X/* Flags to dbm_store() */
X#define DBM_INSERT      0
X#define DBM_REPLACE     1
X
Xtypedef struct {
X	char 	*dptr;
X	int	dsize;
X} datum;
X
Xtypedef DB	DBM;
X
X#ifdef __STDC__ || c_plusplus
Xextern DBM *dbm_open(const char *, int, int);
Xextern void dbm_close(DBM *);
Xextern datum dbm_fetch(DBM *, datum);
Xextern datum dbm_firstkey(DBM *);
Xextern datum dbm_nextkey(DBM *);
Xextern long dbm_forder(DBM *, datum);
Xextern int dbm_delete(DBM *, datum);
Xextern int dbm_store(DBM *, datum, datum, int);
X#else
Xextern DBM *dbm_open();
Xextern void dbm_close();
Xextern datum dbm_fetch();
Xextern datum dbm_firstkey();
Xextern datum dbm_nextkey();
Xextern long dbm_forder();
Xextern int dbm_delete();
Xextern int dbm_store();
X#endif
@@@End of lq-text/src/ozmahash/ndbm.h
echo x - lq-text/src/ozmahash/page.c 1>&2
sed 's/^X//' >lq-text/src/ozmahash/page.c <<'@@@End of lq-text/src/ozmahash/page.c'
X/*-
X * Copyright (c) 1990 The Regents of the University of California.
X * All rights reserved.
X *
X * This code is derived from software contributed to Berkeley by
X * Margo Seltzer.
X *
X * %sccs.include.redist.c%
X */
X
X#if defined(LIBC_SCCS) && !defined(lint)
Xstatic char sccsid[] = "%W% (Berkeley) %G%";
X#endif /* LIBC_SCCS and not lint */
X/******************************************************************************
XPACKAGE:  hashing
X
XDESCRIPTION: 
X	Page manipulation for hashing package.
X
XROUTINES: 
X    External
X	get_page
X	add_ovflpage
X    Internal
X	overflow_page
X	open_temp
X******************************************************************************/
X
X#include <sys/types.h>
X#include <sys/file.h>
X#include <assert.h>
X#include <errno.h>
X#include <db.h>
X#include <hash.h>
X#include <page.h>
X#include <stdio.h>
X#include <endian.h>
X
X/* Externals */
X/* buf.c */
Xextern BUFHEAD	*get_buf();
Xextern void reclaim_buf();
X
X/* big.c */
Xextern int big_split();
Xextern int big_insert();
Xextern int big_delete();
Xextern int find_bigpair();
X
X/* dynahash.c */
Xextern	int	call_hash();
Xextern	int	expand_table();
X
X/* my externals */
Xextern int get_page();
Xextern BUFHEAD *add_ovflpage();
Xextern int split_page();
Xextern int  addel();
X
X/* my internals */
Xstatic u_short overflow_page();
Xstatic int open_temp();
Xstatic void squeeze_key();
Xstatic void putpair();
X
X#ifdef HASH_STATISTICS
Xextern long hash_accesses, hash_collisions, hash_expansions, hash_overflows;
X#endif
X#define	PAGE_INIT(P)					\
X{							\
X    ((u_short *)P)[0] = 0;				\
X    ((u_short *)P)[1] = hashp->BSIZE - 3 * sizeof(u_short);	\
X    ((u_short *)P)[2] = hashp->BSIZE;			\
X}
X
X/*
X	This is called AFTER we have verified that there is room on the
X	page for the pair (PAIRFITS has returned true) so we go right
X	ahead and start moving stuff on.
X*/
Xstatic void
Xputpair(p, key, val)
Xchar *p;
XDBT *key;
XDBT *val;
X{
X	register u_short n;
X	register u_short off;
X	register u_short *bp = (u_short *) p;
X
X/* enter the key first */
X	n = bp[0];
X
X	off = OFFSET(bp) - key->size;
X	bcopy( key->data, p+off, key->size );
X	bp[++n] = off;
X
X/* now the data */
X	off -= val->size;
X	bcopy(val->data,  p + off, val->size);
X	bp[++n] = off;
X
X/* adjust page info */
X	bp[0] = n;
X	bp[n+1] = off - ((n+3)*sizeof(u_short));
X	bp[n+2] = off;
X	return;
X}
X/*
X    0 OK
X    -1 error
X*/
Xextern int
Xdelpair(bufp, ndx)
XBUFHEAD *bufp;
Xregister int ndx;
X{
X	register u_short *bp = (u_short *) bufp->page;
X	register int n = bp[0];
X	register u_short newoff;
X	u_short pairlen;
X
X	if ( bp[ndx+1] < REAL_KEY ) return ( big_delete ( bufp, ndx ) );
X	if ( ndx != 1 ) newoff = bp[ndx-1];
X	else newoff = hashp->BSIZE;
X	pairlen = newoff - bp[ndx+1];
X
X	if ( ndx != (n-1) ) {
X		/* Hard Case -- need to shuffle keys */
X		register int i;
X		register char *src = bufp->page + (int)OFFSET(bp);
X		register char *dst = src + (int)pairlen;
X		bcopy ( src, dst, bp[ndx+1] - OFFSET(bp) );
X
X		/* Now adjust the pointers */
X		for ( i = ndx+2; i <= n; i += 2 ) {
X		    if ( bp[i+1]  == OVFLPAGE ) {
X			bp[i-2] = bp[i];
X			bp[i-1] = bp[i+1];
X		    } else {
X			bp[i-2] = bp[i] + pairlen;
X			bp[i-1] = bp[i+1] + pairlen;
X		    }
X		}
X	}
X
X	/* Finally adjust the page data */
X	bp[n] = OFFSET(bp) + pairlen;
X	bp[n-1] = bp[n+1] + pairlen + 2 * sizeof(u_short);
X	bp[0] = n-2;
X	hashp->NKEYS--;
X
X	bufp->flags |= BUF_MOD;
X	return (0);
X}
X/*
X    -1 ==> Error
X    0 ==> OK
X*/
Xextern int
Xsplit_page(obucket, nbucket)
Xint obucket;
Xint nbucket;
X{
X	DBT key;
X	DBT val;
X
X	register BUFHEAD *new_bufp;
X	register BUFHEAD *old_bufp;
X	register u_short *ino;
X	register char	*np;
X	char	*op;
X
X	u_short copyto = (u_short)hashp->BSIZE;
X	u_short off = (u_short)hashp->BSIZE;
X	int n;
X	u_short diff;
X	u_short moved;
X	int ndx;
X
X	old_bufp = get_buf ( obucket, NULL, 0 );
X	new_bufp = get_buf ( nbucket, NULL, 0 );
X
X	old_bufp->flags |= BUF_MOD;
X	new_bufp->flags |= BUF_MOD;
X
X	ino = (u_short *)(op = old_bufp->page);
X	np = new_bufp->page;
X
X	moved = 0;
X
X	for (n = 1, ndx = 1; n < ino[0]; n+=2) {
X		if ( ino[n+1] < REAL_KEY ) {
X		    return ( ugly_split( obucket, old_bufp, new_bufp, 
X					 copyto, moved ) );
X		}
X		key.data = op + ino[n]; 
X		key.size = off - ino[n];
X
X		if ( call_hash ( key.data, key.size ) == obucket ) {
X		    /* Don't switch page */
X		    diff = copyto - off;
X		    if ( diff ) {
X			copyto = ino[n+1] + diff;
X			bcopy ( op + ino[n+1], op + copyto,  off-ino[n+1]);
X			ino[ndx] = copyto + ino[n] - ino[n+1];
X			ino[ndx+1] = copyto;
X		    } else copyto = ino[n+1];
X		    ndx += 2;
X		} else {
X		    /* Switch page */
X		    val.data = op + ino[n+1];
X		    val.size = ino[n] - ino[n+1];
X		    putpair( np, &key, &val);
X		    moved +=2;
X		}
X
X		off = ino[n+1];
X	}
X
X	/* Now clean up the page */
X	ino[0] -= moved;
X	FREESPACE(ino) = copyto - sizeof(u_short) * (ino[0]+3);
X	OFFSET(ino) = copyto;
X
X#ifdef DEBUG3
X	fprintf(stderr, "split %d/%d\n", 
X	       ((u_short *) np)[0] / 2,
X	       ((u_short *) op)[0] / 2);
X#endif
X	return(0);
X}
X/*
X    0 ==> success
X    -1 ==> failure
X
X    Called when we encounter an overflow page during split handling.
X    this is special cased since we have to begin checking whether
X    the key/data pairs fit on their respective pages and because
X    we may need overflow pages for both the old and new pages
X*/
Xstatic int
Xugly_split( obucket, old_bufp, new_bufp, copyto, moved )
Xint	obucket;	/* Same as split_page */
XBUFHEAD	*old_bufp;		
XBUFHEAD	*new_bufp;
Xu_short	copyto;		/* First byte on page which contains key/data values */
Xint	moved;		/* number of pairs moved to new page */
X{
X    register BUFHEAD *bufp = old_bufp;		/* Buffer header for ino */
X    register u_short	*ino = (u_short *)old_bufp->page;	
X						/* Page keys come off of */
X    register u_short	*np = (u_short *)new_bufp->page;	/* New page */
X    register u_short	*op = (u_short *)old_bufp->page;	
X						/* Page keys go on to if they
X						   aren't moving */
X
X    char	*cino;				/* Character value of ino */
X    BUFHEAD	*last_bfp = NULL;		/* Last buffer header OVFL which
X						   needs to be freed */
X    u_short	ov_addr, last_addr = 0;
X    u_short	n;
X    u_short	off;
X
X    DBT	key, val;
X    SPLIT_RETURN	ret;
X
X    n = ino[0]-1;
X    while ( n < ino[0] ) {
X	if ( ino[n+1] == OVFLPAGE ) {
X	    ov_addr = ino[n];
X	    /* 
X		Fix up the old page -- the extra 2 are the fields which
X		contained the overflow information 
X	    */
X	    ino[0] -= (moved + 2);
X	    FREESPACE(ino) = copyto - sizeof(u_short) * (ino[0]+3);
X	    OFFSET(ino) = copyto;
X
X	    bufp = get_buf ( ov_addr, bufp, 0 );
X	    if ( !bufp ) return(-1);
X
X	    ino = (u_short *)bufp->page;
X	    n = 1;
X	    off = copyto = hashp->BSIZE;
X	    moved = 0;
X
X	    if ( last_bfp ) {
X		free_ovflpage( last_bfp);
X	    }
X	    last_bfp = bufp;
X	} 
X
X	if ( (ino[2] < REAL_KEY) && (ino[2] != OVFLPAGE) ) {
X	    if (big_split (old_bufp, new_bufp, bufp, ov_addr, obucket, &ret)) {
X		return(-1);
X	    }
X	    old_bufp = ret.oldp;
X	    if ( !old_bufp ) return(-1);
X	    op = (u_short *)old_bufp->page;
X	    new_bufp = ret.newp;
X	    if ( !new_bufp ) return(-1);
X	    np = (u_short *)new_bufp->page;
X	    bufp = ret.nextp;
X	    if ( !bufp ) return(0);
X	    cino = (char *)bufp->page;
X	    ino = (u_short *)cino;
X	    last_bfp = ret.nextp;
X	}
X	
X
X	for ( n = 1; (n < ino[0]) && (ino[n+1] >= REAL_KEY); n += 2 ) {
X	    cino = (char *)ino;
X	    key.data = cino + ino[n]; 
X	    key.size = off - ino[n];
X	    val.data = cino + ino[n+1];
X	    val.size = ino[n] - ino[n+1];
X	    off = ino[n+1];
X
X	    if ( call_hash ( key.data, key.size ) == obucket ) {
X		/* Keep on old page */
X		if (PAIRFITS(op,(&key),(&val))) putpair((char *)op, &key, &val);
X		else {
X		    old_bufp = add_ovflpage ( old_bufp );
X		    if ( !old_bufp ) return(-1);
X		    op = (u_short *)old_bufp->page;
X		    putpair ((char *)op, &key, &val);
X		}
X		old_bufp->flags |= BUF_MOD;
X	    } else {
X		/* Move to new page */
X		if (PAIRFITS(np,(&key),(&val))) putpair((char *)np, &key, &val);
X		else {
X		    new_bufp = add_ovflpage ( new_bufp );
X		    if ( !new_bufp )return(-1);
X		    np = (u_short *)new_bufp->page;
X		    putpair ((char *)np, &key, &val);
X		} 
X		new_bufp->flags |= BUF_MOD;
X	    }
X	}
X    }
X    if ( last_bfp ) {
X	free_ovflpage(last_bfp);
X    }
X
X    return (0);
X}
X/*
X    Add the given pair to the page
X    1 ==> failure
X    0 ==> OK
X*/
Xextern int
Xaddel(bufp, key, val)
XBUFHEAD	*bufp;
XDBT	*key;
XDBT	*val;
X{
X    register u_short *bp = (u_short *)bufp->page;
X    register u_short *sop;
X    int	do_expand;
X
X    do_expand = 0;
X    while ( bp[0] &&  (bp[bp[0]] < REAL_KEY) ) {
X	/* Exception case */
X	if ( bp[2] < REAL_KEY ) {
X	    /* This is a big-keydata pair */
X	    bufp = add_ovflpage(bufp);
X	    if ( !bufp ) {
X		return(-1);
X	    }
X	    bp = (u_short *)bufp->page;
X	} else {
X	    /* Try to squeeze key on this page */
X	    if ( FREESPACE(bp) > PAIRSIZE(key,val) ) {
X		squeeze_key ( bp, key, val );
X		return(0);
X	    } else {
X		bufp = get_buf ( bp[bp[0]-1], bufp, 0 );
X		if (!bufp) {
X		    return(-1);
X		}
X		bp = (u_short *)bufp->page;
X	    }
X	}
X    }
X
X    if ( PAIRFITS(bp,key,val) ) putpair (bufp->page, key, val);
X    else {
X	do_expand = 1;
X	bufp = add_ovflpage ( bufp );
X	if (!bufp)return(-1);
X	sop = (u_short *) bufp->page;
X
X	if ( PAIRFITS(sop, key, val) ) putpair ( (char *)sop, key, val );
X	else if ( big_insert ( bufp, key, val ) ) {
X	    return(-1);
X	}
X    }
X    bufp->flags |= BUF_MOD;
X    /* 
X	If the average number of keys per bucket exceeds the fill factor,
X	expand the table
X    */
X    hashp->NKEYS++;
X    if (do_expand || 
X	(hashp->NKEYS / (hashp->MAX_BUCKET+1) > hashp->FFACTOR) ) {
X	 return(expand_table());
X    }
X    return(0);
X}
X
X/*
X    returns a pointer, NULL on error
X*/
Xextern BUFHEAD *
Xadd_ovflpage ( bufp )
XBUFHEAD	*bufp;
X{
X    register	u_short	*sp = (u_short *)bufp->page;
X
X    u_short	ovfl_num;
X    u_short	ndx, newoff;
X    char	*op;
X    DBT	okey, oval;
X#ifdef DEBUG1
X    int	tmp1, tmp2;
X#endif
X
X    bufp->flags |= BUF_MOD;
X    ovfl_num = overflow_page ();
X#ifdef DEBUG1
X    tmp1 = bufp->addr;
X    tmp2 = bufp->ovfl?bufp->ovfl->addr:0;
X#endif
X    if (!ovfl_num || !(bufp->ovfl = get_buf ( ovfl_num, bufp, 1 ))) {
X	return(NULL);
X    }
X    bufp->ovfl->flags |= BUF_MOD;
X#ifdef DEBUG1
X    fprintf ( stderr, "ADDOVFLPAGE: %d->ovfl was %d is now %d\n", tmp1, tmp2, 
X		bufp->ovfl->addr );
X#endif
X    ndx = sp[0];
X    /* 
X	Since a pair is allocated on a page only if there's room
X	to add an overflow page, we know that the OVFL information
X	will fit on the page
X    */
X    sp[ndx+4] = OFFSET(sp);
X    sp[ndx+3] = FREESPACE(sp) - OVFLSIZE;
X    sp[ndx+1] = ovfl_num;
X    sp[ndx+2] = OVFLPAGE;
X    sp[0] = ndx+2;
X#ifdef HASH_STATISTICS
X	    hash_overflows++;
X#endif
X    return(bufp->ovfl);
X}
X
X/*
X    0 indicates SUCCESS
X    -1 indicates FAILURE
X*/
Xextern	int
Xget_page ( p, bucket, is_bucket, is_disk, is_bitmap )
Xchar	*p;
Xint	bucket;
Xint	is_bucket;
Xint	is_disk;
Xint	is_bitmap;
X{
X    register int size;
X    register int fd;
X    register int page;
X    u_short	*bp;
X    int		rsize;
X
X    fd = hashp->fp;
X    size = hashp->BSIZE;
X
X    if ( !fd || (hashp->new_file && !is_disk) ) {
X	PAGE_INIT(p);
X	return(0);
X    }
X
X    if ( is_bucket) page = BUCKET_TO_PAGE (bucket);
X    else page = OADDR_TO_PAGE (bucket);
X    if ((lseek ( fd, page << hashp->BSHIFT, L_SET ) == -1) || 
X	((rsize = read ( fd, p, size )) == -1 )) {
X	return(-1);
X    } 
X    if ( rsize != size ) {
X	errno = EFTYPE;		
X	return(-1);
X    }
X    bp = (u_short *)p;
X    if (!bp[0]) {
X	PAGE_INIT(p);
X    } else if ( hashp->LORDER != BYTE_ORDER ) {
X	register int i;
X	register int max;
X
X	if ( is_bitmap ) {
X	    max = hashp->BSIZE >> 2;	/* divide by 4 */
X	    for ( i=0; i < max; i++ ) {
X		BLSWAP(((long *)p)[i]);
X	    }
X	} else {
X	    BSSWAP(bp[0]);
X	    max = bp[0] + 2;
X	    for ( i=1; i <= max; i++ ) {
X		BSSWAP(bp[i]);
X	    }
X	}
X    }
X    return (0);
X}
X
X/* 
X    Write page p to disk
X    -1==>failure
X    0==> OK
X*/
Xextern int
Xput_page ( p, bucket, is_bucket, is_bitmap )
Xchar	*p;
Xint	bucket;
Xint	is_bucket;
Xint	is_bitmap;
X{
X    register int size;
X    register int fd;
X    register int page;
X    int	wsize;
X
X    size = hashp->BSIZE;
X    if ( !hashp->fp && open_temp() ) return (1);
X    fd = hashp->fp;
X
X    if ( hashp->LORDER != BYTE_ORDER ) {
X	register int i;
X	register int max;
X
X	if ( is_bitmap ) {
X	    max = hashp->BSIZE >> 2;	/* divide by 4 */
X	    for ( i=0; i < max; i++ ) {
X		BLSWAP(((long *)p)[i]);
X	    }
X	} else {
X	    max = ((u_short *)p)[0] + 2;
X	    for ( i=0; i <= max; i++ ) {
X		BSSWAP(((u_short *)p)[i]);
X	    }
X	}
X    }
X    if (is_bucket ) page = BUCKET_TO_PAGE (bucket);
X    else page = OADDR_TO_PAGE ( bucket );
X    if ((lseek ( fd, page << hashp->BSHIFT, L_SET ) == -1) || 
X	((wsize = write ( fd, p, size )) == -1 )) {
X	/* Errno is set */
X	return(-1);
X    }
X    if ( wsize != size ) {
X	errno = EFTYPE;	
X	return(-1);
X    }
X    return(0);
X}
X#define BYTE_MASK	((1 << INT_BYTE_SHIFT) -1)
X/*
X    Initialize a new bitmap page.  Bitmap pages are left in memory
X    once they are read in.
X*/
Xextern u_long *
Xinit_bitmap(pnum, nbits, ndx)
Xu_short	pnum;
Xint	nbits;
Xint	ndx;
X{
X    u_long	*ip;
X    int		clearints;
X    int		clearbytes;
X
X    if ( !(ip = (u_long *)malloc (hashp->BSIZE)) ) return (NULL);
X    clearints = ((nbits - 1) >> INT_BYTE_SHIFT) + 1;
X    clearbytes = clearints << INT_TO_BYTE;
X    memset ((char *)ip, 0, clearbytes );
X    memset ( ((char *) ip) + clearbytes, 0xFF, 
X		hashp->BSIZE-clearbytes );
X    ip[clearints-1] = ALL_SET << (nbits & BYTE_MASK);
X    SETBIT(ip, 0);
X    hashp->BITMAPS[ndx] = pnum;
X    hashp->mapp[ndx] = ip;
X    return(ip);
X}
Xstatic int
Xfirst_free ( map )
Xu_long map;
X{
X    register u_long      mask;
X    register u_long      i;
X
X    mask = 0x1;
X    for ( i=0; i < BITS_PER_MAP; i++ ) {
X	if ( !(mask & map) ) return(i);
X	mask = mask << 1;
X    }
X    return ( i );
X}
X
Xstatic u_short
Xoverflow_page ( )
X{
X    register	int max_free;
X    register	int splitnum;
X    register	u_long *freep;
X    register	int offset;
X    u_short	addr;
X    int		in_use_bits;
X    int		free_page, free_bit;
X    int		i, j, bit;
X#ifdef DEBUG2
X    int	tmp1, tmp2;
X#endif
X
X    splitnum = log2(hashp->MAX_BUCKET);
X    max_free = hashp->SPARES[splitnum];
X
X    free_page = (max_free-1) >> (hashp->BSHIFT + BYTE_SHIFT);
X    free_bit  = (max_free-1) & ((hashp->BSIZE << BYTE_SHIFT) - 1);
X
X    /* Look through all the free maps to find the first free block */
X    for ( i = 0; i <= free_page; i++ ) {
X	if (!(freep = (u_long *)hashp->mapp[i]) ) return(NULL);
X	if ( i == free_page ) in_use_bits = free_bit;
X	else in_use_bits = (hashp->BSIZE << BYTE_SHIFT) -1;
X
X	for (j = 0, bit = 0; bit <= in_use_bits; j++, bit += BITS_PER_MAP ) {
X	     if ( freep[j] != ALL_SET ) goto found;
X	}
X    }
X    /* No Free Page Found */
X    hashp->SPARES[splitnum]++;
X    offset = hashp->SPARES[splitnum] - 
X	     (splitnum ? hashp->SPARES[splitnum-1] : 0);
X
X    /* Check if we need to allocate a new bitmap page */
X    if ( free_bit == (hashp->BSIZE << BYTE_SHIFT) - 1 ) {
X	free_page++;
X	if ( free_page >= NCACHED ) {
X	    fprintf ( stderr, 
X		"HASH: Out of overflow pages.  Increase page size\n" );
X	    return(NULL);
X	}
X	/* 
X	    This is tricky.  The 1 indicates that you want the
X	    new page allocated with 1 clear bit.  Actually, you
X	    are going to allocate 2 pages from this map.  The first
X	    is going to be the map page, the second is the overflow
X	    page we were looking for.  The init_bitmap routine
X	    automatically, sets the first bit of itself to indicate
X	    that the bitmap itself is in use.  We would explicitly
X	    set the second bit, but don't have to if we tell init_bitmap
X	    not to leave it clear in the first place.
X	*/
X	init_bitmap ( OADDR_OF(splitnum, offset), 1, free_page );
X	hashp->SPARES[splitnum]++;
X#ifdef DEBUG2
X	free_bit = 2;
X#endif
X	offset++;
X    } else {
X	/* 
X		Free_bit addresses the last used bit.  Bump it to
X		address the first available bit.
X	*/	
X	free_bit++;
X	SETBIT ( freep, free_bit );
X    }
X
X    /* Calculate address of the new overflow page */
X    if ( offset > SPLITMASK ) {
X	fprintf ( stderr, 
X	    "HASH: Out of overflow pages.  Increase page size\n" );
X	return(NULL);
X    }
X    addr = OADDR_OF(splitnum, offset);
X#ifdef DEBUG2
X    fprintf ( stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n",
X		addr, free_bit, free_page );
X#endif
X    return(addr);
X
Xfound:
X    bit = bit + first_free(freep[j]);
X    SETBIT(freep,bit);
X#ifdef DEBUG2
X    tmp1 = bit;
X    tmp2 = i;
X#endif
X    /* 
X	Bits are addressed starting with 0, but overflow pages are
X	addressed beginning at 1. Bit is a bit addressnumber, so we 
X	need to increment it to convert it to a page number.
X    */
X    bit = 1 + bit + (i * (hashp->BSIZE << BYTE_SHIFT));
X
X    /* Calculate the split number for this page */
X    for ( i = 0; (i < splitnum) && (bit > hashp->SPARES[i]); i++ );
X    offset =(i ? bit - hashp->SPARES[i-1] : bit );
X    if ( offset >= SPLITMASK ) return(NULL);/* Out of overflow pages */
X    addr = OADDR_OF(i, offset);
X#ifdef DEBUG2
X    fprintf ( stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n",
X		addr, tmp1, tmp2 );
X#endif
X
X    /* Allocate and return the overflow page */
X    return (addr);
X}
X
X/*
X    Mark this overflow page as free.
X*/
Xfree_ovflpage ( obufp )
XBUFHEAD	*obufp;
X{
X    register u_short addr = obufp->addr;
X    int	free_page, free_bit;
X    int bit_address;
X    u_short ndx;
X    u_long *freep;
X    int j;
X
X#ifdef DEBUG1
X    fprintf ( stderr, "Freeing %d\n", addr );
X#endif
X    ndx = (((u_short)addr) >> SPLITSHIFT);
X    bit_address = (ndx ? hashp->SPARES[ndx-1] : 0) + (addr & SPLITMASK) - 1;
X    free_page = (bit_address >> (hashp->BSHIFT + BYTE_SHIFT));
X    free_bit  = bit_address & ((hashp->BSIZE << BYTE_SHIFT) - 1);
X
X    freep = hashp->mapp[free_page];
X    assert(freep);
X    CLRBIT(freep, free_bit);
X#ifdef DEBUG2
X    fprintf ( stderr, "FREE_OVFLPAGE: ADDR: %d BIT: %d PAGE %d\n",
X		obufp->addr, free_bit, free_page );
X#endif
X    reclaim_buf ( obufp );
X    return;
X}
X
X/*
X0 success
X-1 failure
X*/
Xstatic int
Xopen_temp()
X{
X    char        *namestr = "_hashXXXXXX";
X
X    if ((hashp->fp = mkstemp ( namestr )) == -1){
X	return (-1);
X    }
X    unlink(namestr);    /* Make sure file goes away at process exit*/
X    return(0);
X}
X
X/* 
X    We have to know that the key will fit, but the
X    last entry on the page is an overflow pair, so we
X    need to shift things.
X*/
Xstatic void
Xsqueeze_key ( sp, key, val )
Xu_short	*sp;
XDBT	*key;
XDBT	*val;
X{
X    register	char	*p = (char *)sp;
X    u_short	free_space, off;
X    u_short	pageno, n;
X
X    n = sp[0];
X    free_space = FREESPACE(sp);
X    off = OFFSET(sp);
X
X    pageno = sp[n-1];
X    off -= key->size;
X    sp[n-1] = off;
X    bcopy ( key->data, p + off, key->size );
X    off -= val->size;
X    sp[n] = off;
X    bcopy ( val->data, p + off, val->size );
X    sp[0] = n+2;
X    sp[n+1] = pageno;
X    sp[n+2] = OVFLPAGE;
X    FREESPACE(sp) = free_space - PAIRSIZE(key,val);
X    OFFSET(sp) = off;
X}
X
X#ifdef DEBUG4
Xprint_chain ( addr )
Xshort	addr;
X{
X	BUFHEAD	*bufp;
X	short	*bp;
X	short	oaddr;
X
X	fprintf ( stderr, "%d ", addr );
X	bufp = get_buf ( (int)addr, NULL, 0 );
X	bp = (short *)bufp->page;
X	while ( bp[0] && 
X		((bp[bp[0]] == OVFLPAGE) ||
X		 ((bp[0] > 2) && bp[2] < REAL_KEY))) {
X		oaddr = bp[bp[0]-1];
X		fprintf ( stderr, "%d ", (int)oaddr );
X		bufp = get_buf ( (int)oaddr, bufp, 0 );
X		bp = (short *)bufp->page;
X	}
X	fprintf ( stderr, "\n" );
X}
X#endif
@@@End of lq-text/src/ozmahash/page.c
echo x - lq-text/src/ozmahash/page.h 1>&2
sed 's/^X//' >lq-text/src/ozmahash/page.h <<'@@@End of lq-text/src/ozmahash/page.h'
X/*
X * Copyright (c) 1989 The Regents of the University of California.
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms are permitted
X * provided that the above copyright notice and this paragraph are
X * duplicated in all such forms and that any documentation,
X * advertising materials, and other materials related to such
X * distribution and use acknowledge that the software was developed
X * by the University of California, Berkeley.  The name of the
X * University may not be used to endorse or promote products derived
X * from this software without specific prior written permission.
X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
X * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
X *
X *	%W% (Berkeley) %G%
X */
X/*
X    RCS INFO
X    $header$
X
X    Definitions for hashing page file format.
X*/
Xextern	HTAB	*hashp;
X/*
X * routines dealing with a data page
X *
X * page format:
X *	+------------------------------+
X * p	| n | keyoff | datoff | keyoff |
X * 	+------------+--------+--------+
X *	| datoff | free  |  ptr  | --> |
X *	+--------+---------------------+
X *	|	 F R E E A R E A       |
X *	+--------------+---------------+
X *	|  <---- - - - | data          |
X *	+--------+-----+----+----------+
X *	|  key   | data     | key      |
X *	+--------+----------+----------+
X *
X * Pointer to the free space is always:  p[p[0] + 2]
X * Amount of free space on the page is:  p[p[0] + 1]
X */
X
X/*
X    How many bytes required for this pair?
X    2 shorts in the table at the top of the page +
X    room for the key and room for the data
X
X    We prohibit entering a pair on a page unless there is also
X    room to append an overflow page. The reason for this it that
X    you can get in a situation where a single key/data pair fits
X    on a page, but you can't append an overflow page and later
X    you'd have to split the key/data and handle like a big pair.
X    You might as well do this up front.
X
X*/
X#define	PAIRSIZE(K,D)	(2*sizeof(u_short) + K->size + D->size)
X#define BIGOVERHEAD	(4*sizeof(u_short))
X#define KEYSIZE(K)	(4*sizeof(u_short) + K->size);
X#define OVFLSIZE	(2*sizeof(u_short))
X#define FREESPACE(P)	(P[P[0]+1])
X#define	OFFSET(P)	(P[P[0]+2])
X#define PAIRFITS(P,K,D)	((P[1] >= REAL_KEY) && \
X			 (PAIRSIZE(K,D) + OVFLSIZE) <= FREESPACE(P))
X#define PAGE_META(N)	((N+3) * sizeof(u_short))
X
X#define	MIN(A,B)	(A<=B?A:B)
X
Xtypedef struct {
X    BUFHEAD	*newp;
X    BUFHEAD	*oldp;
X    BUFHEAD	*nextp;
X    u_short	next_addr;
X} SPLIT_RETURN;
X
@@@End of lq-text/src/ozmahash/page.h
echo x - lq-text/src/ozmahash/search.h 1>&2
sed 's/^X//' >lq-text/src/ozmahash/search.h <<'@@@End of lq-text/src/ozmahash/search.h'
X/*-
X * Copyright (c) 1990 The Regents of the University of California.
X * All rights reserved.
X *
X * This code is derived from software contributed to Berkeley by
X * Margo Seltzer.
X *
X * %sccs.include.redist.c%
X *
X *	%W% (Berkeley) %G%
X */
X
X/* Backward Compatibility to hsearch interface */
Xtypedef struct entry { 
X	char	*key; 
X	char	*data; 
X} ENTRY;
X
Xtypedef enum { FIND, ENTER } ACTION;
@@@End of lq-text/src/ozmahash/search.h
if ! test -d lq-text/src/sdbm
then
    cd lq-text/src
    sh UseHash
fi
echo End of the last part
exit 0
-- 
Liam R. E. Quin,  lee@sq.com, SoftQuad Inc., Toronto, +1 (416) 963-8337