<<-- RopeFromFileImpl.Mesa>> <<-- written by Bill Paxton, February 1981>> <<-- last edit by Paxton, November 12, 1982 3:17 pm>> <> DIRECTORY RopeFrom, RopeEdit, RopeEditingAlloc, RopeReader, Rope, RopeInline, RTStorageOps, File, Space, PropertyTypes, Directory, Inline, IO, RopeEditingBLT; RopeFromFileImpl: CEDAR MONITOR IMPORTS RopeEditingBLT, RopeEditingAlloc, RopeReader, Rope, RopeInline, RopeEdit, RopeFrom, Space, Inline, Directory, RTStorageOps, IO 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] = TRUSTED { 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 = TRUSTED { Directory.GetProperty[file: file, property: PropertyTypes.tByteLength, propertyValue: DESCRIPTOR[@fileLen, SIZE[Offset]]] }; startPage: Offset; startSkip, endSkip: NAT; numPages, numSpaces: Offset; start _ start+charsPerPage; -- to skip over leader page startPage _ start/charsPerPage; startSkip _ 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 _ numSpaces*charsPerSpace-length-startSkip; rope _ MakeRope[0,numSpaces]}; Stream: PUBLIC PROC [stream: IO.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 _ IO.GetBlock[self: stream, block: block, startIndex: 0, stopIndexPlusOne: 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 _ IO.GetLength[stream]; start _ IO.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 numSpaces: INT _ 0; -- number allocated freedSpaces: INT _ 0; GetSpace: ENTRY PROC RETURNS [space: Space.Handle] = TRUSTED { ENABLE UNWIND => NULL; IF fileSpace # Space.nullHandle THEN space _ fileSpace ELSE { -- need to allocate one space _ Space.Create[pagesPerSpace, Space.virtualMemory]; numSpaces _ numSpaces+1 }; fileSpace _ Space.nullHandle }; FreeSpace: ENTRY PROC [space: Space.Handle] = TRUSTED { ENABLE UNWIND => NULL; IF fileSpace = Space.nullHandle THEN Space.Unmap[fileSpace _ space] ELSE { Space.Delete[space]; freedSpaces _ freedSpaces+1 }}; <<-- Operation to get a space for file mapping>> collectibleSpaces: INT _ 0; GetArraySpace: PROC RETURNS [chars: REF CharsArray, space: Space.Handle] = TRUSTED { chars _ NARROW[RTStorageOps.NewCollectibleSubspace[pagesPerSpace,CODE[CharsArray]]]; collectibleSpaces _ collectibleSpaces+1; space _ Space.GetHandle[Space.PageFromLongPointer[LOOPHOLE[chars]]] }; <<-- ***** Initialization>> StartRopeFromFile: PUBLIC PROC = { }; END.