www.pudn.com > pueblo.zip > ChNdbm.cpp
/*----------------------------------------------------------------------------
_ _ _
/\ | | | (_)
/ \ _ __ __| |_ __ ___ _ __ ___ ___ __| |_ __ _
/ /\ \ | '_ \ / _` | '__/ _ \| '_ ` _ \ / _ \/ _` | |/ _` |
/ ____ \| | | | (_| | | | (_) | | | | | | __/ (_| | | (_| |
/_/ \_\_| |_|\__,_|_| \___/|_| |_| |_|\___|\__,_|_|\__,_|
The contents of this file are subject to the Andromedia Public
License Version 1.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of
the License at http://www.andromedia.com/APL/
Software distributed under the License is distributed on an
"AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
implied. See the License for the specific language governing
rights and limitations under the License.
The Original Code is Pueblo client code, released November 4, 1998.
The Initial Developer of the Original Code is Andromedia Incorporated.
Portions created by Andromedia are Copyright (C) 1998 Andromedia
Incorporated. All Rights Reserved.
Andromedia Incorporated 415.365.6700
818 Mission Street - 2nd Floor 415.365.6701 fax
San Francisco, CA 94103
Contributor(s):
--------------------------------------------------------------------------
Chaco team: Dan Greening, Glenn Crocker, Jim Doubek,
Coyote Lussier, Pritham Shetty.
Wrote and designed original codebase.
------------------------------------------------------------------------------
ndbm database implementation
----------------------------------------------------------------------------*/
/*
* Copyright (c) 1983 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*/
#include "headers.h"
#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid[] = "@(#)ndbm.c 5.4 (Berkeley) 9/4/87";
#endif //LIBC_SCCS and not lint
#include "headers.h"
#include
#ifdef CH_UNIX
#include
#define _O_RDONLY O_RDONLY
#define _O_WRONLY O_WRONLY
#define _O_RDWR O_RDWR
#define _open open
#define _fstat fstat
#define _close close
#define _lseek lseek
#define _read read
#define _write write
#define _commit(foo)
#endif
#include
#include
//#include
#include
#include
#ifdef CH_MSW
#include
#endif
#include
#include
#include
#include
#include "ChNdbm.h"
/*----------------------------------------------------------------------------
Constants:
----------------------------------------------------------------------------*/
#define BYTESIZ 8
#undef setbit
#define dbm_rdonly(db) ((db)->dbm_flags & _DBM_RDONLY)
/* for flock(2) and fstat(2) */
#define dbm_dirfno(db) ((db)->dbm_dirf)
#define dbm_pagfno(db) ((db)->dbm_pagf)
/*----------------------------------------------------------------------------
Types:
----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
Variables:
----------------------------------------------------------------------------*/
static datum makdatum();
static long finddatum();
static chint32 additem();
static chint32 delitem();
static long dcalchash();
#if defined( CH_ARCH_32 )
CH_INTERN_VAR HANDLE hMutex = 0;
#define CH_DB_MUTEX_NAME "ChacoDBSyncMutex"
#endif
/*----------------------------------------------------------------------------
Functions:
----------------------------------------------------------------------------*/
CH_INTERN_FUNC( chint32 )
getbit(register DBM *db);
CH_INTERN_FUNC( void )
setbit(register DBM *db);
CH_INTERN_FUNC( void )
dbm_access(register DBM *db, long hash);
CH_INTERN_FUNC( datum )
makdatum(char buf[PBLKSIZ], long n);
CH_INTERN_FUNC( long )
finddatum(char buf[PBLKSIZ], datum item);
CH_INTERN_FUNC( long )
dcalchash(datum item);
CH_INTERN_FUNC( chint32 )
delitem(char buf[PBLKSIZ], long n);
CH_INTERN_FUNC( chint32 )
additem(char buf[PBLKSIZ], datum item, datum item1);
#ifdef DEBUG
CH_INTERN_FUNC( chint32 )
chkblk(char buf[PBLKSIZ]);
#endif
CH_INTERN_FUNC( int )
ReadData( int iFile, long pos, void* pBuf, long lBufSize );
CH_INTERN_FUNC( int )
WriteData( int iFile, long pos, void* pBuf, long lBufSize );
/*----------------------------------------------------------------------------
Macros:
----------------------------------------------------------------------------*/
CH_GLOBAL_FUNC(DBM *)
dbm_open( pstr file, int flags, int mode)
{
#if defined( CH_MSW ) && defined( CH_ARCH_32 )
if ( hMutex == 0)
{
hMutex = CreateMutex( NULL, false, CH_DB_MUTEX_NAME );
if ( GetLastError() == ERROR_ALREADY_EXISTS )
{
hMutex = OpenMutex( MUTEX_ALL_ACCESS | SYNCHRONIZE, false, CH_DB_MUTEX_NAME );
}
}
#endif
#ifdef CH_MSW
struct _stat statb;
#else
struct stat statb;
#endif
register DBM *db;
db = (DBM *)new DBM;
if ( db == 0)
{
errno = ENOMEM;
return ((DBM *)0);
}
db->dbm_flags = (flags & 03) == _O_RDONLY ? _DBM_RDONLY : 0;
if ((flags & 03) == _O_WRONLY)
{
flags = (flags & ~03) | _O_RDWR;
}
#if defined( CH_MSW )
lstrcpy(db->dbm_pagbuf, file);
lstrcat(db->dbm_pagbuf, ".pag");
#else
strcpy(db->dbm_pagbuf, file);
strcat(db->dbm_pagbuf, ".pag");
#endif
db->dbm_pagf = _open(db->dbm_pagbuf, flags, mode);
if (db->dbm_pagf < 0)
goto bad;
#if defined( CH_MSW )
lstrcpy(db->dbm_pagbuf, file);
lstrcat(db->dbm_pagbuf, ".dir");
#else
strcpy(db->dbm_pagbuf, file);
strcat(db->dbm_pagbuf, ".dir");
#endif
db->dbm_dirf = _open(db->dbm_pagbuf, flags, mode);
if (db->dbm_dirf < 0)
goto bad1;
_fstat(db->dbm_dirf, &statb);
db->dbm_maxbno = statb.st_size*BYTESIZ-1;
db->dbm_pagbno = db->dbm_dirbno = -1;
return (db);
bad1:
(void) _close(db->dbm_pagf);
bad:
delete((char *)db);
return ((DBM *)0);
}
CH_GLOBAL_FUNC( void )
dbm_close(DBM *db)
{
#if defined( CH_MSW ) && defined( CH_ARCH_32 )
{
if ( hMutex )
{
CloseHandle( hMutex );
hMutex = 0;
}
}
#endif // defined( CH_MSW ) && defined( CH_ARCH_32 )
(void) _close(db->dbm_dirf);
(void) _close(db->dbm_pagf);
delete((char *)db);
}
CH_INTERN_FUNC( chint32 )
getbit(register DBM *db)
{
long bn;
register long b, i, n;
if (db->dbm_bitno > db->dbm_maxbno)
return (0);
n = db->dbm_bitno % BYTESIZ;
bn = db->dbm_bitno / BYTESIZ;
i = bn % DBLKSIZ;
b = bn / DBLKSIZ;
if (b != db->dbm_dirbno)
{
db->dbm_dirbno = b;
if ( ReadData( db->dbm_dirf, (long)b*DBLKSIZ, db->dbm_dirbuf, DBLKSIZ ) != DBLKSIZ )
{
ChMemClear(db->dbm_dirbuf, DBLKSIZ);
}
//(void) _lseek(db->dbm_dirf, (long)b*DBLKSIZ, SEEK_SET );
//if (read(db->dbm_dirf, db->dbm_dirbuf, DBLKSIZ) != DBLKSIZ)
// ChMemClear(db->dbm_dirbuf, DBLKSIZ);
}
return (db->dbm_dirbuf[i] & (1<dbm_bitno > db->dbm_maxbno)
db->dbm_maxbno = db->dbm_bitno;
n = db->dbm_bitno % BYTESIZ;
bn = db->dbm_bitno / BYTESIZ;
i = bn % DBLKSIZ;
b = bn / DBLKSIZ;
if (b != db->dbm_dirbno)
{
db->dbm_dirbno = b;
if ( ReadData( db->dbm_dirf, (long)b*DBLKSIZ, db->dbm_dirbuf, DBLKSIZ ) != DBLKSIZ )
{
ChMemClear(db->dbm_dirbuf, DBLKSIZ);
}
//(void) _lseek(db->dbm_dirf, (long)b*DBLKSIZ, SEEK_SET );
//if (_read(db->dbm_dirf, db->dbm_dirbuf, DBLKSIZ) != DBLKSIZ)
// ChMemClear(db->dbm_dirbuf, DBLKSIZ);
}
db->dbm_dirbuf[i] |= 1<dbm_dirbno = b;
if ( WriteData( db->dbm_dirf, (long)b*DBLKSIZ, db->dbm_dirbuf, DBLKSIZ ) != DBLKSIZ )
{
db->dbm_flags |= _DBM_IOERR;
}
}
CH_GLOBAL_FUNC( long )
dbm_forder(register DBM *db, datum key )
{
long hash;
hash = dcalchash(key);
for (db->dbm_hmask=0;; db->dbm_hmask=(db->dbm_hmask<<1)+1)
{
db->dbm_blkno = hash & db->dbm_hmask;
db->dbm_bitno = db->dbm_blkno + db->dbm_hmask;
if (getbit(db) == 0)
break;
}
return (db->dbm_blkno);
}
CH_INTERN_FUNC( void )
dbm_access(register DBM *db, long hash)
{
for (db->dbm_hmask=0;; db->dbm_hmask=(db->dbm_hmask<<1)+1)
{
db->dbm_blkno = hash & db->dbm_hmask;
db->dbm_bitno = db->dbm_blkno + db->dbm_hmask;
if (getbit(db) == 0)
break;
}
if (db->dbm_blkno != db->dbm_pagbno)
{
db->dbm_pagbno = db->dbm_blkno;
if ( ReadData( db->dbm_pagf, (long)db->dbm_blkno*PBLKSIZ,
db->dbm_pagbuf, PBLKSIZ ) != PBLKSIZ )
{
ChMemClear(db->dbm_pagbuf, PBLKSIZ);
}
#ifdef DEBUG
else if (chkblk(db->dbm_pagbuf) < 0)
db->dbm_flags |= _DBM_IOERR;
#endif
}
}
CH_GLOBAL_FUNC( datum )
dbm_fetch(register DBM *db, datum key)
{
register long i;
datum item;
if (dbm_error(db))
goto err;
dbm_access(db, dcalchash(key));
if ((i = finddatum(db->dbm_pagbuf, key)) >= 0)
{
item = makdatum(db->dbm_pagbuf, i+1);
if (item.dptr != NULL)
return (item);
}
err:
item.dptr = NULL;
item.dsize = 0;
return (item);
}
CH_GLOBAL_FUNC( chint32 )
dbm_delete(register DBM *db, datum key)
{
register long i;
if (dbm_error(db))
return (-1);
if (dbm_rdonly(db)) {
errno = EPERM;
return (-1);
}
dbm_access(db, dcalchash(key));
if ((i = finddatum(db->dbm_pagbuf, key)) < 0)
return (-1);
if (!delitem(db->dbm_pagbuf, i))
goto err;
db->dbm_pagbno = db->dbm_blkno;
if ( WriteData( db->dbm_pagf, (long)db->dbm_blkno*PBLKSIZ,
db->dbm_pagbuf, PBLKSIZ ) != PBLKSIZ )
{
err:
db->dbm_flags |= _DBM_IOERR;
return (-1);
}
return (0);
}
CH_GLOBAL_FUNC( chint32 )
dbm_store(register DBM *db, datum key, datum dat, chint32 replace)
{
register long i;
datum item, item1;
if (dbm_error(db))
return (-1);
if (dbm_rdonly(db))
{
errno = EPERM;
return (-1);
}
loop:
dbm_access(db, dcalchash(key));
if ((i = finddatum(db->dbm_pagbuf, key)) >= 0)
{
if (!replace)
return (1);
if (!delitem(db->dbm_pagbuf, i))
{
db->dbm_flags |= _DBM_IOERR;
return (-1);
}
}
if (!additem(db->dbm_pagbuf, key, dat))
goto split;
db->dbm_pagbno = db->dbm_blkno;
if ( WriteData( db->dbm_pagf, (long)db->dbm_blkno*PBLKSIZ,
db->dbm_pagbuf, PBLKSIZ ) != PBLKSIZ )
{
db->dbm_flags |= _DBM_IOERR;
return (-1);
}
return (0);
split:
if (key.dsize+dat.dsize+3*sizeof(short) >= PBLKSIZ)
{
db->dbm_flags |= _DBM_IOERR;
errno = ENOSPC;
return (-1);
}
char* ovfbuf = new char[PBLKSIZ];
ChMemClear(ovfbuf, PBLKSIZ);
for (i=0;;)
{
item = makdatum(db->dbm_pagbuf, i);
if (item.dptr == NULL)
break;
if (dcalchash(item) & (db->dbm_hmask+1))
{
item1 = makdatum(db->dbm_pagbuf, i+1);
if (item1.dptr == NULL) {
//fprintf(stderr, "ndbm: split not paired\n");
db->dbm_flags |= _DBM_IOERR;
break;
}
if (!additem(ovfbuf, item, item1) ||
!delitem(db->dbm_pagbuf, i)) {
db->dbm_flags |= _DBM_IOERR;
return (-1);
}
continue;
}
i += 2;
}
db->dbm_pagbno = db->dbm_blkno;
if ( WriteData( db->dbm_pagf, (long)db->dbm_blkno*PBLKSIZ,
db->dbm_pagbuf, PBLKSIZ ) != PBLKSIZ )
{
db->dbm_flags |= _DBM_IOERR;
delete []ovfbuf;
return (-1);
}
if ( WriteData( db->dbm_pagf, (long)(db->dbm_blkno+db->dbm_hmask+1)*PBLKSIZ,
ovfbuf, PBLKSIZ ) != PBLKSIZ )
{
db->dbm_flags |= _DBM_IOERR;
delete []ovfbuf;
return (-1);
}
setbit(db);
delete []ovfbuf;
goto loop;
}
CH_GLOBAL_FUNC( datum )
dbm_firstkey(DBM *db)
{
db->dbm_blkptr = 0L;
db->dbm_keyptr = 0;
return (dbm_nextkey(db));
}
CH_GLOBAL_FUNC( datum )
dbm_nextkey(register DBM *db)
{
datum item;
#if defined( CH_MSW )
struct _stat statb;
#elif defined( CH_UNIX )
struct stat statb;
#else
#error( "Platform not defined!" );
#endif
if (dbm_error( db ) || _fstat( db->dbm_pagf, &statb ) < 0)
goto err;
statb.st_size /= PBLKSIZ;
for (;;)
{
if (db->dbm_blkptr != db->dbm_pagbno)
{
db->dbm_pagbno = db->dbm_blkptr;
if ( ReadData( db->dbm_pagf, (long)db->dbm_blkptr*PBLKSIZ,
db->dbm_pagbuf, PBLKSIZ ) != PBLKSIZ )
{
ChMemClear(db->dbm_pagbuf, PBLKSIZ);
}
//(void) _lseek(db->dbm_pagf, db->dbm_blkptr*PBLKSIZ, SEEK_SET );
//if (_read(db->dbm_pagf, db->dbm_pagbuf, PBLKSIZ) != PBLKSIZ)
// ChMemClear(db->dbm_pagbuf, PBLKSIZ);
#ifdef DEBUG
else if (chkblk(db->dbm_pagbuf) < 0)
db->dbm_flags |= _DBM_IOERR;
#endif
}
if (((short *)db->dbm_pagbuf)[0] != 0)
{
item = makdatum(db->dbm_pagbuf, db->dbm_keyptr);
if (item.dptr != NULL)
{
db->dbm_keyptr += 2;
return (item);
}
db->dbm_keyptr = 0;
}
if (++db->dbm_blkptr >= statb.st_size)
break;
}
err:
item.dptr = NULL;
item.dsize = 0;
return (item);
}
CH_INTERN_FUNC( datum )
makdatum(char buf[PBLKSIZ], long n)
{
register short *sp;
register t;
datum item;
sp = (short *)buf;
if ((long)n >= sp[0])
{
item.dptr = NULL;
item.dsize = 0;
return (item);
}
t = PBLKSIZ;
if (n > 0)
t = sp[n];
item.dptr = buf+sp[n+1];
item.dsize = t - sp[n+1];
return (item);
}
CH_INTERN_FUNC( long )
finddatum(char buf[PBLKSIZ], datum item)
{
register short *sp;
register chint32 i, n, j;
sp = (short *)buf;
n = PBLKSIZ;
for (i=0, j=sp[0]; i= 0; )
{
c = *cp++;
for (j=0; j>= 4;
}
}
return (hashl);
}
/*
* Delete pairs of items (n & n+1).
*/
CH_INTERN_FUNC( chint32 )
delitem(char buf[PBLKSIZ], long n)
{
register short *sp, *sp1;
register i1, i2;
sp = (short *)buf;
i2 = sp[0];
if ((long)n >= i2 || (n & 1))
return (0);
if (n == i2-2)
{
sp[0] -= 2;
return (1);
}
i1 = PBLKSIZ;
if (n > 0)
i1 = sp[n];
i1 -= sp[n+2];
if (i1 > 0)
{
i2 = sp[i2];
ChMemMove( &buf[i2 + i1], &buf[i2], sp[n+2] - i2 );
//bcopy(&buf[i2], &buf[i2 + i1], sp[n+2] - i2);
}
sp[0] -= 2;
for (sp1 = sp + sp[0], sp += n+1; sp <= sp1; sp++)
sp[0] = sp[2] + i1;
return (1);
}
/*
* Add pairs of items (item & item1).
*/
CH_INTERN_FUNC( chint32 )
additem(char buf[PBLKSIZ], datum item, datum item1)
{
register short *sp;
register chint32 i1, i2;
sp = (short *)buf;
i1 = PBLKSIZ;
i2 = sp[0];
if (i2 > 0)
i1 = sp[i2];
i1 -= item.dsize + item1.dsize;
if (i1 <= (i2+3) * (chint32)sizeof(short))
return (0);
sp[0] += 2;
sp[++i2] = (short)(i1 + item1.dsize);
ChMemMove( &buf[i1 + item1.dsize], item.dptr, item.dsize );
//bcopy(item.dptr, &buf[i1 + item1.dsize], item.dsize);
sp[++i2] = (short )i1;
ChMemMove( &buf[i1], item1.dptr, item1.dsize );
//bcopy(item1.dptr, &buf[i1], item1.dsize);
return (1);
}
CH_INTERN_FUNC( int )
ReadData( int iFile, long pos, void* pBuf, long lBufSize )
{
#if defined( CH_MSW ) && defined( CH_ARCH_32 )
// open mutex
::WaitForSingleObject( hMutex, INFINITE );
#endif
// seek file
(void) _lseek(iFile, pos, SEEK_SET );
// read data
int iRead = read( iFile, pBuf, lBufSize);
// close mutex
#if defined( CH_MSW ) && defined( CH_ARCH_32 )
::ReleaseMutex( hMutex );
#endif
return ( iRead );
}
CH_INTERN_FUNC( int )
WriteData( int iFile, long pos, void* pBuf, long lBufSize )
{
// open mutex
#if defined( CH_MSW ) && defined( CH_ARCH_32 )
::WaitForSingleObject( hMutex, INFINITE );
#endif
// seek file
(void) _lseek(iFile, pos, SEEK_SET );
// read data
int iWrite = write( iFile, pBuf, lBufSize);
// flush all the data
_commit( iFile );
// close mutex
#if defined( CH_MSW ) && defined( CH_ARCH_32 )
::ReleaseMutex( hMutex );
#endif
return iWrite;
}
#ifdef DEBUG
CH_INTERN_FUNC( chint32 )
chkblk(char buf[PBLKSIZ])
{
register short *sp;
register t, i;
sp = (short *)buf;
t = PBLKSIZ;
for (i=0; i t)
return (-1);
t = sp[i+1];
}
if (t < (sp[0]+1)*sizeof(short))
return (-1);
return (0);
}
#endif