-- RopeFromFileImpl.Mesa -- written by Bill Paxton, February 1981 -- last edit by Bill Paxton, 17-Jan-82 15:52:46 DIRECTORY RopeFrom, RopeEdit, RopeEditingAlloc, RopeReader, Rope, RopeInline, RTStorageOps, File, Space, Directory, Inline, IOStream, RopeEditingBLT; RopeFromFileImpl: MONITOR IMPORTS RopeEditingBLT, RopeEditingAlloc, RopeReader, Rope, RopeInline, RopeEdit, RopeFrom, Space, Inline, Directory, RTStorageOps, IOStream EXPORTS RopeFrom SHARES Rope = BEGIN OPEN fileI:File, RopeFrom, RopeInline; pagesPerSpace: NAT = RopeReader.pagesPerArray; charsPerPage: NAT = RopeReader.charsPerPage; charsPerArray: NAT = RopeReader.charsPerArray; charsPerSpace: NAT = charsPerArray; spaceMin: NAT = charsPerSpace/2; -- put in shared array if less than 1/2 swapUnitSize: NAT = 16; -- based on Paul's advice CharsArray: TYPE = RopeReader.CharsArray; Chars: TYPE = REF CharsArray; -- ***** Operations File: PUBLIC PROC [ file: fileI.Capability, start: Offset _ 0, length: Offset _ MaxLen, -- defaults to rest of file activate: BOOLEAN _ FALSE, fileLen: Offset _ MaxLen, okToMapFile: BOOLEAN _ TRUE] RETURNS [rope: ROPE] = { MakeRope: PROC [startSpace, numToDo: Offset] RETURNS [rope: ROPE] = { chars: REF RopeReader.CharsArray; fileSpace: Space.Handle; len: NAT _ charsPerSpace; firstSpace: BOOLEAN _ FALSE; IF numToDo = 0 THEN RETURN [NIL]; IF numToDo > 1 THEN { -- split into balanced parts half: Offset _ numToDo/2; front: ROPE _ MakeRope[startSpace, half]; back: ROPE _ MakeRope[startSpace+half, numToDo-half]; RETURN [Rope.Concat[front,back]] }; IF startSpace+1 = numSpaces THEN -- this is the last space len _ len - endSkip; IF startSpace = 0 THEN { -- this is the first space for the file len _ len - startSkip; firstSpace _ TRUE }; IF ~okToMapFile OR len < spaceMin THEN { -- don't make a separate space for it start, nwords: NAT; charOffset, skipWords: NAT _ 0; base: ROPE; IF firstSpace THEN { -- make skipWords*2+charOffset=startSkip charOffset _ startSkip MOD 2; skipWords _ startSkip/2 }; nwords _ (len+charOffset+1)/2; -- number to copy from file [chars, start, base] _ RopeEditingAlloc.AllocWords[nwords]; fileSpace _ GetSpace[]; Space.Map[fileSpace, Space.WindowOrigin[file, startPage+startSpace*pagesPerSpace]]; Space.Activate[fileSpace]; Inline.LongCOPY[from: Space.LongPointer[fileSpace]+skipWords, nwords: nwords, to: LOOPHOLE[chars,LONG POINTER]+start/2]; FreeSpace[fileSpace]; RETURN [qZone.NEW[Tsubstr _ [node[substr[len,base,start+charOffset,RopeEdit.Depth[base]+1]]]]] }; [chars,fileSpace] _ GetArraySpace[]; Space.Map[fileSpace,Space.WindowOrigin[file, startPage+startSpace*pagesPerSpace]]; IF swapUnitSize > pagesPerSpace THEN Space.CreateUniformSwapUnits[swapUnitSize, fileSpace]; IF activate THEN Space.Activate[fileSpace]; rope _ RopeReader.CharsRope[chars,fileSpace]; IF len < charsPerSpace THEN rope _ qZone.NEW[Tsubstr _ [node[substr[len, rope, IF firstSpace THEN startSkip ELSE 0, 1+ RopeEdit.Depth[rope]]]]]}; GetFileLen: PROC = { name: STRING _ [128]; -- don't really want this, but GetProps needs it [,,,fileLen,] _ Directory.GetProps[file,name] }; startPage: Offset; startSkip, endSkip: NAT; numPages, numSpaces: Offset; start _ start+charsPerPage; -- to skip over leader page startPage _ start/charsPerPage; startSkip _ LOOPHOLE[Inline.LowHalf[start MOD charsPerPage]]; IF fileLen = MaxLen THEN GetFileLen; fileLen _ fileLen+charsPerPage; -- to compensate for leader page IF fileLen <= start THEN RETURN [NIL]; length _ MIN[length,fileLen-start]; IF length = 0 THEN RETURN[NIL]; numPages _ (start+length+charsPerPage-1)/charsPerPage-startPage; numSpaces _ (numPages+pagesPerSpace-1)/pagesPerSpace; endSkip _ LOOPHOLE[Inline.LowHalf[numSpaces*charsPerSpace-length-startSkip]]; rope _ MakeRope[0,numSpaces]}; Stream: PUBLIC PROC [stream: IOStream.Handle, length: Offset _ MaxLen] RETURNS [rope: ROPE] = { MakeRope: PROC [len: Offset] RETURNS [ROPE] = { chars: REF RopeReader.CharsArray; start, nchars, charloc: NAT; base: ROPE; IF len = 0 THEN RETURN [NIL]; IF len > charsPerSpace THEN { -- split into balanced parts half: Offset _ MAX[len/2,charsPerSpace]; front: ROPE _ MakeRope[half]; back: ROPE _ MakeRope[len-half]; RETURN [Rope.Concat[front,back]] }; nchars _ Short[len]; [chars, start, base] _ RopeEditingAlloc.AllocWords[(nchars+1)/2]; charloc _ start; UNTIL nchars = 0 DO nread: NAT; block.length _ 0; nread _ IOStream.GetBlock[stream,block,nchars]; RopeEditingBLT.StringToArrayBlt[block,0,chars,charloc,nread]; nchars _ nchars-nread; charloc _ charloc+nread; ENDLOOP; RETURN [qZone.NEW[Tsubstr _ [node[substr[len,base,start,1+RopeEdit.Depth[base]]]]]] }; start,fileLen: Offset; block: REF TEXT; fileLen _ IOStream.GetLength[stream]; start _ IOStream.GetIndex[stream]; IF fileLen <= start THEN RETURN [NIL]; length _ MIN[length,fileLen-start]; IF length = 0 THEN RETURN [NIL]; block _ RopeEditingAlloc.GetBlock[]; rope _ MakeRope[length]; RopeEditingAlloc.FreeBlock[block] }; -- ***** These two routines are the only monitored procedures -- ***** They provide a shared space for reading files fileSpace: Space.Handle _ Space.nullHandle; -- shared file space GetSpace: ENTRY PROC RETURNS [space: Space.Handle] = { space _ IF fileSpace # Space.nullHandle THEN fileSpace ELSE Space.Create[pagesPerSpace, Space.virtualMemory]; fileSpace _ Space.nullHandle }; FreeSpace: ENTRY PROC [space: Space.Handle] = { IF fileSpace = Space.nullHandle THEN Space.Unmap[fileSpace _ space] ELSE Space.Delete[space] }; -- Operation to get a space for file mapping GetArraySpace: PROC RETURNS [chars: REF CharsArray, space: Space.Handle] = { chars _ NARROW[RTStorageOps.NewCollectibleSubspace[pagesPerSpace,CODE[CharsArray]]]; space _ Space.GetHandle[Space.PageFromLongPointer[LOOPHOLE[chars]]] }; -- ***** Initialization StartRopeFromFile: PUBLIC PROC = { }; END.