-- 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.