DIRECTORY TiogaFile USING [commentHeaderId, controlHeaderId, controlTrailerId, fileIdSize], FileWriter USING [BlockList, BlockListBody, blockSize, FileWriterBody, OfWriter, Ref], FS USING [OpenFile, StreamFromOpenFile], IO USING [Close, PutBlock, SetIndex, SetLength, STREAM], MonitoredQueue USING [Add, Close, Create, EndOfQueue, MQ, Remove, Reset], PrincOpsUtils USING [ByteBlt], Rope USING [AppendChars, Concat, FromRefText, ROPE], RopeReader USING [Ref]; FileWriterImpl: CEDAR MONITOR IMPORTS IO, FS, MonitoredQueue, PrincOpsUtils, Rope EXPORTS FileWriter = BEGIN OPEN FileWriter; ROPE: TYPE = Rope.ROPE; minBlt: NAT ~ 16; StringPointer: PROC[ref: REF READONLY TEXT] RETURNS[LONG POINTER] = TRUSTED INLINE { RETURN[LOOPHOLE[ref, LONG POINTER]+SIZE[TEXT[0]]]; }; StringToStringBlt: PROC [ from: REF READONLY TEXT, fromLoc: NAT, to: REF TEXT, toLoc: NAT, nChars: NAT] = { nBlt: NAT ~ (IF nChars>minBlt THEN nChars-1 ELSE 0); IF nChars=0 THEN RETURN; FOR i: NAT IN[nBlt..nChars) DO to[toLoc+i] _ from[fromLoc+i] ENDLOOP; IF nBlt#0 THEN TRUSTED { [] _ PrincOpsUtils.ByteBlt[ to: [blockPointer: StringPointer[to], startIndex: toLoc, stopIndexPlusOne: toLoc+nBlt], from: [blockPointer: StringPointer[from], startIndex: fromLoc, stopIndexPlusOne: fromLoc+nBlt] ]; }; }; OpenC: PUBLIC PROC [capability: FS.OpenFile, start: INT _ 0, makeControl: BOOL _ TRUE] RETURNS [control,comment,data: Ref] = { stream: IO.STREAM = FS.StreamFromOpenFile[capability, $write]; IO.SetLength[stream,start]; IO.SetIndex[stream,start]; [control,comment,data] _ FinishOpen[stream,FALSE,makeControl,TRUE]; }; ToRope: PUBLIC PROC [makeControl: BOOL _ TRUE] RETURNS [control,comment,data: Ref] = { [control,comment,data] _ FinishOpen[NIL,TRUE,makeControl,FALSE]; }; ToStream: PUBLIC PROC [stream: IO.STREAM, makeControl: BOOL _ TRUE] RETURNS [control,comment,data: Ref] = { [control,comment,data] _ FinishOpen[stream,FALSE,makeControl,FALSE]; }; WriteChar: PUBLIC PROC [c: CHAR, writer: Ref] = { loc: NAT; block: REF TEXT _ writer.block; IF (loc_block.length) >= blockSize THEN -- buffer is full { block _ BumpWriter[writer]; loc _ 0 }; block[loc] _ c; block.length _ loc+1; }; WriteRope: PUBLIC PROC [r: ROPE, size: INT, writer: Ref, reader: RopeReader.Ref] = { cnt: NAT; block: REF TEXT _ writer.block; pos: INT _ 0; UNTIL size=0 DO IF block.length >= blockSize THEN -- buffer is full block _ BumpWriter[writer]; cnt _ Rope.AppendChars[buffer: block, rope: r, start: pos]; pos _ pos + cnt; size _ size-cnt; ENDLOOP; }; WriteText: PUBLIC PROC [txt: REF READONLY TEXT, writer: Ref] = { cnt, loc, txtLoc: NAT; size: NAT _ txt.length; block: REF TEXT _ writer.block; txtLoc _ 0; UNTIL size=0 DO IF (loc_block.length) >= blockSize THEN -- buffer is full { block _ BumpWriter[writer]; loc _ block.length }; cnt _ MIN[blockSize-loc,size]; StringToStringBlt[txt, txtLoc, block, loc, cnt]; size _ size-cnt; txtLoc _ txtLoc+cnt; block.length _ loc+cnt; ENDLOOP; }; FinishOpen: PROC [stream: IO.STREAM, toRope: BOOL _ FALSE, makeControl, closeStream: BOOL _ TRUE] RETURNS [control, comment, data: Ref] = { MakeOne: PROC [kind: OfWriter] RETURNS [writer: Ref] = { writer _ GetFileWriter[]; writer.kind _ kind; writer.block _ GetBLOCK[]; writer.stream _ stream; writer.toRope _ toRope; writer.closeStream _ closeStream; }; IF makeControl THEN { -- make comment and control writers control _ MakeOne[control]; FOR i: NAT IN [0..TiogaFile.fileIdSize) DO WriteChar[TiogaFile.controlHeaderId[i], control]; ENDLOOP; THROUGH [0..3] DO WriteChar[0C, control]; ENDLOOP; -- for length comment _ MakeOne[comment]; FOR i:NAT IN [0..TiogaFile.fileIdSize) DO WriteChar[TiogaFile.commentHeaderId[i], comment]; ENDLOOP; THROUGH [0..3] DO WriteChar[0C,comment]; ENDLOOP; -- for length }; data _ MakeOne[data]; IF NOT toRope THEN { -- fork process to write data as it is produced data.blockQueue _ GetMQ[]; data.consumer _ FORK Consumer[data]; }; }; q1, q2: MonitoredQueue.MQ; GetMQ: ENTRY PROC RETURNS [q: MonitoredQueue.MQ] = { ENABLE UNWIND => NULL; IF q1 # NIL THEN { q _ q1; q1 _ NIL } ELSE IF q2 # NIL THEN { q _ q2; q2 _ NIL } ELSE q _ MonitoredQueue.Create[]; }; FreeMQ: ENTRY PROC [q: MonitoredQueue.MQ] = { ENABLE UNWIND => NULL; IF q=NIL THEN RETURN; MonitoredQueue.Reset[q]; IF q1 = NIL THEN q1 _ q ELSE IF q2 = NIL THEN q2 _ q; }; fw1, fw2: Ref; GetFileWriter: ENTRY PROC RETURNS [fw: Ref] = { ENABLE UNWIND => NULL; IF fw1 # NIL THEN { fw _ fw1; fw1 _ NIL } ELSE IF fw2 # NIL THEN { fw _ fw2; fw2 _ NIL } ELSE fw _ NEW[FileWriterBody]; IF fw.kind # unused THEN ERROR; }; FreeFileWriter: ENTRY PROC [fw: Ref] = { ENABLE UNWIND => NULL; fw.block _ NIL; fw.blockList _ NIL; fw.blockQueue _ NIL; fw.blockCount _ 0; fw.stream _ NIL; fw.kind _ unused; IF fw1 = NIL THEN fw1 _ fw ELSE IF fw2 = NIL THEN fw2 _ fw; }; blks: NAT = 8; BlockArray: TYPE = ARRAY [0..blks) OF REF TEXT; blocks: REF BlockArray _ NEW[BlockArray]; GetBLOCK: ENTRY PROC RETURNS [b: REF TEXT] = { ENABLE UNWIND => NULL; FOR i:NAT IN [0..blks) DO IF blocks[i] # NIL THEN { b _ blocks[i]; blocks[i] _ NIL; EXIT }; ENDLOOP; IF b=NIL THEN b _ NEW[TEXT[blockSize]]; b.length _ 0; }; FreeBLOCK: ENTRY PROC [b: REF TEXT] = { ENABLE UNWIND => NULL; FOR i:NAT IN [0..blks) DO IF blocks[i] = NIL THEN { blocks[i] _ b; RETURN }; ENDLOOP; }; BumpWriter: PUBLIC PROC [writer: Ref] RETURNS [block: REF TEXT] = { writer.blockCount _ writer.blockCount+1; -- number added to queue/list IF writer.kind=data AND ~writer.toRope THEN MonitoredQueue.Add[writer.block, writer.blockQueue] ELSE writer.blockList _ NEW[BlockListBody _ [writer.block, writer.blockList]]; writer.block _ block _ GetBLOCK[]; }; Consumer: PROC [data: Ref] = { OPEN MonitoredQueue; queue: MQ _ data.blockQueue; stream: IO.STREAM _ data.stream; DO x: REF ANY _ Remove[queue ! EndOfQueue => EXIT]; WITH x SELECT FROM xx: REF TEXT => { IO.PutBlock[stream,xx]; FreeBLOCK[xx] }; ENDCASE => ERROR; ENDLOOP; }; Close: PUBLIC PROC [control,comment,data: Ref, textOnly: BOOL] RETURNS [dataLen, count: INT, output: ROPE] = { deltaBytes: PACKED ARRAY [0..3] OF CHAR; commentStart, commentLen, controlLen: INT _ 0; stream: IO.STREAM _ data.stream; len, controlBlocks, commentBlocks, dataBlocks: NAT _ 0; toRope: BOOL _ data.toRope; controlList,commentList,dataList: BlockList; controlBlock,commentBlock,dataBlock: REF TEXT; Reverse: PROC [lst: BlockList] RETURNS [prev: BlockList] = { next: BlockList; UNTIL lst=NIL DO next _ lst.next; lst.next _ prev; prev _ lst; lst _ next; ENDLOOP; }; LastBlock: PROC [lst: BlockList] RETURNS [block: REF TEXT] = { FOR l: BlockList _ lst, l.next DO IF l.next=NIL THEN RETURN [l.block]; ENDLOOP; }; MakeRope: PROC [first, num: NAT] RETURNS [ROPE] = { half: NAT; SELECT num FROM 0 => RETURN [NIL]; 1 => RETURN [Rope.FromRefText[GetChars[first]]]; ENDCASE; half _ num/2; RETURN [Rope.Concat[MakeRope[first,half],MakeRope[first+half,num-half]]]; }; FinishWriter: PROC [writer: Ref] RETURNS [list: BlockList, block: REF TEXT] = { list _ Reverse[writer.blockList]; block _ writer.block; FreeFileWriter[writer]; }; GetChars: PROC [num: NAT] RETURNS [chars: REF TEXT] = { GetIt: PROC [blocks: NAT, list: BlockList, block: REF TEXT] RETURNS [ok: BOOL] = { IF num NOT IN [0..blocks) THEN RETURN [FALSE]; FOR lst: BlockList _ list, lst.next UNTIL lst=NIL DO IF num = 0 THEN { chars _ lst.block; RETURN [TRUE] }; num _ num-1; ENDLOOP; IF num = 0 THEN { chars _ block; RETURN [TRUE] }; ERROR; }; IF GetIt[dataBlocks, dataList, dataBlock] THEN RETURN [chars]; num _ num-dataBlocks; IF GetIt[commentBlocks, commentList, commentBlock] THEN RETURN [chars]; num _ num-commentBlocks; IF GetIt[controlBlocks, controlList, controlBlock] THEN RETURN [chars]; ERROR; }; dataBlocks _ data.blockCount; commentStart _ dataBlocks*LONG[blockSize]; IF (len _ data.block.length) MOD 2 = 1 THEN { WriteChar[0C,data]; len _ len+1 }; commentStart _ commentStart+len; dataBlocks _ dataBlocks+1; IF control # NIL THEN { IF NOT textOnly THEN { chars: REF TEXT; WriteControlInt: PROC [int: INT] = { deltaBytes _ LOOPHOLE[int]; FOR i:NAT IN [0..3] DO WriteChar[deltaBytes[i],control]; ENDLOOP; }; Check: PROC [writer: Ref, kind: OfWriter] = { IF writer.kind # kind THEN ERROR; IF writer.toRope # toRope THEN ERROR; IF writer.closeStream # data.closeStream THEN ERROR; IF writer.stream # data.stream THEN ERROR; }; Count: PROC [list: BlockList, writer: Ref, extra: INT _ 0] RETURNS [blocks: NAT, len: INT] = { blocks _ writer.blockCount; len _ LONG[blocks]*blockSize+writer.block.length+extra; blocks _ blocks+1; deltaBytes _ LOOPHOLE[len]; chars _ IF list=NIL THEN writer.block ELSE LastBlock[list]; FOR i: NAT IN [0..3] DO chars[TiogaFile.fileIdSize+i] _ deltaBytes[i]; ENDLOOP; }; Check[comment, comment]; Check[control, control]; FOR i: NAT IN [0..TiogaFile.fileIdSize) DO -- write end trailer WriteChar[TiogaFile.controlTrailerId[i],control]; ENDLOOP; WriteControlInt[0]; -- WriteControlInt[commentStart]; IF comment.block.length MOD 2 = 1 THEN WriteChar[0C, comment]; -- fill word [commentBlocks, commentLen] _ Count[comment.blockList, comment]; [controlBlocks, controlLen] _ Count[control.blockList, control, 4]; -- add 4 for final file length WriteControlInt[commentStart+commentLen+controlLen]; }; [controlList, controlBlock] _ FinishWriter[control]; [commentList, commentBlock] _ FinishWriter[comment]; }; IF data.kind#data THEN ERROR; dataBlock _ data.block; IF toRope THEN { -- return a balanced rope dataList _ Reverse[data.blockList]; output _ MakeRope[0, dataBlocks+commentBlocks+controlBlocks] } ELSE { MonitoredQueue.Close[data.blockQueue]; TRUSTED { JOIN data.consumer }; FreeMQ[data.blockQueue]; IO.PutBlock[stream, dataBlock]; FreeBLOCK[dataBlock]; IF control # NIL THEN { DoBlocks: PROC [list: BlockList, block: REF TEXT] = { FOR lst:BlockList _ list, lst.next UNTIL lst=NIL DO IF ~textOnly THEN IO.PutBlock[stream,lst.block]; FreeBLOCK[lst.block]; ENDLOOP; IF ~textOnly THEN IO.PutBlock[stream,block]; FreeBLOCK[block] }; DoBlocks[commentList,commentBlock]; DoBlocks[controlList,controlBlock]; }; }; count _ commentStart+commentLen+controlLen; dataLen _ commentStart; IF data.closeStream THEN IO.Close[stream]; FreeFileWriter[data]; }; END. ΒFileWriterImpl.mesa Copyright c 1985, 1986 by Xerox Corporation. All rights reserved. written by Paxton. March 1981 McGregor. September 10, 1982 1:15 pm Maxwell, January 5, 1983 12:50 pm Russ Atkinson, September 26, 1983 1:17 pm Plass, March 1, 1985 5:43:21 pm PST Doug Wyatt, September 18, 1986 4:09:40 pm PDT always transfer the last byte with an assignment, to get the bounds check -- this is the place to write out the file props, if any ΚC˜codešœ™Kšœ Οmœ7™BKšœ™Kšœ$™$K™!K™)K™#K™-K˜—šΟk ˜ Kšœ žœB˜QKšœ žœF˜VKšžœžœ ˜(Kšžœžœ(žœ˜8Kšœžœ"žœ˜IKšœžœ ˜Kšœžœ$žœ˜4Kšœ žœ˜—K˜KšΠblœžœž˜Kšžœžœžœ%˜3Kšžœ ˜Kšœžœžœ ˜K˜Kšžœžœžœ˜K˜šœžœ˜K˜—š Οn œžœžœžœžœ˜+Kš žœžœžœžœžœ˜(Kš žœžœžœžœžœžœ˜2K˜K˜—š œžœ˜Kš œžœžœžœ žœ˜&Kšœžœžœ žœ˜Kšœžœ˜Kš œžœžœžœ žœ˜4Kšžœ žœžœ˜š žœžœžœžœžœ˜EK™I—šžœžœžœ˜4šœ%˜%Kšœ1˜1—šœ)˜)Kšœ4˜4—Kšœ˜Kšœ˜—K˜K˜—š œžœž˜Kš œ žœžœžœžœ˜CKšžœ ˜'Kšœžœžœžœ(˜>Kšžœ˜Kšžœ˜Kšœ+žœ žœ˜CKšœ˜K˜—š  œžœžœžœžœ˜.Kšžœ ˜'Kšœ$žœžœ žœ˜@Kšœ˜K˜—š œžœžœ žœžœžœžœ˜Cšžœ ˜'Kšœ+žœ žœ˜D—šœ˜K˜——š  œžœžœžœ˜1Kšœžœ˜ Kšœžœžœ˜šžœ!žœΟc˜9K˜(—K˜%K˜K˜—š   œžœžœžœžœ*˜TKšœžœ˜ Kšœžœžœ˜Kšœžœ˜ šžœž˜šžœžœ‘˜3K˜—Kšœ;˜;Kšœ˜K˜Kšžœ˜—Kšœ˜K˜—š   œžœžœžœžœžœ˜@Kšœžœ˜Kšœžœ˜Kšœžœžœ˜K˜ šžœž˜šžœ!žœ‘˜9K˜3—Kšœžœ˜K˜0K˜=Kšžœ˜—šœ˜K˜——š  œžœ žœžœ žœžœžœžœžœ"˜‹š œžœžœ˜8K˜K˜K˜K˜K˜Kšœ!˜!Kšœ˜—šžœ žœ‘#˜9K˜šžœžœžœž˜*Kšœ1˜1Kšžœ˜—Kšžœžœžœ‘ ˜@K˜šžœžœžœž˜)Kšœ1˜1Kšžœ˜—Kšžœžœžœ‘ ˜?K˜—K˜šžœžœžœ‘/˜DK˜Kšœžœ˜$Kšœ˜—šœ˜K˜——Kšœžœ˜K˜š  œžœžœžœžœ˜4Kšžœžœžœ˜Kšžœžœžœžœ˜%Kš žœžœžœžœžœ˜*Kšžœ˜!Kšœ˜K˜—š œžœžœžœ˜-Kšžœžœžœ˜Kšžœžœžœžœ˜K˜Kšžœžœžœ˜Kšžœžœžœžœ˜Kšœ˜K˜—K˜K˜š  œžœžœžœ˜/Kšžœžœžœ˜Kšžœžœžœžœ˜)Kš žœžœžœžœžœ˜.Kšžœžœ˜Kšžœžœžœ˜Kšœ˜K˜—š œžœžœ˜(Kšžœžœžœ˜Kšœ žœ˜Kšœžœ˜Kšœžœ˜K˜Kšœ žœ˜K˜Kšžœžœžœ ˜Kšžœžœžœžœ ˜ Kšœ˜K˜—Kšœžœ˜Kš œ žœžœ žœžœžœ˜/Kšœžœžœ ˜)K˜š  œžœžœžœžœžœ˜.Kšžœžœžœ˜šžœžœžœ ž˜Kš žœ žœžœžœžœ˜AKšžœ˜—Kš žœžœžœžœžœ ˜'K˜ K˜K˜—š   œžœžœžœžœ˜'Kšžœžœžœ˜šžœžœžœ ž˜Kšžœ žœžœžœ˜2Kšžœ˜Kšœ˜K˜——š   œžœžœžœ žœžœ˜CKšœ)‘˜FKšžœžœžœ4˜_Kšžœžœ3˜NK˜"K˜K˜—š œžœžœ˜3Kšœžœ˜Kšœžœžœ˜ šžœžœžœ žœ˜3Kšžœžœž˜Kšœžœžœžœ&˜:Kšžœžœ˜Kšžœ˜Kšœ˜K˜——š œžœžœ'žœ˜>Kšžœžœ žœ˜/Kš œ žœžœžœžœ˜)Kšœ&žœ˜.Kšœžœžœ˜ Kšœ/žœ˜7Kšœžœ˜K˜,Kšœ%žœžœ˜.K˜š œžœžœ˜šžœž˜!Kšžœžœžœžœ ˜$Kšžœ˜—šœ˜K˜——š  œžœžœžœžœ˜3Kšœžœ˜ šžœž˜Kšœžœžœ˜Kšœžœ%˜0Kšžœ˜—K˜ KšžœC˜IKšœ˜K˜—š   œžœžœžœžœ˜OK˜OK˜K˜—š  œžœžœžœ žœžœ˜7š  œžœ žœžœžœ˜;Kšžœžœ˜Kš žœžœžœ žœžœžœ˜.šžœ!žœžœž˜4Kšžœ žœžœžœ˜5Kšœ žœ˜—Kšžœ žœžœžœ˜1Kšžœ˜Kšœ˜—Kšžœ(žœžœ ˜>K˜Kšžœ1žœžœ ˜GK˜Kšžœ1žœžœ ˜GKšžœ˜Kšœ˜K˜—K˜Kšœžœ ˜*Kšžœžœžœ%˜PK˜ K˜K˜Kšžœ žœžœ˜˜šžœžœ žœ˜Kšœžœžœ˜K˜š œžœžœ˜$Kšœ žœ˜šžœžœžœž˜K˜!Kšžœ˜—šœ˜K˜——š œžœ"˜-Kšžœžœžœ˜!Kšžœžœžœ˜%Kšžœ'žœžœ˜4Kšžœžœžœ˜*Kšœ˜K˜—š œžœ'žœ˜:Kšžœ žœžœ˜#K˜Kšœžœ-˜7K˜Kšœ žœ˜Kš œžœžœžœžœ˜;Kš žœžœžœžœ0žœ˜OKšœ˜K˜—K˜K˜K˜Kšœ8™8K˜š žœžœžœžœ‘˜?Kšœ1˜1Kšžœ˜K˜—Kšœ‘˜+K˜K˜Kšžœžœžœ‘ ˜KK˜K˜@KšœD‘˜bK˜K˜4K˜K˜—K˜4K˜4K˜K˜—Kšžœžœžœ˜Kšœ˜šžœžœ‘˜*K˜#Kšœ<˜