<> <> <> <> DIRECTORY FS USING [defaultStreamOptions, GetName, nullOpenFile, OpenFile, OpenFileFromStream, StreamOpen, StreamOptions], IO USING [Close, GetBlock, GetLength, SetIndex, STREAM], PrincOpsUtils USING [ByteBlt], Process USING [Detach], RopeFile USING [], Rope USING [AppendCharsType, FetchType, MakeRope, MapType, NewText, ROPE, Text], SafeStorage USING [CantEstablishFinalization, EnableFinalization, EstablishFinalization, FinalizationQueue, FQNext, NewFQ, ReEstablishFinalization]; RopeFileImpl: CEDAR MONITOR LOCKS handle USING handle: Handle IMPORTS FS, IO, PrincOpsUtils, Process, Rope, SafeStorage EXPORTS RopeFile SHARES Rope = BEGIN <> ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; Handle: TYPE = REF RopeFileRep; RopeFileRep: TYPE = MONITORED RECORD [ lockChange: CONDITION, next: Handle _ NIL, file: STREAM _ NIL, offset: INT _ 0, locked: BOOL _ FALSE, bufSize: NAT _ 0, data: SEQUENCE buffers: NAT OF Buf]; Buf: TYPE = REF BufRec; BufRec: TYPE = RECORD [start: INT, len: NAT, text: REF TEXT]; useByteBlt: BOOL _ TRUE; <> pagesPerFileBuffer: NAT _ 2; numberOfFilebuffers: NAT _ 2; AcquireLock: ENTRY PROC [handle: Handle] = INLINE { WHILE handle.locked DO WAIT handle.lockChange; ENDLOOP; handle.locked _ TRUE; }; ReleaseLock: ENTRY PROC [handle: Handle] = INLINE { 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, raw: BOOL _ TRUE] RETURNS [rope: ROPE] = { options: FS.StreamOptions _ FS.defaultStreamOptions; stream: STREAM _ NIL; IF raw THEN <> options[tiogaRead] _ FALSE; stream _ FS.StreamOpen[ fileName: name, accessOptions: $read, streamOptions: options, streamBufferParms: [vmPagesPerBuffer: pagesPerFileBuffer, nBuffers: numberOfFilebuffers]]; RETURN [FromStream[stream, start, len, bufSize, buffers]]; }; FromStream: PUBLIC PROC [stream: STREAM, start: INT _ 0, len: INT _ LAST[INT], bufSize: INT _ 512, buffers: NAT _ 4] RETURNS [rope: ROPE] = { handle: Handle _ NIL; filesize: INT _ 0; bestBuffers: INT _ 0; IF start < 0 THEN start _ 0; IF len <= 0 THEN RETURN [NIL]; filesize _ IO.GetLength[stream]; IF len = LAST[INT] OR start+len > filesize THEN len _ filesize - start; IO.SetIndex[stream, 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 _ Rope.NewText[len]; [] _ IO.GetBlock[stream, LOOPHOLE[text], 0, len]; IO.Close[stream]; RETURN [text]; }; handle _ NEW[RopeFileRep[buffers]]; handle.bufSize _ bufSize; handle.file _ stream; 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, MyMap, MyAppendChars]; <> AcquireLock[dummyRopeFile]; handle.next _ dummyRopeFile.next; dummyRopeFile.next _ handle; ropesOutstanding _ ropesOutstanding + 1; SafeStorage.EnableFinalization[handle]; ReleaseLock[dummyRopeFile]; }; 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 => { <> }; 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 { <<[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 => { <> }; 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]]; }; FileFromRope: PUBLIC PROC [rope: ROPE] RETURNS [FS.OpenFile _ FS.nullOpenFile] = TRUSTED { <> WITH r: rope SELECT FROM node => WITH r: r SELECT FROM object => WITH r.base SELECT FROM h: Handle => RETURN [FS.OpenFileFromStream[h.file]]; ENDCASE; ENDCASE; ENDCASE; }; AddrOfChars: PROC [refText: REF TEXT] RETURNS [LONG POINTER] = TRUSTED INLINE { RETURN [LOOPHOLE[refText, LONG POINTER] + SIZE[TEXT[0]] ]; }; MyAppendChars: Rope.AppendCharsType = TRUSTED { <<[buffer: REF TEXT, data: REF, start: INT, len: INT] RETURNS [charsMoved: NAT]; >> <> WITH data SELECT FROM handle: Handle => { <> last: NAT = handle.buffers-1; bPos: NAT _ buffer.length; bLim: NAT = buffer.maxLength; bRem: NAT = bLim-bPos; rem: INT _ len-start; IF bRem < len THEN len _ bRem; charsMoved _ len; buffer.length _ bPos; IF handle.file = NIL THEN ERROR; start _ start + handle.offset; WHILE len > 0 DO buf: Buf _ NIL; delta: INT _ 0; pos: NAT _ delta; spos,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 _ buf.len-pos; IF len < chars THEN chars _ len; IF chars > 4 AND useByteBlt THEN <> [] _ PrincOpsUtils.ByteBlt[ to: [AddrOfChars[buffer], bPos, bPos+chars], from: [AddrOfChars[buf.text], pos, pos+chars]] ELSE <> FOR j: NAT IN [0..chars) DO buffer[bPos+j] _ buf.text[pos+j]; ENDLOOP; <> ReleaseLock[handle]; len _ len - chars; start _ start + chars; bPos _ bPos + chars; ENDLOOP; RETURN; }; ENDCASE => ERROR; }; RopeFileFinalizer: PROC = { <> DO ref: REF _ NIL; ref _ SafeStorage.FQNext[fq]; -- genuflect three times to the GC's conservative scanner WITH ref SELECT FROM rf: Handle => { <> lag: Handle _ dummyRopeFile; found: BOOL _ FALSE; IF rf = dummyRopeFile THEN LOOP; AcquireLock[dummyRopeFile]; AcquireLock[rf]; FOR each: Handle _ dummyRopeFile.next, each.next WHILE each # NIL DO IF each = rf THEN { <> lag.next _ each.next; found _ TRUE; EXIT; }; lag _ each; ENDLOOP; IF found AND rf.file # NIL THEN { <> ENABLE UNWIND => {ReleaseLock[rf]; ReleaseLock[dummyRopeFile]}; IO.Close[rf.file]; rf.file _ NIL; ropesOutstanding _ ropesOutstanding - 1; }; ReleaseLock[rf]; ReleaseLock[dummyRopeFile]; }; ENDCASE; ENDLOOP; }; FilesOnQueue: PROC RETURNS [nameList: LIST OF ROPE _ NIL] = { <> AcquireLock[dummyRopeFile]; FOR each: Handle _ dummyRopeFile.next, each.next WHILE each # NIL DO name: ROPE _ "NIL file!?!?!?!?"; stream: STREAM = each.file; IF stream # NIL THEN name _ FS.GetName[FS.OpenFileFromStream[stream]].fullFName; nameList _ CONS[name, nameList]; ENDLOOP; ReleaseLock[dummyRopeFile]; }; FinalizationProblem: SIGNAL = CODE; dummyRopeFile: Handle _ NIL; -- for synchronization ropesOutstanding: INT _ 0; -- protected by lock fq: SafeStorage.FinalizationQueue = SafeStorage.NewFQ[32]; -- for finalization TRUSTED { ok: BOOL _ TRUE; SafeStorage.EstablishFinalization[CODE[RopeFileRep], 1, fq ! SafeStorage.CantEstablishFinalization => {ok _ FALSE; CONTINUE}]; IF NOT ok THEN { ok _ TRUE; SafeStorage.ReEstablishFinalization[CODE[RopeFileRep], 1, fq ! SafeStorage.CantEstablishFinalization => {ok _ FALSE; CONTINUE}]; IF NOT ok THEN SIGNAL FinalizationProblem; }; dummyRopeFile _ NEW[RopeFileRep]; Process.Detach[FORK RopeFileFinalizer[]]; }; END.