-- TiogaPGStreamsImpl.Mesa -- last written by Pier. February 11, 1981 3:24 PM DIRECTORY TiogaPGStreams: FROM "TiogaPGStreams", TiogaNode: FROM "TiogaNode" USING [ropeZone, blockZone], FileByteStream: FROM "TiogaFBS" USING [Handle, Open, CreateOptions, Transaction, AccessOptions, EndOf, GetBlock, PutBlock, Close ], InlineDefs: FROM "InlineDefs" USING [BytePair, LowHalf], ProcessDefs: FROM "ProcessDefs" USING [Yield], RopeInline: FROM "RopeInline" USING [Tsubstr, Tconcat], Rope: FROM "Rope" USING [Ref, Flatten, Concat, Size]; RopeReader: FROM "RopeReader" USING [Ref, Body, GetBlock, ToPosition]; TiogaPGStreamsImpl: PROGRAM IMPORTS TiogaPGStreams, nodeI: TiogaNode, FBS: FileByteStream, InlineDefs, ProcessDefs, Rope, RopeReader EXPORTS TiogaPGStreams = BEGIN OPEN TiogaPGStreams; Card: TYPE = LONG CARDINAL; OpenPGStreams: PUBLIC PROC [filename: Rope.Ref, transaction: FBS.Transaction _ NIL, createOptions: FBS.CreateOptions _ none, accessOptions: FBS.AccessOptions _ all] RETURNS [control, text: PGStream] = BEGIN file: FBS.Handle; mode: PGStreamMode; blockSeq: BlockSeq; pgm: PGM; file _ FBS.Open[Rope.Flatten[filename], transaction, createOptions, accessOptions]; control _ NEW [PGStreamBody]; text _ NEW [PGStreamBody]; control.kind _ control; text.kind _ text; control.other _ text; text.other _ control; control.file _ text.file _ file; mode _ IF accessOptions=read THEN read ELSE write; pgm _ control.pgm _ text.pgm _ CreatePGM[control.mode _ text.mode _ mode]; SELECT mode FROM read => { blockSeq _ text.blockSeq _ ReadTrailer[file, pgm]; control.process _ text.process _ FORK Reader[pgm, file, blockSeq]}; write => { control.reader _ text.reader _ NEW [RopeReader.Body]; control.process _ text.process _ FORK Writer[pgm, file]}; ENDCASE => ERROR; END; trailerId: ARRAY [0..trailerSize) OF CHARACTER = [0, 0, 0, 0, 212B, 311B, 234B, 232B, 360B, 214B]; trailerSize: NAT = 10; CheckTrailerId: PROC [file: FBS.Handle] RETURNS [BOOLEAN] = { FOR i: NAT IN [0..trailerSize) DO IF FBS.GetChar[file] # trailerId[i] THEN RETURN[FALSE]; ENDLOOP; RETURN [TRUE] }; CheckTrailer: PROC [file: FBS.Handle] RETURNS [ok:BOOLEAN, startControl:Card] = { len: Card _ FBS.GetLength[file]; ok _ FALSE; startControl _ len; IF len < trailerSize*2+4 THEN RETURN; FBS.SetIndex[file, len-trailerSize-4]; IF ~CheckTrailerId[file] THEN RETURN; startControl _ ReadLongCardinal[file]; IF startControl >= len-trailerSize*2-4 THEN RETURN; FBS.SetIndex[file, startControl]; IF ~CheckTrailerId[file] THEN RETURN; ok _ TRUE }; ReadTrailer: PROC [file: FBS.Handle, pgm: PGM] RETURNS [blockSeq: BlockSeq] = { startControl: Card; trailerOk: BOOLEAN; blockNum: NAT; [trailerOk, startControl] _ CheckTrailer[file]; blockNum _ (startControl+blockSize-1)/blockSize; blockSeq _ NEW [BlockSeq[blockNum]]; FOR i: NAT IN [0..blockNum) DO blockSeq[i] _ blockZone.NEW[BlockBody]; ENDLOOP; FBS.SetIndex[file, startControl]; IF ~trailerOk THEN { -- create a dummy control block block: Block _ blockZone.NEW[BlockBody]; style, type: Rope.Ref _ Rope.FromString["Default"]; WriteChar[fOpsI.changeStyle, control]; PutRope[style, Rope.Size[style], control, control]; WriteChar[fOpsI.startNode, control]; PutRope[type, Rope.Size[type], control, control]}; WriteChar[fOpsI.longTextNode, control]; IF startControl < 256 THEN { WriteChar[fOpsI.shortTextNode, control]; WriteByte[startControl, control]} ELSE IF startControl < LAST[CARDINAL] THEN { WriteChar[fOpsI.longTextNode, control]; WriteWord[startControl, control]} ELSE { WriteChar[fOpsI.veryLongTextNode, control]; WriteDoubleWord[startControl, control]}; WriteByte[0, control]; -- no looksRun's WriteByte[fOpsI.endNode, control]; WriteByte[fOpsI.endOfFile, control]; AddToControlList[pgm, block]; }; }; Reader: PROC [pgm: PGM, file: FBS.Handle, seq: BlockSeq] = BEGIN UNTIL FBS.EndOf[file] DO -- read the control blocks block: Block _ blockZone.NEW[BlockBody]; IF FBS.GetBlock[file, block] > 0 THEN AddToControlList[pgm, block]; ENDLOOP; ControlReadsDone[pgm]; FBS.SetIndex[file, 0]; -- start of text FOR i:NAT IN [0..seq.length) DO -- read the text blocks [] _ FBS.GetBlock[file, seq[i]]; ENDLOOP; TextReadsDone[pgm]; END; Writer: PROC [pgm: PGM, file: FBS.Handle] = BEGIN block: Block; DO IF (block _ GetFromWriteList[pgm])=NIL THEN RETURN; -- writes done FBS.PutBlock[file, block]; ENDLOOP; END; ClosePGStreams: PUBLIC PROC [str: PGStream] = BEGIN IF str.mode = write THEN BEGIN Flush: PROC [str: PGStream] = { IF str.block # NIL AND str.loc > 0 THEN WriteBlock[str]}; Flush[str]; Flush[str.other]; WritesDone[str.pgm]; END; JOIN str.process; -- guarantees that last write (if any) is finished FBS.Close[str.file]; END; GetBlock: PROC [str: PGStream] RETURNS [block: Block] = BEGIN pgm: PGM _ str.pgm; SELECT str.kind FROM textBlock => { num: NAT; block _ pgm.blockSeq[num _ pgm.blockNum]; pgm.blockNum _ num+1; }; controlBlock => block _ GetFromControlList[pgm]; ENDCASE => ERROR; str.block _ block; str.loc _ 0; str.after _ IF block = NIL THEN 0 ELSE block.maxLength; END; ReadCharSupport: PUBLIC PROC [str: PGStream] RETURNS [CHARACTER] = BEGIN loc: NAT; block: Block _ GetBlock[str]; IF (loc_str.loc) >= str.after THEN RETURN[0]; str.loc _ loc+1; RETURN[block[loc]]; END; SkipCharSupport: PUBLIC PROC [str: PGStream] = BEGIN loc: NAT; block: Block _ GetBlock[str]; IF (loc_str.loc) < str.after THEN str.loc _ loc+1; END; ReadWord: PUBLIC PROC [str: PGStream] RETURNS [CARDINAL] = BEGIN word: InlineDefs.BytePair; word.high _ ReadByte[str]; word.low _ ReadByte[str]; RETURN [LOOPHOLE[word, CARDINAL]]; END; ReadDoubleWord: PROC [str: PGStream] RETURNS [Card] = BEGIN card: InlineDefs.LongNumber; card.highbits _ ReadWord[str]; card.lowbits _ ReadWord[str]; RETURN [LOOPHOLE[card, CARDINAL]]; END; MakeRope: PUBLIC PROC [len: Card, str: PGStream] RETURNS [rope: Rope.Ref] = BEGIN block: Block; loc, chunksize, after: NAT; size: Card; extension: Rope.Ref; after _ str.after; UNTIL len=0 DO IF (loc_str.loc) >= after THEN { IF (block _ GetBlock[str]) = NIL THEN RETURN; -- end of file after _ str.after; loc _ str.loc } ELSE block _ str.block; chunksize _ IF len >= LAST[NAT] THEN after-loc ELSE MIN[LOOPHOLE[InlineDefs.LowHalf[len]], after-loc]; extension _ nodeI.ropeZone.NEW[ RopeInline.Tsubstr[base: block, start: loc, len: chunksize]; IF rope # NIL THEN { rope _ nodeI.ropeZone.NEW[ RopeInline.Tconcat[base: rope, rest: extension, size: size+chunksize, pos: size]; size _ size+chunksize } ELSE { rope _ extension; size _ chunksize }; str.loc _ loc+chunksize; len _ len-chunksize; ENDLOOP; END; WriteBlock: PROC [str: PGStream] = BEGIN str.block.length _ str.loc; AddToWriteList[str.pgm, str.block]; str.block _ NIL; str.loc _ str.after _ 0; ProcessDefs.Yield[]; END; NewBlock: PROC [str: PGStream] RETURNS [block: Block] = BEGIN block _ str.block _ blockZone.NEW[BlockBody]; str.loc _ 0; str.after _ block.maxLength; END; WriteCharSupport: PUBLIC PROC [char: CHARACTER, str: PGStream] = BEGIN block: Block; loc: NAT; IF str.block # NIL THEN WriteBlock[str]; block _ NewBlock[str]; IF (loc_str.loc) >= str.after THEN ERROR; str.loc _ loc+1; block[loc] _ char; END; WriteWord: PUBLIC PROC [wd: CARDINAL, str: PGStream] = BEGIN word: InlineDefs.BytePair _ LOOPHOLE[wd]; WriteChar[LOOPHOLE[word.high, CHARACTER], str]; WriteChar[LOOPHOLE[word.low, CHARACTER], str]; END; WriteDoubleWord: PROC [dwd: Card, str: PGStream] = BEGIN card: InlineDefs.LongNumber _ LOOPHOLE[dwd]; WriteWord[card.highbits, str]; WriteWord[card.lowbits, str]; END; (635)\94v14V WriteRope: PUBLIC PROC [rope: Rope.Ref, str: PGStream] = BEGIN reader: RopeReader.Ref _ str.reader; block: Block; loc: filesI.BlockIndex; chunksize, after: NAT; len, pos: Card; len _ Rope.Size[rope]; pos _ 0; after _ str.after; UNTIL len = start DO IF (loc_str.loc) >= after THEN { IF str.block # NIL THEN WriteBlock[str]; block _ NewBlock[str]; after _ str.after; loc _ str.loc } ELSE block _ str.block; block.length _ loc; RopeReader.ToPosition[reader, rope, pos]; chunksize _ RopeReader.GetBlock[reader, block]; str.loc _ loc+chunksize; pos _ pos+chunksize; ENDLOOP; END; END. LOG added inlines for ReadChar/WriteChar. Paxton, 2/2/81 changed to use TiogaFBS. Pier 2/11