-- RopeFromFileImpl.Mesa
-- written by Bill Paxton, February 1981
-- last edit by Paxton, November 12, 1982 3:17 pm
Last Edited by: Maxwell, January 5, 1983 12:11 pm
DIRECTORY
RopeFrom,
RopeEdit,
RopeEditingAlloc,
RopeReader,
Rope,
RopeInline,
RTStorageOps,
File,
Space,
PropertyTypes,
Directory,
Inline,
IO,
RopeEditingBLT;
RopeFromFileImpl: CEDAR MONITOR
IMPORTS
RopeEditingBLT, RopeEditingAlloc, RopeReader, Rope, RopeInline, RopeEdit,
RopeFrom, Space, Inline, Directory, RTStorageOps, 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
activate: BOOLEANFALSE,
fileLen: Offset ← MaxLen,
okToMapFile: BOOLEANTRUE]
RETURNS [rope: ROPE] = {
MakeRope: PROC [startSpace, numToDo: Offset]
RETURNS [rope: ROPE] = TRUSTED {
chars: REF RopeReader.CharsArray;
fileSpace: Space.Handle;
len: NAT ← charsPerSpace;
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 ~okToMapFile OR len < spaceMin THEN { -- don't make a separate space for it
start, nwords: NAT;
charOffset, skipWords: NAT ← 0;
base: ROPE; 
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 [qZone.NEW[Tsubstr ← [node[substr[len,base,start+charOffset,RopeEdit.Depth[base]+1]]]]] };
[chars,fileSpace] ← GetArraySpace[];
Space.Map[fileSpace,Space.WindowOrigin[file,
startPage+startSpace*pagesPerSpace]];
IF swapUnitSize > pagesPerSpace THEN
Space.CreateUniformSwapUnits[swapUnitSize, fileSpace];
IF activate THEN Space.Activate[fileSpace];
rope ← RopeReader.CharsRope[chars,fileSpace];
IF len < charsPerSpace THEN
rope ← qZone.NEW[Tsubstr ← [node[substr[len,
rope, IF firstSpace THEN startSkip ELSE 0,
1+ RopeEdit.Depth[rope]]]]]};
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 [qZone.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 }};
-- Operation to get a space for file mapping
collectibleSpaces: INT ← 0;
GetArraySpace: PROC RETURNS [chars: REF CharsArray, space: Space.Handle] = TRUSTED {
chars ← NARROW[RTStorageOps.NewCollectibleSubspace[pagesPerSpace,CODE[CharsArray]]];
collectibleSpaces ← collectibleSpaces+1;
space ← Space.GetHandle[Space.PageFromLongPointer[LOOPHOLE[chars]]] };
-- ***** Initialization
StartRopeFromFile: PUBLIC PROC = {
};
END.