FileWriterImpl.Mesa
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
DIRECTORY
FileWriter,
IO,
FileOps,
FS,
MonitoredQueue,
Rope,
RopeReader,
TextNode;
FileWriterImpl:
CEDAR MONITOR
IMPORTS IO, FileWriter, FS, MonitoredQueue, Rope, TextNode
EXPORTS FileWriter
SHARES FileWriter =
BEGIN OPEN FileWriter;
ROPE: TYPE = Rope.ROPE;
OpenC:
PUBLIC
PROC
[capability: FS.OpenFile, start: Offset ← 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];
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..FileOps.fileIdSize)
DO
WriteChar[FileOps.controlHeaderId[i],control]; ENDLOOP;
THROUGH [0..3] DO WriteChar[0C,control]; ENDLOOP; -- for length
comment ← MakeOne[comment];
FOR i:
NAT
IN [0..FileOps.fileIdSize)
DO
WriteChar[FileOps.commentHeaderId[i],comment]; ENDLOOP;
THROUGH [0..3]
DO WriteChar[0C,comment];
ENDLOOP;
-- for length
};
data ← MakeOne[data];
IF ~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[TextNode.pZone] };
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 ← TextNode.pZone.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 ← TextNode.qZone.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 ← TextNode.pZone.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: Offset, output: ROPE] = {
deltaBytes: PACKED ARRAY [0..3] OF CHARACTER;
commentStart, commentLen, controlLen: Offset ← 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 (lenta.block.length) MOD 2 = 1 THEN { WriteChar[0C,data]; len ← len+1 };
commentStart ← commentStart+len;
dataBlocks ← dataBlocks+1;
IF control # NIL THEN {
IF ~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: Offset ← 0]
RETURNS [blocks: NAT, len: Offset] = {
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[FileOps.fileIdSize+i] ← deltaBytes[i]; ENDLOOP };
Check[comment,comment];
Check[control,control];
-- this is the place to write out the file props, if any
FOR i:
NAT
IN [0..FileOps.fileIdSize)
DO
-- write end trailer
WriteChar[FileOps.controlTrailerId[i],control]; ENDLOOP;
WriteControlInt[0]; -- <file-props-length>
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 {
dataList ← Reverse[data.blockList];
output ← MakeRope[0,dataBlocks+commentBlocks+controlBlocks] } -- return a balanced rope
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] };
Start:
PUBLIC
PROC = {
};
END.