ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /appl/lib/dbm.b/
implement Dbm;
# Copyright © Caldera International Inc. 2001-2002. All rights reserved.
# Limbo transliteration (with amendment) Copyright © 2004 Vita Nuova Holdings Limited.
include "sys.m";
sys: Sys;
OREAD, OWRITE, ORDWR: import Sys;
include "dbm.m";
BYTESIZ: con 8; # bits
SHORTSIZ: con 2; # bytes
PBLKSIZ: con 512;
DBLKSIZ: con 8192; # was 4096
init()
{
sys = load Sys Sys->PATH;
}
Dbf.create(file: string, mode: int): ref Dbf
{
pf := sys->create(file+".pag", ORDWR, mode);
if(pf == nil)
return nil;
df := sys->create(file+".dir", ORDWR, mode);
if(df == nil)
return nil;
return alloc(pf, df, ORDWR);
}
Dbf.open(file: string, flags: int): ref Dbf
{
if((flags & 3) == OWRITE)
flags = (flags & ~3) | ORDWR;
pf := sys->open(file+".pag", flags);
if(pf == nil)
return nil;
df := sys->open(file+".dir", flags);
if(df == nil)
return nil;
return alloc(pf, df, flags);
}
alloc(pf: ref Sys->FD, df: ref Sys->FD, flags: int): ref Dbf
{
db := ref Dbf;
db.pagf = pf;
db.dirf = df;
db.flags = flags & 3;
db.maxbno = 0;
db.bitno = 0;
db.hmask = 0;
db.blkno = 0;
db.pagbno = -1;
db.pagbuf = array[PBLKSIZ] of byte;
db.dirbno = -1;
db.dirbuf = array[DBLKSIZ] of byte;
(ok, d) := sys->fstat(db.dirf);
if(ok < 0)
d.length = big 0;
db.maxbno = int (d.length*big BYTESIZ - big 1);
return db;
}
Dbf.flush(db: self ref Dbf)
{
db.pagbno = db.dirbno = -1;
}
Dbf.isrdonly(db: self ref Dbf): int
{
return db.flags == OREAD;
}
Dbf.fetch(db: self ref Dbf, key: Datum): Datum
{
access(db, calchash(key));
for(i:=0;; i+=2){
item := makdatum(db.pagbuf, i);
if(item == nil)
return item;
if(cmpdatum(key, item) == 0){
item = makdatum(db.pagbuf, i+1);
if(item == nil){
sys->fprint(sys->fildes(2), "dbm: items not in pairs\n");
raise "dbm: items not in pairs";
}
return item;
}
}
}
Dbf.delete(db: self ref Dbf, key: Datum): int
{
if(db.isrdonly())
return -1;
access(db, calchash(key));
for(i:=0;; i+=2){
item := makdatum(db.pagbuf, i);
if(item == nil)
return -1;
if(cmpdatum(key, item) == 0){
delitem(db.pagbuf, i);
delitem(db.pagbuf, i);
break;
}
}
sys->seek(db.pagf, big db.blkno*big PBLKSIZ, 0);
write(db.pagf, db.pagbuf, PBLKSIZ);
db.pagbno = db.blkno;
return 0;
}
Dbf.store(db: self ref Dbf, key: Datum, dat: Datum, replace: int): int
{
if(db.isrdonly())
return -1;
for(;;){
access(db, calchash(key));
for(i:=0;; i+=2){
item := makdatum(db.pagbuf, i);
if(item == nil)
break;
if(cmpdatum(key, item) == 0){
if(!replace)
return 1;
delitem(db.pagbuf, i);
delitem(db.pagbuf, i);
break;
}
}
i = additem(db.pagbuf, key);
if(i >= 0){
if(additem(db.pagbuf, dat) >= 0)
break;
delitem(db.pagbuf, i);
}
if(!split(db, key, dat))
return -1;
}
sys->seek(db.pagf, big db.blkno*big PBLKSIZ, 0);
write(db.pagf, db.pagbuf, PBLKSIZ);
db.pagbno = db.blkno;
return 0;
}
split(db: ref Dbf, key: Datum, dat: Datum): int
{
if(len key+len dat+3*SHORTSIZ >= PBLKSIZ)
return 0;
ovfbuf := array[PBLKSIZ] of {* => byte 0};
for(i:=0;;){
item := makdatum(db.pagbuf, i);
if(item == nil)
break;
if(calchash(item) & (db.hmask+1)){
additem(ovfbuf, item);
delitem(db.pagbuf, i);
item = makdatum(db.pagbuf, i);
if(item == nil){
sys->fprint(sys->fildes(2), "dbm: split not paired\n");
raise "dbm: split not paired";
#break;
}
additem(ovfbuf, item);
delitem(db.pagbuf, i);
continue;
}
i += 2;
}
sys->seek(db.pagf, big db.blkno*big PBLKSIZ, 0);
write(db.pagf, db.pagbuf, PBLKSIZ);
db.pagbno = db.blkno;
sys->seek(db.pagf, (big db.blkno+big db.hmask+big 1)*big PBLKSIZ, 0);
write(db.pagf, ovfbuf, PBLKSIZ);
setbit(db);
return 1;
}
Dbf.firstkey(db: self ref Dbf): Datum
{
return copy(firsthash(db, 0));
}
Dbf.nextkey(db: self ref Dbf, key: Datum): Datum
{
hash := calchash(key);
access(db, hash);
item, bitem: Datum;
for(i:=0;; i+=2){
item = makdatum(db.pagbuf, i);
if(item == nil)
break;
if(cmpdatum(key, item) <= 0)
continue;
if(bitem == nil || cmpdatum(bitem, item) < 0)
bitem = item;
}
if(bitem != nil)
return copy(bitem);
hash = hashinc(db, hash);
if(hash == 0)
return copy(item);
return copy(firsthash(db, hash));
}
firsthash(db: ref Dbf, hash: int): Datum
{
for(;;){
access(db, hash);
bitem := makdatum(db.pagbuf, 0);
item: Datum;
for(i:=2;; i+=2){
item = makdatum(db.pagbuf, i);
if(item == nil)
break;
if(cmpdatum(bitem, item) < 0)
bitem = item;
}
if(bitem != nil)
return bitem;
hash = hashinc(db, hash);
if(hash == 0)
return item;
}
}
access(db: ref Dbf, hash: int)
{
for(db.hmask=0;; db.hmask=(db.hmask<<1)+1){
db.blkno = hash & db.hmask;
db.bitno = db.blkno + db.hmask;
if(getbit(db) == 0)
break;
}
if(db.blkno != db.pagbno){
sys->seek(db.pagf, big db.blkno * big PBLKSIZ, 0);
read(db.pagf, db.pagbuf, PBLKSIZ);
chkblk(db.pagbuf);
db.pagbno = db.blkno;
}
}
getbit(db: ref Dbf): int
{
if(db.bitno > db.maxbno)
return 0;
n := db.bitno % BYTESIZ;
bn := db.bitno / BYTESIZ;
i := bn % DBLKSIZ;
b := bn / DBLKSIZ;
if(b != db.dirbno){
sys->seek(db.dirf, big b * big DBLKSIZ, 0);
read(db.dirf, db.dirbuf, DBLKSIZ);
db.dirbno = b;
}
if(int db.dirbuf[i] & (1<<n))
return 1;
return 0;
}
setbit(db: ref Dbf)
{
if(db.bitno > db.maxbno){
db.maxbno = db.bitno;
getbit(db);
}
n := db.bitno % BYTESIZ;
bn := db.bitno / BYTESIZ;
i := bn % DBLKSIZ;
b := bn / DBLKSIZ;
db.dirbuf[i] |= byte (1<<n);
sys->seek(db.dirf, big b * big DBLKSIZ, 0);
write(db.dirf, db.dirbuf, DBLKSIZ);
db.dirbno = b;
}
makdatum(buf: array of byte, n: int): Datum
{
ne := GETS(buf, 0);
if(n < 0 || n >= ne)
return nil;
t := PBLKSIZ;
if(n > 0)
t = GETS(buf, n+1-1);
v := GETS(buf, n+1);
return buf[v: t]; # size is t-v
}
cmpdatum(d1: Datum, d2: Datum): int
{
n := len d1;
if(n != len d2)
return n - len d2;
if(n == 0)
return 0;
for(i := 0; i < len d1; i++)
if(d1[i] != d2[i])
return int d1[i] - int d2[i];
return 0;
}
copy(d: Datum): Datum
{
if(d == nil)
return nil;
a := array[len d] of byte;
a[0:] = d;
return a;
}
# ken's
#
# 055,043,036,054,063,014,004,005,
# 010,064,077,000,035,027,025,071,
#
hitab := array[16] of {
61, 57, 53, 49, 45, 41, 37, 33,
29, 25, 21, 17, 13, 9, 5, 1,
};
hltab := array[64] of {
8r6100151277,8r6106161736,8r6452611562,8r5001724107,
8r2614772546,8r4120731531,8r4665262210,8r7347467531,
8r6735253126,8r6042345173,8r3072226605,8r1464164730,
8r3247435524,8r7652510057,8r1546775256,8r5714532133,
8r6173260402,8r7517101630,8r2431460343,8r1743245566,
8r0261675137,8r2433103631,8r3421772437,8r4447707466,
8r4435620103,8r3757017115,8r3641531772,8r6767633246,
8r2673230344,8r0260612216,8r4133454451,8r0615531516,
8r6137717526,8r2574116560,8r2304023373,8r7061702261,
8r5153031405,8r5322056705,8r7401116734,8r6552375715,
8r6165233473,8r5311063631,8r1212221723,8r1052267235,
8r6000615237,8r1075222665,8r6330216006,8r4402355630,
8r1451177262,8r2000133436,8r6025467062,8r7121076461,
8r3123433522,8r1010635225,8r1716177066,8r5161746527,
8r1736635071,8r6243505026,8r3637211610,8r1756474365,
8r4723077174,8r3642763134,8r5750130273,8r3655541561,
};
hashinc(db: ref Dbf, hash: int): int
{
hash &= db.hmask;
bit := db.hmask+1;
for(;;){
bit >>= 1;
if(bit == 0)
return 0;
if((hash&bit) == 0)
return hash|bit;
hash &= ~bit;
}
}
calchash(item: Datum): int
{
hashl := 0;
hashi := 0;
for(i:=0; i<len item; i++){
f := int item[i];
for(j:=0; j<BYTESIZ; j+=4){
hashi += hitab[f&16rF];
hashl += hltab[hashi&16r3F];
f >>= 4;
}
}
return hashl;
}
delitem(buf: array of byte, n: int)
{
ne := GETS(buf, 0);
if(n < 0 || n >= ne){
sys->fprint(sys->fildes(2), "dbm: bad delitem\n");
raise "dbm: bad delitem";
}
i1 := GETS(buf, n+1);
i2 := PBLKSIZ;
if(n > 0)
i2 = GETS(buf, n+1-1);
i3 := GETS(buf, ne+1-1);
if(i2 > i1)
while(i1 > i3){
i1--;
i2--;
buf[i2] = buf[i1];
buf[i1] = byte 0;
}
i2 -= i1;
for(i1=n+1; i1<ne; i1++)
PUTS(buf, i1+1-1, GETS(buf, i1+1) + i2);
PUTS(buf, 0, ne-1);
PUTS(buf, ne, 0);
}
additem(buf: array of byte, item: Datum): int
{
i1 := PBLKSIZ;
ne := GETS(buf, 0);
if(ne > 0)
i1 = GETS(buf, ne+1-1);
i1 -= len item;
i2 := (ne+2) * SHORTSIZ;
if(i1 <= i2)
return -1;
PUTS(buf, ne+1, i1);
buf[i1:] = item;
PUTS(buf, 0, ne+1);
return ne;
}
chkblk(buf: array of byte)
{
t := PBLKSIZ;
ne := GETS(buf, 0);
for(i:=0; i<ne; i++){
v := GETS(buf, i+1);
if(v > t)
badblk();
t = v;
}
if(t < (ne+1)*SHORTSIZ)
badblk();
}
read(fd: ref Sys->FD, buf: array of byte, n: int)
{
nr := sys->read(fd, buf, n);
if(nr == 0){
for(i := 0; i < len buf; i++)
buf[i] = byte 0;
}else if(nr != n)
raise "dbm: read error: "+sys->sprint("%r");
}
write(fd: ref Sys->FD, buf: array of byte, n: int)
{
if(sys->write(fd, buf, n) != n)
raise "dbm: write error: "+sys->sprint("%r");
}
badblk()
{
sys->fprint(sys->fildes(2), "dbm: bad block\n");
raise "dbm: bad block";
}
GETS(buf: array of byte, sh: int): int
{
sh *= SHORTSIZ;
return (int buf[sh]<<8) | int buf[sh+1];
}
PUTS(buf: array of byte, sh: int, v: int)
{
sh *= SHORTSIZ;
buf[sh] = byte (v>>8);
buf[sh+1] = byte v;
}