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 = { 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]]; }; 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 { 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. NRopeFileImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Russ Atkinson, March 13, 1985 6:42:16 pm PST Paul Rovner, December 14, 1983 11:31 am T Y P E S This boolean is here to enable speed trials this makes the rope include the formatting information splice this ropeFile onto the global list [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 Returns the file (if any) used by the given rope. [buffer: REF TEXT, data: REF, start: INT, len: INT] RETURNS [charsMoved: NAT]; Appends characters from the given rope (which need not be a RopeFile rope) onto the specified buffer. The move will stop at the end of the specified subrope OR end of the rope OR the end of the buffer. It is the user's responsibility to determine if enough characters have been moved. A BoundsFault will occur if start NOT IN [0..rope.Length[]]. Our special case 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 the right buffer, so move chars. The setup time is worth while to get better throughput The setup time probably dwarfs the following loop We will not touch the buffer until the next time around (if any). This procedure closes the files associated with rope files as part of finalizing the ropes in question. We have to be quite careful to lock things properly, which is why there is a dummyRopeFile. The dummyRopeFile is used to hold on to the ropes created by Create, and to synchronize insertinon of new rope files and deletion of old rope files. Remember: paranoia pays! note: always use the following order in acquiring/releasing locks splice out this file Only close the file if it is on the list AND has been opened. This makes us insensitive to multiple occurences of rope files on the finalization queue. Useful little crock for debugging Κ‹˜codešœ™Kšœ Οmœ1™Kšœ$˜$Kšœžœžœ˜1Kšžœ˜Kšžœ˜K˜—Kšœ žœ˜#K˜Kšœ˜K˜šžœžœžœž˜Kšœ žœ#žœžœ ˜GKšžœ˜—KšœA˜AKšœ)™)Kšœ˜Kšœ!˜!Kšœ˜Kšœ(˜(Kšœ'˜'Kšœ˜K˜K˜—šœ˜Kšœ&™&Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšžœžœžœžœ˜ Kšœ˜K˜šžœžœžœ ž˜K˜Kšœžœ˜šžœžœž˜šœ žœ˜#Kšœ(™(Kšœ˜—šœ ˜ Kšœ#™#Kšžœ˜—šžœ˜ Kšœ™Kšœžœ˜Kšœžœžœ ˜0Kšžœ˜ Kšœ žœ,˜7Kšžœ žœžœ˜0K˜K˜K˜——šžœžœ˜Kšœ™K˜K˜K˜—Kšœ˜Kšžœ˜Kšžœ˜—K˜Kšžœ˜ K˜K˜—šœžœ˜KšœM™MKšœžœ˜Kšœžœ˜Kšžœžœžœžœ˜ Kšœ˜šžœ ž˜Kšœ žœ˜Kšœžœ˜Kšœžœ˜Kšœžœ ˜Kšœžœ ˜Kšœžœ˜Kšœžœ˜Kšœ˜šžœžœžœ ž˜K˜Kšœ˜šžœžœž˜šœ žœ˜#Kšœ(™(Kšœ˜—šœ ˜ Kšœ#™#Kšžœ˜—šžœ˜ Kšœ™Kšœžœ˜Kšœžœžœ ˜/Kšžœ˜Kšœ žœ,˜7Kšžœ žœžœ˜0Kšœ˜K˜K˜——šžœžœ˜Kšœ™K˜K˜K˜—Kšžœ˜Kšžœ˜—KšœL™LKšœ ˜ Kšœžœ˜!Kšžœ žœ ˜ šžœžœžœ ž˜Kšœ˜Kšžœ˜—Kšœ@™@K˜Kšœ,™,šžœžœžœ ž˜Kšžœžœžœžœ˜(Kšžœ˜—Kšœ˜K˜Kšžœ˜—Kšžœžœ˜K˜K˜—š  œžœžœžœ žœ žœžœžœžœžœ˜eKšžœ˜"K˜K˜—š   œžœžœžœžœžœ˜?Kšžœ˜K˜K˜—š  œžœžœžœžœžœ žœžœ˜ZK™1šžœ žœž˜šœ˜šžœžœž˜˜ šžœžœž˜Kšœ žœžœ˜4Kšžœ˜——Kšžœ˜——Kšžœ˜—K˜K˜—š  œžœ žœžœžœžœžœžœžœ˜OKš žœžœ žœžœžœžœ˜:K˜K˜—•StartOfExpansion[]šœ&žœ˜/Kšœ žœžœžœ žœžœžœžœ™OKš œžžœžœmžœ!žœžœ™άšžœžœž˜šœ˜Kšœ™Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšœžœ ˜Kšœžœ ˜Kšžœ žœ ˜Kšœ˜Kšœ˜Kšžœžœžœžœ˜ Kšœ˜šžœ ž˜Kšœ žœ˜Kšœžœ˜Kšœžœ ˜Kšœ žœ˜Kšœ˜šžœžœžœ ž˜K˜Kšœ˜šžœžœž˜šœ žœ˜#Kšœ(™(Kšœ˜—šœ ˜ Kšœ#™#Kšžœ˜—šžœ˜ Kšœ™Kšœžœ˜Kšœžœžœ ˜/Kšžœ˜Kšœ žœ,˜7Kšžœ žœžœ˜0Kšœ˜K˜K˜——šžœžœ˜Kšœ™K˜K˜K˜—Kšžœ˜Kšžœ˜—Kšœ<™