DIRECTORY FileIO USING [CapabilityFromStream, Open], IO USING [Close, GetBlock, GetLength, SetIndex, STREAM], RopeFile USING [], Rope, RopeInline USING [NewText], RTFilesExtra; RopeFileImpl: CEDAR MONITOR LOCKS handle USING handle: Handle IMPORTS FileIO, IO, Rope, RopeInline, RTFilesExtra EXPORTS RopeFile = BEGIN 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 <= (bestBuffers * 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 = { 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 => { }; i < last => LOOP; ENDCASE => { 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 { handle[i] _ handle[i-1]; handle[i-1] _ buf; }; c _ buf.text[delta]; EXIT; ENDLOOP; ReleaseLock[handle]; RETURN [c]; }; MyMap: Rope.MapType = TRUSTED { 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 => { }; i < last => LOOP; ENDCASE => { 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 { handle[i] _ handle[i-1]; handle[i-1] _ buf; }; EXIT; ENDLOOP; 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; ReleaseLock[handle]; 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]]; }; END. n RopeFileImpl.mesa Russ Atkinson, April 19, 1983 9:43 am [data: REF, index: INT] RETURNS [CHAR] this buffer contains the character range we have not come to the last buffer need to swap in new buffer swap to speed searches [base: REF, start, len: INT, action: ActionType] RETURNS [quit: BOOL _ FALSE] this buffer contains the character range we have not come to the last buffer need to swap in new buffer swap to speed searches At this point we have found a good buffer, so we snatch a sub-buffer from it we will not touch the buffer until the next time around (if any) Now yield all of the chars in the sub-buffer Êõ˜J™Jšœ™Jšœ%™%J˜šÏk ˜ Jšœœ˜*Jšœœ(œ˜8Jšœ œ˜Jšœ˜Jšœ œ ˜Jšœ ˜ J˜—šœ ˜Jšœœ˜!Jšœ œ ˜2Jšœ ˜Jšœ˜J˜Jšœœœ˜Jšœœœœ˜Jšœœœ ˜J˜šœ œ œœ˜&Jšœœœ˜Jšœœœ˜Jšœ œ˜Jšœ œ˜Jšœœ˜Jšœœ œœ˜$J˜—Jšœœœ˜Jš œœœ œœœœ˜=J˜—šÏn œœœ˜,Jšœœœœ˜7Jšœœ˜J˜J˜—šž œœœ˜,Jšœœœœ˜ Jšœœ˜Jš œ˜J˜J˜—šžœœ˜š œœ œ œœœ˜2Jšœ œœ˜%Jš œœœœœ˜&—Jšœœ˜Jšœ˜šœ œ˜!Jšœ˜J˜ Jšœ;˜;—Jšœ œœ˜)Jšœ œ˜Jšœ œ ˜Jšœ œœœ˜Jš œœœœœ˜GJšœ˜Jšœ œ ˜ Jšœœ˜"Jšœœ˜,Jšœ,˜,Jšœœ˜4šœœ œœ˜BJšœ*˜*Jšœœœ˜5Jšœ˜Jšœ˜J˜—šœœBœ˜NJšœœ˜ —Jšœ œ˜#J˜Jšœ˜J˜šœœœ˜Jšœ œ#œœ ˜GJšœ˜—Jš œ+œœœœ˜JJ˜J˜—šœ˜Jšœ&™&Jšœœ˜Jšœœ˜Jšœœ˜Jšœœœœ˜ Jšœ˜J˜šœœœ ˜J˜Jšœœ˜šœœ˜šœ œ˜#Jšœ(™(Jšœ˜—šœ ˜ Jšœ#™#Jšœ˜—šœ˜ Jšœ™Jšœœ˜Jšœœœ ˜0Jšœ˜ Jšœ œ,˜7Jšœ œœ˜0J˜J˜J˜——šœœ˜Jšœ™J˜J˜J˜—Jšœ˜Jšœ˜Jšœ˜—J˜Jšœ˜ J˜J˜—šœœ˜JšœM™MJšœœ˜Jšœœ˜Jšœœœœ˜ Jšœ˜šœ ˜Jšœ œ˜Jšœœ˜Jšœœ˜Jšœœ ˜Jšœœ ˜Jšœœ˜Jšœœ˜Jšœ˜šœœœ ˜J˜Jšœ˜šœœ˜šœ œ˜#Jšœ(™(Jšœ˜—šœ ˜ Jšœ#™#Jšœ˜—šœ˜ Jšœ™Jšœœ˜Jšœœœ ˜/Jšœ˜Jšœ œ,˜7Jšœ œœ˜0Jšœ˜J˜J˜——šœœ˜Jšœ™J˜J˜J˜—Jšœ˜Jšœ˜—JšœL™LJšœ ˜ Jšœœ˜!Jšœ œ ˜ šœœœ ˜Jšœ˜Jšœ˜—Jšœ@™@J˜Jšœ,™,šœœœ ˜Jšœœœœ˜(Jšœ˜—Jšœ˜J˜Jšœ˜—Jšœœ˜J˜J˜—šž œœ˜Jšœœ œ œœœœœ˜KJšœ˜"J˜J˜—š ž œœœœœœ˜?Jšœ˜J˜J˜—šœ˜J˜——…—$‡