-- RopeFromFileImpl.Mesa; written by Bill Paxton, February 1981
-- edited by Paxton, August 19, 1983 6:12 pm
-- edited by McGregor, February 8, 1983 11:03 am
-- edited by Maxwell, January 5, 1983 12:11 pm
DIRECTORY
RopeFrom,
RopeEdit,
RopeEditingAlloc,
RopeReader,
Rope,
RopeInline,
File,
Space,
PropertyTypes,
Directory,
Inline,
IO,
RopeEditingBLT;
RopeFromFileImpl: CEDAR MONITOR
IMPORTS RopeEditingBLT, RopeEditingAlloc, Rope, RopeInline, RopeEdit, Space, Inline, Directory, IO
EXPORTS RopeFrom
SHARES Rope =
BEGIN
OPEN fileI:File, RopeFrom, RopeInline;
pagesPerSpace: NAT = RopeReader.pagesPerArray;
charsPerPage: NAT = RopeReader.charsPerPage;
charsPerArray: NAT = RopeReader.charsPerArray;
charsPerSpace: NAT = charsPerArray;
spaceMin: NAT = charsPerSpace/2; -- put in shared array if less than 1/2
swapUnitSize: NAT = 16; -- based on Paul's advice
CharsArray: TYPE = RopeReader.CharsArray;
Chars: TYPE = REF CharsArray;
-- ***** Operations
File: PUBLIC PROC [
file: fileI.Capability,
start: Offset ← 0,
length: Offset ← MaxLen, -- defaults to rest of file
fileLen: Offset ← MaxLen]
RETURNS [rope: ROPE] = {
MakeRope: PROC [startSpace, numToDo: Offset]
RETURNS [rope: ROPE] = TRUSTED {
chars: REF RopeReader.CharsArray;
fileSpace: Space.Handle;
len: NAT ← charsPerSpace;
start, nwords: NAT;
charOffset, skipWords: NAT ← 0;
base: ROPE; 
firstSpace: BOOLEANFALSE;
IF numToDo = 0 THEN RETURN [NIL];
IF numToDo > 1 THEN { -- split into balanced parts
half: Offset ← numToDo/2;
front: ROPE ← MakeRope[startSpace, half];
back: ROPE ← MakeRope[startSpace+half, numToDo-half];
RETURN [Rope.Concat[front,back]] };
IF startSpace+1 = numSpaces THEN -- this is the last space
len ← len - endSkip;
IF startSpace = 0 THEN { -- this is the first space for the file
len ← len - startSkip; firstSpace ← TRUE };
IF firstSpace THEN {
-- make skipWords*2+charOffset=startSkip
charOffset ← startSkip MOD 2;
skipWords ← startSkip/2 };
nwords ← (len+charOffset+1)/2; -- number to copy from file
[chars, start, base] ← RopeEditingAlloc.AllocWords[nwords];
fileSpace ← GetSpace[];
Space.Map[fileSpace, Space.WindowOrigin[file, startPage+startSpace*pagesPerSpace]];
Space.Activate[fileSpace];
Inline.LongCOPY[from: Space.LongPointer[fileSpace]+skipWords,
nwords: nwords, to: LOOPHOLE[chars,LONG POINTER]+start/2];
FreeSpace[fileSpace];
RETURN [NEW[Tsubstr ← [node[substr[len,base,start+charOffset,RopeEdit.Depth[base]+1]]]]] };
GetFileLen: PROC = TRUSTED {
Directory.GetProperty[file: file, property: PropertyTypes.tByteLength,
propertyValue: DESCRIPTOR[@fileLen, SIZE[Offset]]] };
startPage: Offset;
startSkip, endSkip: NAT;
numPages, numSpaces: Offset;
start ← start+charsPerPage; -- to skip over leader page
startPage ← start/charsPerPage;
startSkip ← start MOD charsPerPage;
IF fileLen = MaxLen THEN GetFileLen;
fileLen ← fileLen+charsPerPage; -- to compensate for leader page
IF fileLen <= start THEN RETURN [NIL];
length ← MIN[length,fileLen-start];
IF length = 0 THEN RETURN[NIL];
numPages ← (start+length+charsPerPage-1)/charsPerPage-startPage;
numSpaces ← (numPages+pagesPerSpace-1)/pagesPerSpace;
endSkip ← numSpaces*charsPerSpace-length-startSkip;
rope ← MakeRope[0,numSpaces]};
Stream: PUBLIC PROC [stream: IO.Handle, length: Offset ← MaxLen]
RETURNS [rope: ROPE] = {
MakeRope: PROC [len: Offset] RETURNS [ROPE] = {
chars: REF RopeReader.CharsArray;
start, nchars, charloc: NAT;
base: ROPE;
IF len = 0 THEN RETURN [NIL];
IF len > charsPerSpace THEN { -- split into balanced parts
half: Offset ← MAX[len/2,charsPerSpace];
front: ROPE ← MakeRope[half];
back: ROPE ← MakeRope[len-half];
RETURN [Rope.Concat[front,back]] };
nchars ← Short[len];
[chars, start, base] ← RopeEditingAlloc.AllocWords[(nchars+1)/2];
charloc ← start;
UNTIL nchars = 0 DO
nread: NAT;
block.length ← 0;
nread ← IO.GetBlock[self: stream, block: block, startIndex: 0, stopIndexPlusOne: nchars];
RopeEditingBLT.StringToArrayBlt[block,0,chars,charloc,nread];
nchars ← nchars-nread; charloc ← charloc+nread;
ENDLOOP;
RETURN [NEW[Tsubstr ← [node[substr[len,base,start,1+RopeEdit.Depth[base]]]]]] };
start,fileLen: Offset;
block: REF TEXT;
fileLen ← IO.GetLength[stream];
start ← IO.GetIndex[stream];
IF fileLen <= start THEN RETURN [NIL];
length ← MIN[length,fileLen-start];
IF length = 0 THEN RETURN [NIL];
block ← RopeEditingAlloc.GetBlock[];
rope ← MakeRope[length];
RopeEditingAlloc.FreeBlock[block] };
***** These two routines are the only monitored procedures
***** They provide a shared space for reading files
fileSpace: Space.Handle ← Space.nullHandle; -- shared file space
numSpaces: INT ← 0; -- number allocated
freedSpaces: INT ← 0;
GetSpace: ENTRY PROC RETURNS [space: Space.Handle] = TRUSTED {
ENABLE UNWIND => NULL;
IF fileSpace # Space.nullHandle THEN space ← fileSpace
ELSE { -- need to allocate one
space ← Space.Create[pagesPerSpace, Space.virtualMemory];
numSpaces ← numSpaces+1 };
fileSpace ← Space.nullHandle };
FreeSpace: ENTRY PROC [space: Space.Handle] = TRUSTED {
ENABLE UNWIND => NULL;
IF fileSpace = Space.nullHandle THEN Space.Unmap[fileSpace ← space]
ELSE { Space.Delete[space]; freedSpaces ← freedSpaces+1 }};
END.