summaryrefslogtreecommitdiff
path: root/appl/lib/dbm.b
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
commit37da2899f40661e3e9631e497da8dc59b971cbd0 (patch)
treecbc6d4680e347d906f5fa7fca73214418741df72 /appl/lib/dbm.b
parent54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff)
20060303a
Diffstat (limited to 'appl/lib/dbm.b')
-rw-r--r--appl/lib/dbm.b463
1 files changed, 463 insertions, 0 deletions
diff --git a/appl/lib/dbm.b b/appl/lib/dbm.b
new file mode 100644
index 00000000..e6c27630
--- /dev/null
+++ b/appl/lib/dbm.b
@@ -0,0 +1,463 @@
+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;
+}