Google
 

Trailing-Edge - PDP-10 Archives - decuslib20-03 - decus/20-0078/libsim/dbmmin.sim
There is 1 other file named dbmmin.sim in the archive. Click here to see a list.
OPTIONS(/external);
EXTERNAL TEXT PROCEDURE conc,front,scanto,upcase,
frontstrip,rest,getitem;
EXTERNAL PROCEDURE arrtxt,split;
EXTERNAL INTEGER PROCEDURE checkint,scanint,
maxint,search,splita,hash,arrlgd;
CLASS dbmmin(load_file,imsize,autoclose);
VALUE load_file; TEXT load_file; INTEGER imsize; BOOLEAN autoclose;
VIRTUAL: PROCEDURE delstruc;
BEGIN
  CHARACTER nullc,cback,ckomma,csemikolon;
  BOOLEAN defined__f,nameonly,readonly,break_do,statist;
  BOOLEAN emptybase,newhash;
  INTEGER k__,rlength;
  REF(Directfile) d__file;
  INTEGER n__spa,oflowtop,gen_key;
  INTEGER s_size,prev_syn,pstart,pst1,maxrsize;
  INTEGER nr_record,nr_get,nr_lookup;
  INTEGER nr_load,nr_store;
  TEXT last_d_image,store_buff,blank2,komma,undefined,t_;
  TEXT em,semikolon,arrchar,backslash,synpointer;
  TEXT ARRAY typtext[1:7],noargs[1:1];
  REF (rspec) r__spec,r__s,current_spec,spec__spec;
  REF (record) refr;
  REF (rspec) recordspec,rnext;
  CLASS ilist(num,next); INTEGER num; REF (ilist) next;;
  BOOLEAN wrprotect; REF (ilist) freelist;
  PROCEDURE delstruc(r); REF (record) r;;

  TEXT PROCEDURE helpmess(t); VALUE t; TEXT t;
  INSPECT get(t,"HELPMESS") DO BEGIN
    t:-avalues(2); WHILE t.More DO
    BEGIN Outtext(scanto(t,csemikolon)); Outimage; END;
  END helpmess;

  INTEGER PROCEDURE nextfree;
  IF freelist == NONE THEN
  BEGIN nextfree:=oflowtop; oflowtop:=oflowtop+1; END ELSE
  BEGIN
    nextfree:=freelist.num; freelist:-freelist.next;
  END nextfree;

  PROCEDURE outline2(t,u); VALUE t,u; TEXT t,u;
  BEGIN t:-conc(t,u); WHILE t.Length > Length DO
    BEGIN Outtext(t.Sub(1,Length));
    t:-t.Sub(Length+1,t.Length-Length); END;
    Outtext(t); Outimage;
  END OUTLINE2;

  INTEGER PROCEDURE dbadr(t,bsize,base); TEXT t;
  INTEGER bsize,base;
!---------------------------------------------------------
Computing of a pseudo-adress within a database-area.
if newhash=true then a new and better hashing algorithm
is used, but to access existing data bases the old (and
very bad) algorithm is kept as default alternative.
---------------------------------------------------------;
  BEGIN INTEGER n;
    IF newhash THEN n:=hash(t,bsize) ELSE
    BEGIN
      t.Setpos(1); IF checkint(t) > 0 THEN n:=t.Getint ELSE
      BEGIN  WHILE t.More DO n:=n+Rank(t.Getchar); END;
      n:=Mod(n,bsize);
    END;
    dbadr:=n+base;
  END of dbadr;

  INTEGER PROCEDURE loctext(t,a); TEXT t; TEXT ARRAY a;
!---------------------------------------------------------
Locate text t in array a, if present return index
for it otherwise return zero. a should be logically
ended with an ELEMENT=NOTEXT.
---------------------------------------------------------;
  BEGIN INTEGER n;
    FOR n:= n+1 WHILE a(n) =/= NOTEXT DO
    IF t=a(n) THEN BEGIN loctext:=n; GOTO fin; END;
  fin:  END of loctext;

  CLASS record(spec,avalues);
  REF (rspec) spec; TEXT ARRAY avalues;
!  ---------------------------------------------------
spec	pointer to rspec object with specification for
this record type
avalues	text array with attribute values
---------------------------------------------------------;
  VIRTUAL: TEXT PROCEDURE getkey; REF (record) PROCEDURE load;
  PROCEDURE store;
  BEGIN  INTEGER dbskey;
    TEXT syn_;
    TEXT PROCEDURE getkey;
    getkey:-frontstrip(avalues(spec.keypos));
    REF (record) PROCEDURE load(t); VALUE t; TEXT t;
!--------------------------------------------------------
Load elements from text T to array AVALUES.
Elements are either numbers or text between delimiters.
Arrays become just a single textstring.
--------------------------------------------------------;
    BEGIN INTEGER k,max;
      max:=spec.adim;
      BEGIN TEXT ARRAY avalues(1:max+1);
	FOR k:=k+1 WHILE t.More AND k <= max DO
	avalues(k):-scanto(t,cback);
	!.1d;
	IF spec.type2 =/= NOTEXT THEN
	BEGIN REF (record) r1,refrsave; INTEGER n1,n2;
	  refrsave:-refr;
	  n1:=getrecordspec(spec.type1).adim;
	  r1:-get(frontstrip(avalues(spec.keypos2)),spec.type2);
	  refr:-refrsave; IF r1 =/= NONE THEN
	  BEGIN
	    n2:=r1.spec.adim;
	    FOR k:=1 STEP 1 UNTIL n2 DO avalues(n1+k):-r1.avalues(k);
	  END;
	END;
	!.;
	load:-NEW record(spec,avalues);
      END;
    END of load;
    PROCEDURE store;
!--------------------------------------------------------
Put elements from AVALUES into one single text.
Then use STORERECORD to store total record in database.
--------------------------------------------------------;
    BEGIN TEXT s;
      dbskey:=lookup(getkey,spec);
      IF dbskey = 0 THEN GOTO fin;
      d__file.Locate(dbskey);
      store_buff:=NOTEXT;
      k__:=arrlgd(avalues);
      IF k__ <= store_buff.Length THEN s:-store_buff.Sub(1,k__)
      ELSE s:-Blanks(k__);
      arrtxt(avalues,s,cback);
      storerecord(synpointer,s);
      fin:
    END of store;
    syn_:-Blanks(s_size);
    nr_record:=nr_record+1;
  END of record;

  record CLASS rspec(rname,key,base,size,keypos,adim,anames,atypes);
  TEXT rname,key;
  INTEGER base,size,keypos,adim;
  TEXT ARRAY anames; INTEGER ARRAY atypes;
!  -------------------------------------------------------
rname       	name of record
key      	key attribute
base       	start location for primary data area
size       	size of primary data area
keypos	position of key among parameters
adim       	number of attributes
anames	text array containing all attribute names
atypes	integer array with types:  1=integer, 2=real, 3=text
-----------------------------------------------------------;
  BEGIN REF (record) prototype;
    TEXT type1,type2; INTEGER keypos2; REF (rspec) next;
    REF (rspec) PROCEDURE load(t); VALUE t; TEXT t;
!--------------------------------------------------------
t is total record. Its elements are loaded into the
corresponding attributes of the RSPEC object.
--------------------------------------------------------;
    BEGIN REF (rspec) r;
      INTEGER k,n,r_adim; TEXT ARRAY avalues[1:n__spa+1];
      k:=splita(t,backslash,avalues,n__spa); r_adim:=scanint(avalues(6));
      IF r_adim <= 0 THEN r_adim:=200;
      BEGIN
	TEXT ARRAY r_anames[1:r_adim+1]; INTEGER ARRAY r_atypes[1:r_adim+1];
	n:=splita(avalues(8),komma,r_anames,r_adim);
	FOR k:=1 STEP 1 UNTIL n DO r_atypes(k):=r_anames(k).Getint;
	n:=splita(avalues(7),komma,r_anames,r_adim);
	r:-NEW rspec(spec__spec,avalues,avalues(1),avalues(2),
	avalues(3).Getint,avalues(4).Getint,avalues(5).Getint,r_adim,
	r_anames,r_atypes);
      END;
      load:-r;
    END of load;
    rnext:-recordspec; recordspec:-THIS rspec;
    recordspec.next:-rnext;
  END of rspec;

  PROCEDURE doforeach(rtyp,treat); VALUE rtyp;
!------------------------------------------------------
Iteration procedure used to process all records of
a particular type, calling an arbitrary procedure
TREAT for each record.
------------------------------------------------------;
  TEXT rtyp; PROCEDURE treat;
  BEGIN REF (rspec) r; INTEGER k,n,max; TEXT u;
    REF (record) p; REF (ilist) s1;
    REF (ilist) PROCEDURE merge(n,y); INTEGER n; REF (ilist) y;
    IF y == NONE THEN merge:-NEW ilist(n,NONE) ELSE
    IF n<=y.num THEN merge:-NEW ilist(n,y) ELSE
    BEGIN merge:-y;
      l1: IF y.next == NONE THEN y.next:-NEW ilist(n,NONE) ELSE
      IF n <= y.next.num THEN y.next:-NEW ilist(n,y.next) ELSE
      BEGIN y:-y.next; GOTO l1; END;
    END MERGE;

    break_do:=FALSE;
    r:-getrecordspec(rtyp); IF r == NONE THEN
    BEGIN
      outline2("ERROR: doforeach, record type undefined ",rtyp);
      GOTO fin;
    END;
    max:=r.base+r.size-1;
    FOR k:=r.base STEP 1 UNTIL max DO
    BEGIN
      refr:-r.prototype;
      n:=k; p:-getrec(n);
      IF p =/= NONE THEN
      BEGIN
	treat(p); refr:-r.prototype; IF break_do THEN GOTO fin;
	u:-p.syn_; IF u \= undefined THEN s1:-merge(u.Getint,s1);
      END;
    END;
    WHILE s1 =/= NONE DO
    BEGIN
      p:-getrec(s1.num); s1:-s1.next;
      IF p =/= NONE THEN
      BEGIN
	treat(p); refr:-r.prototype; IF break_do THEN GOTO fin;
	k:=scanint(p.syn_); IF k > 0 THEN s1:-merge(k,s1);
      END;
    END;
    fin:
  END of doforeach;

  REF (record) PROCEDURE getrec(rgetpos);
  NAME rgetpos; INTEGER rgetpos;
!-----------------------------------------------------
rgetpos is location for a record which is read as a
whole and interpreted by the LOAD procedure of the
record refr.
NONE is returned if the record is empty or has been
marked as deleted.
-----------------------------------------------------;
  BEGIN TEXT link,s; REF(record) r;
    d__file.Locate(rgetpos); IF \d__file.Endfile THEN
    BEGIN
      gr1: d__file.Inimage;
      s:-d__file.Image.Strip; IF s.Length < pstart THEN GOTO fin;
      link:-Copy(s.Sub(pstart-s_size,s_size));
      IF s.Sub(1,1) = "+" THEN
      BEGIN ! record has been deleted;
	prev_syn:=rgetpos; rgetpos:=link.Getint;
	d__file.Locate(rgetpos); GOTO gr1;
      END;
      IF s.Sub(1,1) = "-" THEN GOTO fin;
      IF s.Sub(1,1).Getchar = nullc THEN GOTO fin;
      IF s.Length < rlength THEN last_d_image:-s:-
      s.Sub(pstart,s.Length-pst1) ELSE
      s:-loadrecord(s);
      r:-refr.load(s);
      INSPECT r DO BEGIN r.syn_:-link; r.dbskey:=rgetpos; END;
      getrec:-r;
    END;
    fin:
  END of getrec;

  REF (record) PROCEDURE get(rkey,rtype);
  VALUE rkey,rtype; TEXT rkey,rtype;
!----------------------------------------------------
Load a record with given key and type.
Compute pseudo-adress, see if key is same as
searched, if not follow chain of synonym records
until found, or if not found return NONE.
----------------------------------------------------;
  BEGIN REF(record) r; TEXT u; INTEGER n;
    nr_get:=nr_get+1;
    prev_syn:=0;
    r__s:-IF r__spec == NONE THEN getrecordspec(rtype) ELSE r__spec;
    IF r__s == NONE THEN
    outline2("ERROR: undefined record type: ",rtype) ELSE
    BEGIN
      refr:-r__s.prototype;
      n:=dbadr(rkey,r__s.size,r__s.base);
    END;
    nextsym: r:-getrec(n);
    IF r == NONE THEN GOTO fin;
    r.dbskey:=n;
    IF rkey = r.getkey THEN BEGIN get:-r; GOTO fin; END;
    u:-r.syn_;
    IF u = undefined THEN get:-NONE ELSE
    BEGIN prev_syn:=n; n:=u.Getint; GOTO nextsym; END;
    fin:
  END of get;

  INTEGER PROCEDURE lookup(t,rtype); TEXT t;
  REF (rspec) rtype;
!-----------------------------------------------------
When a record is to be stored this procedure is called
to compute a location for it.
Analoguous to GET, but if record is NOT found a
location to an empty place is returned as value,
if found the location for the previous record
is returned.
-----------------------------------------------------;
  BEGIN INTEGER n; REF (record) r; TEXT u;
    PROCEDURE tail(n); INTEGER n;
    BEGIN TEXT u; INTEGER k;
      d__file.Locate(n); d__file.Inimage; u:-d__file.Image;
      IF u.Sub(u.Length,1) = ":" THEN
      BEGIN
	k:=u.Sub(u.Length-10,6).Getint;
	freelist:-NEW ilist(k,freelist); tail(k);
      END;
    END tail;
    nr_lookup:=nr_lookup+1;
    synpointer:=NOTEXT;
    refr:-rtype.prototype;
    n:=dbadr(t,rtype.size,rtype.base);
    nextsym: r:-getrec(n);
    IF r == NONE THEN BEGIN lookup:=n; GOTO fin; END;
    IF t = r.getkey THEN
    BEGIN COMMENT overwrite previous record;
      IF wrprotect THEN
      BEGIN outline2("Can't overwrite record: ",t); GOTO fin; END;
      IF last_d_image.Length > rlength THEN tail(n);
      synpointer.Sub(3,s_size):=r.syn_;
      lookup:=n; GOTO fin;
    END;
    u:-r.syn_; IF u = undefined THEN
    BEGIN COMMENT create one more overflow record;
      ! modify synonym pointer ;
      INTEGER newloc;
      INSPECT d__file DO
      BEGIN
	Locate(n); Inimage;
	newloc:=nextfree; r.syn_.Putint(newloc);
	Image.Sub(pstart-s_size,s_size):=r.syn_;
	Locate(n); Outimage; lookup:=newloc;
      END;
    END ELSE
    BEGIN n:=u.Getint; GOTO nextsym; END;
    fin:
  END of lookup;

  PROCEDURE defaultparms;
!-----------------------------------------------------
Set defaultparameters(internal for system). Create
an RSPEC describing a recordspecification which is
needed to load the other recordspecifications from
file.
-----------------------------------------------------;
  BEGIN
    INTEGER k; TEXT t,u;
    TEXT ARRAY attrarr[1:12]; INTEGER ARRAY typarr[1:12];
    komma:-Copy(","); ckomma:=','; blank2:-Copy("  ");
    semikolon:-Copy(";"); csemikolon:=';'; em:-Copy("/*");
    pstart:=8; pst1:=7; s_size:=5;
    synpointer:-Blanks(pst1);
    undefined:-Blanks(s_size);
    t:-Copy("RNAME,KEY,BASE,SIZE,KEYPOS,ADIM,ANAMES,ATYPES");
    n__spa:=splita(t,t.Sub(6,1),attrarr,12);
    u:-Copy("33111164 ");
    FOR k:=1 STEP 1 UNTIL n__spa DO typarr(k):=u.Sub(k,1).Getint;
    spec__spec:-NEW rspec(NONE,attrarr,Copy("RSPEC"),
    Copy("RNAME"),2,8,1,n__spa,attrarr,typarr);
    spec__spec.spec:-spec__spec;
    spec__spec.prototype:-spec__spec;
  END of defaultparms;

  REF (rspec) PROCEDURE getrecordspec(rcname); VALUE rcname; TEXT rcname;
!-----------------------------------------------------
Locate RSPEC object with name RCNAME in array
RECORDSPEC where all such objects are stored
when the system is initialized.
-----------------------------------------------------;
  BEGIN
    rnext:-recordspec; WHILE rnext =/= NONE DO
    BEGIN
      IF rcname = rnext.rname THEN
      BEGIN getrecordspec:-rnext; GOTO fin; END;
      rnext:-rnext.next;
    END;
    fin:
  END of getrecordspec;

  TEXT PROCEDURE loadrecord(t); TEXT t;
!----------------------------------------------------
t is first part (image) of external record if there
are continuations, load these recursively until
finally the entire logical external record is returned
as value.
The global variable LAST_D_IMAGE is also set to
point on this record.
----------------------------------------------------;
  BEGIN     TEXT tx;
    nr_load:=nr_load+1;
    IF t.Length < rlength THEN
    last_d_image:-t.Sub(pstart,t.Length-pst1).Strip ELSE
    BEGIN
      tx:-t.Sub(t.Length-10,6);
      d__file.Locate(tx.Getint);
      tx:-Copy(t.Sub(pstart,t.Length-pstart-10));
      d__file.Inimage;
      last_d_image:-conc(tx,loadrecord(d__file.Image.Strip));
    END;
    loadrecord:-last_d_image;
  END of loadrecord;

  PROCEDURE storerecord(f,t); TEXT f,t;
!-----------------------------------------------------
File D is located to where an external logical
record is to start.
Write it , and if needed write continuation records
(recursively) in overflowarea and link
them together.
A continuation pointer is a number stored at the end
of the image, and the last character is then set to ':'.
-------------------------------------------------------;
  BEGIN INTEGER n,m,newloc; TEXT tx;
    INSPECT d__file DO
    BEGIN
      nr_store:=nr_store+1;
      Image.Sub(1,pst1):=f;
      m:=Image.Length-pst1; t:-t.Strip; n:=t.Length;
      IF n >= m THEN
      BEGIN
	newloc:=nextfree;
	Image.Sub(Image.Length-10,11):="          :";
	Image.Sub(Image.Length-10,6).Putint(newloc);
	m:=m-11;
	tx:-t.Sub(1,m);
      END ELSE tx:-t;
      Image.Sub(pstart,m):=tx; Outimage;
      IF n >= m THEN
      BEGIN
	Locate(newloc);
	storerecord(blank2,t.Sub(m+1,n-m));
      END;
    END;
  END of storerecord;

  PROCEDURE delete(r); REF (record) r;
  INSPECT r DO
  BEGIN INTEGER psave;
    psave:=prev_syn; delstruc(r); prev_syn:=psave;
    INSPECT d__file DO
    BEGIN
      IF prev_syn > 0 THEN
      BEGIN ! modify SYN pointer to point to next record;
	Locate(prev_syn); Inimage;
	IF syn_ = undefined AND Image.Sub(1,1) = "+" THEN Image:=em ELSE
	Image.Sub(pstart-s_size,s_size):=syn_;
	Locate(prev_syn); Outimage;
	freelist:-NEW ilist(dbskey,freelist);
      END ELSE
      BEGIN ! mark primary record as deleted;
	Locate(dbskey); Inimage;
	IF syn_ = undefined THEN Image:=em ELSE
	Image.Sub(1,1):="+";
	Locate(dbskey); Outimage;
      END;
    END;
  END delete;

  PROCEDURE loadspec;
!------------------------------------------------------
Read OFLOWTOP and GEN_KEY from first image of data base.
Make sure that OFLOWTOP points to top of file.
Load all record-specifications.
They are automatically saved in array RECORDSPEC
when an RSPEC object is created (See above class RSPEC,
its procedure LOAD and its class body).
------------------------------------------------------;
  BEGIN REF (record) r; INTEGER k; TEXT t,u; BOOLEAN again;
    PROCEDURE spec_in(r); REF (rspec) r;
    r.prototype:-NEW record(r,r.anames);
    INSPECT d__file DO
    BEGIN
      Locate(1); Inimage;
      oflowtop:=Inint; gen_key:=Inint;
      t:-Image; backslash:-Copy(t.Sub(13,1));
      arrchar:-Copy(t.Sub(14,1)); cback:=t.Sub(13,1).Getchar;
      newhash:=t.Sub(20,1) = backslash;
      u:-t.Sub(15,5); k:=scanint(u);
      IF k > 0 AND k \= rlength THEN
      BEGIN ! wrong imagesize was given;
	Close;
	defined__f:=FALSE; rlength:=k; GOTO fin;
      END;
      gen_key:=gen_key+100//100*101; ! increment to next multiple of 100;
      k:=oflowtop;
      ! make sure that proper top of file is known;
      WHILE \Endfile AND Image.Strip \= em DO
      BEGIN Locate(k); Inimage; k:=k+1; END;
      IF k > oflowtop THEN oflowtop:=k-1;
    END;
    recordspec:-spec__spec;
    doforeach("RSPEC",spec_in);
    fin:
  END of loadspec;

  PROCEDURE put_record(rtyp,r); REF (rspec) rtyp; TEXT r;
!-------------------------------------------------------
Store text R as an external record of type RTYP.
Produce its key, compute its location and store by
calling STORERECORD.
-------------------------------------------------------;
  INSPECT rtyp DO BEGIN
    TEXT ARRAY ta[1:rtyp.adim+1]; INTEGER k;
    k:=splita(r,backslash,ta,rtyp.adim)+1; IF k < rtyp.adim THEN
    outline2("Put_record: bad record !",NOTEXT) ELSE
    BEGIN
      k:=lookup(ta(rtyp.keypos),rtyp);
      IF k = 0 THEN GOTO fin; d__file.Locate(k);
      storerecord(synpointer,r);
    END;
    fin:
  END of put_record;

  INTEGER PROCEDURE next_key;
  BEGIN ! produce next unique number;
    gen_key:=gen_key+1; next_key:=gen_key;
  END of next_key;

  PROCEDURE closebase;
!-------------------------------------------------------
Update first image which contains pointer to current
top of file and current number for generated data
base keys.
Close file, print statistics over calls to the most
frequent internal procedures.
-------------------------------------------------------;
  BEGIN INTEGER n; REF (record) r;
    INSPECT d__file DO
    BEGIN
      IF \readonly THEN BEGIN
	Locate(1); Inimage;
	n:=Image.Sub(1,6).Getint;
	IF n < oflowtop THEN
	BEGIN
	  Image.Sub(1,6).Putint(oflowtop);
	  Image.Sub(7,6).Putint(gen_key); ! save current unique number;
	  Locate(1); Outimage;
	END;
      END;
      Setpos(1); Close;
    END;
    IF statist THEN BEGIN
      Outint(nr_record,5);
      Outint(nr_get,5); Outint(nr_lookup,5);
      Outint(nr_load,5); Outint(nr_store,5);
      Outimage;
    END;
  END of closebase;

  PROCEDURE openbase(load_file,imsize);
  VALUE load_file; TEXT load_file; INTEGER imsize;
  IF load_file =/= NOTEXT THEN
  BEGIN
    l1: t_:-Blanks(4); t_.Putint(imsize); t_:-frontstrip(t_);
    d__file:-NEW Directfile(conc(load_file,Copy("/i:"),t_));
    rlength:=imsize;
    maxrsize:=rlength+100; store_buff:-Blanks(maxrsize);
    d__file.Open(Blanks(rlength));
    INSPECT d__file DO
    BEGIN ! check if it is an initialized SIMDBM file;
      Locate(1); IF \Endfile THEN
      BEGIN Inimage; defined__f:=Image.Strip.Length > 13; END;
    END;
    IF defined__f THEN
    BEGIN loadspec; IF \defined__f THEN
      BEGIN imsize:=rlength; GOTO l1; END;
    END;
    IF \defined__f AND \emptybase THEN d__file.Close;
  END openbase;

!--------------------------------------------------------
Initiate data base file:
set default parameters and define internal structures,
open data base file
if it is an old database load all record-specifications.
--------------------------------------------------------;

  defaultparms;
  openbase(load_file,imsize);
  INNER;
  IF autoclose AND d__file.Image =/= NOTEXT THEN closebase;

END of class dbm;