RopeFileImpl.mesa
Russ Atkinson, June 3, 1983 4:46 pm
DIRECTORY
Commander USING [CommandProc, Register],
FileIO USING [CapabilityFromStream, Open, OpenFailed],
IO USING [Close, GetBlock, GetLength, PutRope, SetIndex, STREAM],
PutGet USING [FromRope],
RopeFile USING [],
Rope
USING
[Concat, FetchType, Flatten, MakeRope, MapType, ROPE, Size, SkipOver, SkipTo, Text],
RopeInline USING [NewText],
RTFilesExtra USING [IncFileUse],
TEditImpl USING [InitTEditDocument],
TiogaMenuOps USING [DefaultMenus],
ViewerClasses USING [Viewer],
ViewerOps USING [OpenIcon],
ViewerTools USING [MakeNewTextViewer];
RopeFileImpl:
CEDAR MONITOR
LOCKS handle USING handle: Handle
IMPORTS
Commander, FileIO, IO, PutGet, Rope, RopeInline, RTFilesExtra, TEditImpl, TiogaMenuOps, ViewerOps, ViewerTools
EXPORTS RopeFile
= BEGIN
T Y P E S
ROPE: TYPE = Rope.ROPE;
STREAM: TYPE = IO.STREAM;
Handle: TYPE = REF RopeFileRep;
RopeFileRep:
TYPE =
MONITORED
RECORD [
file: STREAM ← NIL,
locked: BOOL ← FALSE,
lockChange: CONDITION,
bufSize: NAT ← 0,
offset: INT ← 0,
data: SEQUENCE buffers: NAT OF Buf];
Buf: TYPE = REF BufRec;
BufRec: TYPE = RECORD [start: INT, len: NAT, text: REF TEXT];
AcquireLock:
ENTRY
PROC [handle: Handle] = {
WHILE handle.locked DO WAIT handle.lockChange; ENDLOOP;
handle.locked ← TRUE;
};
ReleaseLock:
ENTRY
PROC [handle: Handle] = {
IF NOT handle.locked THEN ERROR;
handle.locked ← FALSE;
BROADCAST handle.lockChange;
};
Create:
PUBLIC
PROC
[name:
ROPE, start:
INT ← 0, len:
INT ←
LAST[
INT],
bufSize: INT ← 512, buffers: NAT ← 4,
useMap: BOOL ← TRUE, raw: BOOL ← TRUE]
RETURNS [rope: ROPE] = {
handle: Handle;
fileStream:
STREAM ← FileIO.Open[
fileName: name,
raw: raw,
streamBufferParms: [bufferSize: 2, bufferSwapUnitSize: 1]];
filesize: INT ← IO.GetLength[fileStream];
bestBuffers: INT;
IF start < 0 THEN start ← 0;
IF len <= 0 THEN RETURN [NIL];
IF len = LAST[INT] OR start+len > filesize THEN len ← filesize - start;
IO.SetIndex[fileStream, start];
IF buffers = 0 THEN buffers ← 1;
IF bufSize < 64 THEN bufSize ← 64;
IF bufSize > 16*1024 THEN bufSize ← 16*1024;
bestBuffers ← (len + bufSize - 1) / bufSize;
IF bestBuffers < buffers THEN buffers ← bestBuffers;
IF len < 16*1024
AND len <= (buffers * bufSize)
THEN
TRUSTED {
text: Rope.Text ← RopeInline.NewText[len];
[] ← IO.GetBlock[fileStream, LOOPHOLE[text], 0, len];
IO.Close[fileStream];
RETURN [text];
};
IF
NOT RTFilesExtra.IncFileUse[FileIO.CapabilityFromStream[fileStream],
FALSE]
THEN ERROR;
handle ← NEW[RopeFileRep[buffers]];
handle.bufSize ← bufSize;
handle.file ← fileStream;
handle.offset ← start;
FOR i:
NAT
IN [0..buffers)
DO
handle[i] ← NEW[BufRec ← [start: 0, len: 0, text: NEW[TEXT[bufSize]]]];
ENDLOOP;
rope ← Rope.MakeRope[handle, len, MyFetch, IF useMap THEN MyMap ELSE NIL];
};
MyFetch: Rope.FetchType = {
[data: REF, index: INT] RETURNS [CHAR]
handle: Handle = NARROW[data];
last: NAT = handle.buffers-1;
c: CHAR;
IF handle.file = NIL THEN ERROR;
AcquireLock[handle];
index ← index + handle.offset;
FOR i:
NAT
IN [0..last]
DO
buf: Buf ← handle[i];
delta: INT ← index - buf.start;
SELECT
TRUE
FROM
delta >= 0
AND delta < buf.len => {
this buffer contains the character range
};
i < last =>
we have not come to the last buffer
LOOP;
ENDCASE => {
need to swap in new buffer
length: NAT ← handle.bufSize;
start: INT = index - (delta ← index MOD length);
IO.SetIndex[handle.file, start];
length ← IO.GetBlock[handle.file, buf.text, 0, length];
IF length = 0 THEN {ReleaseLock[handle]; ERROR};
buf.start ← start;
buf.len ← length;
};
IF i # 0
THEN {
swap to speed searches
handle[i] ← handle[i-1];
handle[i-1] ← buf;
};
c ← buf.text[delta];
EXIT;
ENDLOOP;
ReleaseLock[handle];
RETURN [c];
};
MyMap: Rope.MapType =
TRUSTED {
[base: REF, start, len: INT, action: ActionType] RETURNS [quit: BOOL ← FALSE]
handle: Handle ← NARROW[base];
last: NAT = handle.buffers-1;
IF handle.file = NIL THEN ERROR;
start ← start + handle.offset;
WHILE len > 0
DO
buf: Buf ← NIL;
delta: INT ← 0;
subLen: NAT = 32;
string: STRING ← [subLen];
pos: NAT ← delta;
spos: NAT ← 0;
chars: NAT ← 0;
AcquireLock[handle];
FOR i:
NAT
IN [0..last]
DO
buf ← handle[i];
delta ← start - buf.start;
SELECT
TRUE
FROM
delta >= 0
AND delta < buf.len => {
this buffer contains the character range
};
i < last =>
we have not come to the last buffer
LOOP;
ENDCASE => {
need to swap in new buffer
length: NAT ← handle.bufSize;
bpos: INT = start - (delta ← start MOD length);
IO.SetIndex[handle.file, bpos];
length ← IO.GetBlock[handle.file, buf.text, 0, length];
IF length = 0 THEN {ReleaseLock[handle]; ERROR};
buf.start ← bpos;
buf.len ← length;
};
IF i # 0
THEN {
swap to speed searches
handle[i] ← handle[i-1];
handle[i-1] ← buf;
};
EXIT;
ENDLOOP;
At this point we have found a good buffer, so we snatch a sub-buffer from it
pos ← delta;
chars ← MIN[buf.len-pos, subLen];
IF len < chars THEN chars ← len;
FOR j:
NAT
IN [0..chars)
DO
string[j] ← buf.text[pos+j];
ENDLOOP;
we will not touch the buffer until the next time around (if any)
ReleaseLock[handle];
Now yield all of the chars in the sub-buffer
FOR j:
NAT
IN [0..chars)
DO
IF action[string[j]] THEN RETURN [TRUE];
ENDLOOP;
len ← len - chars;
start ← start + chars;
ENDLOOP;
RETURN [FALSE];
};
SubstrCreate:
PUBLIC
PROC
[name: ROPE, start: INT ← 0, len: INT ← LAST[INT]] RETURNS [rope: ROPE] = {
RETURN [Create[name, start, len]];
};
SimpleCreate:
PUBLIC
PROC [name:
ROPE]
RETURNS [rope:
ROPE] = {
RETURN [Create[name]];
};
useMap: BOOL ← TRUE;
extensionList: LIST OF ROPE ← LIST["", ".mesa", ".tioga", ".df"];
OpenHuge: Commander.CommandProc = {
line: ROPE ← cmd.commandLine;
last: INT ← 0;
out: STREAM ← cmd.out;
DO
start: INT ← line.SkipOver[last, " ,;\t\n"];
next: INT ← line.SkipTo[start, " ,;\t\n"];
name: ROPE ← line.Flatten[start, next-start];
rope: ROPE ← NIL;
IF name.Size[] = 0 THEN RETURN;
out.PutRope["Opening "];
out.PutRope[name];
out.PutRope["..."];
last ← next;
FOR list:
LIST
OF
ROPE ← extensionList, list.rest
WHILE list #
NIL
DO
rope ← Create[
name: name.Concat[list.first],
useMap: useMap
! FileIO.OpenFailed => LOOP];
IF rope # NIL THEN EXIT;
ENDLOOP;
IF rope #
NIL
THEN {
v: ViewerClasses.Viewer ← ViewerTools.MakeNewTextViewer[
info: [name: name, column: left, iconic: TRUE], paint: FALSE];
v.data ← PutGet.FromRope[rope];
v.file ← name;
TEditImpl.InitTEditDocument[v];
TiogaMenuOps.DefaultMenus[v];
ViewerOps.OpenIcon[v];
out.PutRope["done.\n"];
}
ELSE {
out.PutRope["failed!\n"];
};
ENDLOOP;
};
Commander.Register["openHuge", OpenHuge, "Opens huge files in a somewhat unsafe fashion."];