-- PPRopeFileImpl.mesa
-- last edit by Russ Atkinson, 10-Mar-82 10:23:26
DIRECTORY
IOStream USING [GetBlock, GetChar, GetLength, Handle, SetIndex],
Inline USING [LowHalf, HighHalf],
Mopcodes USING [zME, zMXD],
PPRopeFile,
Rope USING [ActionType, FromProc, MakeRope, Ref];
PPRopeFileImpl: MONITOR
LOCKS handle USING handle: Handle
IMPORTS IOStream, Inline, Rope
EXPORTS PPRopeFile
= BEGIN OPEN Inline;
Int: TYPE = LONG INTEGER;
RopeRef: TYPE = Rope.Ref;
Handle: TYPE = REF RopeFileRep;
AcquireLock: PROC [h: Handle] RETURNS [BOOLEAN] = MACHINE CODE {
-- returns TRUE if lock acquired, FALSE if not
Mopcodes.zME};
ReleaseLock: PROC [h: Handle] = MACHINE CODE {
Mopcodes.zMXD};
RopeFileRep: PUBLIC TYPE = MONITORED RECORD
[file: IOStream.Handle,
bufSize: NAT,
data: SEQUENCE buffers: NAT OF Buf];
Buf: TYPE = REF BufRec;
BufRec: TYPE = RECORD [start: Int, len: NAT, text: REF TEXT];
Create: PUBLIC PROC
[file: IOStream.Handle, bufSize,buffers: NAT, useMap: BOOLEAN]
RETURNS [handle: Handle, rope: RopeRef] = {
bytes: Int ← 0;
IF buffers = 0 OR buffers > 64 OR file = NIL THEN ERROR;
IF bufSize < 64 THEN bufSize ← 64;
file.SetIndex[0];
bytes ← file.GetLength[];
IF bytes <= LONG[buffers]*LONG[bufSize] THEN
{get: PROC RETURNS [CHARACTER] = {
RETURN [file.GetChar[]]};
rope ← Rope.FromProc[bytes, get];
handle ← NEW[RopeFileRep[0]];
handle.bufSize ← 0;
RETURN};
handle ← NEW[RopeFileRep[buffers]];
handle.bufSize ← bufSize;
handle.file ← file;
FOR i: NAT IN [0..buffers) DO
handle[i] ← NEW[BufRec ← [start: 0, len: 0, text: NEW[TEXT[bufSize]]]];
ENDLOOP;
rope ← Rope.MakeRope
[handle,
IOStream.GetLength[file],
MyFetch,
IF useMap THEN MyMap ELSE NIL];
};
Destroy: PUBLIC ENTRY PROC [handle: Handle] = {
IF handle = NIL THEN RETURN;
handle.file ← NIL;
handle.bufSize ← 0;
FOR i: NAT IN [0..handle.buffers) DO
handle[i].len ← 0;
handle[i].text ← NIL;
ENDLOOP;
};
GetFile: PUBLIC PROC [handle: Handle] RETURNS [IOStream.Handle] = {
RETURN [handle.file]};
MyFetch: PROC [ref: REF, index: Int] RETURNS [CHARACTER] = {
handle: Handle = NARROW[ref];
last: NAT = handle.buffers-1;
i: NAT ← 0;
IF handle.file = NIL THEN ERROR;
UNTIL AcquireLock[handle] DO ENDLOOP;
DO
buf: Buf ← handle[i];
delta: Int ← index - buf.start;
SELECT TRUE FROM
HighHalf[delta] = 0 AND LowHalf[delta] < buf.len => {};
i < last => {i ← i+1; LOOP};
ENDCASE => {
-- need to swap in new buffer
length: Int ← handle.bufSize;
start: Int = index - (delta ← index MOD length);
IOStream.SetIndex[handle.file, start];
length ← IOStream.GetBlock
[handle.file, buf.text, 0, LowHalf[length]];
IF length = 0 THEN {ReleaseLock[handle]; ERROR};
buf.start ← start;
buf.len ← LowHalf[length];
};
IF i # 0 THEN {
-- swap to speed searches
handle[i] ← handle[i-1];
handle[i-1] ← buf;
};
{c: CHARACTER ← buf.text[LowHalf[delta]];
ReleaseLock[handle];
RETURN [c]};
ENDLOOP;
};
MyMap: PROC [base: REF, start,len: Int,
action: Rope.ActionType] RETURNS [BOOLEAN] = {
handle: Handle ← NARROW[base];
last: NAT = handle.buffers-1;
i: NAT ← 0;
IF handle.file = NIL THEN ERROR;
IF len <= 0 THEN RETURN [FALSE];
UNTIL AcquireLock[handle] DO ENDLOOP;
DO
buf: Buf ← handle[i];
bufStart: Int ← buf.start;
delta: Int ← start - bufStart;
SELECT TRUE FROM
HighHalf[delta] = 0 AND LowHalf[delta] < buf.len => {
rem: NAT ← buf.len-LowHalf[delta];
chars: CARDINAL ← 0;
IF len < rem THEN rem ← LowHalf[len];
WHILE chars < rem DO
c: CHARACTER ← buf.text[LowHalf[delta]+chars]; -- eval this while locked
ReleaseLock[handle];
IF action[c] THEN RETURN [TRUE]; -- eval this while unlocked
chars ← chars + 1;
UNTIL AcquireLock[handle] DO ENDLOOP;
IF buf.start # bufStart THEN EXIT; -- abort & retry if buf changed
ENDLOOP;
len ← len - chars; -- update according to the amount done
start ← start + chars;
IF len > 0 THEN {i ← 0; LOOP};
};
i < last => {
-- missed it, unfortunately
i ← i+1;
LOOP};
ENDCASE => {
-- need to swap in new buffer
length: NAT ← handle.bufSize;
base: Int = start - (start MOD length);
IOStream.SetIndex[handle.file, base];
length ← IOStream.GetBlock
[handle.file, buf.text, 0, LowHalf[length]];
IF length = 0 THEN {ReleaseLock[handle]; ERROR};
buf.start ← base;
buf.len ← LowHalf[length];
LOOP;
};
IF i # 0 AND handle[i] = buf THEN {
-- swap to speed searches
handle[i] ← handle[i-1];
handle[i-1] ← buf;
};
ReleaseLock[handle];
RETURN [FALSE];
ENDLOOP;
};
END.