123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463 |
- 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;
- }
|