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